From 7a183f20aad1e4668c3b981454c0a35edf577cb3 Mon Sep 17 00:00:00 2001 From: bridiver Date: Tue, 18 Oct 2016 14:46:05 -0700 Subject: [PATCH] chrome 54 wip --- .gitignore | 6 +- .gitmodules | 9 - .node-version | 2 +- BUILD.gn | 378 ++++ DEPS | 69 + VERSION | 4 + app/linux/BUILD.gn | 84 + app/mac/BUILD.gn | 347 ++++ app/mac/brave_exe_main_mac.c | 90 + app/mac/resources/MainMenu.xib | 180 ++ .../mac/resources/app-Info.plist | 12 +- app/mac/resources/app.saves | 11 + app/mac/resources/framework-Info.plist | 14 + .../mac/resources/helper-Info.plist | 10 +- app/resources/beta/brave.icns | Bin 0 -> 239702 bytes app/resources/beta/brave_linux.png | Bin 0 -> 89236 bytes app/resources/beta/brave_win.ico | Bin 0 -> 85344 bytes app/resources/dev/brave.icns | Bin 0 -> 200209 bytes app/resources/dev/brave_linux.png | Bin 0 -> 98576 bytes app/resources/dev/brave_win.ico | Bin 0 -> 89003 bytes app/resources/nightly/brave.icns | Bin 0 -> 244918 bytes app/resources/nightly/brave_linux.png | Bin 0 -> 86060 bytes app/resources/nightly/brave_win.ico | Bin 0 -> 84653 bytes app/resources/release/brave.icns | Bin 0 -> 181413 bytes app/resources/release/brave_linux.png | Bin 0 -> 55757 bytes app/resources/release/brave_win.ico | Bin 0 -> 68589 bytes app/win/BUILD.gn | 17 + atom/app/BUILD.gn | 42 + atom/app/atom_content_client.cc | 104 +- atom/app/atom_content_client.h | 7 +- atom/app/atom_library_main.mm | 30 +- atom/app/atom_main.cc | 8 +- atom/app/atom_main_delegate.cc | 410 +++- atom/app/atom_main_delegate.h | 16 +- atom/app/atom_main_delegate_mac.mm | 8 +- atom/app/node_main.cc | 8 +- atom/atom_resources.grd | 17 +- atom/browser/BUILD.gn | 332 +++ atom/browser/api/atom_api_app.cc | 133 +- atom/browser/api/atom_api_app.h | 21 +- atom/browser/api/atom_api_autofill.cc | 10 +- atom/browser/api/atom_api_autofill.h | 5 +- atom/browser/api/atom_api_download_item.cc | 4 +- atom/browser/api/atom_api_extension.cc | 94 +- atom/browser/api/atom_api_extension.h | 6 + atom/browser/api/atom_api_importer.cc | 2 - atom/browser/api/atom_api_importer.h | 1 + atom/browser/api/atom_api_menu.cc | 3 +- atom/browser/api/atom_api_menu_mac.mm | 2 +- .../api/atom_api_power_save_blocker.cc | 7 +- atom/browser/api/atom_api_session.cc | 13 +- atom/browser/api/atom_api_tray.cc | 3 +- atom/browser/api/atom_api_user_prefs.cc | 64 +- atom/browser/api/atom_api_user_prefs.h | 9 +- atom/browser/api/atom_api_web_contents.cc | 474 ++--- atom/browser/api/atom_api_web_contents.h | 54 +- atom/browser/api/atom_api_web_request.cc | 23 +- atom/browser/api/atom_api_web_request.h | 5 + atom/browser/api/atom_api_web_view_manager.cc | 45 - atom/browser/api/atom_api_window.cc | 34 +- atom/browser/api/atom_api_window.h | 9 + atom/browser/atom_access_token_store.cc | 10 +- atom/browser/atom_access_token_store.h | 4 +- atom/browser/atom_browser_client.cc | 128 +- atom/browser/atom_browser_client.h | 25 +- atom/browser/atom_browser_context.cc | 28 +- atom/browser/atom_browser_context.h | 7 +- atom/browser/atom_browser_main_parts.cc | 90 +- atom/browser/atom_browser_main_parts.h | 3 +- atom/browser/atom_permission_manager.cc | 2 + atom/browser/atom_permission_manager.h | 4 +- .../atom_security_state_model_client.cc | 26 +- atom/browser/autofill/atom_autofill_client.cc | 30 +- atom/browser/autofill/atom_autofill_client.h | 8 + .../autofill/personal_data_manager_factory.cc | 3 +- atom/browser/browser.cc | 13 +- ...browser_context_keyed_service_factories.cc | 11 +- atom/browser/common_web_contents_delegate.cc | 165 +- .../common_web_contents_delegate_mac.mm | 5 + .../api/atom_extensions_api_client.cc | 17 +- .../api/atom_extensions_api_client.h | 5 +- .../api/messaging/extension_message_port.cc | 310 --- .../api/messaging/extension_message_port.h | 121 -- .../messaging/message_property_provider.cc | 111 -- .../api/messaging/message_property_provider.h | 63 - .../api/messaging/message_service.cc | 907 --------- .../api/messaging/message_service.h | 323 --- .../atom_browser_client_extensions_part.cc | 79 +- .../atom_browser_client_extensions_part.h | 2 + .../extensions/atom_extension_system.cc | 27 +- .../extensions/atom_extension_system.h | 17 +- .../atom_extension_system_factory.cc | 21 +- .../atom_extensions_browser_client.cc | 205 +- .../atom_extensions_network_delegate.cc | 3 +- .../atom_extensions_network_delegate.h | 6 +- .../atom_process_manager_delegate.cc | 34 +- .../atom_process_manager_delegate.h | 8 +- .../extensions/shared_user_script_master.cc | 26 +- .../extensions/shared_user_script_master.h | 2 +- atom/browser/extensions/tab_helper.cc | 21 +- atom/browser/extensions/tab_helper.h | 3 +- atom/browser/importer/profile_writer.cc | 28 - atom/browser/lib/bluetooth_chooser.cc | 29 +- atom/browser/lib/bluetooth_chooser.h | 10 +- atom/browser/mac/atom_application.mm | 10 +- atom/browser/native_window.cc | 26 +- atom/browser/native_window.h | 17 +- atom/browser/native_window_mac.h | 16 +- atom/browser/native_window_mac.mm | 59 +- atom/browser/native_window_views.h | 8 +- atom/browser/net/atom_network_delegate.cc | 18 +- .../net/atom_url_request_job_factory.cc | 10 +- atom/browser/node_debugger.cc | 12 +- atom/browser/osr/osr_output_device.cc | 88 - atom/browser/osr/osr_output_device.h | 44 - .../osr/osr_render_widget_host_view.cc | 913 --------- .../browser/osr/osr_render_widget_host_view.h | 249 --- .../osr/osr_render_widget_host_view_mac.mm | 163 -- atom/browser/osr/osr_web_contents_view.cc | 139 -- atom/browser/osr/osr_web_contents_view.h | 91 - atom/browser/osr/osr_web_contents_view_mac.mm | 54 - atom/browser/resources/mac/electron.icns | Bin 154411 -> 0 bytes atom/browser/ui/accelerator_util.cc | 2 +- atom/browser/ui/atom_menu_model.h | 2 +- .../browser/web_contents_permission_helper.cc | 3 +- atom/browser/web_contents_preferences.cc | 68 - atom/browser/web_contents_preferences.h | 24 - atom/browser/web_view_guest_delegate.cc | 239 --- atom/browser/web_view_guest_delegate.h | 124 -- atom/browser/web_view_manager.cc | 87 - atom/browser/web_view_manager.h | 70 - atom/common/BUILD.gn | 208 ++ atom/common/api/atom_bindings.cc | 4 +- atom/common/api/resources/_api_features.json | 94 - .../api/resources/_permission_features.json | 18 - atom/common/api/resources/app_bindings.js | 10 +- atom/common/api/resources/event_emitter.js | 8 +- atom/common/api/resources/ipc.json | 72 - atom/common/api/resources/ipc_bindings.js | 4 +- atom/common/api/resources/ipc_utils.js | 29 +- .../resources/tab_view_internal_bindings.js | 3 + atom/common/api/resources/web_frame.json | 23 - .../api/resources/web_frame_bindings.js | 29 +- .../api/resources/web_view_api_bindings.js | 210 ++ .../resources/web_view_internal_bindings.js | 42 + atom/common/asar/archive.cc | 3 + atom/common/chrome_version.h | 14 - atom/common/common_message_generator.cc | 7 +- atom/common/common_message_generator.h | 16 +- .../crash_reporter/crash_reporter_mac.mm | 2 +- .../crash_reporter/win/crash_service_main.cc | 2 +- .../extensions/atom_extensions_client.cc | 79 +- .../extensions/atom_extensions_client.h | 9 +- .../permissions/chrome_api_permissions.cc | 62 - .../permissions/chrome_api_permissions.h | 27 - atom/common/javascript_bindings.cc | 64 +- atom/common/javascript_bindings.h | 16 + .../native_mate_converters/blink_converter.cc | 22 +- atom/common/node_bindings.cc | 21 +- atom/common/options_switches.cc | 25 - atom/common/options_switches.h | 12 - atom/common/resources/mac/Info.plist | 16 - atom/common/resources/mac/MainMenu.xib | 180 -- atom/renderer/api/atom_api_renderer_ipc.cc | 74 - .../api/atom_api_spell_check_client.cc | 13 - .../api/atom_api_spell_check_client.h | 7 +- atom/renderer/api/atom_api_web_frame.cc | 301 --- atom/renderer/api/atom_api_web_frame.h | 97 - atom/renderer/atom_render_view_observer.cc | 109 +- atom/renderer/atom_render_view_observer.h | 14 +- atom/renderer/atom_renderer_client.cc | 223 +-- atom/renderer/atom_renderer_client.h | 20 +- .../atom_extensions_dispatcher_delegate.cc | 144 +- .../atom_extensions_dispatcher_delegate.h | 1 - .../atom_extensions_renderer_client.cc | 51 +- .../atom_extensions_renderer_client.h | 10 +- atom/renderer/guest_view_container.cc | 64 - atom/renderer/guest_view_container.h | 45 - atom/renderer/node_array_buffer_bridge.cc | 66 - atom/renderer/node_array_buffer_bridge.h | 15 - atom/renderer/preferences_manager.cc | 34 - atom/renderer/preferences_manager.h | 35 - atom/utility/atom_content_utility_client.cc | 101 +- atom/utility/atom_content_utility_client.h | 10 +- atom/utility/importer/chrome_importer.cc | 39 - autofill.gypi | 65 - brave/brave_resources.grd | 19 + brave/brave_strings.grd | 22 + brave/browser/BUILD.gn | 119 ++ .../api/brave_api_component_updater.cc | 16 +- brave/browser/brave_browser_context.cc | 223 ++- brave/browser/brave_browser_context.h | 48 +- brave/browser/brave_content_browser_client.cc | 390 +++- brave/browser/brave_content_browser_client.h | 40 +- brave/browser/brave_permission_manager.cc | 5 +- brave/browser/brave_permission_manager.h | 3 +- brave/browser/component_updater/BUILD.gn | 19 + .../brave_component_updater_configurator.cc | 21 +- .../extension_installer_traits.cc | 12 +- .../extension_installer_traits.h | 3 +- .../widevine_cdm_component_installer.cc | 9 +- brave/browser/extensions/BUILD.gn | 31 + .../tab_view/tab_view_internal_api.cc | 38 + .../tab_view/tab_view_internal_api.h | 41 + .../brave_guest_view_manager_delegate.cc | 41 + .../brave_guest_view_manager_delegate.h | 35 + .../guest_view/tab_view/tab_view_guest.cc | 270 +++ .../guest_view/tab_view/tab_view_guest.h | 88 + .../platform_notification_service_impl.cc | 3 +- .../platform_notification_service_impl.h | 1 + brave/browser/ui/simple_message_box.cc | 18 + brave/common/brave_paths.cc | 43 + brave/common/brave_paths.h | 21 + brave/common/extensions/api/BUILD.gn | 79 + .../common/extensions/api/_api_features.json | 56 + .../extensions/api/_permission_features.json | 2 + brave/common/extensions/api/empty.json | 6 + brave/common/extensions/api/ipc.json | 38 + brave/common/extensions/api/remote.json | 13 + brave/common/extensions/api/schemas.gni | 21 + .../extensions/api/tab_view_internal.json | 35 + brave/common/extensions/api/web_frame.json | 61 + .../renderer/brave_content_renderer_client.cc | 36 +- .../renderer/extensions/web_frame_bindings.cc | 123 ++ .../renderer/extensions/web_frame_bindings.h | 4 + .../brave_print_web_view_helper_delegate.cc | 80 + .../brave_print_web_view_helper_delegate.h | 25 + .../ipc_renderer_custom_bindings.js | 2 + build/BUILD.gn | 65 + build/node/BUILD.gn | 58 + common.gypi => build/node/node.gypi | 246 +-- build/node/v8.gypi | 84 + chromium_src/BUILD.gn | 270 +++ .../chrome/browser/browser_process.cc | 51 +- chromium_src/chrome/browser/browser_process.h | 58 +- .../host_content_settings_map_factory.cc | 90 + .../host_content_settings_map_factory.h | 37 + .../browser/extensions/extension_tab_util.cc | 257 +++ .../browser/extensions/extension_tab_util.h | 185 ++ .../browser/extensions/extension_util.cc | 250 +++ .../browser/extensions/extension_util.h | 132 ++ .../external_process_importer_client.cc | 29 +- .../external_process_importer_client.h | 3 - .../importer/in_process_importer_bridge.cc | 7 - .../importer/in_process_importer_bridge.h | 2 - .../chrome/browser/importer/profile_writer.h | 2 - .../media/media_capture_devices_dispatcher.cc | 19 + .../media/media_capture_devices_dispatcher.h | 44 + .../browser/printing/pdf_to_emf_converter.cc | 496 ----- .../browser/printing/pdf_to_emf_converter.h | 49 - .../chrome/browser/printing/print_job.cc | 463 ----- .../chrome/browser/printing/print_job.h | 225 --- .../browser/printing/print_job_manager.cc | 164 -- .../browser/printing/print_job_manager.h | 100 - .../browser/printing/print_job_worker.cc | 404 ---- .../browser/printing/print_job_worker.h | 160 -- .../printing/print_job_worker_owner.cc | 27 - .../browser/printing/print_job_worker_owner.h | 66 - .../printing/print_preview_message_handler.cc | 140 -- .../printing/print_preview_message_handler.h | 59 - .../printing/print_view_manager_base.cc | 499 ----- .../printing/print_view_manager_base.h | 163 -- .../printing/print_view_manager_basic.cc | 48 - .../printing/print_view_manager_basic.h | 57 - .../printing/print_view_manager_observer.h | 23 - .../chrome/browser/printing/printer_query.cc | 128 -- .../chrome/browser/printing/printer_query.h | 99 - .../printing/printing_message_filter.cc | 424 ---- .../printing/printing_message_filter.h | 124 -- .../printing_ui_web_contents_observer.cc | 19 - .../printing_ui_web_contents_observer.h | 24 - .../chrome/browser/process_singleton_posix.cc | 5 +- .../browser/profiles/incognito_helpers.cc | 5 +- .../chrome/browser/profiles/profile.cc | 78 + .../chrome/browser/profiles/profile.h | 306 +-- .../browser/profiles/profile_manager.cc | 304 +++ .../chrome/browser/profiles/profile_manager.h | 164 ++ .../chrome/browser/profiles/profiles_state.cc | 13 + .../chrome/browser/profiles/profiles_state.h | 26 + .../chrome_extension_message_filter.cc | 285 --- .../chrome_extension_message_filter.h | 130 -- .../chrome_browser_pepper_host_factory.cc | 93 - .../chrome_browser_pepper_host_factory.h | 38 - .../renderer_host/pepper/monitor_finder_mac.h | 52 - .../pepper/monitor_finder_mac.mm | 62 - .../pepper/pepper_broker_message_filter.cc | 53 - .../pepper/pepper_broker_message_filter.h | 51 - .../pepper/pepper_flash_browser_host.cc | 113 -- .../pepper/pepper_flash_browser_host.h | 59 - .../pepper_flash_clipboard_message_filter.cc | 379 ---- .../pepper_flash_clipboard_message_filter.h | 76 - .../pepper/pepper_flash_drm_host.cc | 217 -- .../pepper/pepper_flash_drm_host.h | 55 - ...per_isolated_file_system_message_filter.cc | 110 - ...pper_isolated_file_system_message_filter.h | 76 - .../pepper/widevine_cdm_message_filter.cc | 70 - .../pepper/widevine_cdm_message_filter.h | 49 - .../chrome/browser/speech/tts_controller.h | 343 ---- .../browser/speech/tts_controller_impl.cc | 463 ----- .../browser/speech/tts_controller_impl.h | 104 - .../chrome/browser/speech/tts_linux.cc | 357 ---- chromium_src/chrome/browser/speech/tts_mac.mm | 352 ---- .../browser/speech/tts_message_filter.cc | 176 -- .../browser/speech/tts_message_filter.h | 64 - .../chrome/browser/speech/tts_platform.cc | 28 - .../chrome/browser/speech/tts_platform.h | 81 - chromium_src/chrome/browser/speech/tts_win.cc | 259 --- .../chrome/browser/ui/browser_otr_state.cc | 13 + .../browser/ui/cocoa/color_chooser_mac.mm | 161 -- .../webui/chrome_web_ui_controller_factory.cc | 192 ++ .../webui/chrome_web_ui_controller_factory.h | 40 + .../ui/zoom/chrome_zoom_level_prefs.cc | 205 -- .../browser/ui/zoom/chrome_zoom_level_prefs.h | 81 - .../chrome/common/chrome_constants.cc | 74 - chromium_src/chrome/common/chrome_constants.h | 58 - chromium_src/chrome/common/chrome_paths.cc | 613 ------ chromium_src/chrome/common/chrome_paths.h | 152 -- .../chrome/common/chrome_paths_internal.h | 118 -- .../chrome/common/chrome_paths_linux.cc | 146 -- .../chrome/common/chrome_paths_mac.mm | 251 --- .../chrome/common/chrome_paths_win.cc | 106 - .../chrome/common/chrome_utility_messages.h | 169 -- .../chrome/common/cloud_devices_urls.cc | 43 - .../chrome/common/cloud_devices_urls.h | 21 - .../common/common_param_traits_macros.h | 16 - .../content_scripts_handler.cc | 477 ----- .../content_scripts_handler.h | 57 - .../chrome/common/importer/importer_bridge.h | 2 - .../profile_import_process_messages.h | 6 - chromium_src/chrome/common/ini_parser.cc | 64 - chromium_src/chrome/common/ini_parser.h | 64 - chromium_src/chrome/common/pref_names.cc | 41 - chromium_src/chrome/common/pref_names.h | 22 - chromium_src/chrome/common/print_messages.cc | 81 - chromium_src/chrome/common/print_messages.h | 381 ---- chromium_src/chrome/common/tts_messages.h | 69 - .../chrome/common/tts_utterance_request.cc | 30 - .../chrome/common/tts_utterance_request.h | 44 - .../chrome/common/widevine_cdm_constants.cc | 13 - .../chrome/common/widevine_cdm_constants.h | 16 - .../chrome/common/widevine_cdm_messages.h | 31 - .../extensions/tabs_custom_bindings.cc | 57 - .../extensions/tabs_custom_bindings.h | 24 - .../renderer/media/chrome_key_systems.cc | 278 --- .../renderer/media/chrome_key_systems.h | 20 - .../chrome_renderer_pepper_host_factory.cc | 83 - .../chrome_renderer_pepper_host_factory.h | 34 - .../pepper/pepper_flash_font_file_host.cc | 106 - .../pepper/pepper_flash_font_file_host.h | 56 - .../pepper/pepper_flash_fullscreen_host.cc | 43 - .../pepper/pepper_flash_fullscreen_host.h | 35 - .../renderer/pepper/pepper_flash_menu_host.cc | 202 -- .../renderer/pepper/pepper_flash_menu_host.h | 69 - .../pepper/pepper_flash_renderer_host.cc | 372 ---- .../pepper/pepper_flash_renderer_host.h | 68 - .../chrome/renderer/pepper/pepper_helper.cc | 30 - .../chrome/renderer/pepper/pepper_helper.h | 26 - .../pepper_shared_memory_message_filter.cc | 64 - .../pepper_shared_memory_message_filter.h | 46 - .../printing/print_web_view_helper.cc | 1454 -------------- .../renderer/printing/print_web_view_helper.h | 392 ---- .../printing/print_web_view_helper_linux.cc | 144 -- .../printing/print_web_view_helper_mac.mm | 130 -- .../printing/print_web_view_helper_pdf_win.cc | 219 -- .../spellchecker/spellcheck_worditerator.cc | 421 ---- .../spellchecker/spellcheck_worditerator.h | 175 -- .../chrome/renderer/tts_dispatcher.cc | 199 -- chromium_src/chrome/renderer/tts_dispatcher.h | 76 - .../external_process_importer_bridge.cc | 25 - .../external_process_importer_bridge.h | 2 - .../utility/importer/firefox_importer.cc | 51 +- .../utility/importer/firefox_importer.h | 1 - .../chrome/utility/importer/nss_decryptor.cc | 382 ---- .../chrome/utility/importer/nss_decryptor.h | 20 - .../utility/importer/nss_decryptor_mac.h | 178 -- .../utility/importer/nss_decryptor_mac.mm | 71 - .../importer/nss_decryptor_system_nss.cc | 273 --- .../importer/nss_decryptor_system_nss.h | 78 - .../utility/importer/nss_decryptor_win.cc | 177 -- .../utility/importer/nss_decryptor_win.h | 193 -- .../components/sessions/core/session_id.cc | 11 - .../components/sessions/core/session_id.h | 28 - .../sessions/core/sessions_export.h | 29 - chromium_src/grit/generated_resources.h | 0 component_updater.gypi | 49 - default_app/BUILD.gn | 23 + default_app/default_app.js | 93 +- default_app/index.html | 217 +- default_app/main.js | 2 - electron.gyp | 691 ------- extensions.gyp | 120 -- extensions.gypi | 178 -- filenames.gypi | 638 ------ importer.gypi | 167 -- lib/BUILD.gn | 92 + lib/browser/api/browser-window.js | 13 +- lib/browser/api/exports/electron.js | 12 +- lib/browser/api/extensions.js | 3 +- lib/browser/api/web-contents.js | 63 +- lib/browser/chrome-extension.js | 428 ---- lib/browser/desktop-capturer.js | 77 - lib/browser/guest-view-manager.js | 269 +-- lib/browser/guest-window-manager.js | 148 -- lib/browser/init.js | 9 - lib/browser/objects-registry.js | 4 + lib/browser/rpc-server.js | 41 +- lib/common/api/callbacks-registry.js | 19 +- lib/common/api/exports/electron.js | 12 +- lib/common/api/is-promise.js | 8 +- lib/electron_api_resources.grd | 22 + lib/renderer/api/desktop-capturer.js | 47 - lib/renderer/api/exports/electron.js | 38 - lib/renderer/api/remote.js | 78 +- lib/renderer/chrome-api.js | 184 -- lib/renderer/content-scripts-injector.js | 61 - lib/renderer/extensions/event.js | 24 - lib/renderer/extensions/i18n.js | 84 - lib/renderer/extensions/storage.js | 67 - lib/renderer/extensions/web-navigation.js | 21 - lib/renderer/init.js | 138 -- lib/renderer/inspector.js | 114 -- lib/renderer/override.js | 228 --- lib/renderer/web-view/guest-view-internal.js | 57 +- lib/renderer/web-view/web-view-attributes.js | 319 --- lib/renderer/web-view/web-view-constants.js | 34 - lib/renderer/web-view/web-view.js | 533 ----- lib/util/buffer.js | 1774 +++++++++++++++++ resource_ids | 11 +- script/bootstrap.py | 60 +- script/create-dist.py | 8 +- toolchain.gypi | 264 --- .../node_gyp_build.py | 0 vendor/brightray | 1 - vendor/brightray/.gitattributes | 2 + vendor/brightray/.gitignore | 17 + vendor/brightray/BUILD.gn | 176 ++ vendor/brightray/LICENSE | 19 + vendor/brightray/LICENSE-CHROMIUM | 27 + vendor/brightray/README.md | 44 + vendor/brightray/browser/brightray_paths.h | 45 + vendor/brightray/browser/browser_client.cc | 98 + vendor/brightray/browser/browser_client.h | 68 + vendor/brightray/browser/browser_context.cc | 215 ++ vendor/brightray/browser/browser_context.h | 138 ++ .../brightray/browser/browser_main_parts.cc | 266 +++ vendor/brightray/browser/browser_main_parts.h | 66 + .../browser/browser_main_parts_mac.mm | 22 + .../devtools_contents_resizing_strategy.cc | 53 + .../devtools_contents_resizing_strategy.h | 48 + .../devtools_embedder_message_dispatcher.cc | 208 ++ .../devtools_embedder_message_dispatcher.h | 97 + .../browser/devtools_file_system_indexer.cc | 490 +++++ .../browser/devtools_file_system_indexer.h | 115 ++ .../browser/devtools_manager_delegate.cc | 173 ++ .../browser/devtools_manager_delegate.h | 44 + vendor/brightray/browser/devtools_ui.cc | 129 ++ vendor/brightray/browser/devtools_ui.h | 27 + .../browser/inspectable_web_contents.cc | 18 + .../browser/inspectable_web_contents.h | 51 + .../inspectable_web_contents_delegate.h | 35 + .../browser/inspectable_web_contents_impl.cc | 769 +++++++ .../browser/inspectable_web_contents_impl.h | 200 ++ .../browser/inspectable_web_contents_view.h | 59 + .../inspectable_web_contents_view_delegate.cc | 10 + .../inspectable_web_contents_view_delegate.h | 28 + .../inspectable_web_contents_view_mac.h | 45 + .../inspectable_web_contents_view_mac.mm | 61 + .../browser/linux/libnotify_loader.cc | 132 ++ .../browser/linux/libnotify_loader.h | 44 + .../browser/linux/libnotify_notification.cc | 165 ++ .../browser/linux/libnotify_notification.h | 44 + .../linux/notification_presenter_linux.cc | 25 + .../linux/notification_presenter_linux.h | 24 + .../brightray/browser/mac/bry_application.h | 7 + .../brightray/browser/mac/bry_application.mm | 19 + .../mac/bry_inspectable_web_contents_view.h | 36 + .../mac/bry_inspectable_web_contents_view.mm | 260 +++ .../browser/mac/cocoa_notification.h | 42 + .../browser/mac/cocoa_notification.mm | 68 + .../browser/mac/event_dispatching_window.h | 19 + .../browser/mac/event_dispatching_window.mm | 34 + .../mac/notification_center_delegate.h | 22 + .../mac/notification_center_delegate.mm | 41 + .../browser/mac/notification_presenter_mac.h | 33 + .../browser/mac/notification_presenter_mac.mm | 38 + .../media/media_capture_devices_dispatcher.cc | 158 ++ .../media/media_capture_devices_dispatcher.h | 84 + .../media/media_stream_devices_controller.cc | 200 ++ .../media/media_stream_devices_controller.h | 47 + .../net/devtools_network_conditions.cc | 35 + .../browser/net/devtools_network_conditions.h | 43 + .../net/devtools_network_controller.cc | 80 + .../browser/net/devtools_network_controller.h | 38 + .../net/devtools_network_controller_handle.cc | 60 + .../net/devtools_network_controller_handle.h | 44 + .../net/devtools_network_interceptor.cc | 288 +++ .../net/devtools_network_interceptor.h | 107 + .../net/devtools_network_protocol_handler.cc | 171 ++ .../net/devtools_network_protocol_handler.h | 48 + .../net/devtools_network_transaction.cc | 303 +++ .../net/devtools_network_transaction.h | 109 + .../devtools_network_transaction_factory.cc | 51 + .../devtools_network_transaction_factory.h | 38 + .../devtools_network_upload_data_stream.cc | 96 + .../net/devtools_network_upload_data_stream.h | 51 + vendor/brightray/browser/net_log.cc | 63 + vendor/brightray/browser/net_log.h | 30 + vendor/brightray/browser/network_delegate.cc | 151 ++ vendor/brightray/browser/network_delegate.h | 81 + vendor/brightray/browser/notification.cc | 42 + vendor/brightray/browser/notification.h | 70 + .../brightray/browser/notification_delegate.h | 23 + .../browser/notification_delegate_adapter.cc | 33 + .../browser/notification_delegate_adapter.h | 38 + .../browser/notification_presenter.cc | 31 + .../browser/notification_presenter.h | 43 + .../brightray/browser/permission_manager.cc | 90 + vendor/brightray/browser/permission_manager.h | 57 + .../browser/platform_notification_service.cc | 110 + .../browser/platform_notification_service.h | 60 + .../browser/special_storage_policy.cc | 43 + .../browser/special_storage_policy.h | 31 + .../browser/url_request_context_getter.cc | 384 ++++ .../browser/url_request_context_getter.h | 104 + .../inspectable_web_contents_view_views.cc | 223 +++ .../inspectable_web_contents_view_views.h | 71 + .../brightray/browser/views/views_delegate.cc | 118 ++ .../brightray/browser/views/views_delegate.h | 65 + .../browser/web_ui_controller_factory.cc | 60 + .../browser/web_ui_controller_factory.h | 45 + .../browser/win/notification_presenter_win.cc | 67 + .../browser/win/notification_presenter_win.h | 45 + .../brightray/browser/win/scoped_hstring.cc | 41 + vendor/brightray/browser/win/scoped_hstring.h | 41 + .../browser/win/windows_toast_notification.cc | 412 ++++ .../browser/win/windows_toast_notification.h | 111 ++ vendor/brightray/common/application_info.h | 13 + .../brightray/common/application_info_mac.mm | 30 + .../brightray/common/application_info_win.cc | 24 + vendor/brightray/common/content_client.cc | 62 + vendor/brightray/common/content_client.h | 34 + vendor/brightray/common/mac/foundation_util.h | 15 + .../common/mac/main_application_bundle.h | 21 + .../common/mac/main_application_bundle.mm | 54 + vendor/brightray/common/main_delegate.cc | 121 ++ vendor/brightray/common/main_delegate.h | 63 + vendor/brightray/common/main_delegate_mac.mm | 50 + vendor/brightray/common/switches.cc | 57 + vendor/brightray/common/switches.h | 26 + vendor/brightray/script/bootstrap | 98 + vendor/brightray/script/build | 40 + vendor/brightray/script/cibuild | 41 + vendor/brightray/script/cpplint | 48 + .../tools/mac/change_mach_o_flags.py | 273 +++ .../brightray/tools/mac/make_more_helpers.sh | 91 + vendor/brightray/vendor/.gitignore | 1 + vendor/brightray/vendor/google-styleguide | 1 + vendor/brightray/vendor/gyp | 1 + vendor/brightray/vendor/libchromiumcontent | 1 + vendor/crashpad | 2 +- vendor/native_mate | 1 - vendor/node | 1 - 562 files changed, 21798 insertions(+), 31550 deletions(-) create mode 100644 BUILD.gn create mode 100644 DEPS create mode 100644 VERSION create mode 100644 app/linux/BUILD.gn create mode 100644 app/mac/BUILD.gn create mode 100644 app/mac/brave_exe_main_mac.c create mode 100644 app/mac/resources/MainMenu.xib rename atom/browser/resources/mac/Info.plist => app/mac/resources/app-Info.plist (78%) create mode 100644 app/mac/resources/app.saves create mode 100644 app/mac/resources/framework-Info.plist rename atom/renderer/resources/mac/Info.plist => app/mac/resources/helper-Info.plist (57%) create mode 100644 app/resources/beta/brave.icns create mode 100644 app/resources/beta/brave_linux.png create mode 100644 app/resources/beta/brave_win.ico create mode 100644 app/resources/dev/brave.icns create mode 100644 app/resources/dev/brave_linux.png create mode 100644 app/resources/dev/brave_win.ico create mode 100644 app/resources/nightly/brave.icns create mode 100644 app/resources/nightly/brave_linux.png create mode 100644 app/resources/nightly/brave_win.ico create mode 100644 app/resources/release/brave.icns create mode 100644 app/resources/release/brave_linux.png create mode 100644 app/resources/release/brave_win.ico create mode 100644 app/win/BUILD.gn create mode 100644 atom/app/BUILD.gn create mode 100644 atom/browser/BUILD.gn delete mode 100644 atom/browser/api/atom_api_web_view_manager.cc delete mode 100644 atom/browser/extensions/api/messaging/extension_message_port.cc delete mode 100644 atom/browser/extensions/api/messaging/extension_message_port.h delete mode 100644 atom/browser/extensions/api/messaging/message_property_provider.cc delete mode 100644 atom/browser/extensions/api/messaging/message_property_provider.h delete mode 100644 atom/browser/extensions/api/messaging/message_service.cc delete mode 100644 atom/browser/extensions/api/messaging/message_service.h delete mode 100644 atom/browser/osr/osr_output_device.cc delete mode 100644 atom/browser/osr/osr_output_device.h delete mode 100644 atom/browser/osr/osr_render_widget_host_view.cc delete mode 100644 atom/browser/osr/osr_render_widget_host_view.h delete mode 100644 atom/browser/osr/osr_render_widget_host_view_mac.mm delete mode 100644 atom/browser/osr/osr_web_contents_view.cc delete mode 100644 atom/browser/osr/osr_web_contents_view.h delete mode 100644 atom/browser/osr/osr_web_contents_view_mac.mm delete mode 100644 atom/browser/resources/mac/electron.icns delete mode 100644 atom/browser/web_view_guest_delegate.cc delete mode 100644 atom/browser/web_view_guest_delegate.h delete mode 100644 atom/browser/web_view_manager.cc delete mode 100644 atom/browser/web_view_manager.h create mode 100644 atom/common/BUILD.gn delete mode 100644 atom/common/api/resources/_api_features.json delete mode 100644 atom/common/api/resources/_permission_features.json delete mode 100644 atom/common/api/resources/ipc.json create mode 100644 atom/common/api/resources/tab_view_internal_bindings.js delete mode 100644 atom/common/api/resources/web_frame.json create mode 100644 atom/common/api/resources/web_view_api_bindings.js create mode 100644 atom/common/api/resources/web_view_internal_bindings.js delete mode 100644 atom/common/chrome_version.h delete mode 100644 atom/common/extensions/permissions/chrome_api_permissions.cc delete mode 100644 atom/common/extensions/permissions/chrome_api_permissions.h delete mode 100644 atom/common/resources/mac/Info.plist delete mode 100644 atom/common/resources/mac/MainMenu.xib delete mode 100644 atom/renderer/api/atom_api_renderer_ipc.cc delete mode 100644 atom/renderer/api/atom_api_web_frame.cc delete mode 100644 atom/renderer/api/atom_api_web_frame.h delete mode 100644 atom/renderer/guest_view_container.cc delete mode 100644 atom/renderer/guest_view_container.h delete mode 100644 atom/renderer/node_array_buffer_bridge.cc delete mode 100644 atom/renderer/node_array_buffer_bridge.h delete mode 100644 atom/renderer/preferences_manager.cc delete mode 100644 atom/renderer/preferences_manager.h delete mode 100644 autofill.gypi create mode 100644 brave/brave_resources.grd create mode 100644 brave/brave_strings.grd create mode 100644 brave/browser/BUILD.gn create mode 100644 brave/browser/component_updater/BUILD.gn create mode 100644 brave/browser/extensions/BUILD.gn create mode 100644 brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.cc create mode 100644 brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.h create mode 100644 brave/browser/guest_view/brave_guest_view_manager_delegate.cc create mode 100644 brave/browser/guest_view/brave_guest_view_manager_delegate.h create mode 100644 brave/browser/guest_view/tab_view/tab_view_guest.cc create mode 100644 brave/browser/guest_view/tab_view/tab_view_guest.h create mode 100644 brave/browser/ui/simple_message_box.cc create mode 100644 brave/common/brave_paths.cc create mode 100644 brave/common/brave_paths.h create mode 100644 brave/common/extensions/api/BUILD.gn create mode 100644 brave/common/extensions/api/_api_features.json create mode 100644 brave/common/extensions/api/_permission_features.json create mode 100644 brave/common/extensions/api/empty.json create mode 100644 brave/common/extensions/api/ipc.json create mode 100644 brave/common/extensions/api/remote.json create mode 100644 brave/common/extensions/api/schemas.gni create mode 100644 brave/common/extensions/api/tab_view_internal.json create mode 100644 brave/common/extensions/api/web_frame.json create mode 100644 brave/renderer/printing/brave_print_web_view_helper_delegate.cc create mode 100644 brave/renderer/printing/brave_print_web_view_helper_delegate.h create mode 100644 brave/renderer/resources/extensions/ipc_renderer_custom_bindings.js create mode 100644 build/BUILD.gn create mode 100644 build/node/BUILD.gn rename common.gypi => build/node/node.gypi (57%) create mode 100644 build/node/v8.gypi create mode 100644 chromium_src/BUILD.gn create mode 100644 chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.cc create mode 100644 chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.h create mode 100644 chromium_src/chrome/browser/extensions/extension_tab_util.cc create mode 100644 chromium_src/chrome/browser/extensions/extension_tab_util.h create mode 100644 chromium_src/chrome/browser/extensions/extension_util.cc create mode 100644 chromium_src/chrome/browser/extensions/extension_util.h create mode 100644 chromium_src/chrome/browser/media/media_capture_devices_dispatcher.cc create mode 100644 chromium_src/chrome/browser/media/media_capture_devices_dispatcher.h delete mode 100644 chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc delete mode 100644 chromium_src/chrome/browser/printing/pdf_to_emf_converter.h delete mode 100644 chromium_src/chrome/browser/printing/print_job.cc delete mode 100644 chromium_src/chrome/browser/printing/print_job.h delete mode 100644 chromium_src/chrome/browser/printing/print_job_manager.cc delete mode 100644 chromium_src/chrome/browser/printing/print_job_manager.h delete mode 100644 chromium_src/chrome/browser/printing/print_job_worker.cc delete mode 100644 chromium_src/chrome/browser/printing/print_job_worker.h delete mode 100644 chromium_src/chrome/browser/printing/print_job_worker_owner.cc delete mode 100644 chromium_src/chrome/browser/printing/print_job_worker_owner.h delete mode 100644 chromium_src/chrome/browser/printing/print_preview_message_handler.cc delete mode 100644 chromium_src/chrome/browser/printing/print_preview_message_handler.h delete mode 100644 chromium_src/chrome/browser/printing/print_view_manager_base.cc delete mode 100644 chromium_src/chrome/browser/printing/print_view_manager_base.h delete mode 100644 chromium_src/chrome/browser/printing/print_view_manager_basic.cc delete mode 100644 chromium_src/chrome/browser/printing/print_view_manager_basic.h delete mode 100644 chromium_src/chrome/browser/printing/print_view_manager_observer.h delete mode 100644 chromium_src/chrome/browser/printing/printer_query.cc delete mode 100644 chromium_src/chrome/browser/printing/printer_query.h delete mode 100644 chromium_src/chrome/browser/printing/printing_message_filter.cc delete mode 100644 chromium_src/chrome/browser/printing/printing_message_filter.h delete mode 100644 chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc delete mode 100644 chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h create mode 100644 chromium_src/chrome/browser/profiles/profile.cc create mode 100644 chromium_src/chrome/browser/profiles/profile_manager.cc create mode 100644 chromium_src/chrome/browser/profiles/profile_manager.h create mode 100644 chromium_src/chrome/browser/profiles/profiles_state.cc create mode 100644 chromium_src/chrome/browser/profiles/profiles_state.h delete mode 100644 chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.mm delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc delete mode 100644 chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h delete mode 100644 chromium_src/chrome/browser/speech/tts_controller.h delete mode 100644 chromium_src/chrome/browser/speech/tts_controller_impl.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_controller_impl.h delete mode 100644 chromium_src/chrome/browser/speech/tts_linux.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_mac.mm delete mode 100644 chromium_src/chrome/browser/speech/tts_message_filter.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_message_filter.h delete mode 100644 chromium_src/chrome/browser/speech/tts_platform.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_platform.h delete mode 100644 chromium_src/chrome/browser/speech/tts_win.cc create mode 100644 chromium_src/chrome/browser/ui/browser_otr_state.cc delete mode 100644 chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm create mode 100644 chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc create mode 100644 chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h delete mode 100644 chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc delete mode 100644 chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.h delete mode 100644 chromium_src/chrome/common/chrome_constants.cc delete mode 100644 chromium_src/chrome/common/chrome_constants.h delete mode 100644 chromium_src/chrome/common/chrome_paths.cc delete mode 100644 chromium_src/chrome/common/chrome_paths.h delete mode 100644 chromium_src/chrome/common/chrome_paths_internal.h delete mode 100644 chromium_src/chrome/common/chrome_paths_linux.cc delete mode 100644 chromium_src/chrome/common/chrome_paths_mac.mm delete mode 100644 chromium_src/chrome/common/chrome_paths_win.cc delete mode 100644 chromium_src/chrome/common/chrome_utility_messages.h delete mode 100644 chromium_src/chrome/common/cloud_devices_urls.cc delete mode 100644 chromium_src/chrome/common/cloud_devices_urls.h delete mode 100644 chromium_src/chrome/common/common_param_traits_macros.h delete mode 100644 chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc delete mode 100644 chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.h delete mode 100644 chromium_src/chrome/common/ini_parser.cc delete mode 100644 chromium_src/chrome/common/ini_parser.h delete mode 100644 chromium_src/chrome/common/pref_names.cc delete mode 100644 chromium_src/chrome/common/pref_names.h delete mode 100644 chromium_src/chrome/common/print_messages.cc delete mode 100644 chromium_src/chrome/common/print_messages.h delete mode 100644 chromium_src/chrome/common/tts_messages.h delete mode 100644 chromium_src/chrome/common/tts_utterance_request.cc delete mode 100644 chromium_src/chrome/common/tts_utterance_request.h delete mode 100644 chromium_src/chrome/common/widevine_cdm_constants.cc delete mode 100644 chromium_src/chrome/common/widevine_cdm_constants.h delete mode 100644 chromium_src/chrome/common/widevine_cdm_messages.h delete mode 100644 chromium_src/chrome/renderer/extensions/tabs_custom_bindings.cc delete mode 100644 chromium_src/chrome/renderer/extensions/tabs_custom_bindings.h delete mode 100644 chromium_src/chrome/renderer/media/chrome_key_systems.cc delete mode 100644 chromium_src/chrome/renderer/media/chrome_key_systems.h delete mode 100644 chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc delete mode 100644 chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.h delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.cc delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.h delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.cc delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.h delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.h delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_helper.cc delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_helper.h delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc delete mode 100644 chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.h delete mode 100644 chromium_src/chrome/renderer/printing/print_web_view_helper.cc delete mode 100644 chromium_src/chrome/renderer/printing/print_web_view_helper.h delete mode 100644 chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc delete mode 100644 chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm delete mode 100644 chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc delete mode 100644 chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc delete mode 100644 chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h delete mode 100644 chromium_src/chrome/renderer/tts_dispatcher.cc delete mode 100644 chromium_src/chrome/renderer/tts_dispatcher.h delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor.cc delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor.h delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor_mac.h delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor_mac.mm delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor_win.cc delete mode 100644 chromium_src/chrome/utility/importer/nss_decryptor_win.h delete mode 100644 chromium_src/components/sessions/core/session_id.cc delete mode 100644 chromium_src/components/sessions/core/session_id.h delete mode 100644 chromium_src/components/sessions/core/sessions_export.h delete mode 100644 chromium_src/grit/generated_resources.h delete mode 100644 component_updater.gypi create mode 100644 default_app/BUILD.gn delete mode 100644 electron.gyp delete mode 100644 extensions.gyp delete mode 100644 extensions.gypi delete mode 100644 filenames.gypi delete mode 100644 importer.gypi create mode 100644 lib/BUILD.gn delete mode 100644 lib/browser/chrome-extension.js delete mode 100644 lib/browser/desktop-capturer.js delete mode 100644 lib/browser/guest-window-manager.js create mode 100644 lib/electron_api_resources.grd delete mode 100644 lib/renderer/api/desktop-capturer.js delete mode 100644 lib/renderer/api/exports/electron.js delete mode 100644 lib/renderer/chrome-api.js delete mode 100644 lib/renderer/content-scripts-injector.js delete mode 100644 lib/renderer/extensions/event.js delete mode 100644 lib/renderer/extensions/i18n.js delete mode 100644 lib/renderer/extensions/storage.js delete mode 100644 lib/renderer/extensions/web-navigation.js delete mode 100644 lib/renderer/init.js delete mode 100644 lib/renderer/inspector.js delete mode 100644 lib/renderer/override.js delete mode 100644 lib/renderer/web-view/web-view-attributes.js delete mode 100644 lib/renderer/web-view/web-view-constants.js delete mode 100644 lib/renderer/web-view/web-view.js create mode 100644 lib/util/buffer.js delete mode 100644 toolchain.gypi rename chromium_src/chrome/browser/ui/simple_message_box.h => tools/node_gyp_build.py (100%) delete mode 160000 vendor/brightray create mode 100644 vendor/brightray/.gitattributes create mode 100644 vendor/brightray/.gitignore create mode 100644 vendor/brightray/BUILD.gn create mode 100644 vendor/brightray/LICENSE create mode 100644 vendor/brightray/LICENSE-CHROMIUM create mode 100644 vendor/brightray/README.md create mode 100644 vendor/brightray/browser/brightray_paths.h create mode 100644 vendor/brightray/browser/browser_client.cc create mode 100644 vendor/brightray/browser/browser_client.h create mode 100644 vendor/brightray/browser/browser_context.cc create mode 100644 vendor/brightray/browser/browser_context.h create mode 100644 vendor/brightray/browser/browser_main_parts.cc create mode 100644 vendor/brightray/browser/browser_main_parts.h create mode 100644 vendor/brightray/browser/browser_main_parts_mac.mm create mode 100644 vendor/brightray/browser/devtools_contents_resizing_strategy.cc create mode 100644 vendor/brightray/browser/devtools_contents_resizing_strategy.h create mode 100644 vendor/brightray/browser/devtools_embedder_message_dispatcher.cc create mode 100644 vendor/brightray/browser/devtools_embedder_message_dispatcher.h create mode 100644 vendor/brightray/browser/devtools_file_system_indexer.cc create mode 100644 vendor/brightray/browser/devtools_file_system_indexer.h create mode 100644 vendor/brightray/browser/devtools_manager_delegate.cc create mode 100644 vendor/brightray/browser/devtools_manager_delegate.h create mode 100644 vendor/brightray/browser/devtools_ui.cc create mode 100644 vendor/brightray/browser/devtools_ui.h create mode 100644 vendor/brightray/browser/inspectable_web_contents.cc create mode 100644 vendor/brightray/browser/inspectable_web_contents.h create mode 100644 vendor/brightray/browser/inspectable_web_contents_delegate.h create mode 100644 vendor/brightray/browser/inspectable_web_contents_impl.cc create mode 100644 vendor/brightray/browser/inspectable_web_contents_impl.h create mode 100644 vendor/brightray/browser/inspectable_web_contents_view.h create mode 100644 vendor/brightray/browser/inspectable_web_contents_view_delegate.cc create mode 100644 vendor/brightray/browser/inspectable_web_contents_view_delegate.h create mode 100644 vendor/brightray/browser/inspectable_web_contents_view_mac.h create mode 100644 vendor/brightray/browser/inspectable_web_contents_view_mac.mm create mode 100644 vendor/brightray/browser/linux/libnotify_loader.cc create mode 100644 vendor/brightray/browser/linux/libnotify_loader.h create mode 100644 vendor/brightray/browser/linux/libnotify_notification.cc create mode 100644 vendor/brightray/browser/linux/libnotify_notification.h create mode 100644 vendor/brightray/browser/linux/notification_presenter_linux.cc create mode 100644 vendor/brightray/browser/linux/notification_presenter_linux.h create mode 100644 vendor/brightray/browser/mac/bry_application.h create mode 100644 vendor/brightray/browser/mac/bry_application.mm create mode 100644 vendor/brightray/browser/mac/bry_inspectable_web_contents_view.h create mode 100644 vendor/brightray/browser/mac/bry_inspectable_web_contents_view.mm create mode 100644 vendor/brightray/browser/mac/cocoa_notification.h create mode 100644 vendor/brightray/browser/mac/cocoa_notification.mm create mode 100644 vendor/brightray/browser/mac/event_dispatching_window.h create mode 100644 vendor/brightray/browser/mac/event_dispatching_window.mm create mode 100644 vendor/brightray/browser/mac/notification_center_delegate.h create mode 100644 vendor/brightray/browser/mac/notification_center_delegate.mm create mode 100644 vendor/brightray/browser/mac/notification_presenter_mac.h create mode 100644 vendor/brightray/browser/mac/notification_presenter_mac.mm create mode 100644 vendor/brightray/browser/media/media_capture_devices_dispatcher.cc create mode 100644 vendor/brightray/browser/media/media_capture_devices_dispatcher.h create mode 100644 vendor/brightray/browser/media/media_stream_devices_controller.cc create mode 100644 vendor/brightray/browser/media/media_stream_devices_controller.h create mode 100644 vendor/brightray/browser/net/devtools_network_conditions.cc create mode 100644 vendor/brightray/browser/net/devtools_network_conditions.h create mode 100644 vendor/brightray/browser/net/devtools_network_controller.cc create mode 100644 vendor/brightray/browser/net/devtools_network_controller.h create mode 100644 vendor/brightray/browser/net/devtools_network_controller_handle.cc create mode 100644 vendor/brightray/browser/net/devtools_network_controller_handle.h create mode 100644 vendor/brightray/browser/net/devtools_network_interceptor.cc create mode 100644 vendor/brightray/browser/net/devtools_network_interceptor.h create mode 100644 vendor/brightray/browser/net/devtools_network_protocol_handler.cc create mode 100644 vendor/brightray/browser/net/devtools_network_protocol_handler.h create mode 100644 vendor/brightray/browser/net/devtools_network_transaction.cc create mode 100644 vendor/brightray/browser/net/devtools_network_transaction.h create mode 100644 vendor/brightray/browser/net/devtools_network_transaction_factory.cc create mode 100644 vendor/brightray/browser/net/devtools_network_transaction_factory.h create mode 100644 vendor/brightray/browser/net/devtools_network_upload_data_stream.cc create mode 100644 vendor/brightray/browser/net/devtools_network_upload_data_stream.h create mode 100644 vendor/brightray/browser/net_log.cc create mode 100644 vendor/brightray/browser/net_log.h create mode 100644 vendor/brightray/browser/network_delegate.cc create mode 100644 vendor/brightray/browser/network_delegate.h create mode 100644 vendor/brightray/browser/notification.cc create mode 100644 vendor/brightray/browser/notification.h create mode 100644 vendor/brightray/browser/notification_delegate.h create mode 100644 vendor/brightray/browser/notification_delegate_adapter.cc create mode 100644 vendor/brightray/browser/notification_delegate_adapter.h create mode 100644 vendor/brightray/browser/notification_presenter.cc create mode 100644 vendor/brightray/browser/notification_presenter.h create mode 100644 vendor/brightray/browser/permission_manager.cc create mode 100644 vendor/brightray/browser/permission_manager.h create mode 100644 vendor/brightray/browser/platform_notification_service.cc create mode 100644 vendor/brightray/browser/platform_notification_service.h create mode 100644 vendor/brightray/browser/special_storage_policy.cc create mode 100644 vendor/brightray/browser/special_storage_policy.h create mode 100644 vendor/brightray/browser/url_request_context_getter.cc create mode 100644 vendor/brightray/browser/url_request_context_getter.h create mode 100644 vendor/brightray/browser/views/inspectable_web_contents_view_views.cc create mode 100644 vendor/brightray/browser/views/inspectable_web_contents_view_views.h create mode 100644 vendor/brightray/browser/views/views_delegate.cc create mode 100644 vendor/brightray/browser/views/views_delegate.h create mode 100644 vendor/brightray/browser/web_ui_controller_factory.cc create mode 100644 vendor/brightray/browser/web_ui_controller_factory.h create mode 100644 vendor/brightray/browser/win/notification_presenter_win.cc create mode 100644 vendor/brightray/browser/win/notification_presenter_win.h create mode 100644 vendor/brightray/browser/win/scoped_hstring.cc create mode 100644 vendor/brightray/browser/win/scoped_hstring.h create mode 100644 vendor/brightray/browser/win/windows_toast_notification.cc create mode 100644 vendor/brightray/browser/win/windows_toast_notification.h create mode 100644 vendor/brightray/common/application_info.h create mode 100644 vendor/brightray/common/application_info_mac.mm create mode 100644 vendor/brightray/common/application_info_win.cc create mode 100644 vendor/brightray/common/content_client.cc create mode 100644 vendor/brightray/common/content_client.h create mode 100644 vendor/brightray/common/mac/foundation_util.h create mode 100644 vendor/brightray/common/mac/main_application_bundle.h create mode 100644 vendor/brightray/common/mac/main_application_bundle.mm create mode 100644 vendor/brightray/common/main_delegate.cc create mode 100644 vendor/brightray/common/main_delegate.h create mode 100644 vendor/brightray/common/main_delegate_mac.mm create mode 100644 vendor/brightray/common/switches.cc create mode 100644 vendor/brightray/common/switches.h create mode 100755 vendor/brightray/script/bootstrap create mode 100755 vendor/brightray/script/build create mode 100755 vendor/brightray/script/cibuild create mode 100755 vendor/brightray/script/cpplint create mode 100755 vendor/brightray/tools/mac/change_mach_o_flags.py create mode 100755 vendor/brightray/tools/mac/make_more_helpers.sh create mode 100644 vendor/brightray/vendor/.gitignore create mode 160000 vendor/brightray/vendor/google-styleguide create mode 160000 vendor/brightray/vendor/gyp create mode 160000 vendor/brightray/vendor/libchromiumcontent delete mode 160000 vendor/native_mate delete mode 160000 vendor/node diff --git a/.gitignore b/.gitignore index 2623f4aaca..bceed517de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .DS_Store .tags* /.idea/ -/build/ /dist/ /external_binaries/ /out/ @@ -11,6 +10,9 @@ /vendor/debian_wheezy_i386-sysroot/ /vendor/python_26/ /vendor/npm/ +/vendor/node/ +/vendor/v8 +/vendor/native_mate/ /vendor/llvm/ /vendor/llvm-build/ /vendor/.gclient @@ -27,4 +29,4 @@ node_modules/ *.vcxproj.filters *.sln debug.log -npm-debug.log \ No newline at end of file +npm-debug.log diff --git a/.gitmodules b/.gitmodules index c8676d2071..7bf901cc76 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,18 +1,9 @@ -[submodule "vendor/brightray"] - path = vendor/brightray - url = https://github.com/brave/brightray -[submodule "vendor/node"] - path = vendor/node - url = https://github.com/brave/node.git [submodule "vendor/depot_tools"] path = vendor/depot_tools url = https://chromium.googlesource.com/chromium/tools/depot_tools.git [submodule "vendor/breakpad"] path = vendor/breakpad url = https://github.com/electron/chromium-breakpad.git -[submodule "vendor/native_mate"] - path = vendor/native_mate - url = https://github.com/zcbenz/native-mate.git [submodule "vendor/crashpad"] path = vendor/crashpad url = https://github.com/electron/crashpad.git diff --git a/.node-version b/.node-version index dffc266d6a..d18077c214 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v6.3.0 +v7.0.0 diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 0000000000..639cee1d12 --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,378 @@ +import("//build/buildflag_header.gni") +import("//build/config/chrome_build.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/features.gni") +import("//build/config/locales.gni") +import("//build/config/crypto.gni") +import("//build/config/sanitizers/sanitizers.gni") +import("//build/config/ui.gni") +import("//tools/grit/grit_rule.gni") +import("//tools/grit/repack.gni") +import("//ui/base/ui_features.gni") +import("//v8/gni/v8.gni") +import("//chrome/version.gni") + +enable_service_discovery = false + +group("electron") { + testonly = false + + if (is_mac) { + deps = [ + "app/mac:electron_app", + ] + + data_deps = [ + "app/mac:electron_app", + ] + } + + if (is_linux) { + deps = [ + "app/linux:electron_app", + ] + + data_deps = [ + "app/linux:electron_app", + ] + } + + if (is_win) { + deps = [ + "app/win:electron_app", + ] + + data_deps = [ + "app/win:electron_app", + ] + } +} + +source_set("main") { + public_configs = [ + "build:electron_config", + ] + + sources = [ + "atom/app/atom_main.cc", + "atom/app/atom_main.h", + # "atom/app/node_main.cc", + # "atom/app/node_main.h", + ] + + if (is_win) { + sources += [ + # 'atom/browser/resources/win/resource.h', + # 'atom/browser/resources/win/atom.ico', + # 'atom/browser/resources/win/atom.rc', + # # Cursors. + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/aliasb.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/cell.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/col_resize.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/copy.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/hand_grab.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/hand_grabbing.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/none.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_east.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_middle.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_north.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_north_east.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_north_west.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_south.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_south_east.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_south_west.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/pan_west.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/row_resize.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/vertical_text.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/zoom_in.cur', + # '<(libchromiumcontent_src_dir)/ui/resources/cursors/zoom_out.cur', + ] + } +} + +grit("atom_resources") { + source = "atom/atom_resources.grd" + output_dir = "$root_gen_dir/atom/" + outputs = [ + "grit/atom_resources.h", + "atom_resources.pak", + ] + + resource_ids = "//electron/resource_ids" +} + +grit("brave_resources") { + source = "brave/brave_resources.grd" + output_dir = "$root_gen_dir/brave/" + outputs = [ + "grit/brave_resources.h", + "brave_resources.pak", + ] + + resource_ids = "//electron/resource_ids" +} + +grit("electron_api_resources") { + source = "lib/electron_api_resources.grd" + output_dir = "$root_gen_dir/atom/" + outputs = [ + "grit/electron_api_resources.h", + "electron_api_resources.pak", + ] + + resource_ids = "//electron/resource_ids" +} + +grit("brave_strings") { + source = "brave/brave_strings.grd" + output_dir = "$root_gen_dir/brave" + use_qualified_include = true + outputs = [ + "grit/brave_strings.h", + "brave_strings.pak", + ] + + resource_ids = "//electron/resource_ids" +} + +group("packed_resources") { + public_deps = [ + ":packed_extra_resources", + "//chrome:packed_resources", + ] +} + +repack("packed_extra_resources") { + visibility = [ "./*" ] + sources = [ + "$root_gen_dir/atom/atom_resources.pak", + "$root_gen_dir/brave/brave_resources.pak", + "$root_gen_dir/brave/brave_strings.pak", + "$root_gen_dir/chrome/common_resources.pak", + "$root_gen_dir/blink/public/resources/blink_resources.pak", + "$root_gen_dir/content/app/strings/content_strings_en-US.pak", + "$root_gen_dir/content/browser/tracing/tracing_resources.pak", + "$root_gen_dir/content/content_resources.pak", + "$root_gen_dir/net/net_resources.pak", + "$root_gen_dir/ui/resources/webui_resources.pak", + "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak", + "$root_gen_dir/ui/strings/ui_strings_en-US.pak", + "$root_gen_dir/components/components_resources.pak", + "$root_gen_dir/blink/devtools_resources.pak", + ] + + deps = [ + ":atom_resources", + ":brave_resources", + ":brave_strings", + "//chrome/common:resources", + "//chrome/renderer:resources", + "//content:resources", + "//content/app/resources", + "//content/app/strings", + "//content/browser/tracing:resources", + "//net:net_resources", + "//third_party/WebKit/public:image_resources", + "//third_party/WebKit/public:resources", + "//ui/resources", + "//ui/strings", + "//content/browser/devtools:resources", + "//components/resources:components_resources", + ] + + if (enable_extensions) { + sources += [ + "$root_gen_dir/atom/electron_api_resources.pak", + "$root_gen_dir/extensions/extensions_renderer_resources.pak", + "$root_gen_dir/extensions/extensions_resources.pak", + ] + deps += [ + ":electron_api_resources", + "//extensions:extensions_resources" + ] + } + + if (is_mac) { + output = "$root_gen_dir/repack/electron_resources.pak" + } else { + output = "$root_out_dir/electron_resources.pak" + } +} + +source_set("browser_dependencies") { + public_configs = [ + "build:electron_config", + ] + + public_deps = [ + "atom/app", + "brave/browser", + ":common", + "lib:atom_js2c", + ] + + sources = [] + + if (enable_plugins) { + public_deps += [ "//ppapi/host" ] + } + + if (enable_basic_printing || enable_print_preview) { + public_deps += [ "//printing" ] + } +} + +group("child_dependencies") { + public_deps = [ + "atom/app", + ":common", + ":renderer", + ":utility", + "//content/public/child", + "//content/public/gpu", + "//third_party/WebKit/public:blink_devtools_frontend_resources", + ] +} + +source_set("common") { + public_deps = [ + "atom/common" + ] + + sources = [ + "brave/common/brave_paths.cc", + "brave/common/brave_paths.h", + ] + + if (enable_extensions) { + public_deps += [ + "brave/common/extensions/api", + ] + } +} + +source_set("utility") { + public_configs = [ + "build:electron_config", + ] + + deps = [ + "//chrome/utility", + "//third_party/protobuf:protobuf_lite", + ] + + sources = [ + "atom/utility/atom_content_utility_client.cc", + "atom/utility/atom_content_utility_client.h", + ] +} + +source_set("renderer") { + public_configs = [ + "build:electron_config", + ] + + sources = [ + "atom/renderer/api/atom_api_spell_check_client.cc", + "atom/renderer/api/atom_api_spell_check_client.h", + "atom/renderer/atom_render_view_observer.cc", + "atom/renderer/atom_render_view_observer.h", + "atom/renderer/atom_renderer_client.cc", + "atom/renderer/atom_renderer_client.h", + "atom/renderer/content_settings_client.cc", + "atom/renderer/content_settings_client.h", + "atom/renderer/content_settings_manager.cc", + "atom/renderer/content_settings_manager.h", + "atom/renderer/content_settings_observer.h", + "brave/renderer/brave_content_renderer_client.cc", + "brave/renderer/brave_content_renderer_client.h", + ] + + public_deps = [ + "//chrome/renderer", + ] + + deps = [ + ":common", + "//third_party/WebKit/public:blink_headers", + ] + + data_deps = [ + ":atom_resources", + ":brave_resources", + ":electron_api_resources", + ] + + if (enable_basic_printing) { + sources += [ + "brave/renderer/printing/brave_print_web_view_helper_delegate.cc", + "brave/renderer/printing/brave_print_web_view_helper_delegate.h", + ] + + deps += [ + "//components/printing/renderer", + ] + } + + if (enable_extensions) { + sources += [ + # TODO(bridiver) - change to brave/renderer/extensions + "atom/common/javascript_bindings.cc", + "atom/common/javascript_bindings.h", + "atom/renderer/extensions/atom_extensions_dispatcher_delegate.cc", + "atom/renderer/extensions/atom_extensions_dispatcher_delegate.h", + "atom/renderer/extensions/atom_extensions_renderer_client.cc", + "atom/renderer/extensions/atom_extensions_renderer_client.h", + "brave/renderer/extensions/content_settings_bindings.cc", + "brave/renderer/extensions/content_settings_bindings.h", + "brave/renderer/extensions/web_frame_bindings.cc", + "brave/renderer/extensions/web_frame_bindings.h", + ] + + deps += [ + ":common", + "chromium_src:renderer", + "//extensions:extensions_resources", + "//extensions/renderer", + "//media/cast:net", + "//media/cast:receiver", + "//media/cast:sender", + ] + } +} + +source_set("native_mate") { + configs += [ + "build:electron_config", + ] + + sources = [ + "vendor/native_mate/native_mate/arguments.cc", + "vendor/native_mate/native_mate/arguments.h", + "vendor/native_mate/native_mate/compat.h", + "vendor/native_mate/native_mate/constructor.h", + "vendor/native_mate/native_mate/converter.cc", + "vendor/native_mate/native_mate/converter.h", + "vendor/native_mate/native_mate/dictionary.cc", + "vendor/native_mate/native_mate/dictionary.h", + "vendor/native_mate/native_mate/function_template.cc", + "vendor/native_mate/native_mate/function_template.h", + "vendor/native_mate/native_mate/handle.h", + "vendor/native_mate/native_mate/object_template_builder.cc", + "vendor/native_mate/native_mate/object_template_builder.h", + "vendor/native_mate/native_mate/persistent_dictionary.cc", + "vendor/native_mate/native_mate/persistent_dictionary.h", + "vendor/native_mate/native_mate/scoped_persistent.h", + "vendor/native_mate/native_mate/template_util.h", + "vendor/native_mate/native_mate/try_catch.cc", + "vendor/native_mate/native_mate/try_catch.h", + "vendor/native_mate/native_mate/wrappable.cc", + "vendor/native_mate/native_mate/wrappable.h", + "vendor/native_mate/native_mate/wrappable_base.h", + ] + + deps = [ + "build/node", + "//third_party/WebKit/public:blink_headers", + ] +} diff --git a/DEPS b/DEPS new file mode 100644 index 0000000000..a98d82da11 --- /dev/null +++ b/DEPS @@ -0,0 +1,69 @@ +include_rules = [ + "+crypto", + "+gpu", + "+net", + "+pdf", + "+printing", + "+sql", + # Browser, renderer, common and tests access V8 for various purposes. + "-v8", + "+v8/include", + + # Limit what we include from nacl. + "-native_client", + + # The subdirectories in chrome/ will manually allow their own include + # directories in chrome/ so we disallow all of them. + "-chrome", + "+chrome/common", + # "+chrome/test", + "+components/content_settings/core/common", + # "+components/error_page/common", + # "+components/omnibox/common", + # "+components/url_formatter", + # "+components/variations", + "+content/public/common", + # "+content/public/test", + "+mojo/common", + "+mojo/public", + + # Don't allow inclusion of these other libs we shouldn't be calling directly. + "-webkit", + "-tools", + + "-crypto/third_party", + + # Allow inclusion of WebKit API files. + "+third_party/WebKit/public/platform", + "+third_party/WebKit/public/web", + + # Allow inclusion of third-party code: + "+third_party/hunspell", + + "+skia", + "+third_party/skia", + "+third_party/skia/include", + "+third_party/skia/include/core", + + "+third_party/icu/source/common/unicode", + "+third_party/icu/source/i18n/unicode", + + "+ui", + + "+electron/vendor/brightray", +] + +use_relative_paths = True + +deps = { + "vendor/node": "https://github.com/brave/node.git@a57e6ac8d99e84bce640bf854c34d4ff0338f915", + "vendor/native_mate": "https://github.com/zcbenz/native-mate.git@b5e5de626c6a57e44c7e6448d8bbaaac475d493c", +} + +hooks = [ + { + 'name': 'bootstrap', + 'pattern': '.', + 'action': ['python', 'src/electron/script/bootstrap.py'], + } +] diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..a068b41855 --- /dev/null +++ b/VERSION @@ -0,0 +1,4 @@ +MAJOR=1 +MINOR=5 +BUILD=0 +PATCH=0 diff --git a/app/linux/BUILD.gn b/app/linux/BUILD.gn new file mode 100644 index 0000000000..5eb9c20716 --- /dev/null +++ b/app/linux/BUILD.gn @@ -0,0 +1,84 @@ +executable("electron_app") { + + output_name = "brave" + + # This is just a stub and doesn't work yet + deps = [ + "//electron:main", + "//electron:app", + "//electron:browser_dependencies", + "//electron:child_dependencies", + "//content/public/app:both", + "//build/config/sanitizers:deps", + ] + + data = [ + "$root_gen_dir/repack/electron_resources.pak", + "$root_out_dir/chrome_100_percent.pak", + ] + + public_deps = [ + "//electron:packed_extra_resources", + "//chrome:packed_resources", + ":electron_resources", + ":node_resources", + ] + + ldflags = [ "-pie" ] + + if (use_x11) { + configs += [ + "//build/config/linux:x11", + "//build/config/linux:xext", + ] + } +} + +copy("node_resources") { + sources = [ + "$root_out_dir/libnode.so", + ] + outputs = [ + "$root_build_dir/libnode.so", + ] + deps = [ + "//electron/build/node", + ] +} + +bundle_data("electron_resources") { + sources = [ + "$root_out_dir/electron.asar", + "$root_out_dir/default_app.asar", + "//electron/app/resources/linux/brave_linux.png", + ] + + outputs = [ + "{{bundle_resources_dir}}/{{source_file_part}}", + ] + + public_deps = [ + "//electron/lib", + "//electron/default_app", + ] +} + + + + "$root_gen_dir/repack/electron_resources.pak", + "$root_out_dir/chrome_100_percent.pak", +] + + +if (icu_use_data_file) { + sources += [ "$root_out_dir/icudtl.dat" ] + public_deps += [ "//third_party/icu:icudata" ] +} + +# if (v8_use_external_startup_data) { + sources += [ + "$root_out_dir/natives_blob.bin", + "$root_out_dir/snapshot_blob.bin", + ] + public_deps += [ "//v8:v8" ] +# } diff --git a/app/mac/BUILD.gn b/app/mac/BUILD.gn new file mode 100644 index 0000000000..119f55c4fa --- /dev/null +++ b/app/mac/BUILD.gn @@ -0,0 +1,347 @@ +import("//build/buildflag_header.gni") +import("//build/config/chrome_build.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/features.gni") +import("//build/config/locales.gni") +import("//build/config/sanitizers/sanitizers.gni") +import("//build/config/ui.gni") +import("//ui/base/ui_features.gni") +import("//v8/gni/v8.gni") +import("//chrome/version.gni") +import("//build/compiled_action.gni") +import("//build/config/mac/rules.gni") +import("//build/config/mac/symbols.gni") +import("//build/mac/tweak_info_plist.gni") +import("//build/util/branding.gni") +import("//build_overrides/v8.gni") +import("//media/cdm/ppapi/cdm_paths.gni") +import("//third_party/icu/config.gni") + +chrome_version_full = "Frameworks" +chrome_framework_name = chrome_product_full_name + " Framework" +chrome_helper_name = chrome_product_full_name + " Helper" + +tweak_info_plist("electron_app_plist") { + info_plist = "resources/app-Info.plist" + _keystone_arg = "0" + args = [ + "--breakpad=0", + "--keystone=$_keystone_arg", + "--scm=1", + "--bundle_id=$chrome_mac_bundle_id", + ] +} + +mac_app_bundle("electron_app") { + + remove_configs = [ "//build/config/mac:strip_all" ] + + output_name = chrome_product_full_name + + info_plist_target = ":electron_app_plist" + extra_substitutions = [ + "ATOM_PROJECT_NAME=brave", + "CHROMIUM_BUNDLE_ID=$chrome_mac_bundle_id", + "CHROMIUM_SHORT_NAME=$chrome_product_short_name", + ] + + extra_configs = [ + "//build/config/compiler:wexit_time_destructors", + ] + + sources = [ + "brave_exe_main_mac.c", + ] + + deps = [ + ":electron_versioned_bundle_data", + ":electron_app_resources", + "//build/config/sanitizers:deps", + "//chrome/common:version_header", + ] + + # Remove the default strip configuration (which strips all symbols) so that + # a saves file can be specified. + # if (enable_stripping) { + # remove_configs = [ "//build/config/mac:strip_all" ] + + # ldflags += + # [ "-Wcrl,strip,-s," + rebase_path("app/app.saves", root_build_dir) ] + # } +} + +tweak_info_plist("electron_framework_plist") { + info_plist = "resources/framework-Info.plist" + args = [ + "--breakpad=0", + "--keystone=0", + "--scm=1", + "--branding", + chrome_product_short_name, + ] +} + +mac_framework_bundle("electron_framework") { + output_name = chrome_framework_name + + configs += [ + "//electron/build/node:node_external_config", + "//build/config/compiler:wexit_time_destructors", + ] + + info_plist_target = ":electron_framework_plist" + extra_substitutions = [ + "CHROMIUM_BUNDLE_ID=$chrome_mac_bundle_id", + "CHROMIUM_SHORT_NAME=$chrome_product_short_name", + ] + + public_deps = [ + ":electron_dll", + ] + + deps = [ + "//chrome:chrome_framework_helpers", + "//chrome:chrome_framework_locales", + ":node_resources", + ":electron_framework_resources", + "//build/config/sanitizers:deps", + ":electron_xibs", + ] + + ldflags = [ + "-Wl,-install_name,@executable_path/../$chrome_version_full/$chrome_framework_name.framework/$chrome_framework_name", + # "-compatibility_version", + # chrome_dylib_version, + # "-current_version", + # chrome_dylib_version, + # "-Wl,-order_file," + rebase_path("app/framework.order", root_build_dir), + ] + + data_deps = [ + ":node_resources" + ] + + if (is_component_build) { + ldflags += [ + "-rpath", + "@loader_path/../../../..", + "-Wl,-reexport_library,libelectron_dll.dylib", + ] + + data_deps += [ + ":electron_dll", + ] + } else { + ldflags += [ + "-rpath", + "@loader_path/Libraries", + ] + } +} + +tweak_info_plist("electron_helper_plist") { + info_plist = "resources/helper-Info.plist" + args = [ + "--breakpad=0", + "--keystone=0", + "--scm=0", + ] +} + +mac_app_bundle("electron_helper_app") { + output_name = chrome_helper_name + + info_plist_target = ":electron_helper_plist" + extra_substitutions = [ + "CHROMIUM_BUNDLE_ID=$chrome_mac_bundle_id", + "CHROMIUM_SHORT_NAME=$chrome_product_short_name", + ] + + sources = [ + "brave_exe_main_mac.c", + ] + + extra_configs = [ "//build/config/compiler:wexit_time_destructors" ] + + defines = [ "HELPER_EXECUTABLE" ] + + deps = [ + "//build/config/sanitizers:deps", + "//chrome/common:version_header", + ] + + ldflags = [] + + if (is_component_build) { + ldflags += [ + # The helper is in Brave.app/Contents/Frameworks/Brave Helper.app/Contents/MacOS/ + # so set rpath up to the base. + "-rpath", + "@loader_path/../../../../../..", + ] + } + + # Remove the default strip configuration (which strips all symbols) so that + # a saves file can be specified. + # if (enable_stripping) { + # remove_configs = [ "//build/config/mac:strip_all" ] + + # ldflags += + # [ "-Wcrl,strip,-s," + rebase_path("app/app.saves", root_build_dir) ] + # } +} + +bundle_data("electron_versioned_bundle_data") { + sources = [ + "$root_out_dir/$chrome_framework_name.framework", + "$root_out_dir/$chrome_helper_name.app", + ] + + outputs = [ + "{{bundle_root_dir}}/$chrome_version_full/{{source_file_part}}", + ] + + public_deps = [ + ":electron_helper_app", + # Before bundling the versioned app components, delete any existing + # versions. + "//chrome:clean_up_old_versions", + ":electron_framework", + ] +} + +bundle_data("electron_app_resources") { + # asar + sources = [ + "$root_out_dir/electron.asar", + "$root_out_dir/default_app.asar", + ] + public_deps = [ + "//electron/lib", + "//electron/default_app", + ] + + # icon + if (is_component_build) { + sources += [ + "//electron/app/resources/dev/brave.icns", + ] + } else { + sources += [ + "//electron/app/resources/nightly/brave.icns", + ] + } + + outputs = [ + "{{bundle_resources_dir}}/{{source_file_part}}", + ] +} + +bundle_data("electron_framework_resources") { + # pak + sources = [ + "$root_gen_dir/repack/electron_resources.pak", + "$root_out_dir/chrome_100_percent.pak", + ] + if (enable_hidpi) { + sources += [ + "$root_out_dir/chrome_200_percent.pak", + ] + } + public_deps = [ + "//electron:packed_resources", + ] + + # v8 + if (icu_use_data_file) { + sources += [ "$root_out_dir/icudtl.dat" ] + public_deps += [ "//third_party/icu:icudata" ] + } + if (v8_use_external_startup_data) { + sources += [ + "$root_out_dir/natives_blob.bin", + "$root_out_dir/snapshot_blob.bin", + ] + public_deps += [ "//v8:v8" ] + } + + outputs = [ + "{{bundle_resources_dir}}/{{source_file_part}}", + ] +} + +# On Mac, speed up the component build by not re-bundling the framework +# every time it changes. Instead, place all the sources and their deps in +# a library that the bundled framework links (and re-exports). That way +# only the library needs to be re-linked when it changes. +if (is_component_build) { + _dll_target_type = "shared_library" +} else { + _dll_target_type = "source_set" +} +target(_dll_target_type, "electron_dll") { + visibility = [ + ":electron_framework", + ":electron_framework_shared_library", + ] + + public_configs = [ + "//electron/build:electron_config", + ] + + sources = [ + "//electron/atom/app/atom_library_main.h", + "//electron/atom/app/atom_library_main.mm", + ] + + deps = [ + "//electron:browser_dependencies", + "//electron:child_dependencies", + "//chrome/common:constants", + "//components/crash/content/app", + "//components/policy:generated", + "//content/public/app:both", + "//third_party/cld", + ] + + ldflags = [ + # "-Wl,-order_file", + # "-Wl," + rebase_path("app/framework.order", root_build_dir), + "-ObjC", + ] + + configs += [ "//build/config/compiler:wexit_time_destructors" ] +} + +bundle_data("node_resources") { + sources = [ + "$root_out_dir/libnode.dylib", + ] + outputs = [ + "{{bundle_root_dir}}/Libraries/{{source_file_part}}", + ] + public_deps = [ + "//electron/build/node", + ] +} + +mac_xib_bundle_data("electron_xibs") { + sources = [ + "resources/MainMenu.xib", + ] +} + +# action("fix_node_path") { +# script = "//build/gn_run_binary.py" +# args = [ +# "/usr/bin/install_name_tool", +# "-change", "/usr/local/lib/libnode.dylib", +# "@executable_path/../Frameworks/Libraries/libnode.dylib", +# rebase_path("$root_out_dir/$chrome_product_short_name.app/Contents/Frameworks/$chrome_framework_name.framework/$chrome_framework_name") ] +# deps = [ +# ":electron_versioned_bundle_data", +# ":build_node" +# ] +# outputs = [ "$root_build_dir/alwaysrun" ] +# } + diff --git a/app/mac/brave_exe_main_mac.c b/app/mac/brave_exe_main_mac.c new file mode 100644 index 0000000000..02c92be67d --- /dev/null +++ b/app/mac/brave_exe_main_mac.c @@ -0,0 +1,90 @@ +// Copyright 2015 The Brave 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 entry point for all Mac Brave processes, including the outer app +// bundle (browser) and helper app (renderer, plugin, and friends). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chrome/common/chrome_version.h" + +typedef int (*ChromeMainPtr)(int, char**); + +__attribute__((visibility("default"))) int main(int argc, char* argv[]) { +#if defined(HELPER_EXECUTABLE) + const char* const rel_path = + "../../../" PRODUCT_FULLNAME_STRING + " Framework.framework/" PRODUCT_FULLNAME_STRING " Framework"; +#else + const char* const rel_path = + "../Frameworks/" PRODUCT_FULLNAME_STRING + " Framework.framework/" PRODUCT_FULLNAME_STRING " Framework"; +#endif // defined(HELPER_EXECUTABLE) + + uint32_t exec_path_size = 0; + int rv = _NSGetExecutablePath(NULL, &exec_path_size); + if (rv != -1) { + fprintf(stderr, "_NSGetExecutablePath: get length failed\n"); + abort(); + } + + char* exec_path = malloc(exec_path_size); + if (!exec_path) { + fprintf(stderr, "malloc %u: %s\n", exec_path_size, strerror(errno)); + abort(); + } + + rv = _NSGetExecutablePath(exec_path, &exec_path_size); + if (rv != 0) { + fprintf(stderr, "_NSGetExecutablePath: get path failed\n"); + abort(); + } + + // Slice off the last part of the main executable path, and append the + // version framework information. + const char* parent_dir = dirname(exec_path); + if (!parent_dir) { + fprintf(stderr, "dirname %s: %s\n", exec_path, strerror(errno)); + abort(); + } + free(exec_path); + + const size_t parent_path_len = strlen(parent_dir); + const size_t rel_path_len = strlen(rel_path); + // 2 accounts for a trailing NUL byte and the '/' in the middle of the paths. + const size_t framework_path_size = parent_path_len + rel_path_len + 2; + char* framework_path = malloc(framework_path_size); + if (!framework_path) { + fprintf(stderr, "malloc %zu: %s\n", framework_path_size, strerror(errno)); + abort(); + } + snprintf(framework_path, framework_path_size, "%s/%s", parent_dir, rel_path); + + void* library = dlopen(framework_path, RTLD_LAZY | RTLD_LOCAL | RTLD_FIRST); + if (!library) { + fprintf(stderr, "dlopen %s: %s\n", framework_path, dlerror()); + abort(); + } + free(framework_path); + + const ChromeMainPtr chrome_main = dlsym(library, "AtomMain"); + if (!chrome_main) { + fprintf(stderr, "dlsym AtomMain: %s\n", dlerror()); + abort(); + } + rv = chrome_main(argc, argv); + + // exit, don't return from main, to avoid the apparent removal of main from + // stack backtraces under tail call optimization. + exit(rv); +} diff --git a/app/mac/resources/MainMenu.xib b/app/mac/resources/MainMenu.xib new file mode 100644 index 0000000000..d9a66e309b --- /dev/null +++ b/app/mac/resources/MainMenu.xib @@ -0,0 +1,180 @@ + + + + 101000 + 14D136 + 7531 + 1347.57 + 758.70 + + com.apple.InterfaceBuilder.CocoaPlugin + 7531 + + + NSCustomObject + NSMenu + NSMenuItem + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + AtomApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + Main Menu + + + + Electron + + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + + Electron + + + + Quit + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + _NSMainMenu + + + + + + + terminate: + + + + 807 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 371 + + + + + 29 + + + + + + + + 56 + + + + + + + + 57 + + + + + + + + 136 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 807 + + 0 + IBCocoaFramework + NO + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + {12, 12} + {10, 2} + + YES + + diff --git a/atom/browser/resources/mac/Info.plist b/app/mac/resources/app-Info.plist similarity index 78% rename from atom/browser/resources/mac/Info.plist rename to app/mac/resources/app-Info.plist index f39a1adb9d..6b6cc1e5bd 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/app/mac/resources/app-Info.plist @@ -3,19 +3,21 @@ CFBundleDisplayName - ${PRODUCT_NAME} + ${EXECUTABLE_NAME} CFBundleExecutable - ${PRODUCT_NAME} + ${EXECUTABLE_NAME} CFBundleIdentifier - ${ATOM_BUNDLE_ID} + ${CHROMIUM_BUNDLE_ID} CFBundleInfoDictionaryVersion 6.0 CFBundleName - ${PRODUCT_NAME} + ${CHROMIUM_SHORT_NAME} + CrProductDirName + ${ATOM_PROJECT_NAME} CFBundlePackageType APPL CFBundleIconFile - electron.icns + brave.icns CFBundleVersion 1.4.25 CFBundleShortVersionString diff --git a/app/mac/resources/app.saves b/app/mac/resources/app.saves new file mode 100644 index 0000000000..b3e9408e4c --- /dev/null +++ b/app/mac/resources/app.saves @@ -0,0 +1,11 @@ +# Copyright (c) 2016 The Brave 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 lists symbols that should not be stripped from the Mac browser and +# helper app executables. + +_main +# _node_module_register +# __attribute__ +# __declspec diff --git a/app/mac/resources/framework-Info.plist b/app/mac/resources/framework-Info.plist new file mode 100644 index 0000000000..92e2ccd58c --- /dev/null +++ b/app/mac/resources/framework-Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleDisplayName + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${CHROMIUM_BUNDLE_ID}.framework + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundlePackageType + FMWK + + diff --git a/atom/renderer/resources/mac/Info.plist b/app/mac/resources/helper-Info.plist similarity index 57% rename from atom/renderer/resources/mac/Info.plist rename to app/mac/resources/helper-Info.plist index 1da2c29fd9..d9bd16f013 100644 --- a/atom/renderer/resources/mac/Info.plist +++ b/app/mac/resources/helper-Info.plist @@ -2,12 +2,18 @@ + CFBundleDisplayName + ${EXECUTABLE_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} CFBundleIdentifier - ${ATOM_BUNDLE_ID} + ${CHROMIUM_BUNDLE_ID}.helper CFBundleName - ${PRODUCT_NAME} + ${CHROMIUM_SHORT_NAME} CFBundlePackageType APPL + LSMinimumSystemVersion + 10.9.0 LSUIElement NSSupportsAutomaticGraphicsSwitching diff --git a/app/resources/beta/brave.icns b/app/resources/beta/brave.icns new file mode 100644 index 0000000000000000000000000000000000000000..61338d582a34a373ab3c0d981db0c082a54fd219 GIT binary patch literal 239702 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ǘu8D?8uF-9D4_mO`XEDs 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Μ~wYX0@w6fHMR} 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

X>s1a|*TQTK^b!5M7v=f+} z`atbg_m;Oyzm>EdSv#lFa2NH&W#=2VuLq$TEJ@{A8EWNR_A2r2KFN-1v`L5C6aars(Zxt z^Ooj2wy~_r1zcC z@Y|0C=Xci*?orO7O9%u09?mXc#y{?~xDFASh=upXIEQSV)~qVYMU1GYFV6pr^UY4V zz*4XHQ>1}kn6X9J*8GCyPr*g~G8)SHhSTnyXvF{|6)1^VB>~sc*MeCp%;(?9#w4Vq z7rhmRbc5N^&maqB5u_#3E=!&Cqr{~r6%{^eI}x2DsWG2LO1ba5^b7e_PkDbPuTJPo z?+2zlp%>{wYVR*bh1prl{;nh4w5Bz;lhFwA{|egtBmIBj!i9EG?COp%#x``6+6?`d zahp}rXWrP*_=y4XbaK;%nNLCd4|agpz3z3v4&a>#ghWyBpo)dn8#Xl`NY{3!W-rWW z_SeomKLr@%)xn|pImcO=c!7uD&DkJFkZq%a;9Sh#K$L#Fu%>Miibo#r@ zZu%P-!86IvOJYWPR}4__DutO}TcRCK`Lbc&RP-d6WTY9o3*tm{k6!#o==GHQPxqN! z4P&AtMs`t3k854fGFie(A3<7G7nk+dgQv>q*)P zgVQl@QdhLmNU-Q5nAOe!K6rrI_BCuzeo0LHb7+U0eblBB1E6mGeo85Udt&dZyM#07 z|0&zho8HDa_XCcs#2VmyvXUifHj-pJAT@~uPq3&LI~B(0gUshC!QkaRoj*sE1W?Wh z5<39c?h8|L4PeBHRq@!eZQI{;-QIyO0(}q^6e6vC z@?NXjvl%9Exph4I<#=A5Wi`#qExT_Wy9jqX?i6sF&9*kj#Nji+VX(+&3c!&nAqfCN zDiWzE-Mb$GfQiWaPEmC=bARfUX0QGjarWOH>*5Yz^2cKeP={28$L==!&246z?x9?a zb9IaP1`#x~ieZRY6VqzfT)JWDT_Ljb=5T7nVIh+h5;PL4xn&h1?&KHB;mqwsqF7)?OL~E z-{$j=TKtUErbi+c1W6*63U&tWafHwjU={mu*=4Oe)7D}QkA2q$7oKUoCtpa&uj^q1 z=UL6R)x3uP=B&Av-M@tyouH!yh*-!p8e;HFF$Kh|3Ru{)FdhVGBzd~hXIc=l{sgn{ zy%+uWehBjxbl?j>eDqY|DhHI<@?R62^P3RQLof*LH=M)+8X`%8FWD?oI$l;p&sQ9@ z6JikIi_vHaZ}y31FJYFy=JmwHKa)%(XA%%AM6YABR9&1@2){;amuF;BOJ0h=T@|PP zh_rGb9bK?XqB#0wi*7*=M>0q~j926@{}UIuW!`mGkG)0A%hdqAPJS67)vrFYBN2#r z=femj9o+dDnBeC)Y~_utPQ03xh*RQs)R)_zwA6z?wCI6v#qZS72j#@CW~pdxRw^Sd z?u;`fja6>hPQ3F~=-5ebvi@JxX_UK)wfmmz902)T(Zm5LBKi-qW`BRA6)ml7DODz? zEUAwR@Q8vtZ~3Dc`*XxFfY1YhOD1*zsZipgP+64ARAh6-T)y|wbxj*?eCataOl7N6 z87C434G%qq3fCAaq*ukBbzC!>Nu0$XRk?GORqk0&h_7c^9|YOKSpl*g$a$!WjV==F zC6cqDlSvrlQwr1&|B8`JV!M3enW_9N0@WcsUT)u7j~DV|9JJXW@_Gn;?OhQ6LYTt* zxD>W8TQ9bv$~o~~eF-+Q>We^CwvuK)88_XyN7>TxMGzwVxhG>;{|JobW{Bma7$(&z z0@eFYY?J{(_`ec9moA)nc|B!+l-K)C{Rv}qXDeVzX`0gm5i%<-o(~&}^`J7qkfN8o z)gp|HY1s*W_Bo4IJqQfEQYS`0-$)G*amtVvYy?yxJB$h+sj_7*GKQ*)0ezSkhD0dy_ID$zXC+ip1#o2<~nFa|b0MHca#nXzj75}W(bX_J6IGGHpK0x42zf?DTa;^rEwpZVBAn+$0OAtj849VOgdLj z5qM@D4I?opO++;>&3Ec(?A%z(_OZj%7P1MyRqVxzi;i9GgX`Ie3^ru33q(Z%oK zcrU&eo(NHhSRo2AjMDpp7bw*qQdbftCdP_I$`5AEp{5L?b1N2uf4I&=s4Q1eoa*;o z_OysHU?}QeM_%gkBiBXD+8!(>)!xc;LWn*0bryZ_ZnO7&3*+I1mTH3zdLe$oW4RNO z2sqdX0M}Inl`2M{ex3^>kX2v#*sI}`L8M}vBL)GJR2R#xpuC=vZ)X7XLAaef9rG*r zBPgTF^f4TFA>JL-nZ{Yg*7s(&QULlt%YQ)2NBX~5S5YfkH5j$E;Z0k}OUd!1?04zG z^j?Tcj;It6RtCW`JAgV<7S#cIzWB(JW!ty*-ni@d`NzyI?tZc;^p`V_mie>{I5>YX zGey`L7>|`BtB8R^Fd{6|Rh#d{6R_TTPb9zy;(y0Q*IUJb?N-yY9Q9$7_lY6`DLDi2 zE3i-lhbWuIGF_;Hh_#G3BneYVB3xPTg_&YTB?9HSRu0_yPo7fZzKB`KNZ7L|2g0!P zP}g?#J0SkIaUA)Xac+syYK!!HF&-tor+W#($2av;j3DlV40%EXT4xAJCOt)*jbBw> zit?F0DK?Z9XW*er9DJ+(Dj1sJ>1>VL_}rT<_3Rf~^vIo9{XdHov;t;FdGZY8Yy}U~ zr3#&C^X){dymGi( z;$_nRJK&wa*il@ol5ks6tn-rBT5iD^i1~>663?ka4n;nS zl1YU}=&wZ?{~+uUml1_jB6_{2B;wXbN%sU#Ks*=sc_xoNIq`HODQN%wbKYY1x4$%d z+rP)eFN?j(ki9{~C#(u>5AE{`4)cdR+Igvu;xK3vmVda;_)H(rVFah?PAhdcm75`p2K+ZTMeiCtu8IL@*)PrHq{ipgN>DTgi%dZ8ijF|ceNR59O3opDp^Vrv_R?x!$Y)`&s*UGj$119-~ zU}UlW@BaVN|0T&Pg%vLUAv`BCRSIc!rql50Yo_5z`bQ(qa*b2ti|H!(g zjc+{XQmd(&#lj23;sl2sKw>s2*SOHCun9y}vk+U$5CmjERr5R*+DHO2E1FQ{_N5#j zwt?{DC%cWH^ZaWm>jET#6&MXMMhR&|oSMzy-08kfA`4+0)2TsvHXJ!sLm+TnmlJ6D zeTlr1H1xb=m5$dZ(q32%;yE2x`bTi#BVcr&b8m_RyvwwTnRv zxpC+ZNm7TC0AL`5==Jhbvek%Lg}zsvfr%Yj>S%Xj{l9>z2`AKtcMPuY&qu8=gF-Hj zWGw?A!ax*Bf(?Wpq_9wM7TB^b!G$|B^R3}KVp8a&&}n6M05_@ts7DFnySQxSn&w@( z?7sAT9UtBg9n%y#RH3~UHMk&;JYS7b6JvtsB0)BOGYhBtI3^4PENUYOzawV!ial$s zf%60#mj2X5|E}|1z;WZZ*ub&pfjl5Kk%c`1F-%98P?Yzb2-J~jENpcMhW-(e^uV;b zwwayxO0&QEl|_GwG40d~1#Dcl5Jevmi9U|JqP8O-g(eFoC8+~SS~-v>w>%QXI9~)K z_$boMUwocbi?fo=N4zY1!idE0rHuZ^`$1X6M9Q*?@bPERcq+N?hwKP}`2A+`!o=$i zHKlV*22XUQn+QcF?%U`K6SW+fl;mq6U%5e4M{KuA`AD9#BYZYSi{UNMj)opzAu&zUix;{ z{$Is1=nt?F{F~X%El32Ahg5=zwogn~DG(MCk~?R~2+Qh9O3SL@S)8-%Z=mC{TjRLY zTYws|?IClZx7dC6)@U8KQV??=@VoxcZz0{t`achps@cSrBbdtntpddTQTi)!R16^4 z0pvx+L&XPxIyI%k09*|ybT8Ymbz^@1iLALW;L0V0VCgK|!C3(u8}43huCpIH*d8>5 zyct}FJvtEa^IVrlB=^&Y5X&Q&LAG_P)i3#h&3=@<+t>`c&%DCgulz&&3eFH%V5BegFaw+6%s_eS6$lp=TmLoK!k9x-R_6rZ!RH%> zBn3#+2CHQG*T;_=XE`p@zQgL5-NB)vcVPP6Z#}19g0ZpIa`TS|@$d-jM+zXhu~3*f zQhh=?5xCTb?yY$6{ju5e-^e`(!_^215aH|HfV|~c<2C%A{Q-V4b(aFven~ZA1nn?_ zdX`CF^_P|s?-cR#c*K4i-ee6WLmm2Bb{>_PVpi3u(Jp-qe`mzsDriC}o;T1Ms~Dz- zclWPjm^!5Y0{|+3>zrI$G;iKKPAQYoQD}K6m1n`%EZfTePXh2RI`}7aR_MGNZ@f{< zfjTv)JX$d9-HMN|S-W+2_u#%kTwsgUjgg=3!%UpT)l>+Q#}F8wC@i8LmV}gW;S5oN zU6-$AnaY>f91AsS{wXm6W~itV&qB84iV-v-5lGb-z;mtriZ@!{saHC)P%Hu=V?fSB z5L&a2Bo?g}8%6ki%=lVHbhU$*Rpw~P$CeCZ)HgyAq2x4YQ?vg+wT(gs!C5Axltwhq z>J^Q$=w0u6m#YB2{!e`_*_H$5doW}&nAs$3KO&Qss&sPj?Af#JWiNY~hALvoN}ZdB zI0gYczjIs5{*8M(wq)4<-+Aig?lz`PI%$Ydfj>P`ux6d%i2-rjfZT^UMTcL+2%HB( z;@u~!ayeo+vXBF0m1-cwE!G}|0=OR6<9+NY*iS5i_11@dp#8GfTJIUpXL_h(CK4k+ zf`G}C41^+nEnCV{Q0Eig_x0F0$amJMY~dFl2<6B)acFqQJ)Pfls`^qZ86sAl=&bV@ zfA}eO`BARgu6|dZ;Yo~x`#RLMNWT@UnepX7084*!mGoJihqZULZ|U3y(Rsh zy-n#oeZ|&a?ksN5+phn+Si^KPFyIW}(n~M(f`!MPhPMkQDCu&h(+Jct(;x}|V+xdK zn$p!*U#%}48>Y@>dL>+VR?C6;o~2E@H)-St5XJjXc}{HjA(KuH_85V_P7i{FF;_`S zPK<;V!>XUc>Bj8<63?AsjDQ&aK7Ygr7O6H;b2O%$<(S&Gid~B@Hhbs?mYPMsNQD!zB*gCDx>jE(;xk6wsG1O9xD6|$ zTS^{;J|^WgjkA0FJ@QWQRlSC$Qk4C8cdl~JmcHVC?lB?7(SIQJymqaz{WnSux^X`X zK^3ahwNi?mfoZj2h7(OW@={O(!gAoNU;S#S0e)%_oeUPy>2#sKmuzg_)RC&}t=+ph z({tkaHlPDT`VU~-W4kXiMNz>og?PTeUe;ES%V&FtGm8~JJ}*%EH$*s+)DT`RJ;R@X zEyYDGH`N~Pp~-63-;0glJ65}OwGAG9g0)|AgY}&CQp)B)5g38*ACOZKKJ{^k@dozF zL;U~n0{rN|#2k_^AQ+KwGv!)m{RtW%m9l{s14i%IwL9`Awfo zbQkfk>?g z6b$M&JM*iMM7uBsvNr6?e}&-wE5o&XSjxr!;iX5_l}r%-Cjkuamm$B!2()%OmzZyy zsitR&69|=nGD(=Q$CfN>*^#T*TLTeSZ&_OGI{SH+TXd0Ca&CYKIp2Vx2^V3VADB>7 zQb$7Qi1nE+n1!Dgs1t_A#w0YlSOSp0KeA^WTtl*W%kXBaC3ymSvWka|~|A zue9!SufucXSQr7`h4LX#cyZZ`v?o9-2><;yOZ~?m6IZVpFJYL3n1RCr){O(Eq)|uC zmow6s$WjUnTF+8z$8KUC{1N9Eyc0N?@oO0qwNv81h+a#9_yrW%%TXk*gH~};I9#Q1 z?n+C~SDv^?zPZqKU;q4ZQuPyzVr9N;CJN{_nJQyBJ z=CRR^{vr>j!b0>%TM-|5trVnKa!nKY*k^nZP$2lw&LD}Yu3>~4vW)mmIo{tR5apry z4;HK|_*1c(Vc*eJ+;z{c!e-@X_a7Dv-~KE2Utj(!ZT}6s07n?V3UIT6rOKHmz?rW9 z&^{k_N51MxNl0a14dAEj06A;|k_LK~uW#C%BT8?c6MSpe{L-q?#k-$*CBy@3p2ZA{ zN0<+#2)8U9a$X=JXUqVo^Kb~F!Aux{&*tPSY8i^z$=B{>mG*7CWJCQ>z1o4;NKAsd zwZC)2k4`k|y5N=UH#n6Uh5T}5)&`+DtO~G0(f;GrX219{{$<_HXnd9o@PzNOcFgFd zcWeTI!v;!8+R{3_0;I6cmHP3&ntkMD7U5H2^ALNrR2PNtZ~L*ZPG$q!J;A^8#7SC6I_5iN+on%}4~? zK-?Fxvjhng;DL0@QrY|zBM^eJe5hxYyH;BRPDS+`4Xx?D`&rMk&S$^MgO&IOpRQdn zD^!E}<1JcA{Jl@VnV=#MqAtLYBnh|(3<4iXOd#n_he>e4&^&g*Tvj=BUf;)V#N+Ry zSp8WIk@DQjkjbz|JiH=?+rr$%&g9bU;!Gdd!3G(rRq9_6>L^JBq{kEOi%{%D-iyEp zDpP#|>%rZHHC*y$lJ{YvT# zK1gb=>jy*MU0<}+t(Rfw`wja4@fc>+2TY-y98_3vBC|gCXhxbtGSeKj<|i(xIA03Y z#bJ_~PKu>1UHH%O{ZU>S4rR(4?M2n$Dn?S7a&syDACqykrrlb&HC+F91`zaro{yg7 z`yuQ?^58dm&_`rEA+UxJQ{Zm@xqu5#o^X{oRRm*(&O zHO8}B@SEQd*CS@)AX4slU>&P16MbG@xB5X+nhxtg*H88CGyCM5QDfdt_kiGjVo#41 zXG8eMp3b+7vUyH!Qk7;n32Tq|6zl+; zt;V>dWCzg5>yuAo2S69-UAd)Y%fRVp6M&{Jm62i1jdbkyM{KS*0J5)3|6ULmT@6H%Xsy$NdgmO=?_>i}K!6c@NBoDxdl?XTaPBb-CL-=w#F&&B0*nMl zkPM!Sv)WrYi05eQKaP-P1S#n?uJ@!d|>-6m7AkKecoFK;ZzO zqk(D%V3Y?x3p&yI6`cMn8-N_xg6djceEGF@tP(VwTQkc9X14b79vR_!Bs+l4SYrwB zV3@FiB4=3i*or4M@3!g#>A8#2Y_p0|5=A*TFROh4i2t*m_!(P#e`fjF3#{!qH{rVb zTt~oEKSno};nFg`;o(svqHt^oq-%yELK%d5CISE>@IO}olJPN)qzDnKW0h78Kw@_@ zt_OxzRF`|Lk%J>E_V2=%;6&?s_I1{G!ueJ_`k4?AFcNV%`sRmdTdYgF7^(6O3u_tJ zlX9uLXuD=GiLjE573ufKpCx$73oNy3quGK6D{v%Oe$m;8{0k88J1t8H@XWq#NMNV} ziUK6!7tM~3Zihn7t1;uF{)r^@lc-*OrOS{+_?igZKY+HWeFsy>nHJCc-5z^_M+3Zp zk{|p29BqTu}pGw`>A1beWkgIBWa6axc1czfEfXvdMCMqa4u*#H1Q07*naRIvjP zCJ8KBpBJd*t1-KU9L)M+0MbW&H=v886X;SQq!VxAU5G4Ptme!+jDd1as@&ad zHJcu@L3HNMGq1LuXT8u0$6t(9{loD&0{gatxCn6)wH>*~1o~-ba2I8%JKA6G8vtZL zo4uSfH8Il9|CJEj3>A0 zD;*W)X}ruY^iil=5{&OlNF*6C<4~68jWg;mIno2EOn%w^sF|NS5rGbf|=5T7;T_cd>LXqL zz>JaBQXNTF?Rd>zKc2$CI$^*FbPS^0hABomZe`0OR=@O4Ry9_^9O|v>^vkUKinmyP z?`E?Py@=C#?ss*;9ft(SlqN_}L?f@hS*&c}Y?b=(^&HmlH7lHTp5>2w263|3IoWx@ zvV>hvw_*<{*VA?VRQhIkn-W4BZG48m5_f1qn0!SXlje3H&GurT0ZzRb2$(_88|eQ9 z9edc+8EsqIVJy{<9kV8Fk^V0aRoDOD@P;?&(b)RGG9hskjsWUA0zgo2jGYc+2hbFt zIDz`^TfMoZsc+W2h|u3G&=B31%@#2OU}kSThMYY<%SV+2@j%?JTk#{C{mAW>W$CHw zoNEx}YmgS^`KYHkMaaGNWT6t$meX`XtP(@h12QUtBy0eZQPBPU$8OHZIaF6twrmzf z`9M^$dO`VBJ63WE@=tBnrX^G}U_EjwI^{CUeeFGF58zLqEEPJKPGsc(k6hh^?y5`} zUL>D=B&yW(;yhMDGpSyqw$s+~HmNH~;l_L~Nka#Xp)uT!r05KRT)JOc>gR4}T@A0V*@k^sCXLSysLWOH*9wX;^H%R2$dpmX#@Z)>dxb!Y|_8qXnqZShs^mVbf9ySE` z-`9JZb)2xuMht{{ln~0mTf|pj^Gf!2rXd-?yv;Y65>~I{8;_kdu-Oq zUvWT5&U#P3$~qtVz70I^eP?d5yDV5M-F!H_;|3%U0560xXutX*RmaAh6~D5SdAZcdXS{bnHa}z%d2GV7v{O znE;W`t-&(Juxl^7Yb73pYaLm&rMUgV>#Xa-8>}$PacTi-`{*BNhIIJMBM zRJzBqefRAxZsw=Xsl;2%2is5o;a+svWtVw@VWRRMqx_Mi_hEp9YEWIbN#))d2;*e6ht$~jNgCSa~Ud@o_yCHE~h%wQo5dqSScCkbOF~?jBjaK z*?C^K?mlZEOt|9xwO{f|>pJgM_y}OlE@uRKU$Y}iT~Y&d(5SovxxweD^VOJyP%U6A zF$0Lj);ux)<-Hq=G=a+Lk?tS7rY0FIf7^?;uKYlU1}J>MQcEy!(PRJ4g#^gs$~1CMgCm zS*cW}`k54WSbQD+BCSODW<=U(1ul4KciAnJ)Bz*C;}4TRF@8kqdA9oHYgr&z*=ieB zblGlNtL48w9!dXKklKPgRL?o*9Dg}5fFQXhB_8^9g!lnGbxb|9NmG8O%nsmqEaMB6 zmZSh(|91JBC-)R8+h=v`%Jdw2S~T#CtF2<+CP##P{Q`18Sb+fuVrK%nKtS+NV%kAS znDCazhy#fK{+j#PP&A)C{;#&4b6%c!6v8N&jXmTvGi;_0+k%2r_-q}Q^_2hm%+0XK zQ%uC88Cds1N~SVNGOo%Xt!Niwx^tIRbD&B6J)g0vFZ`($c5kt6cAWKf6ZF)v9)hNf z;kZAXAvoevwobem#K&QP*|LDcW#-H8x5_X5Dbbej(^oik5xrveyR&D$ z@_SG5I;lodMac=MyhZel&-mJ$g|sFQ&yY~Ou|pH+4u4bmgDNA{0AfgGYOHF_omTgWSF?opI1HpV z_^0bwFuJW&Qh-!aR-Up35$#3v{VbH5+bi^de|93?Uv=l0el8N1h-&-yPD_qtTgO#`#@1A z`$$h82LhDUKJ-$!1G2_807Tiua$QnehUO+OfCeJdGa?jQ9gGf%pyJCQ6G-pj?-Js6?WEQ7boMfUNR3;mg@bdCTFGdUlZSjRJ`Z z1#D&$41a}M1h7ZitJgfyQP|EUekZ9u)Fa`W^naHCRmi1sJO=q!8clp#27hdhqF2TKeAGth%<& z>Nv-57RtU+_9(Ul9_u&ku z-x~%HIyLOIzTt)&hC~b2pkVPH$Y#>HeY7B6#o<$fv;A`cqSwX?}zWvkVecx^|ETAU2mz{MHYSO zjTZgz-?)$KGS9ORGUBW6TdyK4RVA}%7ABx&MXMve{sXt0ec(!@h@Ck6fFf-4aeaS0 z^$WIySe@ux-587NMrn|UMISE*89=rcC{Y`#d<6!(&B34{hbxByRE8MpXelOsu%TeN zLZsP0y{flyZ=&Q zvEQ8R9KSaVAT(T^tj<>4^$;cuDL^CZ1B3BfQb7J|cm4YD4TDv?iqE(p!};@3-KK|_ zA$qL))Qhe6nb)Ec$dC&`I~Qi~oN!oPBTTpmE{+YrQk;-N_$#EwE)+8YEiW}9D)-u~ zH9sZT$B(fc^jY8Hv+yd!z4*k7$O<9KMu5SpM8xAJAfX#gLSz>+atT0ij5nz*VqC&{ ziN&)|vD9~Q82ak_Sk8l(X-fpL%X=_lrJ$(U9oQn`^`-9mPqrYxn%{`bRPCqvO6PY1 z94`RmQ=hr6o?K7hRnMya)`tQ0vJ?>*8&1SFX@J4Sct0Du=wHD_wo#KyF6vXtI$U1$ zmsULlH~q&^zbV8b`__*1ZwrHi?F^$3rjOUPoSbzXEMo_2~d3-W7MpooT9TcN0gD9!-nUnC@y7#os)lh+vm z_oABl*Z_7g>-SmJuFX~@>-(8F=UjTT4IF^u*8SaQ;sqEW;2scwMu>UZ!P-&?VSmfRMnmT zU%l#`MY`Dp*Q z;zC@aC?YCufT-+?vNbe2G+Q_HzE;(H|IhbVb(^ATHf4!W_w?&m_3AD6-Ftug_P`IJ z`cf!@Po?T|3P0WJaGMZ-#!j8LaXf?@=saywFaq2m;z~f2W}sS+AZ;o543MSZJCxO{ zXQ#M0A-OgiV_;dE=_sfjRx*-k5A~lP>$&>zlKKTaCJ_%K)m+;|?1{1elK;DO=@N<) zRdbx_fw%zo^ni7+^Q-^Y+d1Z+nO`RcXq_GVb|^!5?0a>PCpu-zkRd~KWy3JDCrp#3T_1b;)8o^hVHYOd$4Uf>1Op7g?>k+SR0 zzu_F@ebp_{dmH6<%a-ql(%k9nl``EBt2?%z>+R>Z0KYTh3ppL-(HWsKlV7zv*{_!S z9O9T5Cn7}Dsw1og%gvzdPMC-F&Los#E{XHaJtcf>WAIMAuC4-wAb#axJ?21B|0YNM z$BY?c6@U(p{pSw6Yct7fnmhMqjK!%Pza|v@g=r4EG6B}-5hF%;efsqAHf`D@bD-sC zW8VlOs70o&uPNKHbKKMq-+kcbFW(tCZA2uGkkq;X!ws+tKb(Md;BUN|Ay;`hJ zOBmwiV?j`TdDQ~-820G8cGnrUf1YeC_qCzDh=>uaz(__`ijNFLxfM{9r!=vW9%{kfnG(d zcRCVD|14dw0)91~K@y2DHy%WH=Ju-o&ywahz2?Rm?18ZUVLgD|AP$q*e;gy@iSK>yd)}x~ zqeyxbw;&+5E|WKF`@e=nP3xnUK(5-eMlmN&1W5jNtp9)E5P%yXVO%wmFlbyg5rPgO zfhMM03L9oO0*Vc4|Kr)oZ?5x;%Hn5U7|q_XE{rSvbh~a$I;OB4E3xlS^a?hwpuE=* zuVVC_IB1|7V3Ea?sTS7VJpQDVYu!vi$Z;r)5{iUa0#vWg^aEn~0W`VmWbVWJGB5Xkfj$-OL0$9nwDIGxTM}Ma8x~HBYpld?cA+}e3133G5 z)GLq$6eTglGJy^|R&1j>S^(P_bTMVeh7!Zj!$` z(X&~aAbG2RvAsGbuPPe#8n|gkUDXoMWbYwxql4iY+ zo;N>$+Ci7VYUX;eBG_du7Jj#CKI%oQNwak*1b}i%&P0}h*Z@tU0g5#t5Lj(1HBM?W zl^X9roPq9BJ>+UK{(Otb!~RJ`AUy&A>YRUkOpg{!b0~|wWrPm)frTevj7kJy`sE-a zBqN1jt9LutoeuW9y@!BIfT(xSa~wEfdDJj#fE@tW8zHMhU}?z?q> zuZEos32;g+LtIO&@NTREO;5i#Y3}&>8{dicJBJA2`a}%5Kcow|oM5#L> z7G&WM&;(0bH{^PA+l){{&;Z7$bn^Ke;HgQfs?0!YDmAW!X6M?Gq}Q4u5vY$SP&PjM z9T)z^>qu5$`P5ep9ZSKGZ+P*-KBkcLrGz6PiDbnj0$>rF*mWk*wY4aWB*73HQriVGTfFN;Y zLd57lte&9WReeh}Nf%Ag{GcFc$Xb~X^Q=!?wb{lEJ{n-fg$tCXT?k06JqOo0%ol`(8~v5h{se!_OWZ z|K@9(n-;b&Xcw)|X-5^uK>!?yT|@P)b&FdTlG&-%)>Ccb}d5^WeXd_Ju?M>0>90fH3d_ z4?GZ3l~qVUVVW`tN@jzUdXSc|8i$?=ud%M`&i{J+`44vmt2*RnN2|M?LC!5Ggj2k+ zc*GHe`JIv5kwsAy=I&VMl z?7HQ@pL*t#3U6a}_tT6SwcZD1k&aAPoNuCm zX~=mnf5*J(ncDOY22p&GR-f^RpEl4-^uhTFRy<0bzpPE4V2>$Fb}7Hpv+6nzNXQFNuCbx0 zGG2jM6zfpMo<{TlW=6uKm%SacA*sdN7tcXI&<}6In=v1bWS|JLp^UXD0p(GsdmL&L z5NS&xf>2?_I2dJ@ud{4M1*_-cm(VV$BN%)&TtPPzLqy#^J0ds)1l3mm4fyy$Krgu+ z>KY+PKE!+$hs}=b>&HD{ipG(;E_C zFCqtbpHEB8&X5kb= zFq|8rWQ|zjPn#0!Hua_d|EXH7Nd z<>e6v)EtQQA3b`snVl6ysU{?FBD50%0XU4T$&ppy(n~K5XU?3d<*GV@h6GX=y-t(N z=Sm@gi0@Za?keAT!@Uno7&mkI`>`QchV?_f7K!-b`V|S4SezK%#*B2QpCGY`9s+ATb^)eAB1eQ0iA;2fO2j@N926IMqu^*3Sd^b3)1`R?ySoY zmz1+1iiV#OJ^`Bjh<{4=aKi3WXI=YV<@iNgcCYgzVI{Yhd?HNkfM0UIlzuU-n;UMp zA$;bUXUyS8#N|Y6Ckz760Als^ENS4Seb;Y?+FAf#_ke8koJ6+)WNb#X z5;8&O5h0D)e#w>|$5>G?7Q|Z{O5Sf@kmr}?_btC?dEUeyzq{$>hMKx^@WF1nCYfJ= zpAP)e7fE@8^1us!`qQ88)pOR$Uf)lIFR7J$BD~7p?;wmVj^OF1pVq8Jo_+RNLjuZ& z$0W19LPEPkrDhkIiV;E_qttkpH5)dqyYaqlX6Qn z>L>tr_et-*7MdXd+>kMAiIYe=9Pun=`O)*#ga|-F*@y;mDvEU(;5GIfn!t1V@1C1HZT8aQ4ZpeL`fpr(#ZCPa>pl(Qk!Yl;{{@k};&%`SP(oq*H;6HI zm*s>iNuNk|3e#vYvYu7q1!`fih0zuNgh(PkL z5RJ@URz_Ta>eioIhz=2CN$Ds=P-2#YSZ5)Efp7z(K>%eSo)Y$(lZXSqdvOmEC5*yz zWfGR)WI&h(*gPk|{~*@=bgQN;{YF;lD&%|mZGaB~74Qr24O#J7eM^T)tSQF9q#xsy zeVV&Y2|(at0;&nig*QTYb)Ml44LufJu%#_0&@;C?XMoc@2THBobg9E3s%S2#vfq!3}htvuxGMYyRbT zJMO#vZ@+r`=%JSt#OiZudYl>LY%TWvI&$MH1+^;n2$*yk0Ik{rqMor%r+T^dq^-h4 z)nU;qUcB2`SPL%lsxJ7tWeqA=GZ$|-s57wvCa)WN&x{yw3)m$B!15|>7l2{+H}cGH zs#UAz5P>r}%8x)C0o*_~z#1#+PtemDUfsZp@U=h3%OSiryJ|atw43|VZ^_XTVE}xC zW%B9ANwgU+>;apPDG$GfoGp~~E??UM`1WW!Sx>WgdY6Ti!PVZ}x0ZYVB6)LrMh zrq4nSlJm1s#}RlHl8#-Om3#rdVgtlYFbo!D$;FcE?H+o5T8r`>1rOOw3%vGOfj_6I z*V_9Q)=mAeWJ?KmMw1$;0MpC6&m4TXRQ~ZyZKSjo*BBxY(5vE<)^`d(eB@rsGobJL zYYq`OjzCC7h=8D@Y$6a76h>|8+Mn#j{cvSI!vH^`s5tEO41kFSfkKygs2C!@2F2%^ z#a^^>OA_z}z%3M?PN**j>W})bC_ITcd&BCU>i~RZ3<}YeSh_C|sITN71Yrq*B!QoY z$j1P{PI5yaDkq|-3+e->O4iTq*wL@c>d^G)rmUI&K7YptbyZcn!5=D+q#KIWmyMs7 zZlU^M6C5`S3KEEg0hN8jM<0FE^hgpNPX^%U{ZD)ZpqB*8n;a*sE_?3#@4p|C&Pkj> zLWqD*a9MqZ{Vo|Id)G$ke}AhtnXdWm*jV3ms~WP%6+c@@LslPA${ zfHRPWy%r#BQZN(U72w0k8}-(W)U0#KkNf>EJSH(iIRe2BU?f9|;QzJNrxHoj3h@LDQR4m#r`MgSgUn zRWqr(Ia{p0fM0th_E5$st0>Lf3-~?k{2``3e%7s%J|)_VK%D69gy#S>ftpB7sIa=Q zyP^kcYHGqC{NM-T3KB$hiLWMHfU*xMB*6L71S2OXLv+x@VkxSwt_*(s;Kb)=EWK#; zFTQitHD_M-?XLC9KMD%Atw0EkMoh`(=XhDWH<5UB5N5KjxTlu0$blkJXRRj`ce3s0 zm3-jkY+dO!^uK`2Fc)EF{E}C&cA=N8lwn%<^wr#36%i;v;l36a#7_rpZsDdx1FKWO zMS5-)jx$-iHUNQG2;g}vGqAV}5o?|+t!S7HV$ef4Klwpl4x#D&bQA5dl!#@wBpzbr zV>-_BPow)kq?-SWgKNQW^!tyh(rs(6=X5&}T7<1}PpFTaj;)*MbLv*IT zlLh!S%;3?Jqd6p?Nq5_mPd*uH0q|X$7DRd!q1Z;*h<5=`yFd=xpb0y%Dc8O~NKPuq*RjrU0g5pYHMGW(G&UVdkN<#@h_qY& zme*EOYpHR$*h`{BSUv5Ofh4!9Iz^|b`X%K*3wlxwE zH=qTeg+P`Uz6J@n1liamA2x8l6h&36OD&|`ERHfP1zSNBH9O18x7_|;PdxjRr=~r( z>68Ig!6o1F8@imCV9|$>=1R2gWElxp*2>IWXfU;(26UA2(Jf>jM zz8~&CQF3YRl_UjjB5~@OUcB#6kWL;s0EllW#k*2Eh!o%-@Rj82&sh2NEuM%3evTi> z>DtGeRM&gK-@RS&#KgHJ^LU?1kaHO<|2Ddf%x{~Jw;aAKq!&Gq=2eQm;K2tU49ATd zXJR3YfDr_(4K=s7FDJpjlR^YYpGP;r+H2DS5cYR6psWOZH*o-jAkdX4piK%w)bi+@ z0*M5XLcq81sW)bPFl%Ya#{azY`s>Dy_?vUt<)?HkLZ1|SbPRUDJ->>Dy2l?Qx zlfuyw_Gzz;>-5_Jq45Cs)<}srw;^L$@jX%08~c3^K9e?`JOJ|Bx8EJb7rx;VJzAIV z)#!(%6_*oKby+B1r2L{ne`{{f%Kt6)-hOmW$-J<+z80!q!y|F#u~hI*))(@YtZ&hL ze5Q76Le`&f>eQ*B^guH4sYyY6vQho?ac1UE&L-g3#LM&pMK@EStOODXga8;7i3C^z zeC%Y#4M?7>4qDB^~jU=zBKFA@@@kgnn&N_$KtU-DZLWi z&H#0w$dmVATfjv>1mMOl%Z!JAK<<{sR2zB`E5R}|YzHyF#U)|QmM>Ty+Zzy66QTW6=;0X~# zL3lYm`}xy@ek*RAUi zpp&ZKy)^Bezx&PjKYg$>Q4$XOs>o_kSJWHm<|_FL?62AH{1aPFA6gWJYd<0{z$;!B zTJnZ|L#bwT1I{^L0$JeA=t6;y7a}kKrq2@0MfCxGa~?YV0$Be3u=urJ*7`+WblbY* zI~mpIJY9EyCLD=6)y`vQH1u8aSETNsR!~7l0Om7Kobm5(!v1CJu1O z6oecX5|Ak=$qgit08S>s&O{h)ha0F}uzJI)YyS0*Pu&03k~gU=+nm$2r(fT7P$JS; z0lNXac_4t*V$^yRBET9M^|9lLQ!fER1CN02x-*(>IyvhYH>b7RLMz-18s6Ysm|z?UC+(D zr_in396W;C`}6AyOK$zR@yTCLTk>vmU6uU!cR}^HbB%yrx*7TMOV*d7uaP+t*QU4+ zL*hc_PSK}%)y8=zJ>WBD)=qi^;08hnKnueyk~X*jIS)BYK_lnmMrtwS#tC4eBdACU zLIR9PI`VJNy!QGHzkBhqd8O6sz4Nb*)}J*xlpGq7`AvwxAjvIz*NfC%hyZ}rxn};u zoaPhTUa)*J%EEWdB2Z0jfm$*W$*wQ4KnTNh3A#lKl&0=?BzJ;P8Xgm-)Jx#Y(2R$p z(xGQ>Tn^P=hQ@>#2ALrB8nQ1Qq-hjH^*KFMHnE&jiXsi&&uM&UbH}&8`TDx2XM9?+ zoT|oEuu?l5%g;3eeg$-i>Py3~C_ZI?(i{as-a_JR^%vi} z=E^(2GUD>WuDx?(%Vs6=syF+1{+U_df`SgTAc-%C;=0>h)RWw2-OR-|d)Hc$lnnG5 z`<>@i55ERq|21CT=1(yt0uu@xGQR`qEdi4Q-P^tRq3p%Ea6g3vhnNfFJq8kd)XO-K zY@YAqTVQxcjzi=EkVjwAZh!EbOFEDbl$V_q=6CDkFARIEzkhD!v^N(nU5QgqgUoxd z{0jaTuq#=obLx>Rueh=}=Dk1#J0r283eGOxz>V7M+DAX z_z(Z^4^aNlD3le?Rux z3oiafpZe8{g1k)&@oEkH7FXidfXa7^+yj_Zbx0X?RcJ%11c?zj+n3|&3nJ)wj@Q`x zY;*(tP#zY0xuwg^W^SH`0$Pa^YOU{|JbK{&NzyF1hvEbH+%B#Fp;HG46%cc<(etrf z7*Y@vJHo0M303h;65lKK{+jb<@kYZ!H7x z8=(4l09o{&1AOUWq~#azTgomWW0bj}%zNMe{`bwir=T9E;n!m`z^_r*w`CB(zGGrX z+=9_wn--LmN3tKEfByO5&wlnZnU+EpnLRU&T9OZZGl^t2}QIK0xDsDh92HOm~oDQg%&lI_*`~nIF3JBJT5^Wff zTQS!N-f}lBq`Vof#l6lYU%^1H2>{L8vYddUVq?XX8cQcCsK_9>1xoqaR#QjRB;#v? z;_H$)m26`@&w@Y4mS0}#WpRI>T7m&U9-4xWYa|w+53pO9>62r|7U+h1ntm6{pQQRx zFTeW$e|fZL>3=S)d27;~c}uB?Bv)RU_h4z-r25M1piTH!99hP^w`X_{>yyLLhjr1( zbh3aSpl+ciG8^7=&pqCUAAT5KcinXc{4BsYET<@vD3jRKYA%tCs@U@~G*Z$HRL7c| z%Kzz4FTQm9gMWEyS*-mw@8awI`hFLoLnI&xnb~Ccbb#TrnGZ=;s+a(>*n$YqA&3ny zMIpec>PCFdquOkTMXwWwKsa*$p;Q{W){7B=sHi{78z?iRV)OxK14<#6)|)0)K#!a+ z8J}nfl8bcrn$E^m_Vi(v_B?mxEH8WG68b|8gfzg{rGor`czjD`#`Sw4GyN>c2k_-+ z97D&M)u|{F^t&MOT=i+QzxC?6@smDUw9pS?RmfF4lZw7DUD2m;RmPru6nz=<9wYM^ zTZRKTgn@HNKzp&3iYz?uyz{(QUwt*a{A(Al*PJuqe_1 zH_-X@IiD>2cxlPz|99(kW4?RoMI$3!du916KMr#1wi8&S5LCg5lxL#80(NrS;kc4$ zS7e+(i{p;^ZXSgoDO`HxT3oPdhL^jwhu7HmJg<%zgD75j`EUeT6`PXRlI}d|nhY2c z{2wMn1GK>*1eD_@`c9CndIZhN@>|#qPeq~wSIx$zU&b~3DwkoI{44A|`Yd^HU}j|{S+VeV;X_-k`}?fBi2ns=W0;N!(%thp8>N!Dg_>X9F`EG_A> zzRaKr^3dhcE)^3rY(O*3&r;c?%aV z46eE68YPaj(qyf;0q06AZlFBL4fr*c4Gr7xdH97F|IZ&@er82*%C_+0Z}|1ShX(}b zP_4#s19VJO+7y8mR*%4xd^+X??9@UxMN%FQRMn3n&E(K;+~q}494hX>DilNgiz_e=FN<~=v?pKqVx01k8fga|}soPtTzXZY~p-gD1A7e4XC6VeStNJ=W* zfLRJSU(h11Kti}I1ac*AM#eowmV#GiFPS%US;>ZfyXD$3H;x)LGSaON+`#NaZvBp^ z0lNT=NWkox9m!aJatkC)Y^^9GBI~D9eRJi7>Wku=0z*zc=~Q1`a*A4g zPDj%Eo@oakfeZpT_!#YTM*2dNxk*Ssd+7$qMjYOI@4eoXDO2Qoj($)9hM^RMh6I2K zvlJlX66R;_szsG`^@$%n^uh~oFB!J#mn0oI`=aZ**RNg}&1U82tttX-Jz5VvvgGU~DBxBxlV}6A883gb- z29gEL8V_wzYM-tfxbx0CLt+bjMHjMwr5jMLfjxSG^uVGNH!%65#q(w@TeIokZ@K2` zZ(n%vg?_i*zF+)tBB!x}%vJz2ioh63xm@!5g_s4+YS32yQ`~?>`+*2tJ^<+ma<(q@ zNS8{#Vajz+tP3F`0clY!8BtQJFNU3FN<#b8)>D$l^fO$8IR+IJAxboQQFH;IpfC~j z!`!}u{EhAU@A~7?ns*0wt0jMxknFJMBBv)eDv=vxH3<0?Y zl3~W%SlBg{HMLuR_=o48|NbLWo+%DH?TlRb^=Mu1^SNntm;>7$@7M*nNdkguwBNfNh#bJ zw|!#v1^C*8Y%}f1j6enf9GP)6`O|R&$|Nf!aK#l@cncORFmoX|Kzwveh=B9tx+bz^Nr2f%KMRDX7CFHutJ~A00P--rCvCXO0M)Mtmm{Zy>R)%8%uC0zeM~2vUeZ zEV+V+3>nDEU>n^MLcy9~+WU0P{vnQy-dnhYf*fI@p*qMp{cL|r-%Bcfxgt90?;m|< z;nV9`U9Q)6ES_T1p-Vsc@o<-b3 zog6nXc<^9v^5n@OzUk($0yJ?X+L?CumA^}9r!i=dv=slXz0AX1pIaRUSvZA)Y`Bd-PA^=akI$bMk`NIin*@1ziIb$CRg_ zTx&$#k6BASN}0|=v>GwA^ErLbiZ}NA?y)kj|(&Z(h`ea!oY}cs10Dmv4 z&-*KKPh%h-3L!~Dmh!O>2PMU-8_NvvAD!9FAb_JYuC{P>YjFaiWNO+gu!x23!i-%` zdWi*l!i-(g5#rt9223|lR#RVk*SJZ~-}%@Z&#a6UY^S8T-_Uy)sV)>@h~7+vMuY^8 zG9u9JsgGj(1K!0!q)X38ICxArEqd0{TV5$1|GzIzdoR&YQ=XLdL1M}Ld#n<` zEL6#|fD~1hwu;4`G#8@tha13;HpiGTo}Kv{WP0(f{Y+4aThi`vHlbgRuE(MY-yr{NcpbgX{{@ITu$9nMu54&ZP` z>oc6IMeG)`kbpx3jvJUUV}`<5fsHT$UTOIRXh2?3WwRk15-}oyXS$G^6?ezA7lh;yj~! z-$-KcRpINAvlf4E(z5Xny!h7p@dlE0_$KQkx>#1;)>OZNcWwsRwV9RIjSb_#$Z?EDz~UTHIRfLI9F;m7o$=;?TJgqwsQpMW!3zrdGRtFWrFWk)GVI zePhA$MOWVY#JE4sSUIDv@8Ec1=vcq8Ll5eb)h81VnUF3B|BQ`Gbu}dqa2G(<&nXD9 z295GJ_q%NOFFuXGeDlMvJ-6hOr6%hGvOC~z_R7x(5X+TU)()BWVo-hAFQwsERHc-n z#_nr89KdVuK67rS9pw?oAb_Jhz^!kezPJSd3Ns!$bg1{j3olqayAoT#4y#y+{07of zkXcLth=9`8EqS!uksuEkHoN-mE;Db8&D@i#t+ug zPcg^`7`gXtX8-^I2INUZK~%=5evi{5!O*XHQ-c9ZZhC&n6A!)g=F9+DAK*>aOxQ#9 zon7B@@#{T6YB81HQuL&vDADk%PNPx!jE4VUlX7%_&*%V-?)dKa?m`6O1_S_-e+3XS zeW@z#efQnxz4qE`mXMU=(hXp=w#6{JIqoR^SzEHcdGkT_t?nMlQ^Mc=_P3U-BR@ai zQ;F?;XINBMxA2}}D53;=?-~&+5IZV}AR-;s#v#lX%}Z<=*%E^F7aZ&+}~OaORx7%3f>j)y_CTGp5+_{_uyZ?pY-_ zERna$opq@Bg$)axax?m_b?xS$vH7-*Z1jk3WgTy{Twu39VCM__dXDF(_&iyZT)Q(b z?orF#Ybvhh+Fd*|(O2Gg!nt>uEo=?9=kL3F*lk|v{t?s8t-9L9y8qG&?+=aN1Pz*e zM%YHyv(&cO&9-1sglnTl3v;*XZ2WzmZ#=2y(mtLm%F*0jDROMIx^D-rL?er0wuW_6 z)~8emGV&j{kS+T)cGeLU{^D1Uwga-hK3_Pfec1G|RvFy0;jyO zsYPBhzHpwJuikH~`n9>@xkk3|h}31c#$fK%VeZBD^4-F|?zd`xE4$%8dguNhXEi@& zE9*JkZV(n`o3*O2U$T|OE*a#meC!un^7=}JRYm6WDOZ<%_!^wMS-Cbj`k~vaIW8Y2 z=)&}_EAt1~HCbNSV)B~TZ13SkCz5WAx@t3DR69Zxo%+OV{zH$J%@c!1>!08GDt2B{ zg8X*e?8<_}7l)R=8!=dEw%a%A$d#vi6!jEnB$7rM&XKxogt%?4ncMHO1GvUz{IT z_;_)Z-}V0A=3aNX{ObIILst*^%szMH?bjWdZ}B&3#d-X-_LGe!CY4?Hd^BSDNY^gj zBeJuzr(^ka*=iTA_iS!M7(BkD!eVRjsOF1TJ*{%?{oC>L>TjUt-M$I2gJkEP zU!2W(T+ue@aDQsa{d3z!U0z-(8{X~I=1+|Vd~9ZZ_FQfMuWq+4D4da^c`?u>Hryx6 zwcY%cF@hsGNqG)vhYIdy`|flH#so8aK+ub zCmPQBb^Ob7(UTqAn+~7T_IaQui!8aEGc%*N^+2l|^6eKJu5K<%|L%u+#}_x;AZZ<^ zncw^J^mo0px<2kCY+_mLv&qbRvv1_^CiRNLWJG9wTDw7`Q z!n$uB7+*4feS6)!9cP+#^jiAl$>FfREBEFIZufm-ws=*W^b<{Gy-F>f4qW)<50?+g zJ6A1U(VMHKcwg| zZqLikcL%t>W|!~Z&U!OMFnidY2Z9Td(yKPZu7rNquub8D;{j#xb{T%hhGd5 z2B{C-s-80RIRdQcyDIHh10YITxA<6{+LPSX|qsfpfNh{o9MLG+VP0v-{s`<#=!9%>}9(ZDcXh zRrQ3UqrQ`z*Zu$vv(z{Yk@UI`AfXx1y<&r+N7CU?To z%$%doUkrQkYWwDAg|1!Jj9_;6lTXWg2xI$OKGOEu^Jc)D+}`c(znnT=`tz+S|Kqe_ zHpy~R)eNbazl|FlBYJvo@0Kfe)mHj>h1r9**Sj`IgBu?6^6+y%>K4DSJDxk>h{qvd z9r52kKLCJfG#W;qC=vWk78Vx#r$8Y1^N0T7s!(VyWC9`GWzG*g7YIfCg@1VT-@Z2C zV8V%T{2XUGSE*F|wFL_nuy*a*u|0eCu$3!Uvdx<}vkefD|1w6v~oJF3&*$=llX&)!gg*_3swjVPeziXY8oGPfSk_c%o)3bjz zFI_Vj3_Q#aAKtUpty}W=FJHc#zh;sdy3d6Fj2SbSkB<-ky;Le?I-QPR8?{=^XwW** zb6OnL)iwP0TCG;Fa@9KIxxh+fZtmSbc*iw|Nr-nm7#c@uwaibUV?G8$(^xIEAF<}5 zKqz!K7nxgZ{iaZ;Hy8vuyoKh%dHA^r2EigYgcs5H{P}a%tXVT2|9|4YzrR0^|KIW7 z#Fw}4%7mjly@}@?`V5OL7&G&eybt2uAX2C6m8InzCveaKe<7w}xSV5S`e+z#!Cw3$2Exspuqz0H_E+hK@e2KwTi2O zw((UQ^Q+RaKpnIXy;ot|-gO$z)ifzNT__ey1q8zc%Y^^Cw{KaC|4@EtKH@ulo;mZQ zu)a-GbCF0ed_?4ya|5OVI85LSPUBU2!n;56yjeVOIG}fy0UXBbpk2HQ8b&C9`Qz}F za}6Wp&^Ss4HlgKu!PshpRhXP>I%vspq@jPCb{$36uixZp(CZDqufufdVb)* zA>z4B$L_kV)Xo;`4~nsd(88(eY}de1o;XoElOWQ(}>$u@w;EH(6-sE5{x04);$ zbr--o6y?XS7WrR;>mY|#aT2H(BEhu~b1Xmx4Zg38oZ%?FRGZ*5`m z4sC-8{=0XjtXb3lnf>_;f1$g(8?$NLHZpF7T-$%LM(;Pj#?T}VWhYyQHbxJ_rfI0Qvuo_?)9ROcwZy_7QrY#-g5XNT#7VFz$RP_=D}hx8%uOlDSAV|VinKtcDM$_bNg|6=#qxEA8BZr$ib!iIDSU?2}b^VD^Pai zsK-(e1V}*`h5{lLuXwd3*6 z%m3^B>ofcXQi+7{7wy>o4KuT7=##yv*3cqeXCT|?IY-6ypQM8!Ia;7~AYQjXc_IC+ z7tMhy>hiO9dVHLQ8h(4D0H=&Nxr z!N-oDr4(NSF5qO#|csvv8X@X+j3!Z+_tP2gKiwcP9S{f7AY}s#P$Z0fhEl8(TU@A2=UU z0MI45Ry%kqhd6Uww+sU~qOCz&kFt;dMM$a^zB#SuaW)|OdLs#ly9TPOYeA`0fJR*n z%l9D--V$DZXbsGQc)sIWh&ka{IezzEV-|@*-EG+c$KIWI88~wG6D~)Wf8>8|ZY~Q7 z3ZiFd2=Cv2z=T59Eq2N4%7HlmZE=4coTDdO2?LS$BWI|=d8!U|fP)cOzlDEra4^4rAZXE=)ytWvLDvZ1MPv)rx^5W&?uawlnNhRJRvLf>1DHhs zY&pq+q?#j?`9rG%&}cc79}URlwSeo1gvpTx8EiX_@`G!@Mxv~T@-_(91J{8cc-^~_ zYZ@ulF(w*W{KGzW8K+D*MszzGbo|FQURNeRzr&rl%Ii82$?U1 zFdWQ7jP@Vp-=D{y69l0QdA-*V3?>>qbKdksR&tNSILL;J1~bs+iuj|&Z&C%8gZqAxL*1RAa@(W3_v|W`@eWE!2Ra{ zKR?yNoNcwxj(iB`YKU1PgZocqpi)Y}ppn4ZgUIhOxEBx&eqtm3r~{U?cXVBQ!gXL< z^V3&8nlUq@|M_?Jlj0mtPfuRJ=kZUZ9tisy45Hv zDveRz4RxaPrW{~6`bD%J&Ekk2+`k6bf?bRP?*3|&f2BeSRbmNzD3{>FVwkX+WJwIx zAw&c151(*9P=dhy&fEGHtkaHt=U9t2-Ff@7e8oy$KmTn1nmv1tfc9Esr+H>ZDr?hi zP+Hs?+?Udo+72mX-?dz~bkyTyJ=}U?R8kGfIUQtgG+N_1nj45*R=5B9Xu)Ly$xjVv z)pDpn`A0t!YN{nX4a#aU+_;Z;d)KW2%8>x~Hs)7}em2UQsDocx)(rM#k3k|5pp`3E z^ZNN`_|Kd2+y{|NLY z!tj&)x*+7c-$yMA)q`m8-ismuv$k#AmV8K7uzbZ|;y+=+1eTJLVs`%g1-jKGer2u3 zV^%HhO&Ir~Ki?zM0FB~sJ#g{Pct3jw;DdDA#E>3np?`)FZa*^GPr{$% zM<$i>rb$>7c-De8d)K2=IUr4YmYU&YWA z_c64eM%>YkGTI0HK57JE1`ydzU2lN`ZNz`+zsZlu*UwwF1Z|Xvb!~6ibzsunw*#kP z9GIywG>HfF@ijm`kA0>d?)^qeYvB1#x=P2^?-6%TfN-=s+B%&!expHMR^sh7Fm^ur zpiVN#+b#vYRsxlXzgR2=jZzGE9^gJkI*fySIp!||fqyy1W;!r)K61y}f;B??1qJ!b z{tbWATC7~Tf~T^tgJWRtakUr+8VnSdkdG6vNR7U|9{n66-%YlXWS%Gtr?tSK!0Atbwrz4bdyIF z)OB{=gu98B9Yr`9%KmSq#{0;I>d^l*=#}u|Z4EHjYFM^Y4tkB0w>@Noi2v0xBMrJ* zF%(@zyho!?6i9ZI{JBak+0&NYgVK5YSF9G*;cW8Le#d|L%5}8IH3-T+eBKT1$MC5N zE#(rrr0KZg+lUMvru~X=H|Z^1r*kFxo)kMCy-Zh(`vCd>_zfH{LHq-Z{##8o+JD5I zY>));7o!dn4Ie+EPoWmW;j=sqoQV8szj6%=(n6b|D|Qn81uNE>@aN?SC&zpppMMrC zS+;_1u^%(-?Z4n zenH%t=NGL0SNRvz<)1|hG5=h@acd8^IcPVtRQe$ps}TOTe>Dn>NiPY1k{PAyk2rLxH+*Ncnyn8PLjYTT-y_dy51=)W(pn%LN~Gz|L(&Dz zRbY%v{#~o63MJz2n753#KW23{hnJzcbK&A8%NVBoJB^+p`{X!5hd#1_qgd#`1tb0@ zIXCg#$Yp>hZvhGp;vRvxn@0e~Wky+|?@`Y{p-_TDGTKk~YOqU`!R#$k^m!z>CnK*x z3|f?Xk|SCNk|9nbhOfUv`5!3-+t5lq^AKBRc2ael$S$G-@Jrz3y)I=*l*GV#cs zA*jE-#;MgEQI6baYPc8gjo6Z(M*M&L2KTBXWLHrDgK&(-5n_ymGuEL9;C*xu{ zfRxn$3wHyYybkaV)yVkmo$ z{@f=C%9R*m7NY*Ti=iIBr%5>G`_W+AX2@V3|K%%9`ujQlyiODp!y$pFhKO__Kp(q(O8L{wDuZr;+pVFWFB`Ey|7_WrY*t zrv=$h$%>2vGwYTJp@w!n_TSztMmA-DV>E-`8U0sx>_Sokzt? z=r#`Jd5TJ7kDuKzUosf;J?7zndoLl)@oPZ(OFB!R6t^pIUC4$K?xw?_*C6kW@q0yO zHM}pEVca6+_4Xa|ob>ly8R`PY0e4Yng+6He{ZVIqagaaZFEKE$k6_zs(AYZuo8jF5 z1%Gv2{VuOy71{s#5i{@nL^(E>sTy5}Wb*$F+z`w+G>XQYZ8itxVk3f|+fkzF^TQO+ zko~0miAGg}x5#iWlk#yR@tlSRb(ee~vYS2Q5NGt`cpt(C_18}ZLcdQM=BI+@E_;sg z_^()NlAqu0Kd!M+ewMH1{RhvKQ~N#U0{G66)KVU{-*_$8Ia$xO#@x6ll;e_C>NusE zgJ*96if;g{K7et2o&n;Q0POz};63WVABS=OeO;!6eMKsWU#x-1`D)1Dg?rk?8o2jZ z#>-Cd%u1A-DySEPdW-o7fqxYz!2OPmDOECm9oP)q$QMToR;)3{xqp`#IycoH=gytO z?0fbUI1hHTaE{*pz0XqgY0@i|ehbx*p(-S9>~+0A%v z6=Z;*IR+@$!{N|F@Ejd92*K~%FyG>idx{5+5p<3TvH;ApN9j;rYWZB&?{OIUah|W0 z78rvG`)=H3!Amy!|9>L?ru<1xP7aHTiewcPVk*{JdM5wyy$jk$?_8F=!Uhe9^p!Gic!LVc;o07tAt(=_+6+H>}Z7jao#&p$@K`U(K|$I^!?&6KaBd; zFF|W?imd|HBzMK|{%r(nc0Mp?7WO3mEHD4B^AD6iG}V7K8nvM8eYq$#C6c*}96QK) z^pwd%y%){0>ot790JrREez?~TOZuSln_LZUX%cR9mV_IgBIR6DrQGmjDK{ih$_-Ay z5pVo-PBLB}hTj>7j2oU>jW1+XXk5l=;foH@$s!x);f;DMnp>~)l!MnVk%74j$6;>8 zJ*%`fe7+olXG^$%=~AHKoBPM`nI;AAsZtn|BL%OzF?tIAJyi~F8Shn5i!smAAtp02 z%!_^e@Xq|s?epy6FE<2oX$3=ni*!|R^0EXY-;O-~%kus@ey9AumzNihzf>wCy%tC$ zVup1KoOfOT9!ZCX02kpqIZ)*MW^k8Y1I@@_bPgY%{!nrxJ?e%zA27{>LQx)Q( z5ga-D9{F#|-~M&}kmNrsER4tB6cb}D_V;}n=l#7VD3w=RxH^BuMorkgXY5`8zwJ7$ zPa(kQZNT9GPaLCgczlCnE3OlbEx@_s-!2<;+R=D#Tc3gzLB4KGt20<~oUvEna~PQa z>D1# zdWA)^p23mKJ}8WJ3yctU_4Y=AFwe>wZ?<~%DmH)q0!DM^<>m9R^7Hcr3l=V7t5>gJ zAra${|Hht}B}@N3{x#L8%dk#XQc}W-i;Mqq6crV*AAkIj{c!3OJAeM1;HSGkn_Vk8 z%WhsikK^2*j$3%0A6M|RZrr?bp56WVp6JIP&j?PRKF!YHoHX~p!Y(Q*MmT5K-Mc@r zYuB!`($Z3%|0bMyrTE`}{+9#)AqS|>tRoIr9DmnmX43cM(#55S?e~Pcz@8`T-3$f+^jP_*QR|}k-fdWknU;OvbCUP z%a;7JGzay_G;G+AwQ0k<`ix+aeIeMN!@qXz8gB=ys;ZbmpYb1_UR71J}K z^O(c!+TOZ;%N`-KzKnie**xHJg-s{FX}#^5)uZR>9;u{?VIGrzwz^u*&r^f*QNINp zNl8h63jdd1e);`6;Ya;42I`gJ3_`8eAj)5g{|THK8`x#co@>;@)d6EzOUxr!h3lcg zz)dA0GojOlwFL})bP?KJp}}D6voYzkTt+2)#u5|%9RB_L_j&lHykp&`=;6ahtiMBB zYi8p*%OU+^&A?oM9vNzFj|`2WS){^%^%ZS1%Ap24l`)~?l;MNBSw4IEjBLCa@z(^O z_DnkdnSL$Oj(sZl{H6`7=#jp5W44vJ%tpUCTc^Wb0X@aPlz*lil5lJ-<^#%gR-rZe zme^Y&>YZPVxq17-9b1SVGp*JbN64`EjE=;A;J>OC`@^tK8xa;LU}jx2+^4*ij=;FX zE={Sk&(LwbvjJ=6dX8#^9pY<2G`5%&#|!%g#(j`awwa z$YgRJ{-4oL*B(83#PgqIXx^OZ%)Eu$tT3!YI!@4N9J3Vqt{HT0z<5E++2J|mC76c> z8iXpahepa-VQfozaqM@%TCZH!E)jifVc#s`Aa$MS)vBQ=Z+)_~2Ff8}j{>wy(Ze>Z>0?BVF|`^te24J-kZ!7T`(O<@2y0}ac+ICw zRiAneY!hbRz3~Hk@c}G}0oj=;JbdamH0ghOdOEv(n};vVoia|?bL@B5z0rs4k*?5E zPeiXwJ>LuDGF=B}?ik}_iiK5bN?Iu3$OY^Hiov>d2=)+AzeXVDgT|C;nkC>GwOU`i zY3&^L_7m`VB-un%OSLPPZ&`wTtyRd5ixgz6>6TDUQ1Nw}yn52k>1 zhvd*J1$}Y!%NtT|5%UznAnZd3#GX1|>`Soo^gUDZ2*F!fq$WoZ{d}#)ly8iT2xIR` zKUx%@I?Nh6=58H}d96OX|H4T3z2^XN1;`ha|IOb6tUHV`B-XIU|>^Ik1PxnXTG^Ve{1QF&QRO-sWby=%-bAp9$*jQ6_! zQ_$a@gFSw+9Ok)=@@9hnNs8d`Sbqdy$u6V(y?k4ZxofN+VD6Xd_v;Vg8jQexu25sW zkBxjN@7%rrmm~lQ3Guv~q3jE=?~{i=IVab0>*m$0zei@)sClS&<5geZ$5nX?v{M~8bc2mh^yoo8YU5nrou!noW#g5#p{4SemH;8Tqye?RUu zaTtf7j1HfO^6jgDX&bAthEA|0h=UkoLd^Ghh|wmJ3}CKxtQKthuiQlNlT)*PhhNu& zDagv6#OhlKn++THO=r*Vjnd(6!)7h$qKa_^7hR~qqLi9p(Y%{ zmI7RPz@vr#sNwQm1$gAhF>h~_f2x62)zurGy%2L9u|CH}ebA6BWB*rLQhYGMx728; z@0*8jmYRtDXd`!?^_UM}5vJ5!eMs|j9LYS%91T;QXZvY@edp+VI@GZ9lmc^o)p#!D zYp+#R#<~a9|Maz0+=bf+e`J}O>ZPpxk^?CTd@sHEXYkF_l7d-VuOmMa{4XMvntLz! z$vK_gXm{wZ4^;q%OK=YKxkwI-hZ^Q=#PxTp=KGg^eIW&%+E{n2M0r%zRB=~-M)>Y! zYLZ#jdELR31ilx{;xqVG2{D1p=F6xQ+H)F2DAmcBzsES42mbJtv3^DUkTl2GMF1D> z8KF}ine=bu6ola`#Tuss5|&Ef>d(g7gF;@#`A)~Z$n}G!Sv=+j%|?t(PWYVvyl-Td zg8Pp^XwlhYhN6~wj9Vn=VBJw8jCU1SyL<=m@Fn~%k4DU}7KnLxy;gyJL)Gy7jSS&R zvBo6D+BXWWwhC75Mg4cHGKhj@VBz<&0_DGHa&ij8nR)wPxBeChF`>++gIBuCbgVTd zX|w~dU%pu^XSl@E$>BN}aWTOqi%dF6@=rY|TD1%~%==T_NQZSI?Em2|+(OwOjW%E` z?Zs7k%;5qPbPG;Sj3oHD{$zh^>D)2_<=>|Fq(#xl_x7>k>ONT<_VjXGFRZP<{J_Dh z_W(thQ{H@(gELs~rJOz;D4`&hDB#qUT3EGD2}jS@z=NkUj(VxuhGFj5i}Kx6dw5mT zg8bF`QQ67yy!`RVFI<7JH7Bmi0>O}r>9o8wewjot2>u>{@_gJ}( zw|^<=B1E0+FAk$`j5!nb%Qjh; ze``1X+RIkiS6{Zwy!x`)xT~+eNV)K=S=NnL0^eQd937pSzJ2=!=Xh`N`c0|Dv)^8u zzkB_R6_@;q@EJ=>siPm^n_@G{5z%{5d-v{TyLayv?Ao;xKXEO>dOeBt54hxv0;o|tq1VVi#{FJ;Mz;mz9&%`5DdrZqGT$GE@Gyl=*( z$2O6O%X#_6`%qtu?^aY-W2-tISFc{>=Og?{#`f*o$Nx>a6@y-faWRN?6|QI2O&$Ci zdX>C!%E7!wvR-EqqSo2O;zLfyU)nV6HTuB5t@NA-Yt{mK|Aulh|2jY6f2aBl7`mjWwAg3IHH%bn!hlL`>m<(5FucmZ8YisD%7|-FEtm1W z8R7S@^CSL(0|yQe9>R)>3g$XuM7LI>k3JcLagklRO7mrk($If0?$I%rxbanSE%5y) z>~qlz11mKxF^*xKx7~Ahb?czTcb4RGDXtFjhtC=DJi+&X*v_4W{QP-K<_hin3NQQS zVVyRmQb~1N=c&N45RMB$AHi=X#@()Ru44keLqm16*lG3(*;=pSI5K=00Aud$l>D8Sxj`nWqkKf~#5EpJ@A zz~fFd96xr175{kA{NA0*%x35}MV{yf_0EtZ{#c)w1KgZKfJd+JPQGTXf}bAOU{8Y# z24`ch0`{$8PC~`UV!hTM+VSmS^tU@(T)Fh)AM+nSCM+)gk$qdZy;+NZ>lLFH;6jI~ z4XZKEqT|M60P=mKa7_ZR&)p-Z2KRj#-y5;G5a)L**Ef#Uq0C-`%E`TO=H`YE0=unsO)F@LPRI?kZYnq9m86V{)MI(_5j zPi7t?zGA~tuJG>#+NW!@q@!0K8t1?LtFg~8bv5pjOVr>wwFZi>8DpZND|N9@5cPej z!PfP7QQiC|{LRQ#n29X94xcPl(tCzo(zI~yF8NqIdms^W0tNesQ*@|d<<4pti#0&t zjQy6!E>g^gx{Q5s*8VRlasC!{_DzTnSYEz(hFQ094G+TgYnQ0h9KXg-#F_f~AHN33 z!}%%J(5Tfg_nT_;XJw$Qk-+oU*Dox=znCdrS6YEe0VyiE7XTZYkGaFEQcZEaQ;wS)FzQhS4#N#EpdLcYgeyecxcmc)C{G{66I$7F(czI z{LFDX&)_;^%tw7Kgnx}fjks6CX_WtU2c>Z2oP^V(51#ie=0LHZv1!O_EUvO<*Nr)G zp8uCF7BlPiUNO`MXO|>b;`?=Q=9V$QGR4<*OO5Ez`xSB|5aTI*6$*d^ez=6W4170) zd6lZH!_fYT`uSeFa@NFu(Y323ti7G9liSoP*-+$>!+6jQ$uZ~`6>9Zk=V6~dzK1vk z{j;;Tu|MRx4koSDf@7KvZK)1sZ?4vLh^;Z;I~!W-2n~ovzp7w@rrobyyG--*`I4gI zAI%E4tz$Fht?l2%@5)DK%yION`KWIZq%)Y0lIgKm0c`;F1Ebv^h3D={Xy}jm;Zbs3 z13!bIo@a$l=w1fQ;mjKspQ*h_{;yoQ%+HN51jWTg?8wpMBui}1?mbO*?z=E-;*>>B zUa7ZVBrOF9&Z*Kw&Qx&W)8)F*X>wiI38$|o#&Tsks#axXwY`iX1t7}+tMov$=w5y*6FZc-dIj^~1xusw~ ztrR*>d}Zi8s{-@O7vCkM=5*!^YO3kdbArb&qC4g2A4mpSpST*E}7#PYMqm zI&6OG^cl;;2lulX3zoE6ylH>Gd8>Cg%v-zbt2wK8I?YJm=>oUj4sxkl)r2M@Jlg=eN=O=hQzyzXh;r)e5$5 z{U-Chd-sVvTzW8z&hZ&$ZKKk>M)qO*4;&D!TD{JEUQMEEnByD&}tNPpXlR<55K=JM0KbA`+1*B z_!I8c13j}7McxmqsfWBnqDCVOl53j9VGhmVs+E|;-m z$G^7(PR&Mz%*%9`qyf))pkg5!{Xoo7I;7&W!z%Qct5&fQ72p$*pZMhMtB99shY7&-!+xnBZU9F%qXwHE51y4l`)ITaKIjK|JXK^( zTi-D_EFS$!t(i<-V|Dz_F4{IgE)>V|$s$Rdr(xKDE&?M3spkeQcq65K>wvp=Zl z9VS7qm0u4Z(u4TN@11cmQI?Jg4_=TDn7Nj$Hs+7Ea~$Qb*Bn$q#tJE?RLUW89`@Da zdqK?k+@m3b`}2Cn8FVZlG-Y7tWEsrZ4tnZuqhtRC0LlY@lGMUC_}(1lzObJLVis2$ z*s#|I5AW#SXgrxgr(hA|*2lL?2Y6eqSCQ?ODL8KNZoqey(5Jblf@$mI@VZROQNHTc zJM_WueN>OoWud?+kq6l7ACcOlab%Tx)nN|90X-bJ2vArA9QtvbL|V(O-7n=fe~0h? zh(Ey`?BQhtp4E8zM06qeSfyYdzG;JcXDXoUIMAK_$)K;UiN_kWP#P>;Je$rJZ_p)D;^tOB&S6?ew4q=IcQ#tfM;cVo_m)g^UF(xfd= zDr|e%#=8xu-2b_0i)`=jpAS&dC(rlXd;FYxo@6cGxQ#SoQoy|A$y^eNG&O6RPD>(< zcw(D*!#Waa*qE&K`2F~l^}hEb$7F5C?{^*E?t33SCTqFcOd|ap06#6HVQC)@-9HKS z_x7U77hZeicnLz+95!^I_g1Bz={GS%{O#|TPNP2bf=M_L~_OF8w&_ZG_rYi<|EpsjJ-W z&7V8$HNZ}Y&1`eP@Py#9~7sPLt1czX|h_ciMI8(k|$1KY2A{k?ZjSHkuyVf%f* z)FtVqVj|+=pO;Pn_16=kbi|CAgkEBt2iw0Mt9_>--(B-f;fu-uSk3Jid&|=Y>nir{ zFWwXQ7D5GzYFEv!k3Yz|Qh1g25kj4lH@4h)p{X{vtz>D?In^(aP6>-L5tA>Vf`RSd$dI)H}5O zzCLvAVnO~7EwDA;_W!tc=&3E6ciL;wl~b_XAMH$-NX^y>3X&CYq35c+NZ*<1I&>A3#> zZ{A}`9|7K}4X1y7_2ajX-OOH|bQU&qVatgQ^v>z~t-Dray@`5JxO>~N>jkf#{HA%^ zisvxD{y4Pjz=e*sFE*s?`TQ(`PrSNk?+Z7)*;(l)N?*Y5UAy*g-oItx+L@k;=Rx}Y zsO9jkr`9jsR)z}Bg7o{)-|uf(cKYwTsNiLgeqRr&tweoyvGj+GhctZT2vYdI9Xs}i zkw%Oh4)!&Y9rp)<*B|k`kzi(|XVJaBKmYpc&%S%;1iV$C-rhcZy|1UQPeLMb@4)~2 z;RQc_XOl>C&|O$L{vG@$nM4xa^S#5rL3eqi;iM|iV)#`*{BV#)j3GUb{vLS$H{g#M zJp_PnpG6uyg2epYcQ1VAHUn;df>eP1LLj<7(N%au8bwM&_wIvf+(W7G2Cq1=6WzOq zIuqf)_%)Ig0MdFt0RA)ZdSVo5UJ7Xx+)N@3Ige~a>bIdM`uh5NdhXlrjrhB#x7Ut_ z_1()w^nTQP|33Q9{MeYth;XabG_@W5)%!d8J`&x_hO0olzh@Ck$>XzGY$iV__BMho zeI`YB;c5@McDZ{!OD7SBD&!I&J2Yr^-G`UaXEJoxju<`YxK(bDNdz)JGcb@L5b^m8 za>x_*PsQjic1P$rPPEFX<50+fiqup^aA1f)!WOZ}GBcsa8vsoVV_D|M%B+XG3LKt;<{Qeuc(j1%>15{pi1Oa^CJ= zzWw-}3-0Tl`nvLuUp)I}{f-Ht<4hR;-U0NJj6d7s{y2>tVtS#a@q{jzHvRO~@)!y^ z0t4Jb+r)y;t~Ygb6+TN@e4}}(zx~U)>MIqmF@vY#>vz!(5$D`5b@Bn;gDtxl`I)aJZidt=HQH(jrpDlS*#mwh2*1x4fQy-al7Xf;HZT(9mv zC#Z2%mhulb{8av}JXq)#hI(x%qZiF*>7=GQXXn!3qL#+X)a2TN%NjC^J6;bvz&dn7 z5GtokZ*bRdpj)lvMOO-oq5}C8Zm0zaU>z(r7zsvrZL{~6cL>q*u8N{U9hFVvuLKU* z(B6LZTO1M3v~@MO3(%gdJKq#vse5vQ#Lx0F0rvtx58B7jGj{%3)A`4q@uSzYG+cCU znjn8-#kWGB4cG6XH1s(A%pgJ_%qL9Wbw>EaR zmT!^A@aRm@n1+jdbTes`Uv{heYK5M`L{l!@HtEtAARLh4<=r|O=Vya%BWX6 zUDd7=3O{!D4ZCyLT^>P2tl;{zuUVa-VL3MNQYMjVif# z+?v&6d3-*b&k}plXQ4jb1g%F`4H^qyt&;@#@g)Mflo{|%KRPc#cXJW74>hI846@Kr zEg|6YB|*j_^auJJls*qpdeL9Uc`_@T!=Nj9fjj>|{ePh^;1b-1Z3z`e@N_Dff};8X z_1p(T#@FDse$;;t-8m4#qWf*X4PK`o3>{xOghGJP_fbK7^acF0zYi{aV?_53qw)Qy z?>_$By*>o?I|5kpyA2H(^YH$@ep}Dz`}gnNyW88>_lPz3^z{F>Hf7%I*|TQOoVK77 z-R=A1ackaem}0RS4Mx31kqYkT5o>NQ{$-D0YGg!IY)pjJ5G7ykJYP6u&AsS!a`e=w zFmt3yBb92*R+CAikTZ^d3LNuU^DBnAlM|Wu3M#Nh>bT}HVU5puBoB8_FaWIW#D>_RsZ3yO&*uC z_t_KOt;hG>Dk>={XnaF1nKy9#QbK$}!HwqjhMTGHcY5ASs4lE2ss7HO6ivk&=wqUe zq#09ZId0b9`oP@asj1SRbX~2!7$(;9A_uKGOB)}a)YRO)Syq0{RV2%7EG>$bDz$Wj z&zd1)Fh@oanNDxhQ>y7nq3epu%4Z67Qmr0y5Sv3~Vq%OYxv{0~*H2>dDm&{0i-Cg*lT*PTOr#*Q1V z0?$+IF!Gj%*8FkoOkLP_U0-J!ZTq#e-mL#nC$$8~q^sx~He&b!^7x|TB$ zI<-tE%zS9g_tAxzJ*Xnt7(Y2YLKc1bpOy@E{j@?$@NK`L`~R@y9#on(>k8@tk^Re( zd*IWLSn@$+>mSzBYwsC(|6XtZfDIo6=L3oRf6qMg%+aGq4j(>v;x|b6 zq3;gu+rN9)&Zl;4S-W=g&RsipY}v4W;a8ve3^)ufZ$G?m|E{$wmM`7Aq+S%C%?b^FQBmyu58=&c$DEeY-tl$-zr4?fWv9EQFgudEei?tMa$5-|u)XY<%_h zuP-h9q`JG)d-Lkj%>5WYya#>ysns2nU*JLeQv9LU>Ti*9zcd+t^)TD4-?isim==u@mu_u97Z z-FqOcOMMIPK~+!X9oWBN*RC&a|N8#&6^m9aU9<2b^bsf? zM8&^P9N2H)_tcZC&YezOy=v9+Rm;|O_%cW!+lM^1{W&}K9e8s6;x%hmFI~HAK~*pM zawrPle15m>solE|Y+JK(RaVA!Cm6zD6z)U!zS;g%?zU~Kmu*_P@QuG7vE061FTi_AvN&#E4-}JTV$xPmCHq0%zs;mjkb1q*0?slc*&(+HSzB?PdvuGH?uO3(| zIz3nnj3m8+{<@Dn(Le=*|Ly6y_ZK?r`@k5|4)i;=E%-C^^1b^VttXAa#m5xi!XCK{ zp9PG*`zBnR3?l{oiY@D(uRhc{32$)>DIGNPvAgj{ao#$QG@1l|KKZ|{e}J!#A-xX1 z?SI_=H|!ABH-@y}|GGZ~UmrmdKoodyaNUdCsA+z^D^H5FTrpL6L^Aj@t*)Vt^I!BcD#TC|6Bkj zzZ;Xs_m3h)|9b;HC~V+<{0YB~e@dtq-I##y_AOu(>GQ`~h=udc zt{Wha5r&f}w;-*3@By&O{%-Oh{J;)pqaI)O;gdYN*8_F$6OVjgB+1%>?&4|%{`B5O zE!IaaV-imc8xQRSfPfsX2b~>1Y^c!17cq&Eqe#zTr>mnBO$7mwX0t+LBBC)_K7fOzb zpOK6f%R=73T6O||_&3V1Nue-Ak1r59FjI~_zJPO(N!gJokoWiheR!^hCS{o8Vwe>Mt)woNa3DJhg8 zp~b{Yq_QxP#0EUE3y$*bJQ6>LH%7n`QzqZ;`>PMO(t8*0<jCaB*q^l@=02tchX@x!l=dboiIgF^M_+Q-E}kH=55DGA7XkQd?+{girR5 zC(PTdxFmq$b=jlE11b( z3&hlr55P?R^T0q}L_(}W-5aH127jS|Eip`)!!pJO5~(V>fX(G{_)M*W#pfL4+IdH~ z!+5;I+)+F>lMM=E;{>u<3?AR_11Qq{hb-;|++XWMU0Zl85ho*-A?C1|EUIyeEJMoR zvut2cJcdfi;IXqgBLJRlXN_QUxZFI>FfR8Xhs3pUhI2WbT(&=l!4&wvi#mdake?qw z?%W|1NGOt2vVg;3F*zJYYO0XRW!gFZEG}KHrgK?rHV23@SzNmSua(K-aI)FMI2=10 zz`?ZP4U3t}@?$gUf(akqn+Q?O=PX{p$yw)qC6C5X&R&}`KQ$#eR>KZoEuaZmEE|Kx zWO5i19VWnHFjzbRhZYnV7)0gpd2D!ZXFmbk0eBl@IE%xxv5qh&!RNUCFVjim1_(Tl zX7-}4T??61OGZZAa;;Djs+*j=ASqr;BU9NtJ`XlT5oo9^I>7T;!2t|I>au0&R(4+nz zGJ&ECs3}I8fWu=8$#YwwBhuJV-`L#N(RHJ-qV9TUYg2twv)9w?b~V)2)KpiN6%{!e zYKwj>tf{LmFS>f=YJOqy<%)TMTsqJh!=egEKMt@;-G6t<^h^#pVJ20?5>jHOQ-t7Z z*+gNBv)S$OdOhwYAmsKoH`Y6wy@2nsxkohCR#(|7CO9feiVBL$it?{qEhw^;3@a|O z6+Cg-Lgvs}M}Y{9KO_%EANn?d!RLl9=kdT?74sn$a0JW`Z#H^dZo6xOyV(PTTtKhc z<;ryjVf@O9^0Kn>it>`8;?mL*fG!+YRFq$Iwd_+7gUNWBIhw&4Qd!EcNlZSUmL5*$ z^XbO9G$B(Qe7LjT3s82~NOv=U9&(Qx1O?PmTgiyx;-bQwqOpYq`NfwiPf|E^j5u*f zyp$evjw#?X7MK`30VQcNl_v~d+1}uFopO&J!m*XvN^He>MdJzp<6_PBKrW4Sh&h(Q z@gLN~lPI_!wM8>|(xu=#1@trxO+<}#0t0hhqulOhH!$<)qj)1qONxqY#WvXMgo6D1 zg8ZV(6|;jtWQUkz*dqo}pGVYQbezM80Ss{s7hC#i!E zI9Xl>JS;5({K7(z)I?0Eu%Ix%@PeAkV&pN$G1wtPKIVBO??rEk=&9ilK4_*CvNY%f zW~STO*w|24-_YO$O0EX1Z&+M8Za*JK99UY8vZAY3fxw8u{3}=Nm&abgdvKhgvFSb< z!9z4&K(fD=QzeVVuz1R}SSD>jJ5~i(OZ$zEZfI0^>Z@v+JG!qq>l&OMZ;o3$z;p-T z7Zf?_>&h+ApmSB57chhAfrwo6%Ij_CaN`^CSn8swN!OU ziwoH9srd4fQ#o6f%$}@QXreQZ|K#oJ@HW&pXAcl^RF)K$*E=iEA6*bhh-Aho$(b8% zFP!MbC2Sv!;Nf;fNQALr!%xcwC;w_Scvb#syPC z0?7ip4x?KnOAC#a5Ty z%>>R>m)E~BPaQld2&_cFrnC4Wb_g?i_m}1Qwt_LhyXwvUJUS~E3}DiL0X&N}w-^0L zFtiy=AzK_WEmm67>;nFnoGl&BkJoF0f;b{Ri@^kHA|`d*(;Z+N?#9~MipF_CPp8g>D@)8m*- z5BiKRVl31#g&ZM$rlT2rj}E&wS3_f4m;2*&nqbI)SdxH!-F28}HFag~lL34>gG2L^ zEI3nGSCN0Yu(YHw{~&NIzvL?koyEX3XoB&+6yOA8)c4j?LPL_{sbZEmWX`ol&k?uU z<#KyHP4%w!PRHpeGWLM4bq>j-;WGxC&IwlLeqUExbUDAcv>3d=A=pFFrRv3a4>rK3 z^8yNSI{qx)!|fERlp2`=1SG)=Zvr=-c89v$m{|?p4tE+wz~%mkeGZ7crmCutpm2jD zE;p849l*8~kqR+<@x|J$etg;>d|)Xgn;5 zAFx5+K|{o&1~mi@mika-8d*+B0HtRMsZ(n_PLJz3H_Ov_V;YnE^blrsO=;5`WR~Ml!ATT!@0GRl1gu3OA%{mb z&M-7JyF9LA?(qP7V|Iv8Tmor=kEal@%N-U9L*`qq4>k`J_)7US?3b}`84TCY;iI*0 zW8&7uEt8Q&LLPq+ISxW$GpHD#HgvojBBeyRU7!IVsp_iglE$=&lJVPMwF8pM1=P!x zDgK~WIgl|iSX9!bfmDXvhw7FD%BF8ws9}e&_~{z|8Q|@Gz|Bob1t^ zqoA|i`p#EF_`<+_SZD*lRn^5!F9xx>eybaC?m7`9hI;7&7_i+PfSFubQBkO-GPr&j z4OdGGa|ctJ+P&Du4l>6<2{m!VP^g3RRPSnrn3`^6iTNSoh1z-Z=8AT;!~P-1YHlzx zcs!=Q)(uYF2kodVs!f9QmP7xrD!*hffkktl7+mxVi1Oz#zKAkCk<4cc zLc^rEc(HIqpfo=(2c_^3h6@7IxAh@Xw3Yw5Q{YK|aPh(wuV=ZJ$6%F;REB*OoAz71O zj2TmUsq!3@a17RAAAtu&-}8vkkN&fTDPk^CvbX}u_5&QKY;L-%x#`+> z${6ME2xD2qGqmc7)Mh)%Z zMHm}-NhqYwi=yH(DFvJeBt85k@t`}v&?W?#>vP>)@LUa@*b|uy29?GSq3^1$&4-c$);_wppzJFllLhD#>6{RA zXW&px{1RgHom(B}Ki|OM@F5|l2%&w!mL&41KEIvgX1O4L=qyWzv?Q&UqS2dYW+js^0MrW$WLE%Azvnq(+BGgp}}P0Jn2TPGcMDnh&|j zu4Zq2OZ)TE5Xi-0mMDa>xw5vPyuuFU+yFzWW&{c_wT?| zQRkBBz(p+>YiJW*&K3H@S3}dQi5Q?9Dnos&1EGHT!TQ>1ad4axl;2&5Fj6A zPK4aYefNRBzlLwV+=f z=+-F|a{pWm#hgy%iCC8D0s84_GX>KjYkUU$YD@i%0wXv-Ci`_yQKj8i(q`jLU#?yp z0EV2$^k>nz0n=d8?m_?TMc+&fy1bkxUd-q481hUGlfny+TI_W;KkW_z%#LqD!93V( zCi%JMLPuHYqp%Bu`E;n${IND2K|OaKz`l&9c$_<@1}`C?|HE4#;)yskhCClK^~3HE zP}923_jxPhG{9W=w*o4d*~L!k915c4R(5bzlkUO>c$PDg%WX(@I%1!b3OPSe;>K#(A@ z3&1>o0b2YIo2#c#JZM{LVC1$?vV@^cg$@&!Pd95nzE#uG3~dBwy|=se5E0C0iNuTm zUVLKcq+qUq&JJd*`MRO@Pp5jnbw_9j* zc_wby3TOr^|Le|rr>m*HwX4Ez2xQoVJaUj>_fIX}f?LlErNG9NOeX zDcZmpY7URdg}_OrEcoW;_4+1gBthioIWZkaMMY&!oZNKE|nA0%EuoIBe?4D;!H9wSY~5t3P!HA{4K z8c#xv>S}F-W*B&q2k6jfgFl6rI+yqfAa&%3#TXkLRxZ<@(*i7cboFNuryspFleJ91 z5>aM_gR>C(n=|1Mhe6{KZ(eu1J+6ap`q090Nb6D#YZ2Ty15DNlRsv^{33jBK1oh3uM6q_UY5&D{A> z{IOGuI~$xhe;=YyQC3k_SpR*(1eS!5^WanfXG+ZB3=Z433BKQ#;5r_o@CxGf_iXW7 zu!7Ct${}Y2KP^dN2?Y!)F*7}m8*{kR+u+G@(|k;V_MoG>#h>u;V4cJZt=` z9ZpbJeELvb+Wd-9&I#ryp#?XNMTce>i_YQ+xprnSho=0G5Bt##yo$RQ=a)>LLz6Iw z^oW(yWs{;4C=fHbz$IpED4z$J%gz>0leej^zN*T#pUa{GLln4IAz;yk0xEPgxwsn% zzRz!^Z}pEY|11IyK_@shYXzJ-NHS>L_?1&46Dd-BtOo@`tenAz3?MMMuC>AAtf_BU z><1MRo5mx2B!KOFzLd{|!$GhEmT=N}u!Db{3LD|fX{%wUk}PGUtKonpF>5iK3dht? zCD5alAaUpnheQe0sVj)wo zI!j9Swcg;cN*qe%Flh|2DwHFIqdv%=xC|%)=th{Zfu}zn74qwd)r&q|u!tMCI5j0$ z!ez74r9p~WnNgtJ;U=gzZQQXuHlG(9$bi}obAd&b$Zd3rM5BO%6&ZHcbUqyWQKWt! z;r0LP%%9o+*P)$plcaODn5YaXEm_CmkQK8RMw8XyEIw2`qj?;TSO}%59h$=|x=cio z=~O%#lgX9R*$g^Y!smoY{nugj`N!JdKx~*3w5{X{{O4_+N6;wA))0|^MN!O~H^USp zgtM${?g)H}YXc^OuMdIoU>zR}7}7fEQnN*&;7^1>Gym$%`zKR)xL@o&5YAy+XX;nX zrd#9bd?vUbsv>2H8EzMFfiXfjb%o@V!Ddlup^;`jUBb3YdDkA0{Cl zWB3~=5xalyGcgR_GA?8K!ud&o639mRVybXvQaXV1034hIh>gvr&}0z?J%_>pC4?Ck z2@H%gSYn27((K>xG{isVGtQv|NKX5EzDQ?46O$oKTW_L5lFH(8i0EMT%oHCu9QCm| zEDB8#WtD^w0#JA+n@;0v z6eaLjRARR@dwF=H>8A^)$Bi}sl!NDu>z?qLMPe|x)0nu`Z(T)FhtqkdtsS8O~Mj#D6-Uf^Mq6}3^0u53!pW^hQP=m zGr3f@*=Xj0PlZ)7CC~niGvEHFABXw~1%p`H4=j&_sRjsL>c#0YDjS?S9)^H{0y0b< zkon=!Vo**dgD#WASHOJazh;6yLfW z*XY(4NY>kr>YuW5S-f~KRf;%iUbuwA;_IeH3*h*RrDsArC+2(K-1)%B*u$FTa}??$ z|Hr#?l$6N?_%=3G7N0z0T4IcZ4ATrWp@t@q?{4=Ie&KOpe1Rkoe}DnS>-7;v2Gqm? z7L_B0$Fyi*8-SzbPCE~e%YhhQeEiPyI4OipcOILcK*JUaPJqo~L0JMSMbn#~?DXl; z%a7mn6=Y#RufOldHC7&E%{lqL87=(>W zV%u2b7!09t|4rZQ?wQ9w|1Iuv_5(j1n=H`Y<;*k+oxv` zzeH!;2D1D;H-h zfBt9R8sEU5`Hee{GU6(Z3qK*h0j%He+<`}oFv?r#iT>Yi-~LZeK7JIaug8u?^nj-P zfri5;?ft`nG4H|8VtClk4u5|@|C&l9^*#g78r+9T;^X&859bp52IdmMZQV!T*`Llm zn44|0?ccX|&r`df+_huV`b}H6Y~HkK!}|5>)~?OUTD5KoJT>{h=MsC+)p)I56=@;V zcBPH5DTWh-B3GUx8zYyh*e~OezyCdxSTQ#&Ji(gu{n?AFodDEW^V=3nE9AQPBbT9=j)9TOWdJt;goJHi&8 z9d_87V;O5TTdZNVm^1$vPJ9!Dk&7n5wA)il(sX8%S)UYbv>Y`Lw^+>2;QOp5Ydx_3 ze~l-;J2;v6=QC?&X(CJ}vw5b)oNLLo!p+uW_~tNEga>Z^Kc^F6IB{S)@$O5j;x&=l z84FX>Gw02nGaEQCWBRnjsXh)&j*X3pj*i4E4};B&vl=2abAHG1xHtPQe(L1^Gn)vu z@#dl^ZGnwZ&jG>8FM1O{YN34jS1ey+tEZnIj@1Bg2hy@vLeb#vb}x-$Wve z5uVtvcG;6pEnB;M*1R>VV-0%CnnV>q8BDswNSzPskdbB5n8%2r9z<=yha;3V1dCy;A!qUjdI5N^=VtsLufhnk^LdBG28#b(%suD?5dOgrpsWd933h;rdLZVhHk18e- za)m-d8S2)bM6>$QulvI`PhP!YvR<@!y27XdxJbqHxvGWH8i_!xP#d&SQwY7@ppuAm zX?f@CZgjM_RGmMPVHS&(dc6)7V$iGMscNYm@?*qUxk61FIE;qD!@u&vl+*TXAgm^X*0RoMu;FV*V1ss@1bh=tSUb|<4x23hM)$8%LVgk9|;ec9X{yt^3w8>s;~44 z%qD_R51lDfZf9srCRJ9l%4Ad}tx#GtVX~9mO|70hugx>kix+)p5OgF=WWvCtV+01w zEgw@>>L{u|3$i`pgA5#!Eu4RRq&1t=>yk7^vmzsfFq&oCI$*h{27!jv)Yz(rS2`TF zioEi1&=0IAY}_l*$dm_(aY}`n`CtG52F2RvYK#$E3?{uvy(Ug&RnBR)dvkz~BU`+% zw4;wAj=)>BRoLLzYkVo3&{Y)IWC}rM2Z^x?HGFA-Z>sirc={5ZF=*z1QtS2B^+toi zeD#LA)ze~Yoz&LmQvwgL6mR_CAo@hucvY375{~N2@d21)B2a?4&hpZVE0Li}MJ_QO zmdzjXGrq~gPpsPUpAY>S}X2 zTe1fU)m4_)xV&{gys&ykq|q8TCv)vnM_+yOJU-+iiVGS)F;E>7F*5x_BdrD}i-fGgSwFXO= zRios+gg5MkiRdcVr(%Ij1F=FbB~AE2zj@AQQjZ8(T^$MOWe$SV+O^%{sn1{|CVB2OMu+0tvlA6c; zFpw6*++HvG)?`tyPk8!bVCfe=sUQGGmmX2Ks~1!~1)P2>Ft9aNA+>3Jo2!mD?jtcMthX%H=7H`1@_;Rht zHyijI^8aJCGMt#b3W8^tY%TWGPkTc#K5(kF+q(+qHCNgp41u)kYwOA)L$#6_MeeF% zj9gx3_dz=#t_^T!v|KHz#YwzxsIfa+XVJ!N(wl(+Pj-XK9QRJd%<(q1x)XJ}xW*Q{ zHvlxYzREdIq*eXcSm1C#tjQf*Z=-DpR1;eA0QAn;Xn44NQ0ie+<}N^2pqfbYf}a1=JIz`Bn)Ak*`P#tna(qqP!iW~wZp zke_ye_j}IE_Ov*E-cKlZ`J@J0uWhJkeMTgo?JNg(>Hs{D7kImxFH}lUJeAZE{5@8Q z6L?_QnWczU%wI%=0p5Z}a0~#*YIWVr)F@sW!mF#V^n4JivJ_VV8pg|m^?{Qvh=Q;R z8laMdjT%G@?n52N;#H~{>y&1_S(Uu8`*8?WlL{3Gt- zDp+S8Xzo~iI94$e#}=PnSqJg~7&GkoVE0P1Zll$rHxh}9r@FnK762Uw>s%_gSRG(6 z1B?WhTbme4gcaeH`W8C6yrg=*Kr8e45FDQervahrFGxvyDs5Y&!eTL+HY!pf@U?(G zjfa(XeI^Gr10r^xcxvk_n^(XySNmb97&W(Sba`>zGCrW@fWMHdh4AAFtPESuL47*Nwt(PHQ6Wv0v3<&J~k9Y$9a zHLT_8WXfEOs>PFwfciWNx^vQE7SGIc)YUK@A%TdtIR%iFSa+5`Rtfb6;~aEkd3`)<2HX$x*!R-R{;yu->+&?z5p*tx1oK)b5MV_S$jf(Q-n|U+XHa1Y^Q!3s~qu ztok@@k?X0$1~QD3c&?=VNp+YqD@hw>l3UlsuUxg#n%e*J%}&sY_>_ zy9KtDwtL&WV7-C(6brunre!N%AFAiCXe@S=<&=-cf&L|-T5%Ld zkjG{VzfUl#r)McG5Zbch^(K`f(t4&F1eW6!wlue;sZ83i(DgTKJ<#Fkz6YrDp`E9h0RN}#)!?39$Sc|$qI|ntTo8q!3vS> z<%7{JkwmL3%FLVHovm)jks50&9W`#(jjaNm)F_QDt;i`KTUJ?E|A7LY_*5Ro_&V|s z{)>qAhh1aQZi>|!%tT5CcyEKjWJvh3160HArL{EQxEL$5LiLsQ(sy+?Zo#7&ZEfC~ z?@leU5u(Gya2>>}IS-+cMeu~l&@fl#4Q@#lWmQ09QlbZnWUq!veoj=Cj`>! z`1zqp&OGlVNJ+b^*U7XdjnNurHQV$PbsD)!$(!}Hr@R9EiM@QZqqr^|9D(u~%$>`O zp`zC}Re0yQXd+^ZSr3a|H5E!NgGm#cuGgH$(GNe+<85=cb-o!Tw?gf%n+VVC$`pF3 zBD(DA;9s*3N&771qw# zuzdAgdANm0UP4%OcEiX_-PER*LtZijR%ctstKo7;v$ZO{G<0vBv#h4Z?ie=2kcC1$ zX0{La%l>gg>G{iu_VY&B+`XI3ghrQ;r8GjxZBl0{5B&lff5=OL0Osm0Um0pJs#C}KB@qa*%u&?vst9n85)&0FqV4ZThYJ6G-bzFq*t{%Gsaa<=L4(DlnLWAS zhP&kmEVrfAd9CA=Nw1UdsCPSRYpbdUK(jFm%BRXyio=8-L;~>)JlphOs1Q8X5Ai0` z+UU6(HY9JbD&W~-ogr!uc)w?``}Q>6*`rX+YOAemsId=LZg$@?n-1a9`w(Etbez49 z&Hn*r%WA!9%?zww1!^7Ve6M8kOpL`tr~Ia)|H#)Sv2xw4}{oB&27#b^`IZBFIvkQs~^;t z*?5)3^%+pRL$_w4LVpAO`sheu@5l2L)7B71i*|aZG+|NJ5=#bTgU@&;dD@&eOA}Ql zwMzY7YeoIgu6=k#4eJG(|^91LT~N@*`^o`;I6Y6 zt$LL@s`Pqu%VDToysb^$pBr^%ok^!sYlKHyDr#z>R`r$FBTM4IA0Na^)e4LMF;aMc z-?s3KG`ZQRS`)8=Z|w?~oWj90&xh%8-g-j?y-6d0st6r#x2MukSDEc|G8K-J`ZvTH z`60qzp_EzSd;9(|Qut27=DACi;EWf~0c+KnR7+Z$v88f7Ep1+BYu5<{XqZlKF{lXz zq35lwYXTor2}vcm$uVV>MGZ@Fw3$7YP{Brdjc)3o~G7YRqN$ut=1f7Af!qVLa0_a{Y!6cNm*qj_BEwdg$?Jx80-WI z5-uR3XT1ig=EHG6-$3CntEIE;k&19l+$x_#Q%{RO|4YNQ7N`^8NsLy@jsr-K+b;BKo~ zl{nLQp&RC~+|Fyas-H=aYAj~6B6MnA{so7jcX@7XsL~pym#G(> zbN+n8-Q;R(`Y=n!57imuJoV~N8|o{oTyGgAdPp5fDmxJf2F~93crW@if?-1EOj+_u z$=q!VH%^gXrjxbj8s zT>jn`DpzT-9EK}ZP(lC|J?lf@)gv2$VZ#4zP(*Ju6NY(v)<+N;XhtsA8!R%@6ho-a z9AT6ib`*4XxSOES2H&vORA1?6Xel`q#g*ydTePHRjXzAFa2FlXhpVjwUpwRaULR3Z<;YVo({^U+KQ#bVH2=4Hn3VYwPQ)%4@x) zIgvt%MyrOj5ONX&w1Sn;6$X(6f`grgkJTO{BJM>OqKK8V#NIZn~Sn7d>N!Yazd%n0lTy+h>|uU2=gi%POu)8 z`WYnXLv8ay)4?n>ri~VbE_v(H4UQNgDd@Og|APVy68@PZPh1QALd}}Vdb2`1eb?rAwaOT#oPFy$^tM{S ze>^~k#u+@}QdW3tJx>Q+Y&CqbA7Dd*o=f%Fo(QFKpOy974 zLy|^mi2m@V*V_s$xk2b4Uo#TCT+E& zLao9(7-arqGWHD;cBNzxGV(lCgXMROD^x_^eKd7d=HqQJE|nB69wQHOi$`U66~~y+J}&A7Ezynd==?wSMhB(QwNQeQ?2VZx%H zYfM=-|ATfX=;@#>J6sk&%GS3NA~a%6bS|LFb$~8cY7H8n z6@*6OFL8Q&{k}QEA2+8iSB7h%vu15y6e*lDpD@Ak8younDG?^4(rC?YgKD7J>8!2w z9@eO2&?St-O)el{(1${EQlrC4sxWZ3fCj-4{-aWU6&YbhZuOJf;TR!&z0#1rBW2bC zB0`4`g-q&{C@@mJF?4a`b;zw7n_O%8zz4Nl7m2$a0+rmLgG_@^fXf1RVdVYrzX(9r z;| z@N)`lI5<;6rMD3yp-7na_rIqId%m8Lb~r(!SUrsh*UO{!Y)aQd5(ky*>^LZHAXm34 zV}IzremPO7liQRWmBJVfNhK5=gfSu#95(dp{~vEx0T)&F{fEW^>=tV+LP8J)1-m-{ zsc8nLn;~cD?i3KQyHK&k#KcazVTJ|?iTR&5EW56&yZBrEzt1ibZ_YjU^u71{&VzO9 zTs@;nsR;jnM-g^Q3q(68juhR^wz{xnHO84^7~2hXj7;ZlTs;>V0qQON`E1UD*#=s0 zA~=4o)_l`Bda$UOZ@v)Lu^=361AT2neFLr4O<%#z07KXSQ+C z9822@r%!lQTk}({8%-8eLodj|>HJ-g- zxrMRoo`|ULYZz1q(QKH5DOM=gd<2T2xvN5^NqAj7V6K_pJg=7 zs_Ad|K|tblN=tXcDH792-=5-ZJIf3TPuNCTh_-cJXD}Bb4M)8|7szUvZd|!if3_ZE za-F%e;SgZHwvMs(yea6W?(VOUc)w!@yDpRTt?hL57C5@C)rEQ#4vUv=o4p)OTyFq- zQ^v+%0CQ(+E!w8;(k-ei_O zXi`siVOX?#H=UK+SI$`k&I~8+bFH(b0HXOD zZ16N7Nx>Y>IxaRaHe6_9IM>*Uw8c;t03W)uwU$|~)0?fm!T?nZZVn#&a?t1W4JY|7g8hZGxz5bt`9WWd>X%0 zXXg@dLPMR!Ru~6c>n#grLG`0$v{Y-}Vm|=!fqQ%f8axjG5qO}?ZS|J5dL{;lUCc8z z(L?M)TW^l(QUjZ-2>k84h(U3p^DbRDNe7Dpgat?ou_mn*Yy7HZKJyh~FjG1S;DWdL z8;lK+&p=;m&=Eu(y@mGI{t+%%k&ImoZXh&L1CR=gVZ1uH@*m-XFFiNFhN!VI+^(g+ z7<(P4gV4W%3tp9ugt<5DcUTHiH`JYLydk7QM)iAO!KW^p%wY|)-1!=S1wkm^0Sne= zaOuv>)LI#>?|}uIT0S*@4=gCx+1>J8s9??SeG5_D;azgw$d}H);sd*1OUHFdA0Ik# zH&X{XE{{_I(JS~{>x`qGQd zVY=}dEFn|K5YdGU6g`-xLSYg!ku3HVEbx?60iMtd5_%}P2MXOiJp4W7JUqfYRXl|3 zyU2mqSAf8i$N=SGfLL^M_jgTkQ*jltA0zjD)u`}VuPY0BEuu1g`vtJA;Ce= zWmy=Y=nrq;^A-t(zFsOG-dx#n-d7O7<5Kl*Y0d7)Xm1}MVNjr#w@Bn84E74} zRQ2%m@+?BC>95d##}MRS%YnEEH%|{ww?J=qPj8PzC@PDDk($& zQ}^@?_6qhyc|~~+@bVCPh^mkUnZFF<`4<9s@nyyaXXF5NCS2y+5P6zbfO97KGhBKG0OKtGnk~ zw9v(i9~KFp4EFT$hEK=3t9rP4@xxnsJ|j&OWia5s)UnCXrVdKm&bpHP(mK$G^lqrT zz=Izd?&9j|77*x~=r-Bi#giS`F72#(aP`L3hhmr=5*q%pDjx+js&4I#-R&D3dGKc0 zLw^^6D?cijCv;U5_`A8fCJIolegaijo);2nE4y$7Y%!xXZV*bEN=gB`#Uv4dGCb`Wbklfh)M zSu`q*N+Hwfgoo1ZKOd5oVf$+Ky1IJ0CWpE}qh%)5*~5<;%b_x9BDxZTB5D^G(&ZS8FuEMY$;VlpHU<(t-1S*LnBu*qj&u2Q12&iI`BauX=IupJg&#SXz>aCYZM_caccLHcU;#{L{ z;6w)CvZEuO!X=Q1Fbai)vBz;jr9|5cFUNXvV7D zcYYF$bo#4Hb=mC^livP84MatKotd012`nKJ(@tGlG$7$nM?)xPJTxomo+ps z)RsIx9!POOJ5nglFfz;wk8!||xB*TWEEW@r9)NY4sn7=_LqmS1aug70LFnb7j+AXvC-14t?2@$PkJJQ&*iZA@mvmv&4&M7&eT^)8k$=g zD&O5coG7C3Lo+_qx71fvREi}v;;QoEqM|Z!)!Pe^u2ezT{;SXOE2^Nw`v-Bl6%maK z!4AM-9SFUXh?MGgOG|{zuvC7cH;d2aF``8b9-GVN)1qptB;u+HpjqGisiCr@qR2Wmm!K`@R%-?i_PUV)qY}OwW3%8g9M0`dZ~(vQANUHxuT+gLY0Ez+{(+K z*&rZ@ah%eN7KoOP>XzQ*^7-rpZ#I`l4-KSqc$Bop3Q1BQ4ZmU`d|VYiUM*9bKXxF_ zfwV-vul*97+f)Q{V7^>X7c(AnYxH0lfW`@P2O$PfP@GqJ!w!!|hH!9N(HBktx7V7$5+n(@ zAj_~o7ClL(_UhV(hNhO5riPk|(sD^tbA5G1WwoTnM?9lDO1#DLFpRKlazTDcm8AS-Mx-0viNf^?j7~do_2$#!fYmH_ou0o})Z=SOi1BaiJ&hZy~1UEmi{yt8Qp6zn#D*(|ImDA!|6B z?M9Dis07Catryj&*if)oAw~^PB-vZxe9wKTt|$OBscf9G2N)d44&n_}=?bzrD#Ptn z1@X9vLN=evnO?7y0&jdLkk?A>;tUF3!vgy+AD}VDIx?*$G0n3zyG~D3%0@4Z&q<#1)lw z&4}zM3?7H&LO#=iI2AIN+N-vXSS$f!O^F5^d1+p50mQZf|3amrf|6HGy%V9Vm7&U_ zr0Q*HEH_4E2vY!hi3T$Z7VAKi1hi{)LsRMPKq`;rLcRC9J}OG8pW*RXf*s?`I{=mB z<`;qg2lXmF8sS}pRl(rV?~%GX0>L&m$Fba)5LJLUHL0#r5-y$zoFf8~B+_^s&O1nC zh@vaX$_tqeL_7C9NeRFj5Pm}tvLAbcpAS}nD3*4|NYy(|vRyeINnAe1m3*MCvL;GA z2nd!-s(n~2uL_AsJQT6d;%XmTBH>kKZgFu*Q9%TRJ$N?<;?W3yIH$J=e*h`$x$DZ~ zbCcY;JQsSHxT>Zas8x&W%A1c+SrNdQP^b+W8$d$e>|`$aD{(=UMu#>(_f%BzRDMu-Ojx#tWQdJcF&?^Mf6 z%ioiTyN*fnOJq}3E-3MY+y%a@inn!DlmY#skPe_v_QdfyNdgxxmo5tThD0H$4wuo3 zrSe<~Ypa3YVBoK;yrk3>4oh=OM8KX(QGQtfQXI%!mVoPXS)f-O6uLJ!*^L1$_PL3) zP_PL}n0SDsvhfjBKns$92P2v&t0)vlT9GaHOY%yJib8>4UPV}MtQm-bH?yok$qp7` zq(htbQr*K3BnUY)7B|k*IjUC1UR7-b#T9YJ8j*}%X+@#rkTnVI{I;~PxF|$sCUI7d z4mgODK#!%{OAqPR+jE)kLJ8O#mlEPh4)@|R62O>4#Di-po9fjLD+v z>vnje^=@%qNpP>Vq##WCgS8OUdmj>rMjkzU>CWeQ#W94T$uY^nPF$z7=1TCsL6T}o zAcMn`Ih{zXDidRI`E!C0r$~5SR#+S;qglNlp^2n%2j^{aeVGFueXIy)^LWYbEIQjO zHa*tYjkvF=N>c4F9#UOX(R_{K0)-Kz_TN1PDS-;uXdH1@G;j+sLZzT2xB3_uw?8B$ zf-P&|?@1|EI->Oe)0G+L4Pl7p67Llq9m7A-SP?8%sq|sOtBJfTd}Y2@R#u*0>2E{C z5U>v`@{9ZsiXW@bK+O?=q&Yh_yp~bcU^)hQPV*+ml@T7s;IesqSPbxlk-j&Z%4>YZ zBM@fKI}4b6T134VasgCul@-FW!4-uyyRAtMq+Ov^dBwi6@~Y~REddh*8pC7dmHVDZ zfP(F<7Zl}jOel{SabEC+?#jE;00oKu zEROcNv;Zn5Wu$n^a=-;)2Sb9UZ0=iJ$gp>{QEZ;;ZZ5D+kMO1OxI7k@daJ2SQsph4 z3XT=y><%SaB&;bQlvY%hLy=ipUD=Sd%hBG+o|zAcMG-_@abJiWeiLJN;2{v>&95*f!e!V7e-6~1FBhJG?e@>0K?z?0TQM} z;wh5qy7E0#79WoK68=gnsXN5YzjEr@3$D;FNa$CvR^mMAqPRg$K=9uz*G$7j*V6gmNIXNw_1HDQN4 zQc;#Kqh3(_hUNglCm5tdusQWd!@YRv=%!c}-z$O1g@sDI3lN72*fWIUTj?j(l!z;u zN|GroE|Uwj1djrbz&hgaSZmK5Ng+5n$OVZS{#K62N@y^Y!wayK-VM^= zF=B*>RJbhnU=H&s2;Nsbpr*2>>6Say1=jp*4HA)n#W^`(50+QtLm6CDB!rSgmSAzA z*db_wji~OAmSAy+z#pZAP~wFwHWx}%WC6rwyM%HXNbvO)kFBn$u52nwq_Fv*2`5K_ zqoa-cW8k=ktb-X&JB?UJfOr_fY;!?81vn+y2_DrJ6_$EK0s@jypI^5nV#2V4h;=RM zeN4(a>|NwDUH2zNc`#UUu$o0A>+ksytRYA|6k)Tr@d}Sk#-)~v3xPp|9QYrkGXZ3? zAgmgiVCyZd{VTlgr6->s=N6EV;FrLs@p)VpM{o$Tc9?h&R0JJ|Xe42MS#deCkovtY zJ%%Ko0IWJp*YdDb`~?aQfOXX|Tvn`)GutaNJ=O!FCyy(5Eu%KDs;;~#gGLCeE2#Dd zVo(wm6u+f_;Rnk2dM1^8K?&k|>v17jwPIC*wIc?yf(Det)*)X)$N)tLz@lUxKM{P9>6*Y{(K&wp$>J(V zW1!HA#y4(IMZ>4)4X>$adJMHMi%W*x8oLuUh}{W+s2>DAE+`e5GQsY{mq@=P=(%(tSr8Tk z;S^R_3|NK=$d{WcY68Whpx$k{#Q>+_u$_oRJjo_q<{jQLGs&yC0XZN5tAW8eFj~Ky zb|L~EyeUiyh=dh3J=`DM19l*yYN{Z~XhP%{Ln(RzDp3|2wmcjOIK1<&7;xyKQltQa zSOGD*GE|lhhk{70qF|Hy%iTrTe=Bqs9f)J`xW8{}u(^WkP35&v3`_>{)fJNF;uNT% zSv(gG9Zzs}#A8V|?oT9T`GqCLGWs7O!ywWC`i_>DVBhD@T}OcB^++4zD1HY7G#934 zj4U8Ay}fc;%4%Wb1gO_EmtXXta9Mm8rZX49iY>gA4TrbGrM#Au=M{jo6-#o&YRr_yR7Olm5P`p%UmLn^+^uiVMqYa*r~s9Y~H?YkOycH4bu&4^|Eb zRqs&9#U%(p=uasDPOe9KhRKO{WAmLwd*Wi;=qzpwpT*&GSg=3Dp>vYoHq}>Df~6u_ zfjz4VXy98S$vec@Wemd?uxKn6N5tR=1RNGr zF&tE}b065!m)Zu99JCq>pazH2`7CZCpT_px9hu-kg#|FA;;`rSb(Jsx)Ls*MM}P<; z8$+C%N`O6L5E70=bi_k7gqMv#w95W!1OUmk9;AulkYW?82~v|!YLXX|!F8brHrK+2 ziv;$)lz*o#EFM-=3|aYOd%Tq+Y(e9lobXr-LfsduL6Duve4&I?>BL*%?A<&T6l{JF z4ESX4gs4OjlfiM%Zm5Q}lK&rsMa9ZRB}GNmH)ukP5&<^C31|^07hxL8WiJr>U&N9u zoebT@l0qZA8GKq$D2>nLv3x?v-ZAmMY%24#SX@~n`IBo=v3hBKS=oM!9hOLjy>%i1 z7IJcUkjjWYng2zv$w>2+n!vCl-gH-*tcv3@-C|fQx@UB}KbH`e*9`kzzT$CxA_s}& zi{FTL;+&`y0tQ|ui7XW5@UV^r`b2Bwy@r3GpDLZ)x!*Z1na$+5#X*?`6-x|@$79mn z!{dV40h!G;l@cGQU1T;>T3k|CEXja}x9JoDVl`MNFnJWg8w>{HEPJuumo^Fg^}3}u zSp4+ezI0}S3roZq8VhC*Wi;J2I3boETMWwsGEfA*&Z5$nn?# z+@Q$dd&u4rY;YmMsVJ(*C!ebq&5FTL- z1d)bJaYW(`Q3P8`_5yfE?_W-3_eeMONI!Z9AMs+*W4!4C4$XCMQZPheWRu;`6QK?V zF?5et%?`n3B+Dy^;e3UI+bcC~J>{1eoxt#e zB`8#xQLd3m0Wbp>hK~>yt59jP_)N~8WD16aBg*JIQDA9{#yZi+cqe3Ihd{s(;ZVt~ zqT>B&9=_mP-W(-1Tfig| zp&cF`hs9$lP7Y)yEbU+~(it)z2JcM7+dJ<%gh=Hp6x=@PgtqGmJT5tUU$h&89^pgd zb6Iq^=$LSC3LL3{Z7Q{Tb67qR!sIB1PcW0qLSleh%x-TM7`n_Zc{GISiLl9xCA)AP5xU?*Bu9)B8II`e zNxOVL_52-D2JPrRUGrnHlQ_)q__$CCY}T{6d^$fOG|rpF;L0qL26Bdy8bfhmvtZvB z&O)(xv=baFVzDIfpCF+76{KvxbXiYN_oEnANEj3wv3o@fI5q*Bb`PL>M8wDlbGbA} z5)pPOT=~upG!hsbL=iNRK>_U1Or7`3%^BLE=3J^x;Ir6L2_CG5kbu0O4Vo7^e)RWzjeK z<-xOeQpwRIe;OBpB*QZ)))khU2rV{OW{JazSO=nun-f?zSPYpY%mbGG28}TU5J<-Z zC>$YXxKKT#qa*mTI0)=OVF-~L&l7-w5Ria`bw1WAgPi>h^`-;0V#yI-Szz<>r3c0a zbHOc;DS?R*xE^dYbVUNkz+=7Nf{?#Lot=jFXu@mL??$;IXU3sy1~)j?pUq%#=^zc3 zh!3fQO@!)^Kso*iEGXu$(H5H3!RSSYyrB>cfkEjmVbQ*P4%L|}baz7I2zWMx4uU(p z=ehfex@$;8M3A>9gi3H@5)*I&`)iTjZhyzRO#!^u`&2J~ z5!7OcWw8Y;V9^os5Sqdy_&x<>%~uWUE=W~iwEA-~A+RJJg=m~ZC6Ykn$h+6M2kK=8 z^Ea3qbeZXSk&Mg@Iko4}K`D^%3C{k{pi9?RD6~uP_C|nmoDX*e8-Nr58YDoN<{YjA z+9`jP+GTiV55$(2CW3lNb}LwLU=9%d3W|1BIvj|$TnTlD8k|NTItvbiNV?NxJCd}cLt zCGGC{*9-Eqv~n7J;O8*_mX8`=*t-b%xA`N>AZV&Q7G72VUm1`EJMa=Yl)Svg3j_-G z?}Hqj`Zqo>&@R2Dq6B>RiQo@v{bd6_ zD5nI`O!nG%Ww{Il1kumYWfEwWo&zTUBFd_&s><^4O&=@$;s6Xzwf6&Z1C&%z>2Mwo z`*r=lpeT{__moOML&QY(D9?=OXGlOoofhnoxAn&QuGY^0CH`Noo}SO`ZM|00 z-QFQB*6vqSmV>)=#bEAmL9F{<2L9`3kbnvDYnO_3`@sM*dmV8PEY16dMn3#MUVrir z)IRr!wf;3N$b?@7k@knYtv~E_!@8$>&c85#3_ly5Ci&l|{C!rCKK@m+|B(S?_?ht7 z$*&k5@yJeT#jJl|0CBt-(0cpV4ByK@<;?FUMo1T^-%4ehtlyX7fBK6|0{_Io(A@rK z+5d%;Zt&};FwJjWh~sbi(LD3xO!PF42Mv7VLd35rdv5*5iT3vsNM-Wy+ruMejxs9# zN6L);j{4_)@O+Mx{JkiMn7=-}!{Jw6UDARf-;jOpuaGMD#IH0!WcvK?uZ|D}tOED{ zbr(FvHToaRVg*3jBd09CXOa_2EMTvM65sV*NPwU z03-x`I|Rs~hL!*Py7y~kLI(O~2#^(j@bmM0^$FGcjy%Ok@F8=ls09O5|$&J4<`XCGdN>%M!-Y1I)3aC53 zF8d>+BMIQtx2KP!A61n9uMGf*`f1-*e{cP}3g-VyuWqTqx6L0>fIMns^{~}~8xcDnQ01mSqp95MXw7Shu>mu}^nV%-Hule#0!sF#g`dv>68tXw^Yjq~sG`DtO$bKR&;4ih_Zq;)Uu6KD z()r)dy?t7dM~y>Xfc(=h+2Vgd-zfj^uOd8}_zTwHi24uzQ}F9$2{8cSuP^{a{ltG$ zKVksNsHMMV724gS{ZF~KPbG4wp%p*B2t!J*w+jE}{J;YufO9`T1S8^4`FHX6MgZ2& zHvq)^Z2LF963Y0O6k(sIL+i6!S`Vj^E^op$1u>GyhI;LkJwME$IPI(X?5eV+j;qt^V)`AZ?3&g-xFBRt5XM%DaG@pnn{ z`;b6?o$9xLLN-E>{^UQ&01)*b>R<5dce5cT(?^!ig4NDZ>+XBJ`I-Tf-P zzT{vC;Q#-{sr&ioewSWf4zG+l30nIp27rh^U{=X#}0Pwa-_sRj1`9ECBC{Jl8 zR6;+T3E>kRol;j2{(rkvQ04{F_8%4jOuk)OfINut-!8-mhwPPhegVk+maM<}9Zddn zr*!X-|0(YLm0^ejN~mp;9}h3TpIOM4SDy0=Nq-=G+*R zO79a8|2o3I+yGAwz(nxP()NBqq2Z=>X$uh%+mAhS@O?I5MXG|D3eAA~0nL?7{?2ri zNqcz%G>-UIN+fAmE7b4Sl-zU=vherZ1OpP zbN`u>@8wGl9wm`i5O#HcUj)eHySsgqD&1z z<)7ho-{KGvft<2`&o@PYOukDRs)G78lkZakh`{9SH(~*3zFqop(XX3)9|LkKD3zeE zMbJC>D0oHbFVXxL%0PkS5bfWa{r^n9ml+U&vYe9FXK6=!cXvlePfv&{3Z60h z1wQ|`$^Xs(Sb-8s_W@EI_efuv{C`fqPXux*atgM0B(;y7z$5-YC;z7i07yDSO%@h^ zR;K-qyubNtP&YhljZ%aM1^@pv|0O5Lr2GE=^7sFL-^=Fx-zM*b(=$P#BtwHXZ(KEG z;5Y=EI%Li2`Hoe3)Zmc(pry(~8aR z`r_mfcW-72C(rt%p1b(`+_-_ieSAH6*5dQ+KAWpT4oQ+s4U?R%oGl>Kxi_cQ?{TZU z>{zcd z;lrE!p0tyhD<_Ve%+$nYR2&%;7$Mhic*kR&oa^T(O>^{Fyb4s>vCTO!K_lYHz4n!ZJN3`Jb=bSXS#qqZd>=NMo4@xgqaM?fM!iMy zYo^{g{46PLv2Q)bwo}XKx60xzch?<@PFvZfT0*9JT%1sp8=o9C)Lc0-uVDA-LqqGA z+n&f(Id&uMz{v~2Q#>nujhnSgbON8vuY9!a*8AhfV(z8f;@!iL_uFuy5t6M!-X$xwr-K&~?<8@TJ4kpC5No!?E z&)K}S8VX~b$Ca<~(v9A)W;3_cj(Vu-*}B1_=fBomH%jtZv~{m^c}56Jc-mU|_?FPH zO&@hGizOXf?&XYjPjmM(J}&aQF(hMv(yTY}#SJ(2 z)SfSIK4a|UVIO>+*&D2Bjba+eO%(*2ZD@^|aBeuh{`32_loj@7^WSz~3pLz&D0xNx zg7a7RqNRp&*4u7-cS2k3qW`qVvpbdSu6NycIBQ$2J%7u^lCh~*EOR;>IwV~Nty*2q z2a3GPEE_Xf*}I-xJ-7Z&4aJJh-!YD=Tso@ZMbf>XdlQ?Pjb)bVD?Q^MCRLtpu`{!# z>#D6hk)ZV1xnt+pjbY1AwD`0HoY%fS(_>cm{p(LX9(g?V$nm&kCm&_{IJNXfu`WZC z?Qp-C~CncSu(&p}2 zG*JzwS#h}2_J;0GxuDLo@+}*BcI?<*&4`^naVLF)$&Dq$L#HpphsGLjZ@XmKoIW*f zQ~6dWjcumeh95oqdWm{K#v$Hbs(feH#5YH2=eCMp;tD+EtzRvqNrpTf$zS&}20QMw z@tpYG;;O(SWBebi4<~I4_(Xw9`N5xMN zin6EC#yoqZQlMng?ECRt?yZSYtKA0`y>6>%z4_U|#k2c$b>3a}iAo*eh54qbHw)`I zZx>rgkE0yJyyW(#x$oGF?xCYl7N|ALmuz>DtGTX%w$0}1n&t9em|DnBf4?Sb>WVen z=@+N3+j&-d-8q73e6qG_P^$CcYuCy?Y%HiRoV$^d9kgzO%_d#3U3}Tp<5AJ8*PcMn zT;i>0v9Il%N1I1sl4*$2CDayP!U69oqutsHE^o_8F?P&JIeV{^X_QQyqBlq+-J@|v z3LH~TJHn50OVROiPfBiSEB1_E2H$qgpVal?)U%~o3kG@DEJ0O_O5W-i{8VYfg4wBK zsTm(Ajw_dK^xpKDmhz}odCY;+=LW1jmwIf<@N1@>q*3x28YYjY=pC2CJE9)ul%_N8 zj*Ymt>X02PVb?8Rr`OFR_A%khZwK9JnPKzpTpJ#e-9iXn8%;ZSW1e4V&&EflpXEFc zy57II^4cUz#bJ@}jy9!ifBu2zvvj_O>XAm@x%F4`Y2~cT1_c+ahfFS=JW4(y8_NvH zr%m&_t`)T~41IpZ)GemNdak)8S&S<-+P{xDHY+rX|0rQau&CY6qP&4_pL_Y@eljEd z6-M3Xv1wr8S(m&4{+pN+4!^$Raq_pK4aw&|OpZHx@ZsFI{*}7!R=RsiJYt`&>sfib zEUCP@@^k#so~SpIY_Dd8QLZa=4;bLC=1U0ZBdMvA1| zVORZwi)J=ue=5s2!`ZuU-V?W=apX!-{FNzf?>)=5ZA^G<-) zRmj#JGx%7&d!^~4`ZleAJ5x%tI$w~s3(O|HdG%^P?fR#k+^0tGjxbELcMApcS;JF3<_9!SFBAIf!qwvbTT*dA4)?Rp*uB_~% zU?UFCZ+Jy7ygGYW*p+;qqpHBq(xCq86u+fYK84ZUmkk*_cyIkU^}US`E>)J59Vsrx z#HkQdo}dig)LSb@$~_q8tJHXPP1jAEo2BPe(mr+tX4i5vFSM*+q7C7W%GIMk#|>|E@izpi|L>qTW_a4Hi=5o z8i1M?cK?QNuN}1Z%li4@2FzOa&hL&ZPKx^0bBPdZkl01q|8GV zIPMdth0S}6dZ|i4&!{$1bE@-IRHvUby;RhxenR@@jQK9Xk#xh7EW6BAPjvl9w960i z-RzZG)MGy~Onr{Sskusmed)6|o8LP*uXR@UiUGp=^m#)j^NRk{^yAVhy_W|bL(lSeOH^;dER5OV z=-qrEqvmKVflE#)I4jUUaz{67dTc_e&8Lo=f|dnY5|7OMGc*&8!MSIald1?QQxCmN ze3g)piXK;QYNKN+&^NI$x)~zAwK@7wY2xjVDI2dYeDOB5?M~Xw4!jiY!tH*Mvw7CA zN{+=jjFN@wgeRy`=w#Mp^94)oPpw)(YFMUQQS6=)I=N$+5%Z&_Roms(O8a8^` z*sTdhO?g+&`7B^Lmj&G|5tVpf8*kp35YbgqT;I9a_0z$1d1p@hlBN?nXIK}k8Tq8( zxke%Z<&>p(j2U@?=B20cxo(qJTgLt29-1b3&&N)fFi>!;a>QNHU1Jr!xVt&a50sCr zUgqT^=VCX|Uw4Ofl=_+>i>w?sloh*88{u?)mcvJmBk8FImDO>dH+jKH)_2OjRmm>! zXwxh#?0m0o-WgzNckb@^fvXf~qc15M=CDMHd52u}AEOQpJo}<<)?FHR8 zrFEh^`DqkVjoM(9sRbd=(nbdaM;u&ioizIR;x&673wGcB^l@ReFvL4&G^oe0+zn&lVqL#LH13^KX6N-um7^Compimjd)_2S z51qf171Ak3vKynYa7&nnTWcGBvfg9V426TQ1wz(TugU4&BF(OnH>cm!Ql%M(Dhe$0 z#x&aBIAvV$sxoupg4ZX{wD6<1Y>IE6EXL&TWNQr?p`9ltZBw@`t~u9}7W%wIU+>V6 z>1B%CFoE%hH2>Cf8%z(;uc!~tj9+gteiLr-`h)T!blGWXgO}Os=8Sqzaq^^ucPbU) z06TpAfc1m#Gt+X$$z>}&^;KVO$b4UO?X1--hIIePDRRt?x2J)@~%5E5?2j zWv1HVIyU7AExIDx?Js4isRtBwuQGQ(cHdFKdgDY!MOd|lv%&BqbDj;l-dgcOGq3ru zdFQM|3l!}x`tEt9l#yLegvFuldkCm)R4jE z@?L^>p^}&CaNP_wHKqKx^2^1Rh0g8w4)>Ido-ayLK7+5C^8Wc#>y$mWt)9t~o3ae< z)+-NFo=-Jrl;3$q_0ZD@UD(9V58t;m?c=qR`UT2w($;OC&bu*LvnzcDda>=}!^h|T z=HYoGp-1{4;*(cisbS3APxHGJs?1S`FpH;+^M5g~A?#cSX5vxv*YoaoB=T$uV^m(e zW}WMqJwyM>OEL>RtLntD2btZT9^JUKj>jr<16oIv-Pej;wZ}B%U5sMQiJ|ab)v3o` zzQ|T8QKuHNU7mKst7#0zi4+`8nJVMYQ}dx#8N9-;{c=XwgAKDclpNTuj>(}9(~r$r ztVhCh7asM;Oxvqm)O;DE7dr6P?rZyR#Hh}wW=$VN+qiSk9_7Zc*X)-&9=%l^Jj{8u zSbJvNyrA(`JA$$?^DYb-tW43{Td}x1IrW+FgPof+)BNoCl{@C|VX92&D7d;|z-~pC zL-B-oxs-AC%k56BGMy52$4$*xyXSO(@#68}cePmVIV3juCa&-nuddeT!Z^1pOKQ9Y z7K_%B-VdByzUbKGXU8wdKbVoO)D?6aIw8rII;?+_Gk1%X{`wQ6#<)%yK2&>D=-aS2 zHw8MDL6x5qZk)V;x$-2eEJmwq5_izpcAFmWRSz$C&W?(BcEV=i;SpIcBQ@VOVe0OB zNaP)M*M^xHTE91UUbNodTI>+>`FIlQ?&6E;tDKJoKl1w2d8IPVrMR}OfcyB~kZH*Q z#H6gFdpk}MP!?-9IQmE5x41pIW%dEZLjMcq#O##CtRv}X#o6lDKTVk4CQsR}4sp47 zA?t*e+I`gSnT_nyg+onsofj?@9*vHkz|DENb_vTsWy&be40Ol-Gm2v@FE0p`h{nHG zTfN5iGfp?dD=l`!y$u=DXZiZN?t4J5QL5Q@E!+LAQT^FP(P?c5)Gj|Bct)5*Mum(; zC%z^$PO5LpBwD@HzI?o1UlX2A@;acL7k$;`P?~ta<;(GwC)%DYijT6L>9xt=!zGIn z&c_s=Wg-T1$;R5Wu0_V`3u^PsUWe?JPyu$6;k~QNvF@8R&p{SrX4}6Cz#C=GaHkJc|KK%ane8?g zqXk-2`S)h{I~D1w#_9q~&mQ`u&XK`4CM5?b>rXs`S(r7)DiwA6O8l`QxWv6fn!=rq z@6eavMXyj4)~jcs625|h#`<&0R`-XQV0`8g3zr+0CU5=d>VIa(n1#w&cSD9gdC@pD zc?;H}F{RZIUeEQucG;afIh~pUNpV3>1L3`ReBRb~HP!X%ck?HE?r&n7%;sBqf8!V}f!K3A5g-ApR;zp=h+19xsCqs6pl zN=EXY84fe^?;qTFc+ST%)9ZzAtv=+92|+K}Q@^jXM|^hX^A|SstHrm+@H7k_99fZ3 zq8~^;qSl^8R4Aa~vWB+C7C%-UOU6!(EWw^s_^4Poz%5VBpJi|=+k2J&*~x|0NjXGi z^z&`c$2Pv!n{2Ee${91=Es+yAbj|V|MZ%c9gXi-%7hV`P=`y-#TJ2Qj)KtmCrCkqd z2m1LGYj!uiNz*hfV843KnAM&WPE_CDI+>R`q~2=zvDv|itEUz0`#9`^%aWtFTTp4V zvr3_}?!~&!Q*)j%B4owT=CE8ZKM#~bkQeEQ?#p@l9)%CfXE+=_?f)b zGI_HFyfd$6ZJIu8z=(>@0)D;aB+rHfX4_PtBPX`1=ujmGZj06pyl)+Wxqf$ndQxz1`GJO_L5@b#Ol-!Yrp!(B`Q-3E2kpf+ z@xC-V3w=DNpr8x4VG%j&vSrJV7E4V2r3q;h!gnmW;W%r4cK((~OUAXyg;!5sIfkZN zuX&-7P?RWKPt!0qQZ_YCg`N5avQ*UFrCBc?t48xH3s@_1G@jx~EzTS>XtS^CMRzB^ z-45GMv}#heg=M=D@^^1joceNg34LN?zzq~0<-ddw-n7cuJmVBG2lZYvH2H#HwcySR z#@<t>4a4D$n3f+gxa3yeO>&;Ej>o+-(&$Q&H%z710R@ROCtbf0{ zda!BiL5{HjYKo`ENaKq*)9ZJ_pYqPG?tbz1kB-soA@8 zu@Q?iKSet%zEJLSYjgXd_=`P8nF^<0(tM+#(e&M- zt{_w0qsM3IlzGfNG_0lS?g;rBzpyb+Yq!M<4&|BUjuFoAJi=5xvBqd(e0#;n@tJR= zPRq7cu5$gXaIo7$;oA>}Bm7pFu-*_taHc#>ORn3-_ZqQ2jDYg@G#{RTO&5(!j)sB?vCRX-7TbT*&j3c^SYn;AF9jRqo8A-_D)C6+)B89 zq2B<~^_V4#a%^_&4<6|5oO^1>bx9bhM{oYR;BUo$}`43 z7_7SfrRIh6tLK&Y+*QxAEYk97YEz$j?ac8{2j8_74)VV$|8Aoz`t1{)ealsETRmV@ zE>|-S(ez|$Gz6_Weth8Btnqwxb(Nj!L)1?8e9qj{@o1B#dw2OJ&$46no~`>Wdk$Tj zt}eF$mpJ0S{-?702QmT%oH_e4KcgrjQne~aX-q(}&Bt{Ph3DbT8E-N@Ze+MU;O{%Q z$!KEz0B+mhJ5y$QoAEkT=nE}*Z@XLHbeFImpW5o`!=7IMJY>nafI$P@FWz=PeU(T{ z*Eo?N(MV^@%B%OIvEq3QRn3tno>WqbL{wC_ui&%ICgsO-Z!53b9&X& zpYLuPJ0MYHQMrBO5ru${TV^C>t@%(GH@~V!`%*%B59`b&?T`<<@DDUuth_-@9(nXz z!jnr#k-DL?x6j=bd{5O(H^u32&=mZ_LkcNv0k_Praq`5q>hCMwY`?~M7BIK-Wm>*l z{uaFn!%s~AZ8+NNgRZ~A=~Jin=xMjVyU}X&>CI`+9ZP);x1JI3^Hy$bzGb~%4z-C? z#uQx*m`~01Y94g6uyKFgjiPH6!#!TyFU?O!Pae2xVzQgs#kbz$kNaIXcztN`F^~1$);4V z-8eV@n=`u1aMW0)x88j#6Lj0HyuwjK{BEn=#UEfaFb9*|mSErN2hG=Rofsipvb^!u zYjyGBc&Ai_bd=KCBgdw`Gd6qe^;r73Vz?#cv}seewBuQtyNUW9J5~B5c>`i7DSIAo zrTMdg%6nI>wkb>Nt~GVYy>Yzb_~wT=CBd9)moB~*PVrRh(<3S|{Pjok10&%TIxYrw^jFXJ0Vy&bjW@yytQJ)uC7Q6eq+jd>;6O zZ+x_R+hwzuu_%@MWB zlRfU;yKwC4%p%<)ZkBoKa6J+XG4+=LfOmC15Rr%zk$A|!`!J2MLw z?>pnFakJcihcQXsWS^7O0mU62B?E@4J~(h}FX4nw89iHWQZC<^yf52Or{hD_Lv=Yz zwK00WFIBgVZXa%Qtg$S_M8X=l!~3Zz3#2`t)ig)RX<%*6yJ>gkQf+rnkvnN) zGbD3Ynrr#f{m1qzY@P7ZD11T6osFLyj$&&*YR!FS8P|ToH{?|5gp>!~ZT6lX=4MUG z3-m7b$eABjut=PxoOmonv1>$0n)&JFc4vrt7QW3Ja;JnjDCW?ruH|OVgNIHYe_){a z)^>#DAK5${9+N1Smvlm^_P#aT!ckC5((Hh>`p;3s;4$Iun}~|f?wd`} z+j@8Gicz8OEZS`bP}gZtn=S~#>5|JeYqDPJsqxDuydT}Qz&>~8;;zf}N3*?}4(}eI zxD+$@kODSVF(+kBbtoRS%P4k%S6Q*+{I0_8fRBqia>7eR%T|5%Q1a_&Tx}x%=v8q> z$5Vk}bw$%4@{V=uZhj2dY#Vy+j*oHsgXzOka+CL)*+&GgkWU=A{0!^)>?c{}{^KWa zA2{j#8wH}?=__LfuN>b(GBe}fv%TQHChk~n?g_PtPIqjh-FlWB(bh~;JeT-rW1~)$ z(x>V22lhureahY|$1s{xJj5l~Am?p18^Ck1 zOKCRM4{Jo5Xa~UZ6m@@T!<5UJYQ3jBzeWD1wVwiMupkHlQTwen5&x$94|s=Nqck%_ zebH#dpX0H8`mWD?=l<6>?j!NL{ct&E(l{=cr~S_vr2!;}KPtrk;{)|=pW3mvEwJZc zhZl457$h_S44{FaWuTz?!+=IfeGWF>>_5~BRel&Y05|SK`je=TOppvAFQHSuPkO@% zyqu4UwO5RYLijZ5P&+6q%~1=mPrtUNL~Yo3NVT^2VXlsMP^W0IEO45M?H8jV-iLHh zxv)8~Ox`<_5M-L&oA@<>{plnG0vb{P8dpLKAzqkPCoas5$$x`~L(zmL5RLgzVj#z3 z|IA(Yepfwt|6hKVhCv>A#_1>V?@UN%3&Cl_~ z!#Et0#LtMik}!spnw;YoBdive=df=4R{!`K&9Z8)WOHPn7a>v`?X{i+?Vy>#~Bia_$TVrGM=jeXQIoDQ$p1PBLR zekL#kK$A3H*{6Cwv)-Qk_IT|$IcGZW(83>&CH9G>vuG&`y|S1<)QgR&)C4|r*JuAV z{Mdi}`4`@E#Y%T?PXK-bXNK~9Mlb+F{MdXGe&m_;KRR%z<-X(XLxH!qHF|TsE?E1i z_S5r@PB=QsF4xZkSo#aVf?ojDpK5ezaSkM@OQP@4;fF4|Oq|1W`OXi207l>C@!Ry| z3d2Uggw0_XF~sTfcp;4gD9@`(iaaPgq~6+mP<#To$`FB#fOZ5~8kRR_@CR5ziXc<2e(uQ+g{5Beqs5_<}X%C;`IhRi4+Z3YjkBCTlJpDiH zET=n(hE2by$-K$d3sXgX^ymZiTQ~T&)wjtMG=)5ltfFXX)`5$|3!w>iDKmLVa#1?pCQ-c@FEAZ)#%Gxa@ zJc*yul4?I)aOck{Qq}m(ttx^Q-KN%_TTQ5oQ4Ko*MOw3b;+ysWq`S8sa59fpc>gh2 zM{8R@7y{IO2JxsUR99TG2-z@d!vQdjplA!U5bHJKR0xym-YJF)v?Xt0g#q>D(UaX(Sb9`UCqO9UNy`5kTQQgbT`lpjR@%7z1CSVPP{;4D7G1&laP#--OEWAGJX(@t?w zE+Y2iW9x2-NTVKT1!M%v7L}{+-a)l}*Ku|AWs6l&QGq&sqDz(xwL=hx${-j|+U4)@ z5RuO&PJ)awtASib8%eaf%!%Ji`IJO!wGD1dV1_VoTGJ-_7=!}wi@>+rNzPM|DN8qBhd0r4IU3#%!L=PyXb#YOSI)8PI5Pksj{KH2(ReeK; zsPnyjes#&Z1#0cexgznXTGO#+1QCEH7-1~I*#Se@ha{gC<8NC216pY zh^j5yj*5#ewvemBqC$0|bqEBZ@e!kd(tFrBO*uD%#_AHGF5tnNsUtF)ao^5Pxl+`X24j??&8>kVE4Nnu`jyjzaQ}? zp8sQiU!%uncLwQwcskN)VE}p{ZLo+2Lr*;a#=q^{)AXm?cDDw0?QQp-v#dH^URGeE zicd>EiH&ZzB!14@H^N=F9>mXU8uGZdN{PfqM>E5?O~4 zLPC5b=7c2I$T>nN8u6JQuL%RtJ_X!yCMM)WStla|Uf}CoDUgTE&+`Q!&2gWqd%!;^ z(}}zsk6N^Jp<24Q8qQ10gmIRY7K0IBnHr4CB#s3c5B{S}JKzykot?evcx#W^v-gmy zKYUyr21Do_7*;{R({4vB1sB5aqXGI|gSc9%n{p@m)@@IfgPmg{!?to+-|+Ud`f9Vf-kk4#NWbY2n&=h|*cVpO6(L^{sL^|xUd`kNb)P(E~T&bxu;L~RQV*a0++g&-ME zH7+A@?#Q;%FiEE-YV_qLg=)#drRv;OOVqh57pU5K74T@x!<*DpUnwq>6%hAR>YyE#+93nGlvI_V-^F48 zuBoY38#irM2adF0`U7V#yu-*#WCCYMLerrRU^gO0+aC=N{9(o7+aeuDp8pen-%%cF z0uGSfwq}(92y)nLdd~mio8N-u|MRVNEy4YVy1Y3#ZXEs?u{Fb!5A%O@>AFQg-du6s zdFsVoAi92qARdUG1NUAao2>H#HHd~JQ2O&Xp6F4F=9dbCAYzYjexi_0L~x>%xkK8b z)R((NWD+XgrM-Y2fei1JgAGME{af94&kgF<8!t#4_DPrG&aOU0|LIc=4bAW>YzHIi z2UF;Ud-0H7ED-6;(|(*N(`XCWWneKIwv)t5sG3j%WJU>x&krmpcNn=R~Lq1X)=W5fVM#9nT%vI?ZET~Q&sG4 z!Ac8kCk$sETN4m6+W1rf|q z<)wLI(8F=k7NI30z{yZZg%Es0Pa#kSB(upfx`F_mQa$$P;~5zOzPSSrgzs^?RcT?KEbW$+maD}J=0F=M zQ)OkvsJ9e0n;d8zV1_bH13rLR1w&ZDL7xVPB5Eo2Yuxwg53BEf|7Qxy3;ITaH@!Vm zzS$zOcAF1?_J{rLe_X!k{qcd017Cs`Mc^zuWZx?bzM;m2JCQ zH1T^8t(Ds)0TW7fF2Pm9C`XI_L7cG5@K+YVYXLB=Hi7i0JJ=NbbvLNbch; zy%_lEbq|dZmA|Mk7b^3hy5`c=>Qf)R8VKfx1f>)vQpZJ8VO#+w(hHHhqAUmXFP6C| z9g4Ug!5{E5S`k(gI#gS0tExw|;HIW#sMkZPy*s2LuqyPK3?CfYx}N&@?dnkfezVgpFSYYGaulQIQ{$ zn3u@MHc*S;Gfq+XP3f%tDYV~0QIT6+b;U|`*(EDgA2gnW2bp;b zP?-TkVBj4vJrokfQXHJRWTNASEuo;m3BQdZRZ~*~rVmxVtQ^z0N-@m3Jst+$5N(Vu z+^jnq#U2i5DZ>IIE)7$2aH1X_uHr+Ko*`&M%hX5ixJCW!p{LP!EW-mvqo*aNx2N-) zsv!=Yuwgn7iw;KuJzp%EclE(g=NsQctE}mO3|mGy!SpC(@^B++F)9nAu!Q#i3m_Q>FlCH`a%$!pFT zX&5Bob0g0dgy+(ElCXPH?S?80tR>PqopCjZU0id}5!HOWTQ!~NR)Ygw z!mJ95sxeI|S94)wC@q_VBhkuK2~>Hv+XF^VdxB;%B={(%0fRxUnGlxR;K}~#=I}4u zVcJJckqmkO%;*ERT#o5UoBGX5ZveOQ#LM#xvj?E{+|3H)hrxp5<||zEWqY{!C&T{z z!_c6Fc1C)dnX61Q0ISs+1y8hf-PCwu02^w$t+^;9Ee5Ty$fs_2&obGV^TJDep*DBG z6E7F>^-D$iX;oYk-v}5iq%=41leIL10WcgGt=%}hLy(mSVu-Hffl7=FuC=u0Q0fw?$v9-5t#6Q7kWdh17^-wcun7Ui~SMhm382QP{~4sL}bQM;C$VSD97iosa5MPSc3LOM0276k5d$n+lOt} zIl{=WVFpYAgbvLQQMABJ*mp8b%O*hP2D^k#gf&~BVUA`!>5s!rszQ{G#n^(P5sr@^l8Ia9U>R7##?xy|E zSgJ~Q1u{$+7*W_TkH;YlxJ2)`WCO#UZSLG6b^A@L#rX8hi*>36C+Xy335$}E#iVh z8B-9Ejvtd)7YUx;T{8DYoB5n-v=_v}4<#9$zYs(M(u(m12Jt(Hl@BMRei1&!61c?e zLbRj70Wh**u`iG?_&%p7goXAU(0v=*4%41=e~+z4+CYwgglN0yr)CJ$#(H}OWLlF4 zW><`PeQ9x_L}v;GhY@Y57kwWV1|S+0VB&$wPliH9%X-*H!jQ0}ts*&%ZHIYA6rnwc z@XduW^xjY1rRG$WfH8znCTU8y)#N`DzNKdzRP+{`2qh}bxX7O724|W92x)*o86_1_ zcnaQLx~L?80p@W_YUPS5b?f_9LT&F;zxv(o#M(Xww-bgyMEiwNr%WTmDTtWmbKr4$ zj|Bag55_^_2OuXc4T)?xXaIBafV%tkE7e8kFA`T^I{&bIwres;Bj@PMoL0cC8Hj8x zmEkcFolHw3J(mr2$T7~5$wWBiph`=^qK$!V;PRwN;}L;*o{ev5gH@4cdO8LJU9U~Q zNQ_)Y6={#=(^<(Y^Lrk!L1H%yjhcXlPjcJ&d^9wZpW-@+H?TcqN@Tdi1!{wz`}AEZ z-$TPDVK`w)V+zjrPqZBEftw0MmG$sivx{ zD%M}Hrg}4$)@&DDFh6?j70VDu|9Jf6^#{;-&Y9sCjc*bL)&9c`aPh4y6fZXZ7HMuu z90LTojXL-~=bYt;>4&97`OUf~N_i{$!u& zc8s)z1g*6N?IS?-nSJMe1|lmlTcn>5g|6ikU72iCGLnz|;|JXmxNBn8z7={7PtOZZ zBSxe;VZaZQd0&1^<)Oy2&^>ExGu@W$LawZw41)$aWp#Z7RiQHZS^)7^5`Y zIKAbMVm3eyuPx2O9Wq;crWt@~J;&HlkeA=GWI@H#m#>>2TYXNo{gpQksLflB+Bhgg zcFhDdF*@P@f3&e%-d}(?e3}8Uxf)qa!RDJXg74F6jFWr>(Qxns=FO*?K2Y(Z#6V0z z7}wS;pQpa`x%Wfmrz9J{K2z^Zze&*iFp&-nL0B{~eWK7sp0>a25(&{H(>L>*oi>k? z^Ch0o@Io^I&D006+H%`XSE%=2e+9e-hrnc^ZDGu_KwlI00_up(j-a98VUH)jrpw=d z?5W^DE8mHX4oW(4F*A9Z4F(`sfWaoKQ&kluFF>u|y>Ux}ZP)(R7`^bIvdJ@p5F})7 zR_Vpu_jqeByxYp4nrrKP0&B+fn(qpif$?+*#x$H4Cf0Z3lAD@sP#RIS*Ak-waRC4P z4{uW?IIM$4KWZd`Cu8cG{dsmkK;wjVUk+hfchARfhD-T!2ot|%0^$-q!lbg?D?K-j z$6=g+X5`3uWVmbJAzV`WL;8pe8xL*M*3@h;0MiMwTt`V^Q9X{$ewIG_UYy;|`5Mwm zqre-K8ujq&FTptEKfnYVArfUiWu=nLcjFAKLE{CdBs>Vjrat@WJK<+i3Y<9IMnRrgfPD)Q zzsH?Z8R$E)@rVBIoyz4Yl)1k)SY{re*B;<{pfe8y=Bl07QsXXrA@~%@zYNo#A=ww$h^F9p^5u z`t@=+`UvGpw@ele3Lt=toEdOQkPHHcev0!Av!)^!bB}o$^S`TmSiA^RY7EFsrXWT? ztgF-L_lXZ*A#SvqVPuo|(>|uN1&)D$;1Z029B+nwzvu#Z3f_4Oe2x(8<+KipH!9r^iQ2PAQv z>bqpZ7o|A~nd|TKs*B`)6a4(??n|jXQj|QBKOAH}qY;pSKH9K($Vl;ISh5z`>?6)1 z0d+2neqX%z2GqxL4LHmjrpY7q%wDAj0)%Wfp=2KTe4$zkl+}P3p#3u=0MJOA|V0<>}vU(Jc!lA{6Eu0%ypHubf zqS`VLDncg$0+9SNy{G+y7xb7T18B$A+aYX7;Sw3?-{KXCHXDtB^M3{oVT`}F_5tJv zQ^3RIVPzv&7uVxU;GFWM&)uv_@eU4+)cK(q%%;pkfKWaikQPrtI30zt@8VM*ze%k* zXDR35;`*)QBqU=?M&JPShV+j|5o5)p2HNX>6b%pKmPD{Yg{FS73Q4q9U{u*uEJFFZ)sx%vjRMi-kYb<%~swuMKwXeF?R}(+s}QlGwuRrED_*t8;PK z0_(^q*kURXevk&v=l+`iF-e}L(B-$exJ%t0c%B71d)h9px z0URAnxA9EJ>jz!|$Ea!8u}d)=?EX)Y{_y*^R<;ZRW^JUh%>b;9Nk1Kh`30RTm(6)> z&C0nE_ygFrv<8KXl(wmPa|_`M;FnW+Mg7P91dS$vNE5eq0+RVZ)PD{#?FuC2QN)j3 zc&k^`sL%evd*N2gVD9&*@+QLRq9`_8nIKy3!I~xFFwW zGv0o*{xd$`+^RzCqwkgVe@b&QhOvpaIH2@3FXH?ksyq+%NS^i&vif=y4J$c$MO&nX*q<*tK`lAgRV_VFUQfJxPHwr65r2_uutbt>l0Y&yRM z&!qRv`_nNd5Fg0+8L250_N%+^e4kpmY$0M5V6_SH1vC>#)R2h_57zp%3v4DzpJbV* zg8@j>Fq}dMM;lw;d#th3hZB0kocn{AaS%upbNPI19d)YX@B>J>lxKAi<%p_JCn3H= zWa{V|7&ZT=Wp*&qzQUz!ErGXo}!TxI4Sa6ZPY2xNM8Mq*) zz>Q;{fF!vU*Nm`w3EoTA0I$jdf61As84y;5E5_5EJ3_&%FLq4v)_#AcT z2QI<>^nl!>MnGgbRbm!SK^S{bh#JfU2R`6k{U#BK&L-4Cxc9)%SbW$H%MYDTxC@X= zk3=dWFh6G5lbcqUhj|(ChGprx^e#EpFj<5?RX?)%oNbl_7zx}c>;N$Kmxrw|v^%ev z2n#Yw<|AC0D1iw#MtI_(E&OnE7GcEu-12SM1>q`;#Tp+OBV3#Dkpffv;d8ME06!S* zY0bIw)g8Crh#;$ocm#&bF-vda>=rm@cg3QZZg>l7@1iXL(*bS@NN-h>EG4?K%gY9RK&`AlpG{-IshT6 zupo#RY8eR&Fr2~Nb;n<%FPJFKYN8leYIIM2I z;ZnpntXG@1?uLyZ2Yuk`fOIS!FtZA>A!q`2|G9E+@#I#{Lqy;va0OrlIs!t-X391L z7}d8U$69W0Jl;9Yn<;#G9Xo(BybF zFz8o@kF}_M2ah3eYCD!{`{3iz2k*QgVUA?5WO(%cEX>bSg@8k0L5@TMEydWER{Lhy{(JnhX2daB8bm1J#S1FcT_3zo{rG23$#NY7 znWmKyVBoP4$Pq<+0++Y=Zl^o{-=m=cD;zknB6}&D48U+U))_w9*z%sEO}$IoI|mUB zMThStT!`575!AC4$8is1Fc>k12tSqq5K3lHIftbB-^=+wV$hLkP?@6IT~k@CKK-$) zAzAPO7GO@DbjkcnoMcp)Fa3`P)KMIn@p5@}?v4i#0@eh>rxLlU_pxFr#m z|EQLNM)0lG)x_|kpU93*@N^-(i+_|@?VXH{)S;gL{dPg@MTqmicGY~fX61afY8hkU z=R%D};1Vz#PLF7BV6U`xpfv*)n~B7>lTg_gGC49?nZ^JH`FTA55_>ifkaORmCJ=iA z4stoF4j^oJHy9`F9Ben4a&b{!0)8T;VT4OgC{D(kW6lr9*T7E>U;#im4rGX3&>|=_ z_?$$_V4}n!+IjxhUw=w{;Koa&eoj5u-m!RO>g9b32KtE%`G?i@*IuM{AiD8eTk3#M zIG-YY@dPnk=r0TbPxs_5&aU zHJ@=FaL-4sR*Ml&fB`uvIdqvxg^Y6v`W2~5HT~lsx=cO&{M)J>h>R(#%wrjpQ)~l3 zI7&bcDtDH}sQ#kCqL^C^*Q@|pq!Xqmv^66i@-PG#v(lzODdZ~#f#7!#3~cwlCPX+s zf)Lh>H(h__DcsziWL0MQ3+AQ#8tQabZp9vVUeARA3)2?sDCUfz%) zleP==G;q)n(;s8lHGSiB!Nd@xMiE504n%<}#euF@UVZ^W%&$rqd{b$WrX2GvV}N$? zECDyB0w4SEjW}kmQFTJFXbm7;fQ6$!z2EJQP%JX+EvQ`gk%9JYTO+{%*uflR09i}f zVgSTZO!4BF>f0jSy?x8;kG5ZPqLslgqX=aWf`t&I^}c3KfpnhXy%W}dLGjTrAyUZv z9tgzw_U2P#0tWe z9Y@rg?;KD&_a1{(=@+EnfhClJoPKCcMYl^%)=gA|@g6DKx~m&PMkA^NzDQ(ZG$Vna zCuEFplZJZh6UNp9)%c~?cdOsMwhOAS51LX5rV6#{ymK&JfR?~e+mw95{H$8i@x`_a z@k0vwa3mYm^19s(U{1$W9S#LKi1Pz`aH28|dV~kHHir7vFyXWas~})B03VYCSb$1P-8Nh$nY=%rh@6x&ah_HGC3jXg7cODj~Gl=$5zy3 zni|IcW}R9KaKKiBsldW2b?0sGQ$K#-Nib)eFOb%$01?aqhajBt6b;-!sz_g8du4TWK}aRE`{BC{c3 zX#MX&cxWEXsSP^g5v?`<#}QUs|0jiU0}$0rs>SoG%hbJ}cn^?FtA@y6+AzU4QDAgF zs_!)1Rg~o-MEHg3rt8mxROnEfwj5F$w;sYtx-FO{pslbwc&$T2sA^IZA>tV(GG3)p z8?o9-e2411xv!TNwI!PHC`Pm*mdWW&eM1}6>}K^i2#$7wJ8r%R#?C8+AkxJIH1TW0 zC+F<@4m7I&`01|@_y0J;nClSbB>FsP1a{2BC7wU}ZfpXSoLa-vQwNTd=^J64)O7;_ z`6e8gQ&wJ(i3Cq3&ikVN`x)QQsTM7mhv2O%aVEhEwRl05tQb+l;F#!h1V$Bm${&QA zFacyNg55DY-Ltd{aLV8vk-cd9v61#0gB z7{dm(gsV#MfaR^2yLXpK_D{#w-1Px?j$P&$iQcD*) z9@3kZP!lkTYP2i^;vPW}fD~e)0nlp^9+%H}*vNN({2N&>|BL%>oN>RU5;TKa7RQ2; zzu{P``m3+q4{QDqEbv~~BcQFJU12ah@nHbL8YxeLWny1+cTkwVkoz3BvB@wk;sS|~ zze&WL(~}Vn5Bo){s4Od0t1nriF2D3#wfdY|v5U}_z{6H}G#cyXbZ?~Yu{BNRH`+^S z3=QQISQV&0(uC8F`oPS13K80tq_s?@w3G8NP&TIl?pQe3t8$7Le^@mYJPU0=vL@re z$?vA`PBs_-hnmcs=R-w0yL;CiZS1-1L>o@2#gvyFg-U0mk%$Lzaw{(#gQ(_B1O&~g zxts>3fuqC@;21Hgl*2eJ=N24ddBb~F15p{>XELh)!~oS(2hI!i{F)n$fc7xRBmn%!hxHS-KmPgeWXX=& z1|4T25%xk$=TTCgcTZ6#$IlSF)Pd>}jzQ=ngLkkD;sxwPI6f>_;`v>6@wqq?aTzql z62O%!BSATx2y07XKVdt>@Cqg}lf*ISRFbDr^Gw2zfA}W#Z$Eli17u>EG}&qevd|{n z#0#gp;O4?P7tam0|NaCD;5s&I$#}{J12COHhm~q*Y`g7fQ=hlD*DvZn23ghxiDW#? zgI4zroa{nNk~vrQ!jnH}s=$p^kYq73$&(aQ+|& zfBBNRGR0%`W=8sDoy;S?jIj`GxVOKzsG+g* zmL?nljwLL+him*WC=mg;JTwnsyt((EPBx4dJRaf_ap)r+2t-Bt(+jK|icrVA5_7+c36i^BJcWmtV9RN5rpGYu7B6)em~)(U|M!d5F*u3^S#) zr-gq6hEr_NB(l>?U_awT7+^CpHYE89=HG1(ul>OUcD#(l_6F>!Qg5hqd`L?$8*`+3}TP?*Ej zmo1fP0>hiXv;8o%f&;2m{=x_a>8_{@^Xs2 ztY1q)Y?i?Rr{b;*>CBausU!n0uHgePC{v$^L?%uZes~z}-aaoJpcbo3)}5;^x^TIg zH>VVaRXglN0UVvi=_1{NX%EnIdrId_620l47zCkZa5}YMeg%xcSE>gd`ITP6gw{13 zrftf`w*d`RA8ZjCyxLb#dx26Lw&59RGvhu1nPvdeS0K2o{ljAZp~mAl>uw+r1JODl zo#Sqo-R1`an2RvsBmnXLgH$DR7!SB4I1WCEj;jTfQVTISAN$ayKoVNW4P2%AjQ(L; zm`HGF2HH%cA=Ps2!ASR4(hD7x?!5IPOcBn*%0U~nf`e+)wt7qvTGbHdxztv;rpRA6`Ja&^U}D{&fhwH$`Z_51)1NsR$?v|&)iXA;eU zKn8SKwt@W=`H_@0*+1+fwG2*qc+C6t*RE4GKC%jDp;53Ed+^Z_85TBs~^Kisc08y}i6Sav< zf`lVeJ>(BSn$z*81c!v4ckWVk`NgZ$xv=6_;Xu?_OndhAz}brLFfa$aVuZPCcZsc*t?1pF!uZ-qs3cgp1PeebO7rAoU>}G8c5}SS zVn!z-G^GLG>&J#07hH6yYExaKR*+$sjc!beUu+)}hmP%JBC*JkRJchuUM?jSpN~Jr4t2zGQ*A40HR7FIbNC z`*Lvz;uhb&0jT(}32;0;h}TcJas8j;Pl7k_9Np{e{bKzx6#!Ejf);SarK{A-uPso! z4>X{?bP$@+RKUU(abJyK8E}Pb?t8DpHsKf0LpS=LQJ;WJG62C0bRec(f4go!*3<`= z)li(034|fme-a3}D>v5=(MwBn)xjej!tPmsfzH_BCgb3okaIKfFE8|AzNmc%4AG5! zXL7$;V=APc+}mSw8RR8W>-A$TO`Mq1QnC%=7&F#a4G+kL^Do3{$!pa9`Zl$3%R%)9 zrU~^&ThLEPL-dul5=#2y)@kI_(;@B+SU9g7{uFLuGJI$H48ZD%1rF9bcTO1wL>oQz zxFokk=Vw2S^AEi-E6YpNjn}TiQt2ufM(2x)Zyb3zhGbkG7rYDck31d2pZl~ zFLQElWQqX@+5q_w-@z6=(bj(cp@z<@PPFz>rE}R}v}4!!M*~)Swsqk+@N%p%)2K(p z9S;G7r~r4W#v}?N4(FvqLwZWVG7aIejx!}0bSC1HiqU-KuvQTykO zJx|^J!Ar30v{(J?@%5@3hA>K0VFIGsqX0=pQWJ(zNZBg!%H#HAP9w-vDMhE3Shglo zF0sz7ssM9=b^$6Sk+U1gN$15o(8Ne6cir}0xYAw$7v&OU(@Un^u%MIZ>0HHDVd$W( zB(H=dmw4t}6NG|Sf^!kI0_REOVx=m8Db4iZo9G`)Cq=jkLu@Fyxv2Wexp;a4n}tX$ zRE0B=gc37SG71G?>@=XjHMeGPC{l!d@jMei)M5gZ2tZYm*9_^+_5U(x0F>;)0GMH- z1Yi*nbFeO}v5^>gs+T4zFgwrKLVnm<8pwMx4mKj)d9nJ=*Y8nF zYAeOdP5Vax=@Ly9#1WeZC4Mos!fN$Dkzv{w2I=Ws6U5RicWD+w)1s|`%y%pUAR(|X zc6iJE-Cz6x4qE-7?9=GOIy&Pb&@{>oK!(9f-(?JmE7NXfR$ru0A~kaF31d2tnVYek zc|!;VE|A*tut-|X@^Hh@XhtAp3ZHq~QJ&?V^= zaG3Wxp#c!Vi-2TCa*ahYRwMWDBBckX6J^%MlMy5$Y0V^M1lB1VWNIQJks!$UNXXii z)uy_!yt#C7nfk`xeN=IuI`=sU6GcAum$0Ch9^plQ{ACL(A$<|;7*dv+(M%~zGh*$E zTYy!7;$j}vt{DJXn2~;jxQ4$?i2sIb*QyR|ZDzbZF3FO)k=fHQXr^e)ulsMHkP<{^ z&`k|B) zQaS`UN#hSTMvwV&@()*4a9=gg1YkRex=i4xxa4WPbmn0yhZaDDCQ~4irAp+)L~yE- zWhDp(g34q*#$wMFd`jvVn{1j*0?|am{Sol)8w`q*)ZcvRgD@Owq$A<6tAq~WM55%b z!Fe?$vJ1`_Lrs}URZaPl$9hkm-Oj;lcO-ob%i3hPw84;R42TSyIBKhIP8T zo)HFM8fkHQdv{-~*XHnTT+CK7I?rB@_fW6wXg27Ux<#r>EXg1{GD76yQMIT@h`?zEIj=>mfw zI6%j`Jg~DcK#38093(!cO3$U(1@q^q+itu_Rxs%8$Z=sJvoe~3+&GK>5XW?15R?Qf z9CYF$3}%?f7Ew+VPL3YsaOZ6p?m5UJK_ddg^l+FIjm!uGAo8*CL<}VR^_P@F707u9 zyY^y0W}87!K4c>NDC~(aTn?;2OhB;%0O<&rv5Do=qE0T%t-4#V35Q!WX>WM9CGir3 z5MW;^NiV%%u>{zl6ei(oIM`*~e+3MH`S1haaq3zFNCz~4m?(xC7wexhrveTz*r+2+ zhI4*B4&45G?uuG*7BX9*XHFOCXn#7(04#u$#LeSaw(achmgyRq0l10 zr>HMD*s?7e>|?{B3R*LObao@Dtc);#MEM#6U7>PA&xs zf&qd+F!=Q2OuxbcuN(}*-+Xs$VzFilKpQ~a-X=&OTF|A;84kwS+RlvMgagkgeBY(Fm7rl zG966wWdl*LsDwnTfm%n@e zp>VK=uSYDQmN7tPi~*oe66!02Zj~1!aKpT!!-E0OfrT~sup-A}(D)NcPz5iLm^-_M zh-1QItxfA@Uh|n#0tFQ%J#AI}IIeE< z_I<)^sTFBqHiNd3vCyR$yE)-nyj}Hg7{ z`MLh0!h*NwK!QMp!ZbjTmmUxz<@uXa25##mewTp(XjKxxNzn#!T%sC*PZ!M}Jn*s@ zp}<4%KJHUGrFidbyTv{bl!gd2ipE%g zRfxDN+pVn%_?!%a!9QuU7@2;gsR8Ne#0<(HKC(}Yc9aVv?0-J+YjxnzG1z9fT4h#5 zrdN83I>wjLdqOSKZeJ8{^cFAUtH80LHxUyW0AuAdTo^Y2Y1N!$nWn@sKpV_zO-)q^>PFcd7<^<|3~f7tjZHyL#lVFP zV^32HPFpeT2L%%&4V4t-tA`$YO}+Ti8-RC?#cVb1PP>7GEj;66(SXC1Q{LX!`pzzv zWq0OS2Ca;ds5CjVrL!YigNfz&>}o-E!S4M0f`;qNJ)ovTFwzFFwXQ)EmoQEC+nSqW{?FZn z3EM!L`z(;8iwZ6lEvUh`!ikh{rw;i&nGmvHTj*er~8X4RnU?7fZQl9kg_T!&-A4&eqP&KT=o zM!}O&1_1a8aR9l(ptKs+6Y{_;d5 zW=WNdR2rgBJ@RZu;Ef;t{8i1NvFXN0qg3yn4flv-bn(r@etZT1ePi=M7~y;}A17S6 zN+2%4Q2%KIG5!+*)3kLE@W($+tHRj$G8Ykdd3rPFzib~h5n9FvF+F+qH=D%qN3Zng z_fBfYS-=4ZKlX_mx2Rb$mcw^`_=KGKK(AFYUp?rD03lHWmtAQEaB z2l3NVIXO)R6Z$5J)_EkylKHuC1*W$-YOS~$Y|@`(-Vfyds&w$nTbJjycDOAxwWnf@z178D>8w5tc7-YDun znHm|NQOnuCDi{DISujs)+t>!w$1&oOUmTah0zEct13xqn(n;y%_1m#a?id&rT!wLv zXDCiEa%pX5x(Z`oIsK$PB_BI04`OrHH@^KKfB-K>5nCGeX{Zm7C2$cR2L?d60B(py z5Cnk0ce2HggYKWb9z9Ijp#sN93%{$vFC+R>KosC4hL!Mk+9J` zX!C5+1cZJu{QRa`|KL*_A!QMh9!$X?pG*&6g{DEO7EypGg?W$g7!O+0r4lIo?Y^?RE8-!chz0G;E~J)2%-n@0orO-kgGV_xja8{`1dp zfN3Xu|LNHX_yW(EFAa&KOU{a;@gSURs&1fTXA=}A?$Fh)oN1`!R7GT*0T@`pCPb*& zTRY>eE>CV9qr^P-+g2D+7fl=2iB@fF094UiuQfyA)h?)hr7opUF2U1xRXFw z2e~r0z=q%jS5ySV6T_54{r&%W5)pcjNLh3iA`_s-a3&|+AMC)RS`CT60dWD|LVN(3 z`*Wj@g@mG#g8@|FKvABkOX4I0%yjp_V_*lU0CNK3%CKs7lV~>>!Q; z8D{`iH{cd5{Smidk0u?vu{r!z^swj@ObGR)qp&?mF8D{{d53)5CKf13l1;4o0- zw@odqDHW+j7;Afrg+s~NW_ZnV*)}rP2$rnqr@;eDsh#OM*%X7cdS&48XV;5e!Z0QC zbcRhy>+8~|#I%Xts8032|K%aLmmdVob770h$kb5~7a9~|p@i&iU)h1MzjGfuhRr|{ z91}22{~)G-TviwWfGlpoZ~=}~zz(o{LE*k&ET_H}Zo$L;NQ5ev&*O^Yun-u!>eXY< z?NkR2w+o@JT0Td#0tWmfv!I=T20|hoiQ5o_gq%hP5p)F7&?VA|{pCEB#;IYP75C+@ zK7?bwyJU;4mP+a7>M0;R#apByZt$V)gNKeh`1D&i4T!`Cd>{qki`VQw`^G6!&0K7@ z;<~%ZCmE+$@uaewm4JvU#wtL0SsA<%wINV5W7=Z4EEyJtwSCuNwSH5bteew*p^ax* z!Rtx(1|TH`ph;Y|q&xLDzxOy!rrr&0F%NjA50w?zPpZVJWRk(;5*Vo7y<@oN5V!v_ zFem|(HC~w&24K2FH66(@uPU#%vMhfqcl|MJ6%iSR7aIwh#uu*Xzy4-DBGBTD03<@> znkyC~#1!KGU%edLPD@1C@HAms$vGG1`FV^D*n=|yDG90Wa|w}>o=aD(rwb?ge&y>A z%OMv$M3hDyJ)f3pPWvAsT0;7x{l9&+4hK;*t327^#~>dgYP3jV^p6J5T4(^G9RLQx zCo+USESp43V`B+4fJKN>G~#-!nUb!b8h~aNk3YA;Iz-fLn#oY(rZIYK38NG-s3$|6 z{pZfA`V}y62LG^Q`44Wu>xa7b@EzPV{y(QKA{{xh!T_ux z*KTBh`MgS>YVY-IteIO#RbAo&80$5iT1d1!gn|)*C{bS7a8NzX~U%P>-%%!x3?J|l54v7g2ONDs9##8EW<5`31a#G z8%ll}i_>Q9Ke;s$4;OZ*Xu#?6mWKw~cWe&J_Fn|aj-}^Rr;{s9eJ3jnz*J4-L5jI> zPEhUKg5B6Y+FV+kg9Ad~UST0PB{E%0nU@YhywAfw8BlcNJ4bQ&MIG zel4xE^pqB!hCf17WK>{)Jb9Ns3F?lKV=Ry6a79bvge3;3^;)MwS zW{{e-^Q(1SJZNxim|;Hr*31m&2|%NJ>V-GJ(E4QxM}}+0I+IPEsBFzZ8ABl?^ZW-M zepx;9;;X6Y+S!e+6yxmX*_UrZ{B$(*C`p-mtNY}HhF#zBMP6*-I z?(t}T4yv_#4us=5hvvtVc&y5VRmnD#pY_tU*yo8wHI5CfPnegT&&A9~_9 z;=e%KQih3NgGiVq^?&U6jmNk4G`%*A2X4LmPb$e?${GVO_yGaWY(|tI zQTMeqSyLm4oGLD*VHd(k(;pA-V35r~OKq0uBX`j=!%^B=aEuD+D? zB>E>`*a{}sDa56B_azMzG7}b@+A#P=ZA}^4i+dI$LrH0F-RRVyC zC>(*6)&e;7Ld*tCy`KH`TR2v%JF!uS2)W+{q5@wB`DUDAIp4t~x`&3)6YxBIhg)vyN5k7nbc}Rjn z@F4IGcmy(3IOoNrOx~x(o5aFIsqOLOk#!EKS zFnZP|{X#o=Sn2VWF7+HX3ULZ1QyMTlGv>+fq^Ff9m%_S8{C?mGqe%byrLF3F|M{fY zDafRVS6NA%|HJVo774gqITif_?c28CP*83L(g;lnl*N?Y+R!meRg8K)4mGDNZ%=-H zUPDD$t{u`G5fMZM*A#~V<8KKS4r85I^{x3M;UfcvoE$u=&dlreI)x!{QxKoi$>@pa zwyXVzT0u}=b;Tu%)h#!iD?SFb^Gd|YhJFE0J^!})=6}2>4n$c+2J}M{4Ve1-I(u zE)IqkAsi4QwS)=>K})D`{d$s&v2Nf?%9eNZ4m$2AWN=ZQ-~uNjS+8z`X3&Dw1RBlg z$ySYJ$obF&Y7nk`*^&yie)C=h{{;2%J1&!ek3`yN6dQ0%!$+!1%a!gUolu~jLd$vR znN4EQETJ_THj9%DQe;YSY_g5oO|G@N6@X~2Toxlr-V zVWguO7GE-20PQZ+Sbo2Lhr0Fsmm;`mxg2srn~br)jILznzYOuy^Rp0)ee2F+>Kp(1 z5ZVqX0$2E4GtO*LKTsDcek>NjIk9P=o2vW4o{Ar%}P5R+tZz%V#poJ^>2zQ?yIRX8P}M_@5HK8cqSmeQQQ1vDNqwY2rC zceXXESJxjI^n z5&jN)fvDW>tJUL__3}kBQhZDQEKNldeg+zvqSvg&z?r@rIX*aZQ9GMJogj1KlI-Bn zuzCSg0b1g<>vD<;(r}9!B$*3|KNq%@eV7J*EhbJ=M`Z4sV0Wc6LD%gJwp?F=GvDOy4(`JD!`Dzh_RREcGRgh$cD-6%LO;ioMd390b7N;H4%=5scR0^SE&) zZm7}HiBD-S(2y4QV>E(^8;4}`5DY@}F=&Ph@<06Z)9Tl+?h%I~y&;5kQcX*0qP#u5 zKWqo3BGvq1Y*Rjgz!MhpBORqqR@DZ79q}`OD5iAEf zoLIt6fF*H}vRb~w=?#w#HxhW<{-!J;#myvmv5k6_f^iDz8tzyB^e;aL!|B8rbIUZ> z`cK~;5E&hG81et!^7kEKXS9SOa-N2ibp~J!whfWN*AkcLq|*bLlJzOQj5{1a~RPL=piSv>8Z3eP8?5 z)9SUi4vHg^i6x*9Y9Qm_Csix->!k~E5&-*ddOTs7<5s5IVp_xCqg1K+Vd0dn3;@D?3apFuTg^*ZBHLyT3}G2` z6P9-2<%znSvaqo?twypu_MMh}o>fS{kFS2y+S3m^MI;k@`p33q+~3yr-&oj*?Y}B8 z0PCM|{}sV95%k6_bjA;|&Hzk{O?Qd>)m6pY%ggcy5bnxJf)E0?29=1D2o(;00M45^ z(3Y*x6qoqsYa|Lk2A?0g1j}3#_s2st*7yI|I}ZT4uIldJ+1`8Au6pm1C0ot5+$1+) zY`_LPbofF@2%&^PXd%X-B=md`><_aruKuplTHQ`v4Tz&CG6AtnTZ-a%*~3J{y1 zHs-Ir7wyq(BQa4!^as)DjC3MZ%7@3Z=q+-yGG(5+QRhS_$OiB~vrQD1iodgKK2`U;Fm2Whf?}ei0k5RK0jRKZ_yV zgW_V548Fg#c)=>^M;DW1V1v^Vs!q@oAg;6s2ir z3D*JKDuN4-1dXA>b*zUX$zP8iM$~7X5dmP=KQ2>ZBD%6Gboflv<9r18jUy10(e_V& z^P=MMAJAF0bOLf{@kD@5@&W__;O-Fu02mGj1E-Tp{5f;#a5rn_IJZw`z+?obR^JDG zh!hXxC^eC#6Wt4o*1L~<=I8Fwxhr(sSs{)fL0nS0js{b#ULM2W0pk~*F*#i|LlD|~ zfxz?&#p!9~T|^Ped{B38R+@}xGF3y2bS8V<*Z%WC_w64()TmK!3;C&wair+9;$+1s z>19&g`|iBn+F%a4iu&9eylirUIng`wV*QT53&vAl|ITmSy7k+{p?Ld#xDH_8A{78x z?bp{S;*Z4G+SkhV&NuhphK3qCbzz+U+Zg->Jq8TgXCWyOfQijaOFgcpv<#fH*~l z;`oxVV2qI+JeUvKWQT(YY}s+p-TU1~m8~b+jU3v?4H+Q2hCzk0dMvVYZ~Q3O5z&TY zSqMQpbH*z|sTlDGIUdnQ85`|hkcBw^SXpu+aqM`7l3`Z3^_%vnz5Oz*+U>UO*e8yl z$|C;IuW00=znX3J(GyF)9+xZjh#~#m=RWaH`7h*a4y%orqr+f4(a$%|j~=wD8ZSgX z&|Yv&A85~>*BD~s;@vmD_j?`rZFK2MmV((Y#IDWygE}tAyT<8a1lkhaQgarFywj?n zSYy{e#@RTg3(_Co$VDLlk@4BO>2u7$lBD`2uO}=l?UVHppZ3}&$JQPsXV(vM24WU` zOkRRm{QZ z)D&zvh?<(Bh{#f^y(F{dXXmf6-5SFph{7lZR4Faab)$#(k>5bR9f`>Y!{&l9GKWNI z0ubrLafMW?JX0l;=#$9gj{v*mquuF4(QsPFH+N`rVg(FBxOc#M@ZYKdUQx60M zE&nOSX@q$F&7C*6_uqAc=28w?x&~5uyM0jDtetYgbjUAXTTlK=#` ztL2MvrA3)*sw&ftCuoB~>=sO8$AadmTLw~SA)`&Zl;ip3EdPQXwTa-(ZNW#4Y zCH0(9oe>`rWLUJK$Ux~AL?V=F?Ev&f*6DBM@HKg4lX@iSR+gw_dmcReN$@&DroHi%cUn0HaUdi5v zkDf9!-XK^*Wb&F#46x*hh6sR=9+oRxuE9Hm6x0K;jlf;A=URUOK)o)39S!S6?Af>P zI^;g5SOPz~@4eDw%Uzdxg>>g?m%$wv5hp!8(S7YRw+V3uyC47JS+`ftG@KN`ad>XP zv$bgFl~%=eU9LwaOu;SjjYu#{K?V%_PgdL4kMEQ@>=wJvI3gvYAO<~bDE~P+1A#dz z!_R>FKx*ayg2y@^ zVt@6lnz5p0x?KGFU}2c*e)`L2>rUrprN3I3pFV>f4}fEV4d4VcAQ}ci zN+@E-W+ zVAr*x?}kWl2Vs#sn-9fL)V!u9BwCEV0OsoJb*do(02u)djlk$RSa@^=6p)p*@Gr*e zulshrtPjD6Ro_mORVZXzANThkp5ta*G1B51mRFoM4h8~+`A2~KR=NIw1Ju>k609!- zi69=oe|Wz8(_`~AF0b&57ZC$~dQP$RF^)DjuW){TPPX7#GJ#4H>AN_+MS{AR1+b$1 zm|^{t**2WIxKB|g$|sVCa@~G}f^xKn9)R5zga%Lnq_zRa6Ay?6h9KMz@cVnMO6u-f z(d|_NG+c|>9XWH)5sSHW#TIw}Z(cG*ocC;_FI}s$qq{&d5QV}CL77B?JY`6_@AG%K zuYL9%;sVm_8j*IG9`5BC3gfdEzvvRz*$H8VDa<285$!(X;Kd06FRs~Xfry-+`=--^ zpnmEI<~z8?Iw0#~>tCU)K)2s=rTf3%{(Cq5@{yM6T@Y@++mV-`&AO^<%#~=Iy{NaV zh360nQBJ1I$ZOe(?e13(&J}m2qdxV)5_fd9vI}sZ#v7`aa#hQfP*xdReeIRQmK5a5 zEx8)X-2L&N>n>kx0w5$dplV1@jd7Dl=dDdkPu-oDLzplUrhBAf(E(!18K@b^({Uj3 zCqSs5S{3dGB>+}@0y5K+%+fD7U1;?{I~RD~Yzm}6#8MEw0x#t2Ol81%=B3rr3lO-Y zDJf?=RdwrfGvv~4k73IxDd29uX`*}JKR@JV&m3>*(}*4nw*aA|1kXh`W(0u=;di~5 zQ|7Y07X;RjRQE%OV^RnH2|N24q`A@#f2qi~;DKf)_DbeA#VSc0= z#+?M8gF3tVPTzQzl@Etw+!J$O(fsJB5^bPD1XF>^((!O-?CqV4B z-aB~A`or(G>x=LphE+&RdE1TC-B14W9~8NHjOM!n)U;in{-6usc+uXOnm0H-wzgVF z2;ZS2Clp^`uZVNB5mL}V@ZQmg&i`-Bv5tA`%xW^Dc<~kE`_#*-RECO%dJN~HmWxUN zg3(7bqA{0`&8?Tu_L9K^@^uu}Pd4&CMjMC#-~f0>j#g;5A!i^C6`ta^oEpojRWjaUz>jD;wS2&AFMkZ8L=YD$}0>O7{4F0N2xc`P8Yk= z>1_tK>WDV4*y`G_n>4<3K}sTS)BzlJTE%(4;i3|N$mkl~gNgBfrZ#P9a&lb5gfXQt zvt|xsClA2_v*SVl4r|fVWs!%M7o0&b$P!3k8otv+}!u|Na{=r>)X<5@Rge`eo>rn7mkY!$Dgi_tC%!l_$zd&x+Yu;N2_wtd)X5RfOc7wA>sP$ z^jJ4$Xx6$=Oyag}yAH>c6lXV(JvcijC5*?Mmp8TBY39gLZetl7o^t>bcGuTFs60m(Yt=4&SlFhPuY>%7eDhi?n|G2kJ%EO zVAt1glygONTKB|X3C14;L@AU585w*aI`q}nyiK97y&BK!b)!$>AnQ=PW@Kip96O?T zms*U`k+Re`tzq`O_NJZ6djbLQGZ6}*$%-k+P2bh8G=DweydVGT8VIP^-EnT*s6MvH z1P5Uk#x)ouY_CwM2Vfr@B!`m#QC(Y=E(3`WTlLT$8gr}<`3`3R@)gLU4ksQo=xE!y zFW52L>({xsP8$F{3QD{ev3MlL;eUPQ-R_@1KF8#5(u!fN$Grik)YZ0wF*NP^C{rul z%xUA@e}D7iimNzT+{P(8_XqhQvXtOC`*NP(8=`EASQ%#(DnTvE%YF8p^*jU8o6T8pbZ6Lzz%9=Iq~T$`r00T0a% z_O1)s*`7iGf;F|p>rO4#vZdLLZ2)z_BH^3LkpUmFB#3`GmpVC`XMG50862F^K97b?73* z6eS<;pa0~Y?weovperiOkqw4+eO*KRf#6(d#E4dIHQX% z1Bf}X&8H4`?83tASLa;U|EN0Y53e-;>VA(XJ%RuPb0Lj8R53OwDdk8(Ugnx2v753V zZ@}sc-~+i=uH7exk1YnoLkH!%JKr|JT{B~tWvd0~E#IGJ7o=okW}>klAXIpc6oE7l zwAc9xpfeC?sS=Qi-4O%>fo{5HoUz^kYt4NZvoKCxAcb)QB8FsD;z$+0g-`$O&4#?3 z-NQLS*qV?Sal+24h5TpS6_ZA|pM2*N?mch6N=n8vCR(6RfOs~4?&6G?al-zicR>`p ze8Mo-ucS!*^j?NO!}i7LThOY(nF4?)_Mgkh?z13U!BgM{?Drlb|I?QqK>%!0fM1W9 zF@sBmsG+3!efwl-$4xLBYVB@14wU()0(}x93}oNA`?yR&cUijdJ8mB5=FA%71}pmy z1(}_^f<&qTQZ7ToKPS1jTFG&q){0NhJ_ruMDhpc}fa9eaaX!4?Y|5?woWesuHb%9c^ps;!mCmxm&Lr zvM4iEYfzddzAc7pLz@d5R91~KrD4#YEsAzD$Q7zn3%e$e`$uyaAO+NCc}<{ghxaGu0|7&mM{cBM>yR}|>DPe>m2$ufle+KyOspE3+i zK~aB&j3j@+kNxnG3Pti&>Tu-*T(oSvv9C8@H_}a;G{6jlN{X`V*bZHR0g#>v&XC(< z%|Hlt8W3puKo8N5+Y6p=ThUVHUCu$slg{arxwE5t9ya>SrUNqNX zj)$+BHhjSViHzZfhS)$s9jwWD>_(TX?lA-)(qqZ{u-uv=y(^UD0(N-02-}$|*8_PK zQ&<@JLiq(?A{&Iw0+89|+0oW-JSeVUjr+4WgB^R0DfCr?&hGn*GZ=5Fz~W^@gKW>e z5CI4a1>^`2gzqe>vBF~Nu9-F>a^E?G%wiv}002M$NklhkWF(dl9VT1ZLaskn6c%G)WjcUUt z|HsFL6D!Vy8b%E1vyhwQ3lOmXZ9H@FF7-45U~HQfwE;VmNn;Av#Ky)Q>fbjjPAt0} zt*~dYLlLH{Ouqi)3ZJJSH9pHsdRmGb&^K4IertOv|ctj;-IiH2_3J9+Yq0a zvME111$aya^pD#{tCogJqbf?Wnk3E~IEfHeZ( z5CY2+Ad!NjM4nx;e24qf6DySrbdzcHFP}KTz3ry4Zu%92WM`h{>Qxy#r$9vj)gaA( zrrIOD)Y(}R2K_qU?@WOKy51pN1TXp39~ZiB{^)UC_mPo2A|UP{a^a#17eIuajqWpF z`I)QG`Fb*eNR1O{5(io$RJ&t|T8GAa+Pc>GtteNhAup?;U=u6ecR^;?f30FjY; zDk(y$&DM<=T)c?uvI&R*amBXU?-+p`QH(%MtFiDbKB58Nql>RhDp(N_GYJ+8kScbU z1tG3Ji?V&^G55?18{89r6<46sgaYXmX3Dte8i@&mWJrVY4~RtlJlC>~==Lu5XMmc3 zpg=eBG1+*2__OES5B}d@?Q?YBxz`T|Hd>;9j?*#}S8wh5?e3rc^?tYiaGB*+#L|-a zHbKYuT1x!@5&PdezrBzz*sNzI&eW?ApOTk2qg7m=}L{=xkomLrjTq%tZhiB zntu6!1Km&e7lz1qksGf+DPKBZKFs0}9N#v%|rbS&V-f1hHCvr5b;lF<4QTN0%uS)rs zVunm!4sG5z^Ai-C6LD5>0*E>%PnNq+e(|R|PTeGBYlfY-b2?y<9z1lB->ol3Vy z)247t{jX84(A(6RxOSoXOFP)Ckf$IsyU$Anx!J&QB4U?ys0D0t@nV;rNC23V04`%Q z#IC1jrY!_<(@l+_M;06d3nyZlASi8aU`kk-EwA#XWWf#8ljln@g80>!i z@hl=~vV07D{OoyQFR*>*5hctxs?D)}rWEhkh!Za6_6oViv**Xdp4&XEQuzfbdPreGB&%lmbtOx7w7|HgBU^-Q@v-h5$T=uidG) zz4p3$o%M-+Ny%oXH^+{ibSqYE(Wwtg;if1--L3!Z?nlF~6RL1Sywd-c7Zvwk;Ohx_ zH`1O#FIx?*rxF0$TrdmfL_lo$+WfrCE&C3X#Z;WCt-o^WK;+yoHta{F%?G*jI|vmH zuuvhp1SbL52c)DBoI%?B$ZsuY1_1Y50hw|=yAQ`fRvjq4Dx*f-b8)7S@$f>)g~Ly# z+@e>?FXGc*{jKHovpD(M`MZn!!90N2hzy82?51f-vHjmaf6V>lfB&je1&ps0`%nMi zEYPEz?-GshL0i6AwEu)iOVIeYyMsrLMYzEWDxw*)q0PeN|JT~>pv4^ zgD&^D_lpC)U7QA;Qv+GCsi`RkWWT;HRY<$|)g283N^@lORctIT@+$uRzivenpr zQg9{f_=KdLa0Z>@X8Qx+Sx*R+7EJPVmQDzM{fB4Vw}14gOG{2s4?J;T@(3 z=Y9k|nE(KLl$bvGDWrgUS+)RpwS|_wzT3S0N@c`@pZ~EFWY0;nqbjclXg=T66R@=; z2?b~%0A5?JU9OXPr6Xo9FC`OMa+j^z9%;+(GMzo&N%dX+nRe*A?YjDm7?horDn;K) z_oZ+BLFT6ZxDMslxmDK5AWA!ZwUlc)S?TWazsz@E{^oB)Xq7EVx)<~U9LqB6wiPm> zZcGYeiY@`tN7mnd;-26Y+{?Q7-Ct?ax9xYU)@`xyWf3DOHY46T9 zud5%*{zI&YnE2x-W&c05|KjY7B*Zcq07T76n@?Zdi#?eD*vx>?u<64G7Ov3oRF%%? zV<7;$_8t%2c+Ch)n0erExisBLw)1%c;DIH2Y5^V;M##!!z0Hv)p|SAMH+AsY69Ioz zwrJUAospk3B_UkE`JTi8@Uh&p(+`b-#GEOL)%UaCEpYe!@G-lNR9+{s2E9dRzz<+D zrl$9fF@QcmRLCHlEvv$p7rpL2^W_KJAO zuLkQ&|3!qw0zCWDa*JAgHiVCncRc!*tvCN}n@dbggOspwxjE@ap!%CPJtaCzk&^$ci1uE zGcT-?c%UGyC?m0)RQ>dq(r#ZO)zcoLTXefT6TBj~HGAVX<^=1-ROu#O7e}^m>1yc# zk~9{tpltTQ`TfGg0|RPWN6GQYOD`K+dQwd)RD?HMKfletvD$kw0kDy=<7vI)`W9uE z73F8GMB6Vw3)gBF{Ob$r^;_6oAwwN3yz*tR63}uz)2skA2KyouoCIXbjhORtfI1rZ znmrWt#i+ap20I{0Dd)Yk(#?Nmi_D6XjTHwm&XqDO=sRQgfL?Ee4L^i95uGQVS>-DIhUe2ayV%jq19MkFhgHUT>k8iZXnnJ`##><_`#B{ zLF*@a0K$Z6;RT;kfa#3>spr-iu;CMsVUdFUoY;d=Ko=|&>@l_gEDol{0YXIG+U}(C z41kAE2gX3(zkjY7BH>mH^#?J!Qcrsg1kbX2GCt1rYvq)Lh8r#X>h=32>p$$i{)0y? zFCN~BL7arPfQ&J#vxd|FDFn~#C(XzQr6Fzkv}a!XPaVFV zre4!e2!_ep8Z&`epMGwk`}CLoM`^kj%dpCsZi2RniO>D8=8UN&A>cQg)28Y>{N(#X zS~emUCA(kz`mb*D_MIA+-~EyO&!(swXIXGj$J9KOc(Sry5sFI|rN|fIOf|Cr9q8Hy zbC!+2;m2e--Sdk8#4-gBEXiM;o}N-B^WdyK`;HUmKSb)Uc`t2r*Iz|o$ZR)gV7}YB z?XYR=nS6i?zym=%Ao?)ju>B|kaSDQUl~J(W)81_GEQL4N-0bW$InL~G_x<2;_pLA9 zr8YdGvEz03bbz|)BD+1GQSQ-a44-Il-Nyayv1eX$&o5YO$vELU7#HGB@Qig%fBaZw z#~`T0^cAC>WF32QKViRM{qu6tr9>U3I1AZA)>;voYPV+nPPbW#G}!wT5mUl}Ar?S{ z;0UNE8iASHK+#4&NLzlQtjzu3frqso%iWyWQ{9!*Mw`Ciw9&9(aCMGgpE z(DRPpnHLB!eWrr>_=iVW%zmPG4$DlDHOjeF#Jo!HCR}jR6A~3TClzI}nrO3W$oO3 zrCk$8!18+wx{AuvZq0_>?z#D^+~Q^H6^8woA#0k($hbLK;Osw8MAAlpG$0x-ZDpo=yO45^Pb!;n;%QEF}v`dsWK5DPm`BVnJY7TiAMbD^iDz8eXhe z_8IP_gX;|wb=54eD8t`5qN&#T6329 z>Syk-041kHU;@)*fVXAc!8La8rky-S1~v*}Duw3!C0i7@V})C~Vyh*;z-uupGu4nV z*!2OcpdJ8UITZ{!Ejg$JF^IlxxHg4Wyh!62WJYWv%#5L$Cn>%KP_H7;|S`bHp(+SpW z$L@XZNB959J@)h>chhx~-SsmkxRRnAi`YXS5H%n|JnsotbB@3k*y_eKnb-s&o%AHT z2YxlrJ@Vvpnqy)!h$toc<3Re-?ZSduL*0JT#)$q!mXhHz5zy(=hcC|0$z6uOmXd(tk~&pxo(sN1zo;! zw~TiV7^1-qu*0G-IxgFR(vmCzZovN0OvlBPV`>W#z&r?L9?Y(dR{j+s>pw~{_phJ4 zT~TTJC>zfi3p`5S)NPdj$~emNaxc%)Clrzul@euM-ryd8W`)d+cc_gTb6d`oww+y% zHrZ)~rxC=bUN5IJreyU6_P(s#CkGueph)ScM!V~;9;Hk{h1Nbue^j{w7&B(nF)=<{ zP@O`qLP#qG#1N!h9qZk5$4qzI>?!h&+^R@Hue)WhZIrlFZbz*s4nGfke_i5km^sPKzIKvw8RqFe~ViY zrMlrRQsfg-DI5DA{N#^{->}>ikmy}#lNbH=&l)xQQ@9>Ti%Nc4Rc!cJ?FR8tqK zpE9xdWvWv6uUhFB;`<<O^NsLAA^ za^6WZ+503!LF1#%WZjRr0kN!?Xr!*#i^_faZ>;<3-09;)QI1vqgHdjVUfXsZP(qf~ z?!^VGWNN(6{4d~sKt99_Bo7D+x)9!J5Rn1mMZKvCHd1!-r2F+lbKTtMmbsZ%PEbE5 zyWvBM)eo{9D}9@AI3OnUhwl(M^b4D}9diGD|HE#>)}2CrB0)#|5i~ax?(KXH?bhah zc-%`6&98U_iKctugH~mhg^B5DzWIC*`v*^ch$Om z7B(E^AB2P40!lk(J0hD%dqCu`v)d`emTd3i#Ny#kfEhAK2ogXLs-&R0Pg?q2`;NNz zy>o{8k|czZiV;j>0O~BeKBCGK^H#g3pI_C6?jfU2q7#yZ-jup zYai!YLxa2sr6b7BPLK$4%02S<3-0-smn-_xSa-{;%iYM~rMBZk^e7NoBYL4&w{XdN z_miLhS>q{F^k)bo`i3B}iRV5b6x}YKc69r(-~rY}W37edPfkgFL2HfNuf0Dcwnj_I zr1$XB8wdbXXp^b=mV0ngt(0U-l$hh%4e^JJbrqv!06}a3*kNS*efv)cK@)XcR$+-n z2Mx$`myPS|-Y&zT9m*)Q&cp)kE+PQ?4xTim!LA-_I%}e^LAq!J?1fv}1}BA+H?- zLTJ>8_&f)D?~&Of{z)M|ioAQ?IbE4&#=9W{3v^FdK}@P#{VCj1b)-i&V|Ci(Yqi59 z(q6kI@{@lC_LU655D#aZM1_)r!bO0v*IzY3Nm;gwt$#%^6E;`^QWTVA6ozGokgyhz z01Tz@*sRz1FxrX{QOe3p6Tzr;Jd%twZDEw{Jjty#F?(vL%R^Rq@>u;!9%Cq`|laQtSCPR zkuXFADeCEHs=L<1^M6-wApnt?Fqca=uy6ii5_BXd$H!pX51g?hkSz$nBc#_Zg#l-Q zkac09w{AaT0A++=Y^nUUN7{8x43H&Z>w~lEK$G4QSy)sE0QZBeI498_3dRP>(XhS{ z1d#zd8(+R=n_IDVhqTs-Lh=+thKf_C-3di9qQ7|T@q8dN^L*p3tahlA`e+kmhCs37 z_bthBAGv9g#Ec29v{*nVP5>n+hz9XUP=7dzG=e08(i#xkCIVayxYihStQs(5hC5+y z;$(P(NP@!={l8o($R|%2YDb7qh!8B0;;`&wrMRAC`yE0FF(Ia-6*a(f?A9n!L1ftb zIvtt6IR8~4I)+U$cG5k}aiw%T24SDm*@;1O*+UJ&F0f*(G2U4?+Z2hanP18!MJ#*J-vefy4CJ( zyxotDtMnJ00prA&h%uK-x4`@85&hlRQ3I8oX}5dy$wl%LSRZDrw)|JA201Sh* zU0-9fZgu5h0GU8$zkiQF$>e{z|JKIk+M(U(oZ4v*)*VC?Mv62JU@5#76M?S z7qtgWWAk&;_w+5vU8mGw1BB?pM@0w|BBH!Pw(c*3pg6I>>|DW)!LAMf!Je_htXi|z zcAE*KOI?CA=(pW8PI2%~$l7k7+qmnV3Q} z+5~CbDToe+_8ZmHbL)b+A(rEF2qMG|IRsAxI5=uE*3e@vJ!##?Doyr zd_i>E-Jy@XC?H^qVwZ#jinrQO&wU_-e{Z}~UtyV<1LJ!1Jy05+Qj{d-df4C+_odIg zM{_w<$8?LNjMed|<_V8QwET!CG;ib9{j3M;L!HbCeYY@e?993b2j|y~3(bpuR$=$% zdA|Aj?4$kG`VZ~VHt&qVp3^yfxU7t{7qTStj|nKUZ9LYx2~0y9HF4tQ7D868!@E7Y~&FS{7%!fi+i?Ma<`)A zl$UcB9+rjOW+7Rsn>4P}-E#dXxg3{EBfn3l1|kI7l^_HF8mv0^Q%;|*)rmx=)z9wO z67vA60KxgWK2TCcfRH-42cYK#u(R`HKM=21nT&T7T1F|lrzwHSb@|BxpYFzCS zF;3DLt09cK53$0dp`R=20*k;*dg;g1)ZdofqAw6&^aULihzvRit51z!2SPt%#bMB9 zcMJ7EsFRc9;`Z#TaPPWf#Il@BJRbieAyrQv3w@)T2Wvyy}m=%b`4(~b|( z5x`BX6i2X2O2c|ZAEqt{3d8`jS`v=1vr&IrU>X3I{T2lg!F3)A2uiS!#i%4 zj6dr#H-2QP$^KaL(Kb8&*&+V90;?vB@d)aP7g)>ufV!392*MIY#!D<1KYE~ByJ4pi zu`HCbZ>@1fSUU173O(kA&%vCuHIE(kT+aq`7}OEm51WtPg8FfULa$)NI?|@K)$*a> z;SSz`H4%3?q}9hu46T?obNtKPE<)3*BDYYOu-Hj4<16rw5= zuJ)6dnDEJpst^t`L)Q-N)>hu%tLkz+2*G zE8X}}5t3Ew_A5B(QCa-?Fk2|GP%dI;U#@zPR{=!o?`i=Ud5+$3R*#aPsPdaY4jRJg*6bq`j zkY*F4T}7e7wR008$g_9=S99=!=))FrFIsi-1d#d1j&+2D`pNCtUuLh8q6{}qgy8xs zhq{S6Jy^40zl>6j=)|GY>gAlDIy{MK4u~DC9b5=RhvcM`Yo?EMcMI_^pD@V8jxrK; z%GSUu-2Cok0N<0LOY_D|gKYY*BSRe_>J%a%1QW*%aTCW4cCW46BGF)hTPo{9<{(}K zquaz&Q*9!a8Z-_}?+{bO5!MzKJvffE32yP{YzZjSkD^uFPMs_(bOqwpq+7p z*qs3v_pnQ0uRlr#Fn{|u*T`6Dh;bAU6M&3*MW)mr6p&tVMSprr@rdG;r-(Q3_!Sl2 zB60GBVIrnO z_g_UJPDqU3f3VCAAKGUzz7J}%9+&j4tbh00JbzELR;V*x#ln9B2q=nJnCKD!5vI88;wawOh1fl)gy%p$EZktt56hrv z-isUDpZ~JjVgO8?G{7|VB0wevkVOcvWJlxqmhA;?2gr{>%3 zL=12QSkMZ+8Sn;#0GSeY9AhKm4?O#agN zGB)z;=#=I_;df~6wJ_oOT{Z;B0>mGglCA0QxIqwXZ`#~#|pUev;(~1gZ@7P#y1j_L$u^sIB z=8N3!oLRm0YtuTndvnweJO`nY_*Rd|Ic3t|1!<{-N5>zsWjuSE0N|QdAAja5KM40rVJ92Cn6gfVZmAtAf+CjhYQ#( z@4+vA;}1$`vCsVU$#8=*(%TPo4>&I}9JOvf3Kd|rQD|!FG#5U45E+@j{EeT7vWoPQvx{D7La!jt&4qux{f)$@T@xCS2%-4$hYc;!(F=*AN}(gI8982qf{0bL->@ z__=@oo%_IDSG#xII$0bH0Y_>jQ+iIox7o5Mz2ke(cL-XQj!zThH5n6=;vRo`seAC@ zXWX%}a_I#W(MLK0+rfp(AQ*^*O##kacKj&{QWY)VYwpqOVAIhcSK@A!Mgr5dr+@{< zL7`vT{TsB&#N_8^?7L#((AT-HO(y6>kj?x+7Sb%Rq^{p0G#Z`=m7@y%Qf+#EvvqkO9>#b zGo4ma5&)6oMywH2MQlLc*A#?g!{&qLp*M1Pp#pN`yIo@SiS2jzXoY$p(_kqV=>t)1 zPRUB{M?aryA-O*G{_CwTm8S^Yf#DIdvuB@M_M<0%XKWs+ec+G`Whg#yNbvw3d`yl% z%kkdQ&L1E@i0ly`C1|QdEN}N9`Cfz-y07+{bnHmgt(l{%(|S6)Mdr8 z;9IeJuUnpn`mX?B*B-=~4&910dW3S&6unz!=E; zKE5Dft95ynsG7`-!f~oo26tXGGR6w1Wbz|Cg>uPb(Hj+0&aZv-+tqs zeR-XQ-=c@y<3)n*0eJ@lIIll3r@V}CNhgEIM1YfJ%N0BE>kqhRUwp;I3Guy1!1%p# zkdqeaa};V#NRX(+QJie&V=+OY^cbxh7EPt!UJ{ECE|BmCk~-E+IFdU%`AK`lm9|5<@R7-omaN zx_ZiBGd0D^FF4Z&Segg{?50P)GI0RFk;M^^*B(1BJ5JNRO-*~?@ya~gne<$$WK-@Z z#Fj^DFV7l55udVQ%K^7l9*F&=rx-Q7$lV|!aOg<6Tfga`Yz)d=Z8h=kIC>LAToij>$Ae0YqWU#r;Jt?y`o{b)Z^-Is>)~D=d2E6E<%54U{Y*+~ zy5^|X#(@@xot``kWM4+HhByXi#o9oegB`#5xczTTg9LUN_DOQA@@cboP zLxkg+IJU&B{4lB^gDyKuw6Y0;fI$vXeJGp+5QHKzBvV#>kKO_7hR7d&!)py>N9ibk z1mhe6*b0D_Qwfm41Bf#>WK4}qWoA55gy5PPLuH6`%EX1;@<2qnWr0KV%);fHWGJ)4 z-EqrQh4~sPeS`X_hT#_AQrhcVbU!cTXAE*q&zhX`P*E$0hy{`0D-o++ch=yPoBU^xN3NpD4MV@pk+{@JSCmAS=67r4)KOdY;lLa;Xt)3t;;pEN}qPB>>8R zgWx^@E>V61@j?=E8emlPoa_l;C#PLL$lWY2#F4{_Oen(|FiE67CM z3o?wzVqkUXaGAIquhmDlLJ8)u^*B9Y;64n3XuHcr_Xcay;bZCogo1w*?&YOtYiUo0 zN6$;F3BrGIOV3&mdep_zn>hd`UF#8!O-@W|$j(S!R9uj8!#W`e?w*9M+9v0nk;4jI zQGSMY(y?yQvK?;A_QSS|vI}vJ%-W$N@c9C93X(!fi`>zQ0&@J$P7J332N3M~kqt(_ zHkAPA=oe42>rseBxL=AfSu^V7pvY_qa6rNpxn6z1(1jDGpFjA5TeWtl`}+^gwg9JR zbOeT&LV;Cot4;gy0{o%vkmB~$D6ogpxVvMDwfKwQJmy|rvO?$c&gJA}Xgw6mU+&g& z+%nF|q7zHLZ^|0Pj^7c|M-Rl0mlGStPN5GVXs1OtvKAfP%kL59LY3NodxldRi8P@D z3jg)K_gu5QxF8qquUCctYHNOaGXZEeo_?kIi}Mr;Oa>+~CneKMSrZ})Ff8fbeaGAx z?LfHrVlTikA)0?I?=aLMU?r@0smyvI08se=X`3|?e2Gv101!nW=D;Wgd9I@p)CosG zfm4AXa74Ip$yOmriW@$-z`PJSS)hJm6A*o9;j5d(N*{55_x@Q5tvyP>Ck3PwXu*9} z;RW~;43UwtOY)sO0ht-;?lp-l5B&1aZu5>k8XJK^#kJ_@FI^`9g!Mk9^}yYk&}JYP zA_Jwfd9#}$e}eWQxb?XAc0A?LkJ@LRgW$Kd#{8y1nc|Y0ZM{6rhTc;Y{>xP>1>N)h ztBaWb(u?4h$S=L*tv3?@TYCM8xCf`K(N4Nup|A%Dp&F!IiqY;ImL@rbZ2#Poo6Jja z%4PlCyJV>Ix^~$$Ix#@*B)~?6L;wI1jXpX7KDWEOMs^$k1`Yrjze|K80u(I?0Yr$p zDrCE`R5=aSY&c*>O#}MnSbT!r3TM7u(Tzz-{_o%Yle`hHbRWKFru-KKjUo;Zl3+JH z-*89YQM@rfUdQ2dVv*_|e`>M&-`_uF$vV@O95W-`Co_RnXI$hVB(xc50qN%*jyQ2#eJ(3IV}VBz6DG;efNL=Pj`yFWct963$gP%OC9DR#rv6tos4p0Q-R9vcK+`&U-b`pTu z?irb9+o^!<+FhKWR;@=aXaUf{G>U3ntj_z8N^BBpvxXo(Ye>P|m}A_=!sW2=}18pjs(p|>glgCW@J0h5UdiCFW^?^l|w9Vy;+CwA5&5x@N` zX`Df7h{6)-0v>*9l{+BzT0R8suG=O$1s4%e*PGIi9RN$T_7Tdnz99Q4aS#-L6bNAX z-+~Ty?H%OzM1^R>$oG6FhCc>HPd~rjtyDk~IX}6pWnz5G>~U__{-f@nzVd77HMVQF z^?nRdn_AbJ?dDzJ8OYB`3TOL=4pzE*zx50E(&AU$u;KlspD2=cUk+2!18m)X(2hET z<5WXfc2}_JjTA#hYyi-IfQOAcb%(VY;WFXg@7U;JJlBQ@f?*8vlyo{~`bD4Ek)=y0MiW@u9a90GmXw&v!)1eKjvPwN88J!h!}*~vVGPU(M2z=#OH&tx=i!hw?0n!(#Q>1aPHBn=gl7*6)_m~oZ?AUQ!j!t{TO;rgUIg#-G4FPh2r@V|a zI-5?kdWODmdH_+0@O@3l+U)M&J_u*B#1g3u?O8L~Hjbp(?ajXUF?&4$V^OTgIzqHh zojl~lekJ+Tlfb&D*fceJxR-so6bOJ#x!}M+Vc6i}c?1E`*?-Ix()jBL&60&+YHAjH zT6`9m+W}4xjvaw8;hY3O7#2%MF!zP^QXD23A($=GUEGC(Xfy0^06aSy$|HhNpaU?3 zGo*QAP(b(aai>1tR}^oag*Hn&0VPC$F=591%BwpRXmgKy=!vD$ZM>o}VOs3R#cl~Q zbhiNbw8?zL#?-nrDN&z&ak;zyS5H{B<*g!?#4SYmXb8z%L>jW`wa;klwd-n*Y#hu@ zcRSL?{fx<+dDLdh`dCl;jXy?;xD^mp+Iw+oI)Tv5Mqpb#)zk>}S)I(8rkwj_Gm?n- zc*SKypG}BQlqe~HQQ^1VUV6I-VCxWB1Y4AX-0X$9*=c*X?K)gKdRS3|So|0geoR-E zU}Y0BgC977K!N0`y=u@}5_+51{KN=cv1*Sypr9WE`sP}+pLg6k!GeLVl!FgxxX>P> z)y570Ej&s@0Pl^#1Q4oSQ;=#Fe*#`#XA__qa;du@DB5fcnh3K+RAKT3?tqioywCl^ zM`kN;L53_1IU&F%U+`>@9ht3m-?&Nj${o4Bo;2uU{p(`f{XhSUd*YeJN`A`Eu=)@u zZvVMrY5p!en_#-Ji;x+MU1bEJBjXS z%YLW<`#B=7h#Rbz`(LcG8*jRD%E)EhF5(-J|9hwTZ}gBa6%Igni&=0uz9>Iy?~p-- zFA#`h&Bg87xcS!^fY(vGr;W;$44xtPwf#E+TaBtLp(uY zDcdIzppculV57VWAChO}A#<&*QO4rfSjYupD+;+Lf)ezMwmDG^DYA8Z0%Amsl%nD= z%4^-%zx`YH;3F?MxxpLZV_ujuxDnQe!bw_{5(?=*FaeU za}p*Dj-*PvedXyhc2sGtscq3_E1m7yjs4X9xSFZH8aTu0Q-(h}{qkYT1QbqUu2s%< znSDok$lpta00{AHO@xdJ3R;o!$5BJ`-KK3vVmC@Ft&rX!SU41C$j|_8^CCbNgb-N> zi$KDJQy>;8LTn0-Eq{9Qb#Vr}-B4v3zEgx?>SYT5B^mSBakAzrW}pxT0?-+dS%vNX z0SVPMN1k4uCH4y?>a5#eo9T%p^kKmX^8+vi!Hu*uMnX;0q%Zm?X9&H5UYp+jq(GXMZ(SO@@ zg`_QKy~R+v^c9YRwZaMzP6<{&Q|pDp7i@l|oTf;YN@GnJ>WKz@>Z}jcsV7lpTJ#@r zdWolHk3H*}34e@{lbGP>l41Xu@JoUKu&w~N5W6-lIphZQFM1(4HS^Um!}DXGpTDJU zpA1rRvr@t+`v4TUJAeZ~X1xtqH5nj0GBxr(az58l6av7>5!cq`tM@DF_LMDJHHM)OIXhFb)GWrs^D@~?mI z5hdeTVML%#dHjTt9dV(8&8N}5^XZuZ%X&- zf7nhmLNPU`t7>8f4k(;=%MBA(>Y+FtEgJz)KRT(X{U&{+RVR-JD zx|pnA4IPxnt{;B(rA=i4IHYZ(+6v}-lJw0pv z=-~zNb#>wT<*)Aw$!agmSwE3`L1KV3kY`QXtzExN3O)*sIo{Ttwe}nX*C7PPdWs{c zl9Flul5Os(d1S$@b>l|%aqqtEGD}JVsBsOzLs3U#UF8S>*hT^%ANr=}ItF0+M`2vF z3-VD9O27wx`>gx!PyQ;d0>UIVU&s!TK-5r73q7Qx%=A?EXW8_B=SRPDWm0THBnaY3 z^q%e&j~@_O`ohaf(Zl(G2| z%`^XJ{OAIOnaXHDLyjpY{`)c%a<|SN(ui#EJ+FCtFB zrL?}a(<=dDz)28t?2)IJxUYZbQRys5hN37uB4R`uBC$q&INm+*zjKwT=Mh(JDZ=Rk zx&lC?>+l}j$rwFx_2Uit(pje)5nuWk5Eiiw#3#i#{s%$O5O#k%cI~7>`e8de>j=Bo zMq&Nwt9A|hmHE={-@uyQaLwi4yKHo6g9yJ4TMS*gI+QkA>Uozgg=0XQRRBjNoDgsF zbIr7IY3}9M3Vs!?EBnCIi6xUCom*CS_(*wNeokumg+*IJGsFelDIb9OuWWNWb{;Vx zA>hVoDZq}23qWDWN(0c5x~+lW55L(iEui8aP7r{v7Z+`IJNF!OqlXt8As9Qd*sYXR z;dVs=Vz)&JNrmjbl-AH>EgJAsUmyas*$Jzm<3B;)u`A$Q{N<$^7j81ak%sfxfZdI**@SF&QsOcv@LqNy~#aPV;L-GD(vmm-2Q=`>Yz~Zcz zKlm4H5;UUq7DuRE}3%45CB^o{n6Tlgj*1T_-iH?RLq@!;=YkXs~;P-xln+9 zM9DY%+xk89_zHKO5`NB}IoxfRR)5Lz9g-z|TB#E{3Bbz^r<~}G$+k_yRxFClbGHPs5xYca{gp)yhNlHp` zTci)!swl)6Y2@sya~0*~u9iL}iR&>XcYyreZpI#9g6VUU2;ecVt^P+NKF?XGFu8Ao z_yi2WjTo+td4`~np$YB?1h&<-S!utY)X(mYQjb3I3=#qJk2fF$heR#3t$RuNtH?|7 zF>s6@eE-|;oj$P?TY?xVV~Np1b$&4IH`%LxF`lMZ^|*u1Y7GI5FfsM3svByrzAQa% z)X=_hZZTfd!2iJ!Djc2!g66Bz_A`~w*bXnB?BeIcX}jU z7)^G#>S`D}18=YISznF^k0=EAycageSw}hov6I(dHQc>z_E^_XK}iTBj`rGQ&qmR} z`ftE!+=YNMIE^qFpu&Ja`Yu6CA-S%7*|=NQvrf^C~OzV&pjToIp0omCroz<<|9tBW8eq82-TvHURz6~<(XTeBCOmA8oGVUU+GG_}!oADXVx z28uiHkOboExK?Y+Is*f|C^BP~SGM_xRM*$n#0?oX;(;%GV%7`#Nq&Hq_m8T&-mul$ z^Z#5@901cD09sNaiym&cYgVEA{-OR~jj67_ddkGUgSPHGRzG=s-`LE|M*ShD$&J=KUA6_1Ld=>l^w5 z6eE5JE!8(a9x1zQAo6-q-RB0zZC-?Ab_!s1LJa(I9P`F5&SkRwAm5|iN4`HK7i~hG z17O*WMBY~iVQhigY;YRS5NMBMU>k3KeZy#6&~`ZKMjj z6C8rydiHY*&mRKh>b<^P2K=*9P@6FYwLG83+jSGWT97nV@ki;%opt`~UzZ zlSxEDR8dK;uE$!^5C}1T6+FkdnOj~#;dvuqs)PDEB33-c3j)CS5~c8@E^Gyu>j+J< zdsthY6k+R8k7Lyb?s?lMKYq`+LlOrPg#2_s&H6P7`f{nfUy=lXwPO9H2M8%{L+$i& zY4PuzHR{iYD|5a%c35`&sWT1LLk8rAAOic%opa@Ml5`*%LyE!DJ-?#sm z;su_u@iE?(h|ie0#@thwUuKp$X{!WtMHXT>EAA(?Se1wi97*&vs*k!o9Fp;tB9>Pr z^yxSH-fw*V=DDgjQN)8q<@R=3YOD3$b?uTQ04zadk;4hn2h@rPTsbj4HZ80FS8GDq ze|uN%Qw5RcT| zhYsu5Nk~oD?n>?MGvrM;d~l&#uDpLF_WMs$b>==gkpE}rxH9JRWV;La-p9T#tR{(K(r8l z2K19fW=^`X_xlf?kO9wW+m%830P*dCcK@h8)rFxB?F4OcJ1y}YV$8`wvN$jZra5Zq z$9RbV5TRi1gC;beVTov=gv10PdP7omIBCfre*6y~o-`^aMr&CY5&15uBfntm(eIZm z2M|3-eU^wDl7Ub-<`eH8cp|rG%)81?g*FZ8mzhvpoLeiUlUTr5af(2(2R4v-kC2i` zLCas?Ek~f07CP)kvFf)-ZpUR90>LvV>e%H-E&}l|@j6mfi+03zVZ zJNNl@?qvn}$P$|}b|jgNQeCk!3DFj9a33-)$khxJ+G^+mT=U)V#x|aPcV@6df={@q zLg`V`qX_o z#)jiQ`8Pv%7xx`|>xon0Z8yyv6kk-7U87_bG1@`2@a!cR;Kazz|CojP8Z;o+ID%(& zWcU~92w?N?xMiHXS{8pe7oi^jVk!kmxDp4uT4(SRO$Y#NSXbob9cmZz;^ixMneV}M zR}FL5Ts2h2NjT;BICiGo5{qq+YCXQNpxPYqJz}C+=lFA=6s$uB7unn#6bH3s>p}Cf zq=)D>AY9h(E_YcgB1k3T5CoG#`^;~e<~v*SLcJsuCA}K#J`PzBBsepFCr`;~OdA2` z`Vy_GrQl0ERb9XCf$#q9O}AV-{$N#2U96O~062D zf4F=2w!Gr8Hyl>h?pv=Po-lY|epTh^D#?R#=@ke7P@F{{KXF>=z4J^nPXr!v2R{1r zYKsrR^*e8yV8@3LjU$RLNNhmZM@|5`TAB!f-`$XxVfpteCLyO1I3A^m=*+ryxVvil z5DRRATnlRpu|d`bp}L1uUF}`-`#?NiYSP+d8&sYM_{@W$f^@xCmr0uVg8FXS* zO=#Hz-}}4kZn^HVecJhBWeIvo8v8U~!RFuI6gmKg%x(efLIfJ>W9ky(!m%H_bNJ2$ zucckHX6GXA>td);AWFM`x! zV-Lo&1AFq@ozeSy88gLvD8ymF>7ZkQs$gY#QZWgcpMbz}KXZ;K1wk;5K*+D)A=Fe? zkegAfbxJBJ&i~;bfA-nig#5~|Ru}fj&m(Uxcr_tDrpxuldXE^^M`Djh{GGdxxYg@++p!+^$H}`8*4q@7 znC$95h}oUO{k#}6?L**lja$oWrp{<3n^$v};i zs&RV{o~)U3)3o=^`_orGHDSzv1QB#)l5UjwyFW?Y8B`Cw_svQG3~ux%B)<>Tel^Pa zkZ||ySN-q1-#&dxYD(NI${3Vz&5U8S+R^J@TfJM{Kth~UvSQq2z6&HbnE@dX!mvw22(WpFBd`Ih4v?gyLOa~!5#Kw@1c*OU^8Lhd{mn~n zyAXfXnq6+cauhHYj{A6y9hvWNK76LWt`u%#%)CH+zNhOXgjC`j?QxBMK#=KMD|tef zXbYvRbOnk5Sw%$O`hvVn#W|@@2*)J8@|~}L^osA_`+?soFiN73zZ$}-M`*HZo$Sg$ zdf7d18XZ7e>%zhV04Usqq&3=Y$zn@TxTW+jbna264tA zA0P-4)Dzu5fFD^Kb{nxH5o}ldeWf0r;T04d2*!Lhif_|8J{?fd1_zB^LC%^ z)?HBppdPB1hF|vnAt?np;;YTdOpTSO5OejFWAFRor{4AN<3{#v5Qh}6zEn$53xS}% z78Oz1o2R^~2>{D1xHA9{AV?&L)u~Z^Y+*r8%*Wn$^H-i<`1&&omTvypjn|GGzGwf5 zS^-$>nBm1Suddu3))7(&>p_qn;02fmW!LY&k7QOhe zWaaH5Jcsm-LXu-f+7A2;tO7*|q90zdC!-m;#LHPqCcC)`rlQ0s5{0&HSIRHR_a5=- zyXQ&*yrxURXZivM00E<3h#5T%`fqJU{xBATrVy+HaVkgL3ywiLmTmL~MElQNRa8iy zV3jn-S=xqnNI@IYJT)l5Nqx1ejaOWsEnocXoge<_duJ__hCe}pS;DfpsMY;Ke*F5| z>QQv>F8$AMdIAu&#I_-b2ncRLw{XJjnUfX{9#As*&riSdU7^R{3TWy_k0`F&vi(RL zCj!b9r!!^3M|lJSNRR6{3c0;NM^t3YA*S3ja{lSJHQPOamhDWQzU9!D)+$HPJghi0_}Jug!6Y3L%D(0I9-C`fc~n zMexnv;;-qmIa+BB*zqyusTQ#zt1w1Y%q!2r=|HD(pN7c#eyc8(#`Z=aQf~sudyC8WZ*h%TNFOoBi)w1Rx3m47LPaUL=T-NYD^B zxPMXgr~c2|KKA%CuROJ6`PTbWQc{YQ7obLlI=G1uC_({rOv`MDd`jAX5UfZ$=lV?t z%-b-@#Y7G2eMhKawxC8y%J8vCN-U7EquE%)51)6KP0kA}%Z#}~!5F+6V*weZM zz}!d(OlRQS9vv0J5#%W?T7LyN8K~f;N}Ot31$qi(YC}+`<4qwMhBma%nfe-O^f}Ih z1A(wL?f4#{gErgfjV_4;5c|&VZ&cOLSI2-BY=%|^KytG;ELT?54Ki0m#f6Gl8j zY=8j3WpT0sfnPmUcqV9D8lSo{VV3dNxyG>bFg1_*ROiopKLD8$ZXfU9dD5xXC9Ijs69g$x_7#GXVcetpe8 zw?+92I7cTe8HY2RvC|jWZ&+e~v=_DGwXr+_QZ|YPw5h_88AeSYQkGbIc6aK;+L7(F zN+Gw7Do_ex#7|1g4nd-`Abo4ZhcLBGh-i!7=r@^zs?PWm5Rm~-RP`uIP5Ma;fC9<& z$oa>PS9s0|5k=?x-qe|{AwKwQ&N{2N@5~Kt=z3W4d?;RFy8HCW4l9;GLS{z#Bj3O8 zqaVL^#+V}_7KtK2wNg%c8mCUY#q!$%HT&}x0cbWI&AtGC0<@S0Z5=fNrnsR4`;>k8 z(|6wW(4QB+f7SY(Kb$jrY}V^*_SCG|uwQr*YyJkhKZJ}=OhD}UPf82`c_9SI!pQRw ziPw~RY^#p*#!Im{RV@05p#_$7WcS_^;G~%VBde2syhw&Gfl`g#v$N8Y5TK!lqoCa* zM^A}BRTvTqw^WFScgnU>J2B8jTxcnN2lqrLsMCIE=MM-LnKL^nfuMoFT&Hb*M9e7C z2?>En$t`$1(RJxL^cHHN92Znt`_nKwGmCxfTGu02mlHZb1G73GaQ^%wLaQ zQ!@Y2rV8OKVE1d%#YNTpxHMLLBUxj@SaxfjfhKHm|AkZ+_PnQj6}YL}kO((s@}Igww{5vIDpo3(&iN*gaMS^DcaQPW7ASo z6F&6r>wd6c>4xX$y}a^gQzn*PF?c{;&9g79kKMfOaI6e-!s)3AvO#a{kFGjw7X}$& zbNL=A}sxcB0V?M_bth-SE{ZAu>dQ+`Y-o< zL`GXHB~m5G47y(W4Knt!NXYtcA-~OV=YQVVs&`)hrFe&6Q&D96aUluw;;)@CcJ0SM zaMP82`{dl0oe>}Np4%rY<5o;9+(5t5Y*=9PL<6W>7LP1GWSGSTkSx4$%OUr~+|_RF zhW&D@EwEgL0~NZQdkK{d=fHVB^|e)J%Qnu}cZNgz^b7q4dJe8}me0;zB%g?*Qt)lq zwBKw9=!c0ix~U`Nr#}6zT#gA44}A^Q4(OL3I#pE@ciDs?-+thGe?R>LcgNxjsi%-I{}~>4++6z`jzB|{_cG@e&y<^!)M0D)@__T zzBE2HC8<_MITEQAd_qns)>La6pvzuS23yV%nD;mgo2>vL#WCi=3h~xiW846pE`Vs> z-d_Pkg?Sf)2n2`^LbL00j7QRrF{4U#0+MGe{)SEa+#W?cB9kv%4ota*XPiqLj7a13 z`1uWo6v(q)JA93-5#uEVY`yy(Gq3%{_y6$=V@LE0YuEq(?Oh3URMi=tN#?zT2qbAB z8-(mDVF?KdyDXKkDI&Wl4WhCX#nWn~w)N0cm$pZ3J#qvO)-G!6DYbQJ#e<3{ZNQ2M z$R@$CNJv;h7Bb1?&CHvA-+eQg2_u*TGl9_i&Y77vGw^nLeE8^ndzm&%JZ`N6}|cH_9sza;J(zP7*|GG zto_her)G=grv;RxEIUTWA{Z3~;AvYJhvXn!-`|UHpk}-9j3hY--GRqSpRm)%=G_hGbgl)Pw5Al=_19)cS*c&E5fC ze|u5`xWapihmQ}UP;DWN;3@0ma-sc!d#0}}8kAbv(9n2n%&;_N%&-jnrR%T~@* z3ok0UJr6V)0q4tHPNUP#$K@5sBz#b){}YfCfEkG*dxi@OIl)h9%#Gs-^u{LnVjpas zW1{m?R%SdBrY4JJy@Q8pgt|}dMA!nvG=&Cv!1NNpXM32|(9nmuzEekm5DmWnmh;V8 z)zpc_Q~vepBg=~mGBo^UL71^7#(=?)_M&n5cG(n=8o*_f&U>N&Ebxq9s0|S3d@|KL zW{!QYU_g4opI_hdOjc^lT?uiK&hk%>*sD&~DV!riV-SfcJ%IPNXaGh41ITG;u!$u_ z=GNmfAuAW9pt?{#>JCntkPCl;v!(+`4ssrma&$NnYB`n+IigDe&$qve?R$6$4Sd|E zbZ2lPa~y@m5e%6@AaA~zZv~(k01v2A@v<*tm)fP$W_oT zqFcl(PpzJR|L~%$Ixs>NCIJVoOFH#j>_%PY?~(yrCgJ=}!!wQt-p~dx->0J*q_WaK zE6Muvhi+f|yT82u=9d*!>n4rQN!a`4DQ)NOV-^4=1X9#USRpXt3lsxzT(^fBES3jp zXoCzEpsqv};u9z9Oe}Dcn;maTMz)}X6{q1*cv_?=ClKKb14+z;90F+Kv!ibwEy9qt zl=wzo0<6EAlE8czOmyNzEpqzT38o-C9fffp^B_z~THVGvRDf8&Xtl`R`llwH#%_2GMg7I21i<@KRtz;>k#AYi2QYX~@^^N?A` zi>}1Vz`T_D$A7$FUSe$Yd=#6lpFBQC8CsBnQgv9%LO>C10|cLE<32$|Ffqj#Lv#sb zTK*~7sSh)VhpPfCLH_RM{itJ5Zz@0$6P6uIMjSs@Acg&a?GSW?61!mA7rxjp+vag> zlj=T`iwqr-Y8rsri44)*@!1j6K`iSrDJflyxe@pR+ZY>T3}_$V_{ccCS7Y=E%|ZMG zrl$8zjM1_(5)_QF>BfW}Si5S19#i;KU_LryfD%AiYTqAV`E|vjBj2 z*zt)C6}S;t1pfhb`J&0Mef~w|M=x*KvL-(#_O?C=k&f*+SeMG=U(3xtrc_pRI6 zvrYwlB@UhMLPFSc;f)gluglwORkRQ`A#B7L5}uAX`F%{eQe|t46o)C2`Q3O@=MM5V zGF*xk2PI&!uTmJUu!;{GSv#MTdK1@?bo;Pjq3#i}b^u0$9>5v`EGp-Zmsf z`Tj`3H6ul!$>q+*L6zpYUZ*{np7Qt+TuVbF;o!KvxPNP+Z?jKyE6W!<`GG;xKD zd$$B1+aZ_GAdKb6QX%26o__BvcFg6c%&qMqz~!YcD%8vbP?vf{bJ||wXi^6`6{&x4 z>%QNR^uA>^U^CX=fYo7Ip`jLw)u2z{3vKHeE@x17$Urd7K)ye31^xn%da3GzlmW6j z23!Y9{I`DB8x-7jI{cK}9yZih0Ko07pOu0{doVs$;edP>*r8abds(0TIy4r@5_30h z^U#InrwEVe7n})Wm~Vq|KkRvO;6#HUp>CElvp($Z#!DHqUIhUL4ji&I4PCzDb>8=R z)^fY!gzkjay&4sn)T^Sh{-^(r>jVb@e6iE^WLQ(E%%HgHiw~ga$h04|*T=RE#vZy;nVNqhy)_ zUR=E0%?N(CY(li1QFUfKE{yjhzFvb}=~@4>GbKnukdJAo%A{H83)|DPGViH~L@ZU8 zPzZB>-Lsk?TxmliKFAKXE%$>F{?OJ57#w;Ro6LjsN!er*zm0S#WGeF z^1@m`n84q>b(DVggaLKeYg0G;cqUTYBW8vScGB1qg(DHN9ZtAto58YClgaLtMFbuJ4LFwpM*Tq}-6fw-4F&2yQZLuE4ld5S z93_jUQ6P6*dfMG#NC;JqXN7mpKy5}+GP+q2K5w2)t(SamNtJ?58>Jo=+!(t3~a zouCN^gS5vq{YRvrg=08!KictjGsDWo*u77Fq8D5#(;Tr<0I~NU)X2~ zI3yuv)V`e)-TBqy00T8lzBi09f~X!q*8P=(ok!o^VPg0Pb4HEXP2IOPEG*-ZA=?{^ z9lDUj@OC*huDe%R2&NygXB|Q=8Wf3#iYin2{Ck*dANk;@ER(E2m@%c3VL#7}(;2;1bH~!ux7JCAX$+h@>viq@Eger-& zlHTpD=O4$1D!-JGbd-9avUl_dm<}+aM4t;>i7M?R)>vFpGi*S(-#--HIq7 zWtkEpt;cNoui!f=Ky_$0j=$MtB6+g-WJAu@);8Umgb=g@c$uqK--~1G!3;)a?aZmF zP@)%cY){_SE-&DjQq^^wspXs`f8J3dh+|6U_rYMoB659Z>kSHFJlW9`rc{zPB1t3` zF*+csntjI@#lc);+`nCh)_V3J+|c%UNyJ5Ue6l?XZe3FQKw=UztWWS4WoRcxk*9hu zmtJs({W@6SI85xYVcu?+q&bZDYxLyC^`3J2&pLb0(H854-{}#jea-El5{lqzLdIq% z>dKv^#UHPRWXRHJ>xX~a#M_!hQO~^Ufjl6_=-qhXhv%BXBx*;*4uY~fg{{w)jA0>3 zME;TYTqek3X^sF(ctnqF32kCCEcA-=BO>eb#mJ3?s!M93g%n&G?0jF{s_~Tq}Oses=Ne z6H$pu18vWZ+wrpha=?Xph{-mAL}U3$&jHyCWDz66-z0Vo?<*pC9}%U&8hZ`x)bwFh znUlgxqup3&A!pHdQV}ra-1VsTT+#c=jH3pItxwMiHh{USf9L39XeJ~1I+LXJtZ)7I z?A;0*DQge!8BWty`AhpV%YW7fEbq%>86)0ThN$TM;S%{5^b?$dIEuLRHg!04>@>CP zbHZzjl}b-=WfU3Q4jUOH7}V?iRcGCpkrT2dr)}o?CQ3E{{Y@@!1Zza?MDNe@ker(c zrpQfivz-yGA>rqX=T9ALcXqNJ1H4G*-ikJG(!_6eBO5LHjngP{3hC!r8()y;cq&I^ z4mmMojP(4Uev5pTqd! z`pj~7S4UDa4OmRfKk_@5N8&L7kxZt`Q{u9;9Uda=wYR%J24Q6TT5oasZ|1enF$fvH zb=Ht7D;DSt4dao*Dw;w|MTOFXGUC145pg`I9^(ati!kEcOFw-=OSe!Wskd8=QTcv$ ztz``^2%|t7XbPC~)??vQ9R#q*{$#mEpl!}Y6LF$p=`W079eS9p*LdVZ=sm6;#CzaXMV3kEJ!uAE{VM*_c%ib|!(gKT6Jf1|L>* z4*VMw3BO+~Zaas>_4P}#%VD_uNdFj4j&nQzW7QAX7@vHx(&G8KhweQ+k?~62OUSR> z0a*-n4I8OOZW)Rvg^{h`wbS2@%zpXE9@U1CauyA%kGh(Mu-ncnMGaSZ~3 zB^PXq^r@P7Njnk6MvPNWapv7-u2-1PHq{b>8u8Q+EI3kB*M9KL9kiPP<_==7+n8ms z?506gN9WJCIPcv!E~H7~2iWOL+hYfxp~^6SC6+}^w!(JDqHn-E)h)iYTVrsms<6{m zVD1OE*>2Cwq!u~7EVKVSa!^zj85{wc#9`xc`(7@K?1qFa4f#vwkVINgAma3n@;^At zr28Fr9~<|M0&l;LPa2C974Y_wkyMnZ5Hkw4Hf7@l0JTb^lodatArm9NeT60?Eujhk zfZjfW0Em#cw=1vd2LJ$!W-TtREF&&XsqErtVQps)0Pw`dzEvZVYTydBxLVTCj;Yyw z409_MMx#SZ()^fP*BrRA!sU=6!?j4gZ+3GsmO41b!`bqjt0bxH$9|ZO+tbkirF?gd zu_RQ-`W=~Wahmd-P~BaK@_zKMqcncbH0$Q-Rr3?S%goa(-;*ZGQ~3R+QtgyPTp<3> ziVp-Fo30V@k%I3NLL+hrAh}v9WbgG$Fz5z)lR3zFWL5;R$-U+-9_VR*+QYG#(IzMKhf=N zsPxE{e#@46Lfo{q_y0q>zLb)lh$Re3SYG{E`3vh*LhttyLTP(;L3#aGs(`&vu1n_4 z50T^CdqZlS^JS$;y&0VzJq@3}TwL&jVNuPxKhOJJ9_`h#F7|c~Km8o`^_F;gX!Vx9 z@psAQ{deHEe;Jzc0pY3eM{)>WPe^27u9WD_-sVg7vMeSXkqz;VOd!j*d~{vbEq(i6 z`{Qz`1l-dUa`;CNL8>{g61CAT&`KRhnMzEo(&C4v!=rnZ-Z28dY`-wBvY^0rv|1$i zcd2X_g4Xhn%76G|I0{AjePg9owlo|2C+S43;%2)tv9Nd z?9%ysTMI)fAZ0LZukuP?K*x~k5`ac!s=b6hZ> zBASY+%m<~EA3fq{vt|jBp_@|d7Cq{IY8AjAWXQ3OwziI5D*1 zW--ZJ>bH$rm$}s6m3CCEG1d6ha7wDDMKL03e|hBCgC3vAW=b4tR1YUjSN%acQ+y6b zXrp+Za+66W#Fbo3DL)VQBZ~>c2jt_d)FKw`zmUM6u3p7orE|1k;OMe^&Ui$HwF{pg zKeBAb)d5mSp+WsL4us=F2&?csm!XR_OPyqi(tarG)3?Vr9S=s9H(mwB9~gd0pN9xyC`lqFJCH@~Kdh^9 z?J@@J9okRdUY&+-@@n<}yFmXOGqTe6^(%g1Yztk87!BT^spnBNOC)WN$fXk$mZFx- z#jcfv4Y9V7LbSd;Y_RSyhKeJ(ocJUbI>l&Vr8j#l4q1Tb{m3 zFQaDC>U%O^O|qotQx=asn_T7 zIIXsBz%nIV8le66>Loz)@M5h#6+kf%;HqlOh}l{n488HFd1=6D^ruJ#bUOVu)L2!p zK$V8T0^Moh*5fg}Q#J|j&W9OnJI;>}C_b?2dS0qe&voqW#IyP-&;@55ZT|e>7xj43 z_Y#_(%Oq)E)`hJ#5kCILxuJLZYD*W0g5WW=??J`>;Gn{rcqI&1n#1?8gnwL$_uAQ7 zW8tT$jNZ+8xo(sFa2a)U$g2RUH4PI8tO*#xZG4ptxNc?0a%57&tN-DK6Frj+~opXx@jkxxW^HAo8O?Da zsQ-z>fc!T-P>FchAn><;G(HUE&>j^SoQ2Dih^6`Z=dR^Gf(z9W14?>$+; zb1n1(Pfy10`iad65%@A>Wt)fnYfZjDq5VS&|K~ zS^0Q-n(>%D*Ua!r+UL&~&{;bl7|=PcAegt1D(|y6sSe=>@fRSeVRa_NeuIMx20Bq~ zi{rWjgg8y%l5YRIb$}KJP)J*5v+^78%#gGMbk-jsAO}Q+-r2T4hsY`EpJqOxhL>$o z-QJ8!gxzue)=P?95+B|jmBA)VVIB+uYG7mTOq@Mlreg@*UkR#`VVaM~IyrYj%>bLh z77H?UY_Oi!M^-)G&7te4ajN$CoayI~%a4NnK}&gRHLkc2nB5;qgq+ZCY^XibSEu|G zT=?Gj@-Wds@Lir{fD-(hCsN{R%EV2C1Q8e>HN7tHOt4b6+mY*f#>cQu_Do}+)RuU< z#pzi9s?60<0r_y8UrdvnsI@U1unR3s(Rl6~U-A*MpANYO!E6=gGI;;pPeW5SE-C9} zcKCCgA)()*g=Y!}U+Nh~>^o7Pys#|hkQ7u%W+X*bGqDb8kX5W?R}?-({TJ1jv*0eq z?wRahq+%2wErVo2X)#o38W^n}E^k3P``L)n>gs*>55DX$?EsX|n_@7E!;|!OMz?>X zAG%vqwT;98In?twsn$mCnnxtPXcFu5!+?g*fu!o7IrbC&=J@ACd2-xx_!w%^|14Ub z5%|A}5JPRf+gYc`=3$Jt+6+2M&y9Z`|Ma9(CMh_N>&3TMIfT@p#flKpPX@lp9qCz8gkMJ)PO6iD zdETx+Za|0;`K|cnC)DDNt(Kv8d`fa;nE4>QnP$HsNtgsqfg$!@r|UaLHb;lyf344q zfZnxFm6t2mo80g>xgyaIYnH%sq+&R;i=5(oE)J+F{CYh1AmDCGJc|wqBJy6PZ?{8jSM9$YgvT~lE;L#QR#{G#9A^BrpkM3@cmZaSJZ~c#o zpPNXKc(P9O!X`zgFN4T_XSSd>U_V3NT$zs(cp_S2x^-l8zTU|h4zPgv>z7n!6gy2T zVKC$22-&L(MDYpD-3tVbz_ccw`(a>kjC73ZOBCn-0-;kH>cD?d0qN~l+0T`0Laz(e z=X98XXbWVD&6B*0ksXSaqRH(pOu^Mzfg88)PwO?r`u2i|Suja=$ptD+#tA5tQ9Jb& zl*xAVnjkXblyunjGr~iJ)0{g~uRrJSLE*^ZQ7I3Z{W~q9a(kcnETMG3HeywpQmAYH|Y^p^4WQ6u{e3P9c-UQOKh1GQUEq` zy-O<3j{&y6Q&@YdlneYj!~e{`?Pi^-t=GyWD<`MPLF`^D1!&qyZ!u+7<+fd8VWDTd7N@Qb`N=bQK?k&aMC0HW$03thHe8=@44GAja10k%| zWQ2f23BT9D19PT#Y?HIIE|cOv#(JZW1)n)ThWSn~@i%B7^9Pkl1I%g}TPCbJQ=G8l zgTS1C7^o5y>5gF#Onah}fC?2pfio|7@!U!45y|*3z(1eM=~Hwp^PYdMdWUoV++UOe ze_r`}poq*~Be`&Em{^RGG8a3N{+ye*R=Q~F8(yd(_AAc^AF*cVXdw0^xBq!AhW|e# zfupDc{Tn8BOz4{s4yKgI>Z5@3qOvjIoxSZu(dvH%dH8mrzDk{b^m{HUZmLfH;#}Oq zeFJW!RR^VyKkV_V&AbMh(+MuW`t8woJerF5r#w~m?Zm7#7$V>}Wt)LEgB=$JA%qF2 zFbLwe2GjVU1|^KP%kBHzjs||xdlu~h~LD|Kn-gs*5?2Gid@^9Fu!Hz1^&j-~z$6t`RFqL`Y#>`cdBJoBGXsVXxx7e-8A#O? zIAuKj4JaLYZ{>hbh6OVR4}Y&&dG7I`{8=zo^!(TQ!CU5Ll`s!>M90yU*RS7I)D z=kZ#FrYoAd0d9)yb@9RWz(l3}iw^VrIC9sHNT0KOoA<=Rj>-C80y9WWdG=y+Dyc0y z=?L(7J_zfbSs`t<1O`0WEsXaC=*=sV-0pYnU}1gJ(u>*1Ubd55tYSt3m`1*Bf{MVs_rEq7@cJ|_ZxYZoAhGz*8w2z2nX|8WdP z!enRr=9S@MWgYrvss$4!^q1=Y#pufjtyeoMvllo4Mf`h9(K*M^b20+{F;>HD5yDkT zXYxv6NC^JKYx4PkQ~P;W^RXowYAecI2Z$b8Z&V|V+CvIAw8Xu;GJwuO1V{Pu>$Jn^ z`L^Y;NVxMNAg(gI@b)6>*rz}31sf~0KcR3NPg_6Mfi3WcSvhIucl`}Dvo&I+&xdHn z*>|PV>1FSX6+<4Ie2?I}nwx~?G>&|ePQgU5QHAejf}#}&)*o2HQf-XOcQ>2)X5HTx zj*VvbEAx8)bsR|OEUC3B;ZsdRq14b~k?e~lNZYud^9eZW`kp8(p(-pGeXoJt;}-1y zQt1|#xPlLvLFDihtO2`Bi{y)LSowffcKJoY(wo zTMstpZ6`!+tf+<6#VjX|0Ns*&^@|v|a{no~GVsD}JD*ki?n&2W@9j@MgNlMK6%wx{ zZ)eJI)`wg8WBd0;WoW$GaC6t}OHS;i%W&?2k$ibsr|{gUi3KuY3{2joThS!#w43gSx3OCSyMjsRHhhkzzg{W#7~L?IH3^ zv1enAh6zmn%$M{K&^*!N?JpdCzLZGE)^9&ri7kK9eJOV9w;>@W0K&a3k|msBI<$8f znNt_bTP09fx%GoZj`V^>43X^hK1_3Cyo1W5wz2$LH9odnE{@h8UJ5;_0Aa3 zW9YlNd={}Lu<(e%)$)Grva6Q>0iElLgh+SZ&GDC=AU3>4R~Br|gDC;Y>7f95x4L*c zl>6#QbsZ{>9tpft!&Tq((Nm``<=XVwY=+{9tS{U1=;q3Lsq^Gvb*pW-kli8$dLiZ& zIm4cD(Qp`YkwUD53 z4_al%2SEuCt<2(^Gt1ypXNCheD|rL0ug_aX8irL|EpRLWrXM3B1Jgfv zd+XGp@dvgIU?Yl)^&qu-mX{0HA`VRk*qg}*w>->|*^6``T;{e+_9cXor zgG1Wcx)qsFubyjISN%a80bqI~f&yEtk3SS>A;G3^ zQMB`%x>5@IAG0XHf$>fRhS?G4<#A!11=@L1ZW{jz)5 z&}_4ktARRc?Epo5g6hS~xZ%?eHGPF@z&Pva`5^q^an}5m+%ozSkVOYVN14Er7)T5G zMgUh%xOn+S1>KA<&{g7?HP|TW03|i5wlO$8bCJ66JTCY@G+lim7GKxp%V$WgScv$p*PVTi~6`jH3#dueSFDn#=&yVdv2N)&yD|qP8Iq_$O{383I`ccShUo;j!>CJX5n8ziQ|Meqp*s2P*mTCl5#f!vpyTu4 zkVSZ9DGp5e%%TdbUz2o(hsn7Li`BL7X>02i^|rUY#ZP{H-<9GyL3&)Xynr%G~4q!?{0A*WhZxTvKjM?K?3fr-zB1jx+JgeY|8(%z2dbw_*#OLi%4J3zk ze)+aBSC!NIe{NoTTYq{@S@&B>yPXW$bNPkm?hb=i>enLba&Ux~(P*xW?3!5HS1%7x z=A>R2_Bpa>@?JK0o6&{+>eoisr_81&D*nwR+p9`kqPL(>zsmglslSRkpJT7nm0&+P z_5H|HCGiXRpM-%tGHTUBq&?t{==xEgws)VJxThm;W?UQ_N(9d;e^uSxKXSkp=h+hf zuhaJPze6;U2llwiEuQw(yS!ObMCgCI5bb{ik7mR2uG%7#huiJw?|zrs-~6@Ox|+oS za0#-f95kQ}n-?PM;V|EkUl`r6XFPy2rz%&L^or3@F zj@iT**^F6FsyDg(6RT(3-zPD<$?HF#ey9Yr%ZwOXzZFH-6ugIk;c`j=d7UNn2zf8~ z{3nLDE6$t~dl&0TudNgm6g}shJcrJ-d8j#4l^b^)eZ1TJV_q0iZ!CoH#~WJ^!uANK zu-kC0^zb*cw!S97$N%7q$udW>G^=pGVemPL^ZiH87O;sGR^sUi-{P(@V3lv?bAF^% zgTaLObY@bzWG_5#Pp8}6yMasD7rm1LmM@d3a&j)?8p*qYflEPsma+6?au7{6#pl7x z?v97qk{4n4Zex?pKqUS>tAlM66!|(7V8GP?{=k%he*J{`-5qOrDEW$G+dV?doVJ#H z>ig^K@xLQaztl@RVZ*jEp+I^7sc;vNUHjp0V9>f!P-DQ)j-dToo>v)C`MV`&+KRg} z5ZQd9h<)y(tpt~!AXdy980XK!cSmasFR`{`l0 zZIx|%WkcYu66U}zrf2cVjgH^Alp79-cGKQ2;eLC|Vj}qS#4`ndXWr|5alC%zoQj%1 zyS;t6)9KIF(-Fm(+7?G9CV#F^$*OANQ4zzbKlQO7-in^HZN+Xi=m}k7IQ3lL#p`Rt zoGkTHIY<6$2b36|p$HYLG2UokF~^ERqa7nNd4!baOvGwb=b}TdY4eri++zHGj^U&{<4SVP$t;5%J2e zdwS$##-59iYi1i5VEvsMOVQP|1W;{LKXJ)w$Z$k#1FFxi5}sJYKaN~Vboz|Ttu)s1 zrW&?Gw#37X!qMi6fTukk4rRYwBG;PO>@>ZvuEa|*rv#znl*@*^tFRCJHlgL6c!Z=w zZx;QxxzOVL8%rT8{r9w{46Y|V?pGn@^MV;-o#gvz*Vps4tXO!Qx3tdm>$}qb%=pl9 z{@OB=Pxxw$-m3KL%o?4YC)pUviR};@+ z_5l2pk-93YLP^^GjZ;_G-Nl&iP;j&M$K`hZ0EI z3Aldz$t+D)pWTJ{+PHUTDOlMu8mn#n;SI(!%l@aNs|doKv+{VjdSdr85r$oV#Zrw7@g*sa z>^^C>FL+pA68^|1*Fa%|r@;EwvzODyCB@C{xHSJsFT^Nx{$)dUk5EB^%J{F3>(V~C z96)=~1vml7r`nh=7WNhf5(4sz;PXEaK}R=)7F%A7@+YjWj6iO$QN9#F9L)N&o&UJi zpa1aM=jkz#r}xG?uVI}N3%A8r88ivbNM_)ajV*^i+ScB^vaRV2qCF>0Jj*75z0y@* zKU`M7-|PAo!oI(OMyXAYIh|oAjX9keTijXV^+_F&?IZNl*sLG^9Nx;rCyIXKkV~+3 zn}Nzmys_IieZWYy3xf^E8LRpnA>1Oe_gcP~H=M5@EwXM~WO4#H9%h%%-%P<=&8mkq zaYHq!C~jw2zhDfPLqmo>2ndb*sPiG8ZGGYT@kG`n6!-%r9q)r6kUpnw=gd(ULQnZX zC1U#VMjiB?2uk=HKQeGduF})r+{x?&n61Ev_{20y8TRit59P0jmNn$acI69B*PNXl zBW^lFcYq2x=-Ta=1)fS1;=8FQjQWaSon*X1;^VZ?3Eo#37lChDP)DTJ|B@kh{2@mc z&5QK@UTS(UFQw0<`q857R)!6%tU$S;i1!(U%OAK+BqARq#D*lROv-hVY4}l1MJO1u z!#Q&19srMd>v>#CC!TUKpkvR)=j)t`--UHxX|K*GRB+n5Bl0cE7d!L*u>y;I}~{Q9JHR46wCQ z#0(7oXN(INj(OMg9v>w(gk3or$@xkShUf&%L{I_ZLv5@?v8XC6jqDd~Y^ z!c(6uI#Y{1BX=>W;)$So^Pn@ug;qflTFbfDo9^&dHO~(mX7Txf)GCk0~5?lI2+{` z{}}n48wx}`X{DgC!6vb*FO}FTQt$sR!od5T;3s-oU?3eGkc3&kxZ#;mq`2IyEuC$h z{^hmwt*%t?tQ$uh)7EO35vtwhv-dN}8bdx1yTA%0r(F^} zlM0z3sVQK9-DTVufK%XKxFBNC-M$s*6gzCU6f1zWqW{4$dU}F=K1DLXD$c@_;O| zU<`n!SLK_63_1eK@5^h7W9*_pBTres!GCYWIwJh!4s?3E~*FfFqs}#{v=QDf?aZ1CU7I5>V|{o*vRdR*QeL2Jfx7 zdLtdp>(_zByDlT;88R-pK_qYL3VIAwEuyaYtXs=Gp9Hr4vG))?sQXS)p2>q}fgX-* zc}}7UQM#v!XY64?E(DRMt~z%eqThGT)xt%Lh6n-6NC`}0_Pe_ft)>|rrp~jIi}|fi zk(rB0_P5`Z=#f^meEjkp{}cTw-t#8&LAL}Q(e(oz9439&Tk16QFFT@ug#V&Y{b{qm z!|h}30gSs-d8ey86xK<(gq9JqDoh&qSk;1%J7rUyn-HN9o!OS4Re&jl?e+#5)ZO#Czk7bUF4?`n-2<%0jD%T^s%g}Ylj(=izk zO7no^Z5|4vwhH%K#tgI!TGQ5_LhvGIjCn7M5?Z~y;XiuDqU<%$i-(dK%AZTOSQ15( z`nf>C&cl3eoEP6rh7+~egUtCwfesIUrRBx!;xwKmK+kiN>;^Rz+E0me7XGHhGg!kb zZ~&;7izr_lJpyt)MHcQ|lg8S44_n!Z#ExK0wjDt=3azB;7AodgjQ(3=z{ZH7ED$q` zJ73PM6DRE;%l_BY9s;)& z(TjZ>|2_sOAQbB-6PhxWv!)PS&s4^rl-k#O{7(sJM-^lP_=K@}k^q`i#9A-3b= zXtcHUn7)FFpVaZeQI8p8YvsDF&%fj8N5BW&GECHH#S>q ze{r8E>v#CgNNtF)9NwVGOIqHNe^@HUB!UmqqdglG*A&JnfRhqW@mWF}exz1IOK;@W ztxCrdh>XYc!geBkg_p+NCD5|Qo9kkd7A-b!5Iv^+<5-?UUx2UqXyW`@;@XyVS+!1Y zyrThzGueb8p}?h8(vnt?s`3!RPJ=mhc)|z;GQvxAwJ=}mCa)vf@fBNksA9*?+QQp9 zu14 zAJLo;)NdD$da2b1~;QySXNr z@x8L5H1*IXTGB$O+;KaNQus^zXd=Lyy8~IUe&vK3LBiMOiZ9N2jLMD({it$*HJc6+ zDS4cg*(J+HnlCDM{V7act2}g)3vPG52NWxsEX-REK2IC+c&Csi%G=nX5(>aKm>w|W zVXu|Ii`0PGM3PD~h#6A{=kOk|RAW(&tDJ+RDf;LSFf$W?B}XLz^k$gO2gx)KGDs=G zHWBtaKsKPT)%@e&XN0i;6jCQts(f-BdED8;2wl|qsD=jh z$&_7=A?L^8#}iR)C1H*@GM>1o1IbbrR=|Wk?uwJwiuA*FAG5TPzc7W`RK}1NW2>#@ z>vPSd08@W1;q?ajp>1b@6KOCqX-jcG6JzD`U2O_|D4TP%;prurBPS!i3qv^Wmmi(rRuXyGTXoVZO&S26`x_8IuD%(#^Kho- zrDWy3H{KKIfu3^px6P8|Ix@K^cUdn9v%j*)aBJoKrd*@0y!@MbaGp?j>(uFS7A!81 zeA(xW`(4pXW|sc1i%q@AQ>s6oC&|8_l{y6nLLvxSD{f2idi<@-G)d*bU)Oj0AyIhd z-s*_%z@ytWz8N`-Vk*q*{wXK)-1yqsSarnfVChdIks%GxGj1u_33W5@G$It;Q-pHd zEa?h`)5Zyv3diG4{>=SL*g8SVtg87a*W#j{=)ZDv<;PTsJBvR!#&9YImO>yDPVlj) zNw0DBd~fFs)!g|AIx6bK-GKR!g%Z+y%G>!WVMI=Hh#+IS)25+S`cp)4vb4(Y9ZZF& zvmR}?8dVPmerWPA(>H@bpkH74h6JVZ+3~&__FafDjWD#(W|qm-!5y&1Ix>F42C(tx%9!plrQ{w)eXdSA{;lRAlAO`gY(_>)I#NlSjtsrW zt{z6{5!+GYUKBCSH4$K=x$8y>YsGTu?^5Gt?$4wah-`R!~qI3Kx!B7eeE99~Tho4TtEIf9@r*53U_+TXEzv(&E zv^7gH=73tRuzu=Sy2xc-04B-NxuTz-$?qGI@$oRpT1vtvjt?-0Bq@iJHrlnYR^WBs>gFcl z0`vQ>_Rg_TQNlE;BXY!0GB6EMK#(k0gf3xnv^MJWJ2NKq_zmSf>0qu9;LfQ!OQL+* z@-C}n%zId*wt;HNpzFzUt9jjagmcM4&FNuUVleRFFlKTzUpI~GM}hY5QZUpQYRRj! zvv;G4ZsPWJl(T7RL3s0MXMDq(y-;%S+XIQ^gg1>)A|tFg&HxP!h9FO*Dwu{441E8W zvruSu7v|NUg}w&HF&cPPk_KfBsMAx@5CJe>gIx&_Ubb!a_Qyk@*4}#*F?W~ZW13gL zTeJ!Rq)4ZeGsYWmftW2Xvwf=Lj-4RiAhkV+(K`3PCj5gRRE@9pw&j@AtA|5khyO4t zd?ImQES8)bH+l;-hC;6La{wIcP}O8Pb;C8O`2PpTKsdiVupbNzVh2nvA>P2U$P_rY zxJ2fKDKan5z?g8B>^3V5&@^NPQ($+1DR4^*;=NPj5X;;r>%kLIe2G50XBL;VVy9mZ z)l#w8={F*71gnG+S$HRHvwFoi%n;PTSd|aWUJWurTB&bf7ROE+1Y`FHcysaiUD><47e*3hCkzBr5L`Xs6^hjsD2Iz#?1( z2fz#f7k;^J%mBpV<*@#XavrIHSt61LV#Uz{)gVH-uo|zeD5v*6c8G3uyEOXFUj!bS zSfX)wK#Wh!3pYhhGi+I&C(Ft#t(x&^UIdYV{Q(#Qo0CP!U=VPqFLGaa(fY1;OL5|< z0$_`M7VooOKRYLviXbs5hEBh!v4nQDSHp;}A|yS2v~Zw`|3ihx^{qDVbCLybkZoHk z#KFXtr2?0(Z-qK+lE%kpX<>GV78f85f^Q+K2+TOF@nQz}C*mDys8bB99(N&#y%6EU z{Su!h1m`kk7mz8ZkW3)>#_W9jW=ul5$5KW1z^Ou%%_125ss&LugygIR8;*Gx+;(1i zLHq{T#vdmCsgeENOM3e*E{9Pg&7DQ}>iXfXiX&#i}=skyd9=*?ey>jsUE z&HHvAhwtk%0JX?hz%GwmPMqzhB_tebs?4Fox86#>@WF#o8Q;vD8Pbnw8i1?f!0@ct zMOv0;5sP4j>{bhQpJp%y_ykwL>H{G$z|WVNgK*2Xtug za!gvv&BjhJrhtp&%m9#qZ?t1 zz*6T2=)%=e;Za~-o&zJWKxXq2tync+24G0Y@di{NoBZ4?1HkPKaJ5uL`6eTF_0Z89 zK;ZQT6WlBD9XtEViZa+}l)xLK2;shqC_mqwj|1aprieET4oJ3CocK;Fkoc?jIOhiE z?fCqcpQXvMZh~j#O^kwCSAK2|17HGvm{W4g<5yq!#Wi05!Pahby(X@8MyLQbOZ?e? zNGHPcdKYRIQ2{wCAcyIVRT&1XpUA8HblAsa?rxl~%!O8em|dU$^m3?yMFkwJxb zx>h%CSv_#5qmd5pZNf8jD_y@aK_|}-QP+(dw7fJ;_Ej@ktO!VCaY8zvFa(=X|9IN+ z!fa63C{K@FJq!GJ4UHxv#Q0gT`^kcfs{!u?hGVmA-B%R1O+zz&G*bW{l;5@-uT z&kWQmhf&;QVTnamWgfljkvr)--!p@G8lq`P0;0tnZE|>N)!boSdSqqx(l;?F4jY{I zCG8miCjOVMjeP3jmEljY*xvy*`?a@wZ?g7ZYo(be96HcQO?4%HRoFEzmc*Ys8}o?@ zSX*5{XU?;T)^8XIJ>f`zO#CfKP*eybrJA}e^vS1=#(Yfd>Za_d`2g$`0%*?T{ECkrLY$;04W}j8i5^e6Rr^@6sYEOj}H1r%$kr z$bx4G4pRZuHEg3>@eDDs3&k%#lD<=|4ez3C$SSGeo@h&JIkm%fK%?0YRnRP*JUd9| zFLu%bq7mBc%+y(-mO+|pFavyWyNHU1{wn}>&^AhU9hC4 zq_SBU`!?2JbkBsI=VOinv0rOWtE)~(ikfI(^ave$jqL&E;U4Ij z%v#Ol^$0NFSY3QyWS?l3a(fBi%+ z2rIksZTtq|aK3jQ+2UvHr>^cW#PP2pT3}#_rV4;z-+jkcs)9u@Yb~FDoY&IKV z;E2~dUmr(D4!4M1k7XIVHH(3M>8X3D*~cqlJ;?F8$BBx%dZ$G2n(p2i8XFs+#o1vH ze(cNy6=7z8r5IXjY^ny4u|Wke9uk3Jf)9FuT_Om-0iOmi5g-gXc_l&;!q3hv@SBg+ zH$NUYil+)7*nEN!a>gqcHYl7zP%5^uIew}Wu?DVzIb+GJ6O0@)o-Uy#;PqQut*<=8 z>_2ZZYQ$P0+kryxIfEvMJr|uGiLxMRs;owA1q4*ug7^xxaJ4i-)X!s7i9FYhl?Zqv zzLV_%vXR{3AHVu0T|W0ynqIIAbqSA2mMCLDgQyYOtb_0jI2{N%R_h`0Deu?AR=LW8GyBC#zssW`#^`tdHFDWy6fofgz2%@9wm0-$cDIQXO%ek1q(U= zw?iiljm?26n4x~StPPAz(b8-mo!FR>8GX*G$&_1Pq%J8wDJxqVIut@f<*C9oLk#KBZL5Z7i zB71h>hfnk;I37;qFk;9Bw;YcxQ6GYeIYv^-s7G0`{|i?15bZ{LK6cJ@*o)@JW0r zEX=08yK1Pcz(C~{jr7QUJCk~+8;CJp2X-@LZeCbl^R5y;?3~%~($xvK3Sofe=OBWH zKAP17I_QD3vk`nJw}eW{YQgZ7h=X^FIXOnmA7W!>iURCK0{c#;^JJ6&tcM?7D>nFa zVEtISGdMg4x7=B%4W_}wjf>b4meqL}h+0J;o&17Ij|jgBL6q3t&WHpGTC69Bv1Ue% zMgCf>neWcd_xpO|iRL{f@Szj~R23tTKtBDiFVIl$IhtBR$nFr^h5}#KabrNEb0W`4 zF{A)6yY!YsJR9ELqx&tLB>+2+bf=BcH9Wo)0`W&h@O<01!WC>k2p0K#3qB_!jSN1XjaSQ8f^IZrkW%0yzGZ~;ZRKI{M4z`3@rk?uaS zEv1M*qk?zloJfa@5#oCmE9d8C!52H1+L|g6d%%v}-w{D&`uEw^W} zJ{NwWtA}zWx&JZ~REyXk_uqS%{_Fd5kai5v%(7Q{k=&zCrO_b(3F$g8;@47X)ZvUZ zof&{?7<}bx@RA-J@w(})g&fue9^ThL9PeK(tml6dHUoB6GP=tk+|dr$8^lY$2)kfb z70kj_EtZ9&0v6_12{iz_7IpQ6Cv}L$*eZ%NR94Zr=_Yh{OBpk=FYt zz4*cu)E;J&XM z-Q7ifxZwdzX9i#yJM+PTM zTxUHnN9~JcnStg13iQ)lD$5J}qKL(^-(axW_^lqsh(`~$iKH|_ZxXDcye2UqF%VM> ztXk|aMAp+|5ACGZoxGIn&Hw;F07*naRQFJU33dMp|N$5{t=o?wMz>^}hF&xUqfIK=NuuXj;*-};GnCZ-ldwmxoSf)5x60f}W{K4fHU zQFNgPvr&nVqKPOhVy!IH(p`7nju61?vz^Y5Pa-fZ`;-_69RnH|E^>T;in3gK;?WMO zY1~VNU;<*i9|?VcDPB{nGhT%OU}AEy#|d}o{06^LQk=7Z<8mE>=;Xm*$hU0UT0wUm zX$8luu*~y|u&{sm8uFYggx0t2P2WL~Inaz+T9CLU)@2xW0BT`(z(ka?d^_#ke}wM8 zt4&x^Ct?h-?=zi$60ygg>>>Ubu&z2FjVq>iz4LY|Ev=)X9Iwq%{6~{MJ?f0b&!o?6 zg3lyv-*#(g~j!U_BYbLTN?O8TpLA3y60cIj#%~_ASkrL zp=i}ohnV;Vjs_FMGCs+<)#2=n!l{Yb&?%m!5HFRMEqm$xkM5?z0A?zl_lUVh^Se8R)ynweRcnpV6E*gdQPu>)dR956m)CV-0p z;pa+Ay{SWE*)PL}#~Ns#O zaY}Cf-O3Uf2C{O>UefAv1|w^oI<3dMgzn*OW90jNnPpXjZd}+P{DC`|tJYc_RRm z3Qf|Q0dOzCv0dA1x)42UiX(t=;e;?RV%LA8XMJNocl$Lz29S+T4HClvNqkd(uGwf4 zH#jZ<7hW_D1QP4QT85f3NKNi~2kqZo=VSCI#f!1Vy@W3@5D5l2@s$zk$Y1(}yQmc5 zj`3QOMuA}>Qg0|YrpqdLE6ipoSv?aN0FIqI*IZwU1x0F6xYIrV@>Lq=q}rP-R;wLB zhGsobVl4%*n60$1w2E196f_eu&BfO4pt2;3wr$&kP{cceMEXnDeUxW8NCd_9s6HPFXDau^o>zKjN=Glr9l z)r2HmiGd8kfNB)T#>o#p{16f<i3JhuWwOP663^_F{{?7<8XWBCW?o~g7l&njz zox2E#v{+xmYz6nDi42a*i~qUy3@r1!m-3fwUg zf`dx8$0yz96MWU=)y%*EM+@YX)a^TtQV9Z{nPjBE4Hw>+SMyB}|A7rFrb&g2c;i6| zz+v=G@9$_hq0u@WoIE62#f2YPzfVknW_TOKs#KIIdVd!2O-?Pvq#HK+tK+~T@4cHE z>I!`H{xRuJ0+kq$7>FST*e2kqCl671^)9FaG}0(=9b9X4hCWka`~O>~O4{AZ2nN8t zYR3!mOp7qqyU4C=oWv(urDwovOxRFUgajp^qA@5HWqATB#{ig&fdqhs3$^7rblZ_z z>0n2_B>pj2LkUu1Af6atbpWR||HM=G2&2Ga#Pg7m0)wxB-cdqL*Kel|go<3Ue84m0JOWC9sWyNA8P7@sM zQcbhKcvIw^SbEL^j*DRH%5^)Mss#H`&neD17x} zU_V;s&Q{0+or<#l-<0K|mWBrU@cRx1iS)M?YBK84SqlmBy2La}QmvUdUV?0ihlM9%;angwdCE~9roa3BPRNbVYf-#VxUaU3*KrkD z(gy?B4QC<`!0Cf676;M?=hG99?4_#Oov;V6!Pqx_mNtT!I5isGA4bqFEt+H;0{}Gu z3+Uh4yQ}8BK1;i_jk5%9;ulculbTm#>_gI?XqU1QAOHX9= z0M7k;qGLzxDM;6C+aq)dWPksvaxZZnawqFq8{LuJ5zz(RV!FvFNB}rYK|@suz4ze` z+7eL~!;E}vB3nG!N@5@hF~Cs*wRjI4InYSkcHT*aIj)Qg5`@4%X9muG9&X{8TQd1g zeyX^@Fk4!df2p)MPw2(MeGE9Dkk%blV|sRlI9X4$i>m);xa|MGS^Yn2JIV_*^w5KM zU5YI#Z?`AT_qpZOM>%-~kOZXG93ma9Mw>N=w$DV`K3&>nIt6&Ux*~T1 zIMUjo1EO70bsU4L(NUCBE4@>ez>8K zGuKck8tW)wBpC+WDzzjVRYHbiw>hb-h?u)Dn z&ghcS3;?76Gg(=hOIunhPZ9zHwX;V+h@~7mbE83VqFpxIViNt2hr%v9qTq!!v5ENq zyxC`$zbD^&RQ%W#pppE|=tm|JgvmA%14)emkuLxN0k*YN(C+;YQX!;-Y#)%W1-pL^ zrTUe0onJWfl5cak1h&){59H(+Mq6MIXEf?V!e4Dov8Ub;2mgtI5v>NI|FRM#0gweD zhL@Hi_x~@;bJ4>O-V67ia?iNs7l{Fh0f_;|02>A7lePtZ|NnyB zfAr(2ry#~Mo&kUqV8D>2>CMSDOzU+TVkRJz;;a83;qbpPD5dxns{Z*C5dR|!a73jb zM@!qc@2A@jw@`L=Xbet79mz%#0}=x%je#N<0etw$$0!%>(RpELL{npa z&3Fc&tFK36cR2E9XP4R-&kKtiXW7fcFQd?HFJMF9pYctI=GR+WkN6*zA{GB<=`~bS zUQ8c;;sBKtZT1(P@sG=T%O&rn7zU&nfd7KADuBzDrZPHu`y-iCd@ z>h<4Nrk#<0Ure_(i)22M zA`tEA8<^a~O8maTS#qgt@H*J6^U?ngk8I2^tOIJzTde;tFIDvat5yg6|HDr)T#~P+ z{re8m9Y?iaa@=*yLSF&bGU_gohf-V!k0`u|>^r83NMukP?$QKZy?lCCzZZ(OG8Z!E%d~ryFGRULAsHbBnC1Q1L;T8qPmV&t5f6)xcC11sHiB9 zj7S-tPQ_`r>~1>vo8rBnPV)+PE*Z@L09oyquaE8nan;T*T4`oxWkdW`S3>;n-Z@Op zEYm8~2C*ot|L1NrkmHA||7X#EZAB41j*MTGKB0;uA7^Y-9FdPlwv-r<7>G3n%&Rse z3TvaLEw>U&6W#I1V%=@L;A>ni+pol{vt;cj(;0w02;j4ab>qE*aODf3)FNPpT82hq z6a0tav;JS@xx>}}mlYc5;DJ4KcyFURRQV|}ATi*B0rj`#b4h*;zyPZPcr)?%L%XP= zatjq?r`JXRQh@gdOgZCDGnN5>_&;xWWbW4GWeWo0hsFS~UcRQ%%K!|=DESZX`hETX z!_ohnvWT{B-9hihk2ZQGV;_^~p-@6fDh%*%Ob54L9lT`J1L?tu52wpP7OPDhoa;lq zu0=F0KDaF(nNTcdJGH_!`i?vHQ)zJ#f}f=i>*R9Ss+$k}_Jf(Qj%&>^lbynMU%N5d z!H5~1S`I0Z4~yjuoJtR6aoIu(3y}xr5kS`XQ&ODm(c825KUDoczmuBkD(Qhc+wo&f z<47LB8@pf5E-caX%o3t7F4Gj$H?wm~_*|fwd3>K;f=j1`mdzG&z)Qn|9Ox#K0f(MU zMkD3mdqF`qvN;w~b!9O=3#hiLh?*P9f%`NLgSN&qm(G`&SgVok_!;T#AEU9!8Jd`w zqtWqMnwXr$@7auJ#7dS`>rGxg216E}l^nSK=2B5%K6K)_aH-9wV(7+8iV!~^&rwN9 z0TmbIf?3d`y@E})-{B*bi^vy%)WP?@qmB9oj?&8U=V=_FgF~>DPKjwd9p+DBpwFem zU}G@IjAa1u!|WKFnBUFf|Gt3`#sAelyZ@1e7%YbZvcgAiKRy|Hz^bx>EINGP7CN%O zF=k_nHQ@OrGYB~d`s^~zgOE?oEYb{!`4kBK{K5)N;|0F3xDruRMDc>hOCEU@*PI6L z@ak(z>6Y#FbYM>-b?j)OoNS0yWJwteFtK+!6rw*dIY%eYb<+8Zy>$J?AiQao#XPin z?DnzNb|;7#n922u71DsIsd;g%SC8-I=42zMNH!RSJSqUgP+9_p0pClC3#k}Ec8b9` z@HsORfy@C+eg*hsBd#1JFbnU=d*88@#>U3Tyn2S_R?f+m9l`5Uk8&RW&mW- z|L$FT=%IVIh0|4B>e}oM?8=uFLJnd+17gkues&%^c%)ID$0zUN7Z%Le*(WwXhZ6*$ ze{fP9KY0EO#KSt|h^V21yPN3H-d0#fSHuNpvXk&IpziRkHalGeu|IjXi!NO5rm^vv zH31390Pe0&{?}|C^v3dvnfO?*Uv9{0z6)}*sld$)6ft7}DMLvyBnb${&x}GL&Wph~ zWE=e%&_pl@<`o-lX)L27hj-G{L>F1?OHh%dff~f+%5^y`Phm;^k=G^DN=7mOSmZrj zJrf2fIKK9+ z*CGDRp^jZmbhx9L4s^6oVP3dd72`GX%;{%%bdt_oxIt$xcGK0)0kVM!5M>?lJ3rC9 z1CJ*04+rBMJdA%Md~8%$1tFvcP%%_sbFihkj*cGM2?haKd*W17RluD`wt%_0k6wTI z8Je(Z(n1vgDZod7M#%ug=~+r>H`x7aSIzc)*oAKA41m1@Ywr+z2U=V0P5#5N|3e8F zf@2vkfKs?E10^92oXNT}AwR`l+&D!^#e(5?F#U`KI~ zRy$A9>~iWq?@qh_n$H7y4{@*zCFQdMecmM7e2Jgyk(09k8`0M{28|wc{@|<+f-G@M2Y7(~lXFQlpTDs?s zEnq@E4AJ-vy3uu(=2z^rV$~6B>N2(r4o$(vWQxA`<5Q53=xLYmENB+Sgbh9_rHn<8 zd*V#+`PuL39fiey7o9oZNnO1oASy9@7jvgvQsDW_(z_@Jp?K>X+UfSA2WZ!hN-Bjb zq8;vdi72cJ$jODr(f)e6(S3-lU1w5W72tAMo0q4~-2;5SmdSPAtKa2kN?m0}i9csa@AXdv5+6#vD z-fRqa+rcInBbGp|vV*Q%AES#`2WVj6Dsdhoi!HN#>)~+%!qzK}FaOI+V3rDL-<~F6 zKd^si6VwNpr9#nabhNU(0>jZRI(4>F?Dm)7iy!X-PyKjz8B%{7b~hWp@rtS%+P!-p zJW(pC3UNrPEAz>SSQbqD6X6Y@4xan3t1hDZ@7_lfqgU|zwx+x)z-e3jMa)k!0O}&A z;pd)_vANxB{MXYz9n$nTAy2hW^uLj@82bO?2dUA}r9-$!X>RZBMbXlHt1gUO$~cwQXr61J_4T@4lK zKsve_@(w>stEVen{dD~7bvk*b6U^aw!pLv3K7pKcr??=G+O{8{op>fV^mrZIkBf?p zVng7tyNDC@B)Ty3p$C_;v%QLr-u?(w0pEryz#9Z9(c_Dxf4g<*!R-$`opbg1k8bRG z6-ft4*d|t6$E2Go#Q!A&Lo*@bX^IY`M{YODGV(pX8^N#FglbdT0OaLLjY;b6zJ>rlbF^a9 zuPfz<1c#~EhF!w0TpJPxyBanj8sX2r_cpro)~%^H9ltTN%?#U!JonNW`nT`A6jCr9 zKL9a0VTOcDGS~p>buP-w)>BjSE^2RUqQ-_2YQe!94|Y^!ojV)fC343fY#Ho$ZcVU{ zy8j(psO!dlvUi`O>7`_4t%A)U;F`-(WLfC>2(Er9;7;0}W()&>6-4OYF&O{pXb>^{ zts!RdtA&_LE$rVOtHLiNl>xBA!g+2kxHuFMY-!K#gRsJFN*SXvRuAmmT`LZbY2YZ(x!>^SNi~l|$|HJHPTs3{^bQ8u8k2ILh8{ z{PEL}0`;z4DFg2?|B;Q~a&=WLH8<}-2t z<-!}`@kfu)SH5}!YJg?f{sh)aopXnE{%0{-$pECzl5IRv?EWpzf*)y}Z}{IBG60JHU(u5@k>s6IU_sR=%|CBcZ~lqJq(5Jv5QZRFS0UBbZH1(#ns)D~rX5?W$cP*xb|fL@mDi~W6cx^K*P1J zu>*FyD|Zz-v{2pnN{~=TjRS9i#9(S>m6A8v-``(Y%cMelrh_JuaRdrNV)j%D$)X~9?I?6-b2nR&`O!Unu zu-jh?AA$)0H}&2}b|FwvFT4WILKU!9?WDhMur7~$0;BvtNk7sk3@}p|fIU6^lY7|s zuWxWB9^8Rc*&HNI)$1_0v!EEw+XM~1m}_Wkc049)tVpD=cFQOj@) z#s3@W|2ZdlLv1;I_=){6NK{9Zo}W26S#$#dMpS#Db;F%F7 z^y7@y09nMJ3*Y<_r1))Y+eZ)HwM`iGG5mBV`hhsKzyRQb@7ax{V12Z_#9jfhV6y^y zjrzI9Zl3%YpuQ4y$Vmn;1zsnc#@HSN@7cq~e^3L246Mc{_Wvk|e<&FPLk1pDvAoo) z{|^-Z=jQ0Bt#unce0TDt`3;yxOi$VF;iCA!79>HVgm+Z*U znBHQ6$*Bb}qCCgs_m=1zo<=z6Bo=SGT>v-BJ!Ts;2;iinhK3#Vj@$Mi!ec!mluR3Jx6a=WH9#TM0O4nvis_p$y1>(#0f6{Fe|&P` z!1A)CL4^K={y)^>FaD41Gf-Kc=NZ=I)S~A*zo#0jtLW!GaKLjd{$H5jBoE=qLZ-w6 zyKBTTzjT<6pBbdLPxn!O-xXQ}Bd}Angz$_rneeMC&Vh<{BRu)Brd`P4Fh--(_RaEI zD(JXmxA!)6png>ntIrt6ZXv8eYl`BP1~Ta=$ocOPw~I}SF})a)TT zadv=CoVrFMqx}d!41H?~`y&UguNHcBt-5m!yqRR@4i}r*#nfhYQ!`dZ7di-bb2AxS zR8*Kpd-mKRl5bU(7Eo4}7C9btu*=}ta!{cNIEv7Rky16;$nnYd?4|MXK``bQ!I*p4 zu9TUF-7@tdNCCc@&Iy9jngPHMvSW07ezy?+_l0i!SA_^%Y>YoJ~%BiEw!TEv;?z$bH*W!tg7Sxksao0shl`G~^Ufc}X7K11|~A*Kp#@5S>4N z4(^202tSO#Dk(0JH6j9(N@${iw^+f@Kt+*@EiyCFHZZhFS(KmlhF$;$gyqdMYH8~( zL}fezzx}#m%0}cnb`55RjDM@@k(BdH;Gj54U|}HwfbH2y^V5T5MhNw2WpIwyv>eu@ zBM#d_EBd<;uRi15Puem75dT-*=$+gTaa4&NJCbm$_(k?V_euTXW58k~M86Rip?Nfbpz3U>u z6C`EB50@0?6C0bQLaDAUfT{vaSJE;7kO8=vfIxg|5tf&Kat-xc=;*Eck%Xs$3JOeA zSb#)b2CcAaXQHpzV+6LA=e`)=1X%aq)lQeM!GmY^4Vqg?kq20vgv*sF~hw-1D1`zo#WFtPj8ESo_|BZzhHEDpQ zT}_dYYAqqALoybX_N(#8oZb0IOg<7hk0PQxo;a0GTND)vKxkoY?G)!BDJ4e)1B19)|_i62(fMv#^a0CxXc ztIe@@YIb=iC;sUj2wnW22Pf3wHbp#1e?jiVN%M;rrTC zCf0v6aS?4;I*4l3@Wbt31Ug`xG(5fwA&4iDvbcwaM*3*UtU+vn+5j;)&l zg_Hl}7ZlUf+-OpcR#l~BX`Pf`oK0J|9iUxX>!`l2kQ(ZWu-i9@akC4bH0s;j5?UYy zIMmTV7ccLosfl5lPB8`mp@DC)u8bT47GB*PCX%ytS~37Y^_DBwhW9!g8q>0QHE3kL zwU%RP*$REJ6aLm(ad5UPi#fJCnYno{b6HLwiRe@EEUI| zowd|A&_Mk|v(ydu(5qLj)A-B`>;<5LNkUt$TlY7KnCs!qI5D+(w_H=x48}Y87yvfy z$CRSaZRX5-#ng&tf-~>2nHneIkwiaw&y5|5e-3$kcz+`e^fl6A{{UH2iwV$L zmxrGMMkE6ey~iTvkoNQsO$+0{zQL*R%#rc-pP60uWacX^%B5j*Z0!K%kPVLuiz0Vz zgYNM_lQ>VVKP)g!JxkuTaF`EC&n?@l#4$71MAy3;sRxFlup6Q7-Y%M*UxtbR3z7td znbC&Y5_=;jg9ktvji$rHr;h%->wkIMsmY+aa1p ztiE*gA5HM{tAPZLr2xCORneuL9W*}H2Pr^m*#g)tvv)%cPzg0aa%HuO!rG>t1OVcH z)7<>ZzPb6;HbUs%o_;U@VHQsMqsHbhD+L1(!!W=+G=wT9yU>^EX$Mk*s(pt!Y*{@* z3~Dl3)h+u$&49+5gHvIM$|zoh(qF-fxNt2VpT?}6lMS#>x;<*iOY&3 zapBZCYGQFw0Sx-uX-i8T?cP~M+qYIw7VLr?$h@}-qR(TMCAJt)r2rkfYU%3bCR!R8 zpjB()eox`f*5$A^A~f)WX!kGSwoUf>X~zHn;X5y1A7$Tv?G#c{F>wvIOig>U{*{%) zJ^>5AgjfwwQ&CSop>n2-1c2AEsjh@fa9v!n#FqhJJ}E!jh&Ucq)Y#MpH?(>LjjE$; zV-|=$jQJqqw^$XGo}~Tp`NgSHfcDmM+S756#zy2r-!=?|BfBfnPYs zIUf#)hEJRwq;nTXsJDNJ#wI6`p>P?x%=qjcTN^7=3j53|mcxQRH#-M$G(yY0vDpc7 zERB-lLaM25LRLuxhdSCqrG*OfZiN)WHwh2_oMnkG22_uL?OV&~xWuA;%w$e3^y#zvoc*#@eqte{(VchFsk3fWv=Dw+fm{f*$r`?1FWt6XX; z3#fhPVH%zI5g35fQEQRQQDmO)`~a}@N3q9L{CY?$1^{T=bG36+B>ov3o+S=M5owv8 zTLJ@6BAS;K=f#`>Ft1q2XjH{wk^zWx-D2IYAlF0>-nmsAU5I)AqZhBx`3qNRL?r8p z{<^V|+JW6ov7S~;;J3Ea)5R;75OFX1MX%SuQ>L08fAk)@<46ni`Z}^hug`=ZQhur1 zk3Pyb4Uvs3i%W87OLG~$@mlH`6(d97!%zc!4v2_dZ1_!MIXnPq!vJ9S&w{-@W0OmJ zh;$Iu&W4AHfZA*azRe=qOle=WSc|K*XM{2?SE0g}*bC^5wb1MUsndtckT-rjrf z+Wwz2(nuq%+DJ31b6`Ev%$-}#jP5=6l<(ZK8ov3}D;<_O+FeQ%-5emrCV`!4P-_!I zKCKR>A~f)Fm>OS*CyqK!#`uRA(!u!UbUMQ7TE@pAWeopmY7I?DqfkJWC=x(yQmJ%k zZjdwpV)I#y`j;%sgkSvE-IxdddN3xl&rq0_Ea=4^i$que`7_L*6F7ZghQ$a!{^56- z#h(HUXtz&)62%S&)R;s`E|1B*C;hnR#SW+a_cWdafUYT!jjJ{4E+yvdRI5XK!6*q3 zL4XnwMnCOj#3U19jZ*>@Cnbj0y>q;=`-$T7DQCk!{=-Um_<=QGF$kBlcSg2N3l(eJ zA&>if)BxyoI>EX=BPSak|Hb#1P(G1;as}(NRFNwODB?+heoi`__?s%%mP7w0Q)nt7 zNfAdmM&k(CWp3fD@t%Rn4nsbQy;qG8()Zl92)_2cgrV{hfklu|q}>r2da-b;t$YF4jlp6EbzeLameD^=X8DzZ^AYG7Qs>HBxBPE@7V~0U%l*TxG*} zO;)C*5r5=US(%|}0BDSz2qeV1FpKR6V8obZGF>=H5&)dMh^NgF?)PNFY8T6Wf*GA4|wxNc0Ni7+S4X ziK|f+oX1H?BbtGgRut#6T>inuYUdX6s~+mnxd^M?nq% z_``7c0XROe#iU~laexp6=VR8nw6x&V__GUxyDwT;M8GdI@Hdzoq$A+vOXgyJ3O!k1%y%obS>ymH2>@Z{JFn&r{%yr%S^Zc=W~QkH=ic#* zbo2uV5%`-j1G1EA5ie2C*$Xw$%Ooy}Ey{w7HO+*qXEAF-dRMra0n>DM{YR1+cnWMG2bD}mq-Dnd4 z$P5qyjpM@H+izuRpk0|MM2&*4uAIGC>y~0xn*4n6E0(Si209-y8pJa<@#%*UWplC& zi2TM+6Q@9^Sfxn9wh&{I>fp}-iv4dxi1n71TK+dj@D6oy4haBpXa6Ub#+?9wegISF z%E!tx(~RvHe>_6*`=RdI+cF zK2apVj7$?|*5nDyg+Bp;j3ww;iDI7vr2Z!yhkBgf`mModX$?CklH{FAT~-aM|6|B$ zA#g6mod7^XKUX^1F)}{YuGecDD8Wu@YG}KuE^-94ugR1dP%Ii89EP_(Jm|=9CRRIA zz2W5mg|JQ8^GXET^8DFZffkMGa^q;jPftrmM?iiN4da;5Yiw*BeR#RTa8wUf>YNV_ z$T7^hw4x2R?fx8*9AoF1%2m3!ga73|rnnRUaDYSIedq^Z*4zC6LNfz&bR#05ozhc7 ztBbA2+wu6RGT4GS=A9(%fSkOD%}-sugRC1#zynyaFt#HA*nv_`0HUmPei{uDm^!+g zzdN=*=@@$)P^o0l(`|*9-`WY?-JxY^4fp8fDs$Z0|B07zDFC1uV6?RpVS&?4i~x`w zVqSpWz7ZH3w@qZpS|#SQ7B^|n2OAGVT@$Nuk_3Qo|LBEouePy+SCuN266VA>1hD&n zXXKbsrO$8YS6$zNinq9&qTJaNx8F(iPxR8;yP>9rkaS~<)cX9m1OQ^6Lfrnk_MaIo zHyL$Z7>7JS-utRh(gUL%k_!lwCNeB=SN9McICcqf0fzB8Ndf@-vc}?(1jpi1e*QxS zeKI=Daq~jN;>cb5oQ~N!HA;Ta++`JwAeT%4u8fSKZ?yJnFhk~R?;L;=r%uIC&uv{z zk}I1PY7^Q2X(EnGidzAop>@1>cx0-@n1bPe7>bn92Oh6!NgF*tk`-WiK z=jUO3B8D7hSO!T)2M4SOC0$&`FMmjSn$ejT!ckLff5=2XoRk!P=Ck6mTFD0>Mj!hW z`hIBDO898YS=jmc-k9`jk}Gs^t^TK1D{ch<9NSP=-(MmbNZlT`dd(H-J|IVJ{rA6LlD(c-VysZonr1@C{f8OH7A(~v6;a|LouDhUAm)OTU-W2nu= ztpI?202B4C6P4N7Ha~!@OrBv#Zk+AlY5*uFfXJ%J?j9@CgC3j!pMG_RTW_SVvBH5( zyUy|sWt$f#5diEn8lRc~gI)_+S^NZm$%#q$VB;ZB%H(WSAszX7AcWLZLf@>z#s3m| z{J;3ddS(+7qs*Anq%ijX<{0gNK;4d80f0vT`EhGivPRvlRx7#;$r?!31)r95#5xB3 z1GY?%gezde^u1zIPp{4FL~+nfEnV=(7bPJ;On!?|cW2Kqe17O6ud0&}`+jMhL;$b@ zr+1jv@EIro@P!24z4tU@5@YKM=_pA6A+axzo5|z9xwRLbeD-~uFP&!7K<M*DwU+hF5b0O%c@8W^x*dSIh1J@A}a{9=*aU&u~QSW=ofG;9I)y4}C`_B;Jf zNZ8o5?;O0n{)l@q>3bA&;GGW-fpvI7%30y)9%biD*7478uzBZE7`EC>Vvfo}`5*<>gu=aP_ma0i z{f(f*>r*dohT6KSX=P&Vgi@PM0syma#9H6^^f#^r0D=XsZW^!5%CI!z`-!Wm43;d+ zf!lAM4+aCjkR)QNODLd-nE|OSQk(Yn4Kqi5vI>;ADA2j*-q;EIj+9XmiK3VTudY1+ zqoZTIdzh1D;eAgm+(3}PspvanF!1vOps|gjGwfUMe~zhvh0)%XvX3~f?IMabYzHKpF zwlEuIg1{%0?p`aVC$&u<#uN>xFve9(qP*EG8SOp?6B85Y53mJJ71gnqq+^tEpsukK zj+`U}QT{Z$Fq8jHF>nW^_034v^L7Mehqi0aN$5j?Sfc3S0IB_H=l|X(N8#Y%qtVs5 zAbTTIXewoL4cY(WURI2I0RZ2Dp}wK12G$P%LD`wtLPpM3i)O%Gw=RUV0CNS9S2A}| zwoo*Hh(sCyhVV5sr8wK7o#o?y{sfB4TSRPKsyY}BOyT?W{F{4#1hrsj(cG+HZj1VR zD6KEWg%{>#2i4bc?AEnof0a$O^ysmU$}iSES#U|GktJpCszOvFEb$Z zySL9u2ZqcjsJK%fq6I$oyLCVgXA(u81Ge2i?Sz8c3Ym<0w3E9EdXXQMustxPXb~Qk zzyA&Of)WJ1^7b~6i)(okcEE918*4TFY<~%?UB5A|7Ju}*wyKhI&!hUkKMo(=v>_#g z0C4E+Xe)wIb?TC}O_Uy(7Soc_7TCJG7#drgI|`*j2FowYg?sO~3}zLgvyum}9~VF% zN(~&i3&Awz`#WJp?15pluMrsx8c*GUuC9Lg(c|l(f6yr`_^VgCE)EAyU8sk$ir^iG zGt*4?jN|(!X~8@i27@u^2XN|K8PwFZ1%1DCV`v!F?jOb=|pyZ}gvJDu+JK0;86l@qQ2R~nn>gV|TQL z);5H;Go3RtE1HKGa^EJ0`=CPLvApp?GPnL|8XQUb15yf|e7Hx$aA{lx3 zmmz-jrX3gHd`UC<2RIi}BdXsSL14{dn2n|ZO4rLS%3=coaUB6o*biS+QQZtb{MCA2 zMWkyYap1Lg_k~n5K>*J!3=OF=qz;7P=1I&6pps)GKwdg;NMQHwz4Z20242|xy!2go z4zS66PM)uW=U!e1L&I@uZcLK*tF)Qt(EiU4zEgOXhponh5dbb!jCNZ`r&?3=+6J<- zQcSWZqEx)+i<@903IdnP+gt?!)CgRSrh)l$GaR>ytpRX^0-_echz^YY=wfLt{P_1D z`70(}7mx##v9YziJER&7=p2`xCayLqqz(#nGa>pWquER)=h)SPsJpJd6;7Nk58_Vg z7Cszc7ypYGa{Skq$=Ar~1kIOBB4}E2_pdD3pm^}Fj;H1q}0*Nn3dr> zJD_!V3{Dr-!zbI$!$ou$9OI<4RCAzOQ*lvF7ZDY(7a@Te!!nv2%xFIigif8Ugx@@~ zC6J<0838!(_WJ!H*OG2AK}vFP-ti$f9_9*+DQeK^0(+Nzb?uF{+wpRIIidL)Z@90q zi+_1_2R!xcI_U0>?`R{r%5({NoDkuE?7NgO0s#5}Oty7RA}p{yJuu>u2STIh6L9WA zBYcX20EG??p=Z7e;YMZ+;t1S{fI<{Gpx_7xF6Dy!LZ*q{0h`J#MXy1%XJC))z_E+) z^eelAY*o4~P!7Dm@dWhrhQ2Gx&9Pv@o4}rJpnZ$Ras&xXj(|pEXnp`RHqyH6IUG3j zY-l=%Xym52ebVZbP~Y4Ge|l~`w0FimDlVv!voFwVB`#N^tM>SxaX9?mcY3taWYBdg zm9l;U0Mekhf1osqPA?^;t&AXW^kgkM!8kDls18B}q{x8>?^z00Us(tll+jI~8K;L3 z0kJN6SOzph1WG$TKMgPc{Xn3_rE)wuFoBLyAABNAhQ`8N{uuc@yBih72@+V3`Qr2P zvIObR+I72NY+~H6KLADaO1gr;Sx#wvTiXWU56^yt>ByS{zcE%zCdoUM+RP%f|2v(( zVs$J$`khb$Ktt-fw~AaY6bW&6Us`*3)f;r=H&gwY9a;+*QH)CRgba zPW?~JODF*V%>dT!eo7B)3kUS)2jJ`NYVR6wTqd&snFspFUeBQvy$UW{kOlW*^Z=O% z$a#>D)`6|cfe45*nAr^ym@VV&NT_sgf3y!aY(49(h;%*(4h&<4x(%C8265-~TQnLq zWTyG{Ntu3EY@VU_QH4MLS;daJHOK0st|#v3;^EJIlm8=&a1Z`~W;XkiPyAhhrcC z2UXW~!RDP8VduU}Ku$BG7)`(>G7+p;ngaX!o{NMlm`a?5!8%$NY|7ul?U z-pYD9&p@dDeK%~|bJ5df>0A&Tc;%h_Ffuk8#GTV`v6xaAyZH1g@p~p&uLQ0$Z73rh z#;~-HHcF~cG!b}H4<15^QeOUhY~{V+$-D! znUifoulT_IQG)Jxl-x*;fcP6k)0kPX9&P*V1au?Zr(Z1q$6O~^<8e??5;{o?4vxV) zA0C1eCr>7nzeJK;*{D!kNCS{?lyCyT(D2kCrU!1s^uTrWv@-*Y2JrM|a5Dp@x@86= zAfdX3F4%v#0zN`v;P{z3^rUt57FVFyT{y!GH(fg$od=h}$}7-$5b+7zQxKr60jw54 zt5$#p5xIh)iSY^e(~Db}zDPnqFdPI%>+|wEd+{h75?W!d6IUX*Ud7g3Hb+1uq^IWy z*732iF?eg;=hJ>W?hQ!mzdJ{4YX2cCnzXi^gnb7N;s#-QnRq@USL(_BpK$7bdZq~{ z0H7bh82SNJBY+R%2$-E2d}0!=06=L@y%B{HR#de!SqMMfZVM;u;bi)w`$!!+Z)O^- zxp4v9ch?fQd`TXd(QLp{cMnDa*vV)JQfqwT7epU<@%UWU34O>8urisP#GU4u97s0)^Uj~a8W@C;5r@h%|@Gfiw&~30< zN8p!FZGejE&OnMwW$YXvzmAvQ{v2orD-_8v_P-#`lK{E%Y6>F(D8_vLtSk@``hYMq z-h6i#IeBTBe-z)57-uF3Y{?-1CP%aRz3B0?0BamN4B9cs@sgWu46^S@@8neJZznEd;aR1O$9r^(p%8hH=CkAfl;^Bz2Yw(P{9O>*w)`Bw+95 z9(#HR;D3I-4thA|1@860J`1-S4y?V%l%n1Li*N09ap)AHCz)wxAZOG>B#lae&Vc-c zW`BI5h?iI2-j2Bj6#j}P>~&iFDXs7B{pIk+J0GDxOM)5wIZxhh|8L}+N~n(sDFC1! zz(jZ7WK~8gn;tlL0idJXi3cD=We3gq9z$rcp{s?2PLR$Ry94Uv_-&zM_V~HWadwWO< zO=>Y-OPKV(?tDnRna9q7i&dkS5D%akZII*Sz(*?lV2DW#Y`@W$BVaIfQ)a@Nx=z@8 zunawP&w&FF$X*%sSb+nWkz<5wSI&fc?^q01E-!%042mQOe9CKT>3~On`7RK&ZEzIq zeZjRB?z<@W`RniR6{Xp^1sV9Pf_wKw=}?#|oj?IVi})FNdBPw9x_6rr1-WPe0{=O< zR~1*^lQDwmAZ{vNdVL-G6$ux~5~EBSQ|a>#qy0Z&8IgE1kNr(OaAvd<6a2O5k~K{V zxg0E(;2i6^P<=jW-<^}aUMX-gAohnfVjYM2rXI(_UftLQJ^k3Wy#THEDkbGwx4^2a z=fGX77cnrz6iE<>EKpO|3XlHc1GYKo$l<`jqZLqH+sZj3+{amk!ta2E>z#Ntkt3i^ zqk<(13q)z8w!Q`SA1ijz0&sL_qO=*!l~QIuTKtb8M#&4Wet;Ukgh_et^|8nl+6tLm zQ{%Nj0$fN~0RW$^6>&rA%m$r3956UD0IBS|I9UBcm;t=KcYV0v6EUD2=zO?scQI_k zNP-fKBp4pHIU~|ypM?KuP|Y_RvlHHk7z4LqXyN?1nV{GEwl$Yl)WeS+|HxbO33+~K z2;<8?JSfW8HEI>41_|sWN{7N&DdPx;PH6eLCQvHGS(@K^cLzEfN)Z8UTHmw9P4L{S z>!5!yAsybQz1?!Pf#P`+GX9@sM#2gJ=m#*>+c!~`k#1xJfUJz*VS%}L0I7CFK$gKm z9=m&;SO9FqK_6xcJYU?1FvJ&N&%sg*?L!!1R9TbfAPEYjUM7^ed2=%0=IiFd^@uz; z6JdzGfeD?xPzk?!dXtddj_JC#?kR$nwk}b|U@;p&mmD0ZlPDbubER_hL8jEe${@{e z{<_=R-UZwDoN>_t_`4?_uT%ELOBHSKmzO?3K(YRK9AZ#oQfksDmvsE+IrhPwgcSgY zCx?ngD>71ztvLT!Q!yk-gGh4q_`iPqaZjQ=M!c%(_lSbV-?Q7IId;yQRRE`RM&x?p)AHZXq`#prd7Mh+F2 zz4Yl9KEU}Q;h^rlKeeQ!A&mb&g7$xL)KA`9j;bpOEdUf(jrCZ^rkXJpy8&>g%zy#~ z@pw9WxPU;x`4R-uEG_(G_otn`UaOk6aI7W!=@u8NvOMg;-^ zSkm*vVlnP`2VmpQWB6X<3!h-5LK8~^`VBq(!bi~D+MZye_dQ&hLRXq3Q`Y$|75`Tg zS^z*Zz$k{o*P6{KA(;UNFcs;r-J@26Sp9mvu(8eD*N`BAX(}Tjz-aK_&V)Uz@c5}3 z*s-Ss&Y>NUd=_MQzs*FD#H0XCPfdY^h(34~Y6a%ZOlNYoZ`^qT-dcYoAt8hN2duDl z_Zcqh1^+w~LjV(*$PrM1fIV{xvPESBlPPc_aW;As=TB*U30vf;=QcupL&Ao+dws9u zDkJ4OPI$&7v;aVFM`zzeX;y~GCIEzx9+;aMkbwpE`s#&tp}W`Vz+*DFngRUPOksuf zn4swB$!hov{RK+VF4#9{1;P)I;e`Yj;I2SV-UWAQ1&W8{p=&x7N zdHvOQ4`OySr_Y5Lz0A!PE*qX0{R>B(N<`wEgOI>t?$3u2`ufuyM-svpfEIruf+_#Y zE88%7rOI|f`qza6s^rX*sQ&MBIhlZ85?%ni708_SiK5BKw!bMO7#a97D~(nez?M)Y1#%s4bAA zHh|R(Y~FPmii+#)<^U1`;#UxO@y-31A5XTho=-;4Y_icNI^lW+#uT-vnB>&P z*w{F1+;%LkwE(pEcVkr0+6_nG^tscJI7SbCzaqI(o3QDB-8q%;GZ6X#jQ0&qR#{9b zOgw02kl}z#WH>t(-Fq(N0p2jf6^v7{M2 zhX-sh#5jqr-+T-%mJ(_v4V3=eGl;~8P;la01$SLBV38rK#WvOix!yy$QM8? zqBe+je10NsGX;{(f52`dM%WD(J@)bQD!2$px)5mMc>##Cea2C#1z9CxgD; zL4?zGM(o5gzqoxT7;{F$vEwWXIiDqJT3;?pe129XXCFfQzXXMdT#^I;WWvtF!}VH? zx*hGZT{?uz48*(M(;L>_<(yo^L>uk%eb@ae9G1Fc! z)QReHmP8c*aA$7&*^?iDc$x-wEZieu479X%!(+ew5K#vSA&>2eyAZ(M#R>!L_vk*3 zW7uRk2n9~YVJjSM#X=iNQ{qiUN%lpOo~*aGG!}T|-bY&e70M)p1HS<4KG_8037po~ zoj2aT%N06O|0jI-AIl%{esydMF>pjbfXRWu$%-^fvds}NBRFI-Zff8(5hDS5`kX3i zcSOK&vAYlZ@UtgBgeFW?>=Xigway9x;aOmP^~Be;bhF;qWx=l5*CfH}lPdDg0SB)U z3n1MRT+R>gwMXU(jY)%Aq#d7?!rwpmG~v_w2Kp4_8p}m2+Y)H`crPaj0QM(b(J)b( zlVxd40@)O!0kAs`@p%Dy`$k|4Qw$R6F~m!Q=}yI<-d=k$@j!NM zmlXv3y<)5|;CgqYzFABf$jeF0jE|=z$Tcg7+D@= ztwr-^MNRN38P3>0zT>6Ee?MY(yz$OQFl>!)Yy-Z$@l`%auBun4Eo1_a$j$+Ovva~Q z3e5l`eM3{VMx)-22N0wgfa3@#W@4Z&dn5os`Whps1)vwCzNrmffA;|HMBKvy+1Do! zVSp6`A}gFz_QZQ{Um?1EegGG||N7;7n3`2wv_bX>MB7m%SArUM~+kvo(MB8@D081^X@h9AOrG{D%=p=t~fkj84kOPS?OIuYUl8Ly{vq9~R1$dRqJw zI{mLZza#;G!;yjE$@0upLmiz8Ha#$9#7r*}-baaU4na^s+mM@tPvvRY;Zg!!?p8a$9%W@tTA8~{h|DsDx@cirR z5W+h#1xF6+cz=|uO=nU4FHUZ6-o1&A8zcdM^Bm5Wj+SL+nwn)Y**GoUgp1FYIyfc( zix~v~M%?FHi~sUH=m)@(NgzZ(wm!bcG2Ho6C|6x`JN)^T-8>dEf5Jdyv;kVJ0{-iJ zt0B!IzP)&0U>N@P!5;X=f4t0OUo5ZgaQKn%eGbu79(`#T^}bT3U>1DZ?GK{5x~RAj zo_b*`{O3*%b*x@ zcl`OqccB;Y3?%mv9?Y{9ozSG@@8Nw{oZlcK0(b0j?!cMRt~;*PH0zQzO#}Ty`II5A zxwV(qKt^zq8yFg8oB+v}z+}`w4`%Qanz)ew;SL3K9}77lQB3;;`XZH7)We@%+zJ2v z{WYLQFXh0=UD#q5e>%Ef3%u4~OBUv(!f*cPA$a1s%~0Ri8f-NI-`CwU0H1C@j)|8} zKyFSNEL)ThOBUpzCL#-LIVuz=ONO5hfee6p#iH%ek=_L|2lb<)V;^S9tf}jO^CgvV z_Ch7XOt)gJI{Rz`sXeqZmo1zJzxdHt7;qdhoi4pC_Kx1uqVM>^?*jq-)YNsuGtaF> zFs^>v&%v9ujbGIXQ(C6bRLK=r{mfU(W{%4^I9T|Ef^WCq|X z0MO_=x&}}HurQ2B!>}?j1OUSCAH|3R6$%-lBn1-UX;GFzB8p4apqg^;;qyRP_uv2K zjXZ?_Zf_#L*v+1q3NQZt>+k~R2H3UlH15pBpLJ=Gs&8y({xl-^DD-&ae&$|@};4Aq7 z6z19l01Np6R10t8q=}^$0fEx7ezFa!Gzu8S41bX#G6RrpSBw21+PeEB=(KA1=9jMy zK?sP{Xq@e;FhuRq@7)5oUOylH@Z45t?+mXAadL7J+B$ofzvHLMn2xEC&zb2aMi^MO zC>JhUFawNwoh^eSW^oi+Q^4--S+3B6IyN%Nz|VG}=HkrxYB+VS5^CyO5HYe3CJ`(u zk~C*#KK%5DUq!(w83mIu0-@?ib1*8R9Eb2x_ zK(>CfK?AK;ClaApZF;MB6a@es;vQ%;wiLIt1bn#p5D>KTSMI+O$Q%;Ov~*z({O!-* zfoEUa3%d`V0Sd*86piAd#%c+6?X#hp>XWsQJ0lIv1~cHw%L`#{K^BwFnHFya?$m=7 z3P>X{J~iQ3)JxH8|H#QwwB=VLA|$4e7TJc+hsTsu=$jAU3HRZypHf$oRw9rE+g}@j z)FH&8@7@ne>-)?L8=Nh4}~!5&`sBuHUN0C0QICDmj7*3qe^6ig4? z(>pjP3)2JFHt^f^b)n6lCQh1sDNbKWKqq!Jxm$A~ePxpZQ6)x0GPJfuP5>Z302(i8 z0O&~Lr%Cbc5BFi-do?_8*Rqht8Vxs-HsFWfzXk5RWidSd*NxEGg(}%7p#k&?zf@Mw z{H@=36tN*>U@;{_L2d>tU6=<~EXjx2Xhv`s#2CR~YSQt(I);PKt_d+7PM)ns+kFjG zRyAUt`R=gV?;U*!;6q_yENkVT)2`k}Srxrb!)r|hE7y}k?K zwkt$w!*k2=c~&OZlpzK{-Q2=i@j1W{MkNUVZpmEIci=9fR9`fb0@WK||MKl{=WQ#P6x_J+51QCT zpH;Mbd#WMI7C!r@QpwPVXb4_?XCD+77coM#;|A&=HctOZ6gUUuYC{Q@ZVvp8#3?BW z03M!oNAE;wR)(d%x~2_C0}u?+kC?>)pu|982mstrKy!pc0Y`A*K(h(6CE^MI#5=MH zKmW#d2EO!`Rr7*jXH?xoLV#8)gMa(>&9M5$h4B05H$zt!zi5$24T#dl(jQBc6C*G+ zN!Sgsx2WXj<)B99L5!NPFb;{qEdJa@-`B$K>l(|;P>$p_L5qJM!ilf_@EDvvcfuvD zFWb*h+DO!U5&iEl+W-9llq4j3V%sn)(j(h>bfm^)(6!?3VjxAY0==Gps?tu31Yn63 z1laFF9Y;~^K7d}=M3llfl9L3x<0p-W1~mZgL>}}{y}SjEoT_%e6tmw6FW~aUGvK+$ zzYeQ!xZIX^K4wP~I~`ZZKoN`4$x9^I!bwcS25fB(KtE{NII06k`7HA6ods zwfGMWj$u~6Q*h|W0q<#jBl#nD;ZlrzNlF^fq!b*Ck=};Ao00(Ff#nUY<3mH%sX9Z7 zu8uATqXi%v9!p{g08~Hu0nlWnMD@E~uVI@E`;i0y+r)~YfNGl~pnE6CdF^-4ZiJIX z3|rkV$Lx1{zW?#vTj6JqehJMz!9hqzYVhQ*5lfVloe97F*+0QoA6f;vWF><=5$MjJ z7JPQmcVG3H1s@qfJMnNV`Yg}co&WgQBy8Ar8g}j7?J=z{241qah%S&g8j6Dta%J)* zNK({G_J7Y0QxX6?@CE$UA5k8uZ#Y-}!fztoR*JLB$gOCIOFU)LhA1}?xHro;dp=YsUmGx5I(`l|2 zK&;Gw-S(tF)P5xW0LWo%1b6r}p=s@*fS!0l=O_`*)30oY;bEsO+FRLJJD;0lg#Z2b zJJ2?LAEH3=7aU`4yy56?dRi*{`myi8|M}+api;|yLi*A|kFTI}r|+SI%nl!Y8rg;4 zLy@qZ(jZ#q$>XC1>tI`@bKlDyXXC;wI^}^70tp**4Um)u>z5 zYE_q7CC9~;f5IJZVxZJiaTWtEBrw4aN0NpSh={ZRIzIvc^|7k989v=n6v^(2);5I; z-hShJ`17y74l9?>XLur7V{u%u49!}%-nbH8e)`|wip2#mGCtn|Gguo_?pv&|`NzsxUm3luoi7NRoG|bTdw{U!~)8 z4tV&fOfQs(XV45VI%uWzz}h+*e`W^%XaGX-Vkym{V?PRd?86CUl4dj{lS!O50`2hd z@E9|>YIRD7>ebi1n|7XxODzC9=A0}8{PutE$7q2&z>t#6o|cXQaKL0T;;#SOm{a0D zMnyh6;_Adr>mjr7`&snarlMuhXZu4(IbtavJ5vjl6&JXteSeP5XiVRIj?h)PO2@$e zO7?&64?q$Cyz>SFcS$ty_jP10{H6(`3-z_fP8e~pHj_Q_~XwX zf`9$i9iUH+tfnSfxvkfyz~evp4*cXlA4E+O`jnu`pWY^R;rG^W$D)rQK+H}*1}wgm z-TlEBAOgc+{7z3DUh%7bgi(rgHzr2Xf<0lq&c$(2n?T~?8#{`bu%NdWM{AM^v57#x|ZF&dMZXn@P+XTidG znLh5AeyNk|2aqbx;><+^GzH%iGBS1|Vfroit=%#zYO<>$E%4o#M2rW)&*Vz?Vwz9bi}TwdU+m-A6a z2mZ)eN;QcS3BU~nG#SJ(Gq7XubsQQVMU|~>%SLd(fe;}C4h+R#IA9=SC*Od#pZy*z zm|GA`lxZMML~R-J6}jfhCGhUc{};!f3zTBm8?kDA=IE`Ehx1;P2Odg>{}fHU0|+)k zIT!qreU>IMIZ&a;Z=G>oK&CPtNBh6c{u<|F4P|)tS1Xh%;``pSW#eU;nWiSR**RvY zd9%_mKm1%K!HvJs)9!rSo;o%qS%v9xmHuwwecg%+?}$CypgDq(0OXKJSpjL1(rG=T z0lE(^(I+&cUJHNy&BO3--?`H(f}%UmV);8c?vW3~FMsrH_%R|UPIy_>nbY_lh4g+z zvjl(-G2o2SK2;*I{5FWz_bIhLcb90j$5F+S0O03g?mcPknV5n`U9!eSos@-H0`ItW zAz1u{_u&lYFazL(lq!-RKsV+NCsJb!x+cKI*U!*{x;$tlKL9{9z`z6@N`0!=1*R-f0gIE6aUEB@l#Ym3>?`7kJYryhhoRDJ&@p}h&29a-qsJ8v zNv~7E?KdreIkVEyB;dUw$_&U-nrMJP!)y~lffhM#4)KH4j_#*MgtI_Mr`4g&S9%R8 zbdqvT{O+-b;Co+RjoJVkmO`v={+Pude;iu14u0@255Z4=_+0f;v=FQG!hC@OC_fOCsMyMF5H;svwe`6vG!ZoX~_!t}~f zLzIY1zUxyPN6h~Pb2CsQh?@sL#QN3vDJEUEb3mptQ2O5hoGT=jNdiCs@4&t@qZJux zrY78_S&bA9FPP6mJ>dX&p)AjH8?IY9(?uI##YJ(DSr`FSIJp}{0PE>NG(dJ=Widof0I=G90LUSbvLO1R zj5G@@T{y#6k%YcxNY=pDAG!*D``E)M5M&`QSRsJeLZ5AaT!DbDM3Pu*&fJ*=@XX`? z0uSDE6|?InFMnz0@A_oO58$dR3L(u$3Tbxt&mLWCkiNNdK&H@E%9SaVlKtPEGoHUo z0)S_}OgoF~TDjShTt^o&(E*SlTye`Wt?iCI#lTg;7v@{w9@GZR%ujX9+3lVEwtMj3 zG~5jdO%vmj&~JwnHKKq);3|Yz45Wp zNpyzHf;n@Sx`=izB{?&+gaYWo8IND&O8t3M|95%3D4m?f0nY0+?V5Cg!-4Mp$Qw zA`(Ob04@HsEA^!Tpir#b(mo3AHRAXY6HFk1-Tja_l#CH?&{{$3b2E8 z3Ir&N;$`o zqW%1`MKj>}-+coe1+M~yLVZftS_RxHVfS+f^;E^L1Q((d0gdtA;YB@L_fGxxZhlfL-m zfFuC;WRLUW+9#Hcp&vkvA;lIBn2CM>o~Wm1m~jw1e!7mC2&oM1hVydFj^d=E55z;> zaYKL~x;Z%k%#nlywvHpn$s}SDc*X)yV(jx*?pqPysH9@<93UsbfBp8&@B}*7WoM=_ zmu-T84LHa7*(MM$@`1e)=Qa1T)4$U#+Uu}L%u>R{#N^Ked>-W_Rzp3W0*XJiUp8Dap`N%ntl9GZS!dcUUg#Os;i5hzbH zr_@n_f?SK&a-@~5sBVW%J1;<4RR_+Zu0?5LTDmwFt&AT4 zsyDr00nA2K@xnRe)Fx3V99Xs}2NMf@7yV7H#COTFwuN25*@Zup_PF;du)Qzce>1%H z%)dfrs>vbja?41c1LFW`OBT(|gq$o}*eCZc#`zB?jOY=oxV?+6&vKRiG}`~2;ZV`l zy{PPsBmnSuEFIkwmvXXgegJ6{&+kV+Iyy2AC(qQvmd`FiQ;Ta_>IJjY;l4YUz`Qw` zf(Zfxn0k23jxQR?q(CG9(1N2>%BDFZ63gzqRSNy%@!I(<`h$PG6;loW1DctfS|-0Y zg;0RB4zE1@AMj6)+!}SeewSgzf(igTOwNEaXId~L;uRP%;W7f8W8A(=U%hew(f_3I zKd;>Jd_fWbJoCll+1EZDY}RPhO=^|0ivWNEwdIqO;NC;!aIv(-rC#zSSh*|@?!Rj( zELxC-Ui02Oyj^bd`h^zVUJM5$(reWr*XOk%KNrXmkorQ-fbNKZ6#94F%2|GDkS_D( z0Hvya=h=TmcxqfkDASGzMD;#&|4s0>Xa5s2(bhkPwtjJ15~%Z}d85B5C-UJSXTUjy z=|H(5SaR84&<{!??;J>y_h}5X4|y*jUGc<$sQ&$W>To2_AuRCZ$mmqH(GCkdcec&` z8fc#?YdRf8X>nwU@B@~kNq_`_70dD=B?J$Ct{*_85CN^Dm=6GTmh|T?)ZB2*LNKK` zCpne}(lK-n5Tx>d|MP12(=Q&uNE6RKD#5mrk)8_A{o(uYy+>|i7Jl;bj|Fo6%A&is zl+0^ZK^iQ-;wnc+U1I-ZgWZn4hx>zEsV%`an6Ik4V$*LCpLv;P74 zIT=7sYZ3|10s5_2Qnj$+@|oaB80!4#pduqyL>b|E6G)9RWy&$M|Jy)F0=Ypd!*GD} zib)&^gaZw2<7GKnmWCvdO{rCK-n;dVE+-(+80G@siIBoO_g#X9mL5m*WcHhZSOd4N znhRe*KY_&yvoXV;2Nz+-9nKHEeNKTQQhop<==(s^6>Sm~9N?N2OJ+fSUU=-(PL3qz zCl%&l_)qi=_{x2&LSn!-A6^aL|K=KemXa|#Bt`~s^CX7O=HTzXpkSV@G+*gIfHvZ_ zh*F!rBVNZQ=C~vQfG2m?ZX4>7%M=Zm?v}|mwP+qcZ+0#q5Q_-d-Z23C4pqRWT^Hd} zWgCoQEImspIyGF5vEL7%dEka?XZzI(blZIZ=zepAasrH@I&*@Y5y`e`Pj^Z_6L;LS z1SspBMB(882?5{u($$ci!Ov91_HOpfTzKH_YXC#l@lnR!4m8$!Oxl%P%q6ns)+;fY z)|88XMGUf^eT_svCdrlUYJL7GN&U~C)zWOtlV_A381VoqjYbfnF` zrKP%N2JFKyzEhZ6;FGQA;5Z%$-=8Hi8RR3D0BHs8yK4zthFSrw2lTK}RKE{0VR!&W zgO;}mfo(6;Bjp5Okj~JoK{P8r2eZOuhgz+4KTi&j5b*UcU(e?ib+qtLkE~`)II+0+ zI|dO$A5Z}Ras^_B%&;Ix7RS68Ym!Wuc`j3EE@G2yVQw}qnv(>8ARZ&GZKav1w)DVc z25?`!<24^-B6I>P&?Zu0OaL^#q4+=yw2b8y!{ zwpx~Y?Wuvuryx05&7KbHkJSs8k`{`wnjA+-3rmhAt&nJ-rw52F2F5|ezh$>UW?0Px5b z-zU$Mjh1I-nj0}JYMe^WnU&%D2F_K2$?fg8?d(Y{ZuSG%L*{{_C#&J(EjFz{HwWy8 zCb>LZ{BJ=a;QqUp!V(k;Xuzv9eGDfDC=}C> z;+nt0U;BCJ@JEvaEl3iDj{_t$C!>IWC58v<)zb7F0OzNax(vzw9{{5~6_MUBPh9bH z?ic^Oco5gNTAfzaOhxn24509^llN8)*YiKs5?Htvs}F?8sij(&e0&?Ef65@c1YR06dt*EsE<~1@3I?=o~Hn zxibRyZs(c-_?Qpa_B%QU;rOW8BuG=jF;|O8?s{UJLPEvm^in`Ft_jt}HXd$Or&b#jJwVfNSdL8gP_lAcY;@ z_n16s9K|r+`a3Z-6aRpZ2yZ{PO7!W%DfVUoxq8|fIzH<=w zKTXQ|=Z#HaIZw}GV9*(U%54wpIchH9iOES;pG5n0Z4Ezz)eMZI!16vDgdt! zK&D9E>-C~^!7m3q|N8yjCKWhYVy#S1H8x^c+X!(Z3$p_TlGB^Y5?Bw5*-v49YE5Vm zz*J{f*ASdQV4)3L&cYGY4zTspHKI&RLA+|^OnBg)We7G>049^TAVF+O;9C*+M=QYi zY@?9=4Tb|H<*m?%AXI#)tGfsK2b^iG`P?g&4V?p|1)y9SH{NhPs$pVLj}bZ#0;pj^ ztf(e0rkeGE+!j!o z3B(YnLjQn_=0E=p?RaconKp5)n?7$zxE#5h1{ zncv-T+=k1*qZjm!uKnmB;{-^dX%dB>0}6EBT)ub)XeIu8WIl%fRVy^96_WkmBYOfm zDG2}pvB`6xu`wt&Tas-802Y~5h$(^toGUvzg)swo?ip$sdYrqrGer$>@OTw8x4Wu= zQ&9`Ba(O;{5hDmzUQqz4n3r5ABImz!ivfXvq*qKRp7O1Sd@zopR_MZ|a=y1R<#+Er z2^Y(on9tlW)1WGoZsNrO#teW_LS!P;>cmOy&50c5)wgIVEK}$x{VzY&RZw@9Rx&5Yvm`!dG%$=P9Dgo5?x!I|_q9c5bN4mz317oNO zDy{5*XI|O}W8=Z8kjEz`;E88Gz=)G}#u-o&0{A_HkV@l5f(2m8$Os*effjrEBS10s zb4dM*f}3SZ{by0ojhOC&Bmji)_@4TEU%kou52bqh`rzkJyba_>5L2dr;A9>T9Ekj>tjyp9 z0CvG=7JXDr(tlh~A=1*tEF~q4XbdxV#%%wiq~nqR5W<5XSYY%6s5GSL9A~Q&J$>T{NPf!2ZLP@bUKZaIUx!=pL46x0jDz z$TwU&2X4Q`rWN4ptVocil%iIm@Ns~4{s)g%!sCBg5B)>JBuDG(AAp}e{stUBU4zM; zV(59s@&adnWoXehV-$^ohaUjD@F$@OfeG^P@W^6%@kids{_GrwJ{RoAOk$}YAl+Rd9yPBcFDy!(+02$b%Y8PL6KFyDLa2!S*$ z7|{`s4~<|}K}txpb59BEKZ06;hHgymL!t!z0T8}CfgfFa=zc;`PoLAoU=Bb7;H}+GG*xtV)j?6Q z9P-0vb_ z)L&SL1i??Y?Sbx|0r>F`zJNG2G8h#}>kyU!MR6kt;{ao$lZ*y{7Ja$ zd-0n@W&9^-ZJ|aIQLXoS-rh5pn*U zOqiLU2K|Fp7heK@^-I_JaiAL0ME~)HopAJ2_!fV*ha_z5J9Gkm{P+hjj*CB~#g<4Q z4sbO2By0_%v#<=4G9c=xEtZdM2Im@D_-P)QMCZ?VQ}eUKbU(@!xCTKoS6g zr_k`Tb-0R~u<*8Gmn4qV(hH_*b5f;ZtbunRxu zYTSQRXiQt9qWCrxt{T{m? zfDFCz#K;ObiMx;1)_ypTVT5NdHb858ALb#jnK7#CI^7^>Nn7C582IbEblo!t&J;Dm zQ!j0Us@ew61;Tu`vbqKy{plM}gW4fU2ndn+oZUYwu(vf=Gd=)Qxad!yP(Ow++hpHQ zNI#n-dAG(m|A?gi5Ai*--!2IN!tfDBCZ;Nk#^gGxuDjQ!dZ!=o<(K99Y@w5b2$*I` z@mV&Qi`_jOGl0>*V`4CEu=iMp$DpCH8;UQrKz(BmT&irr?1Y}u&`N>;%QL>mV`pmN z$rnC>rq*`8w|Xuw5Kco=OA9>q_?s9x(!dA--m(mO?nGjrr8S>1?Q<4?;>b8|`@1lb zr@OZc{Q$;c+^*(NWNyio+H)BG=Kx+yWL^j{c1Zvbf{R!O`n;89q#77M06FfqSrDGQ z93y04=$tniz$|UnQQMALt5!fV$GOhiJ$%kn=0TQ>=x7)YA`J+c2+mw6hyMOiA5BVv zfDh)lTq1y<&kvNtlh1tuojp#okxQWnf9b&tg8%QycQEfnCF86nmt2f|J`q~vqLd(QKn4Z9|PTtnJ!8KfIyF`vuCm*E7M$$VNnwVr(0Ih z=BW9q<#~u)<(bu_#^Y)MECTxh^!7Ui08fa3j!ug3!D^jAgu!NqNx*v(l3BnzZvYV* zM`7FNCGh9hKSjq%CoY4pdm?phaCjJ={OelSeV~Lf_DDj2>yuy?eP-czy=41lxAl|u zaCBrGPMkOkc#z1>0fpMKSz?B89Pqp<30Hr@)P#5dqv-xtrPHdI^l1d3(`(1$r(`^f zd^iMlbUL&21)5AKP{#ISA4ru*WER;3;HgjBJ}+|90?;eR{&~7XI>+Te4+?M_ww;An z-rfRMYitNgqquW@?$!11$+j~vJ~aV~q{NO8@VDsGCV;W$+ZTU&{-j0gMD>4NeQ9Fa z`nf*>`{yLNvRPx8eO9vndtgqGrz8O&NXB`(ZG38~%xtpx0ig;yL>!+m(iwH;)Nv|CeSg%tvSObnuh2cX@Lj!ty6z(OW`#S*!z4hKk?Eg_1o18$Dy2KL#*oEKQlX5KlZUq!*LZob+C(qTx z5N<0ZvT;DB&`|ncVG>+OOl3GA&AbA>2#5zznweo_R{gAu6lgzUAJP-32wDfD|1bL8TUS`q*RBilt$vH(#Ga~_|uEK z;n0cm2(%R210${WXZw%BZ=e2zfs&3#O@QA;A9=v;_BmR9j}Mld|0c$#pdAIkrsnX6 zd3qIGJoYxMC#RI!^i5%HF##Hr1ONda^8b8m(IozUD^m1Y=9f!afN_l5J$kYVX8>D6 z3ub3PUakW%*wH!{5XfQ{)(pTE0E`jv18D2$hL-kT$5^Eg-Z@Z#=zhQb%N96)sUl3a zUvFDg?|lsB%BizO@V~!a3nc8ui%$T%@O$HjW6}4w>T@cfbj#fc;eGr}Jy^%WKRtWZ zOmO-biH}KgRgGGoUm@B5h2?(u-!NhOA9b}M9zeO-V)Fwa0f2}W_o9+!M_;d5Q3wXN zkUZ85KyyV}ny>CdhJ>c2P32pE*v;l+yqZN^C?s!z)!SJ#4a7? z`K7$F68`fiuR#|sa2k~|CdbGw`o4C`?848JJxp<`x1$5+i*tJs=`WZXLc7P;xI(Kr zw7caB?QyjK$GAQ$G#-d?wKRxRjk?uA^!*DgNKS!Mbp%37T5^nATK?nSax& zIiS)wb1`*v+psODIAs$QlC0=ZG;EJ0A|HTo|HvmWhP|e)0UDbnEr7S@dgN3s{PDR> z(9qK1tw>y)Z*FNu)WBDu6vJe-YGurfiGDM_emCr*ABy0nz)<17z7a&#Y=xeI@EiYq zjW5yMGu7zt3TP{;=e7S)l22eD19!hk$h6@qVH>AWEcKW`$>_yYRm@OfI{-I zqn`t2wXwU6HOx07>0&^ym=@5><{aqv9*`c22d1Cq{!F8FUF$J z9j6l0{az|>gOeu?Ao3sbK(cl_*dtSDk2tA1^x zhXHPBK{X%Rd|8?|GYu|Zl83-A&Kws)X9na(0+Vy|wTL*uBWf#;n zbu%UbIqr(ZrfKUQ{z$~$?Q_ROj(-G_^x6IjC@ndIpOf%+%KlQhB@^^>Qj%4boU=Va zM=$!YB>_Mr7Eeq-xxr}n1F(k!x;Ir;(TW-KoM0bw3Q`@8faI)~k|La~ei9uF`#8A? z+_e(!eG~foID(sy0H8w0$d0al_}lx3F$t2>=RxT9qNzSId2HWX3Qs=2l|lB46-@td z^N@T+KHGl~e*NUTuw!2d3=NHh#YvF6;D>wZCC9&Gr|aS5sUu*=3v*zGgq#1E%OvVt z4`g!9`Hiox8ItIavdR4hQVU zNCP6;^%yZhQ8Q1zax5C)yB#@0<3<~#x&mDtGErPWqE(v5) zX_(?yrSk6OTvFN!C(--Yozl~6Ktk^4gZx1nxcz zM^Dw^q8MKuPeVgvP>qOw>o%T+-~DABhCiOi$d75t=3pOVLfl6hqoXHI!Y_aS8uIZ3 zVOH_$%{@2ootW#z?;>fhl28D{%`SO?CtF_}WQh373&aUL9y z1OOh);#2hc6>~;#(XY^G6m2BfXZS()E5)78k&`tp*1y6$3+59LE@r>C*OqU9#u4tM zK%?kyb`1Ie06+jqL_t&;DQ6~cn;C%mFfcRB{?`rq+p2>!wQwp8$@60gFEaVR&L^mW56 zU6~n(2hh;e}gr%js}Bg|6u1=!n*Zz8MFOm%yHb=b@*kUw{^ctD>iW5dQYw$FSj3 z9o%*Mt#Ie+#rPg6ft=__5Ww!o3{kef4?aE%r%vyKodEDNJ{_v z>C3lBkVqmv5J>fs*F@4 zES#4I-+N>YMrky{hAqc1kyQzdV+2Pu5Lt?P28Q6h4Vz)}j$Ls5HP^wz4_pQMWZ|@A zVvmzl{~v8S5BvA+g3g{1M{{z!{hzc02qfY-AC3>K??E`);?oA=|L@~dUMkC<1Cjv1 zpJxHzk&!B^o|>}MoWE2*VMx))HCh!64>{HF-YRNt>vsqMREGAE%P-4iatj>B%z~r> z4Tea)M}R;Xh)tkXD?p=B0_6z^2aSx3G4@+(ON;GtiTYL5&bG@ShD0w`1Zrs zL1u=Y5s;%*8$gTyx=)H=-`*Y2g6Xkc-WiSwAW~s~k!qKH=iHA;a?Pji=cMn#av+H$ ze~HB7z@gI>|L4Gw;vWwTTJw&asDY~5cE63yEJ%avt}JBAQSN{g4tD<_F4#v<2xx5$ zPDM+ogY##lGj;DiPztRmY=nzeT{RmN$|Ru7hZi{65!v=>{mIR;zz@E64=i1j&7O{` z16poxI214=UM|M>=zncOst;5_Ivt}=X?|$_rn2W$#69}a@ zK88ygkNJ&H*?>ES1xX7-b-(=95hyC!5AEF})7~33M#qqYER`D`9_3Sg$Yo`SRYJ&v zBmnSWSIF)9svt?OY%}HEkyW(u0YMiiu8`}ieP7qadQW_{Db_Vf10WV>*?Om-6q5th zkpPgHWe7+B;35JNhW>tp4yF{sltnNZch9S@pAVNR+tDmgkIqE=Ce~im0I)=n0O1q> ztY`)>sMMg5!9Yfa7PXaS>-->iiF;2XnEr z6z)(!@&k~|wUDeM9{}&vijILVy}cDvvP1+zk2W8G*7kn* zhX<|9sE8+RqYGBj0 zLb&CI6>#&aIY5~($0o-a^FW|=@hNA+_9s<7z=0z*uyy+$sH&?&6N>LzBBlYbJ0JS) zUHGq}{r{8jx0-k@N&Z`5F#K602Hou zzPJ$)<+{;FU=~hhwnzbj5T223grWpA^H*khzHiu3D|S!6trV_+Bd&+4J=uhMW;ohy2c(jg@8;4kCmZNRfD$J=-Cow zrkUWL+b>6Ne$b7rtF-_`i$9s@DDTGR zT_tej$bP7A?R4>{@YD<;WIBF_R7p6Ajk=Az^2RahB)O_~`@7$&k>dXyED`O4BmjuU zT4%-RP*r9!C)e8q06kC+0Dr{w18|1~?nVs#t)E@QkgZHy6lb7)79nwZjO07b=R@a$_n z@Xd#Bf>kRibuv3^`MNXkYvj>?;ZifI`pcLd{|G|QMgtKz%<{_$;cH*K8kQ}}7o*z_{Ia?SZ>i0{xX&wZPhs_QUD3rHC`&*|Cj6`-CJmq|M!cmeE$r%_NwJ@_2q^5d$DOz#_&f=|_S!!W;qWDC@a zu|kM@zk}^hEZ--StB%HU|6{4|k^m5fr-j)~FXd#KYinxT7$+@ z!0<3?0C1wDIbW;s?FYc^FHNE~7=re~8=IlM^Jci^dQ|0xm?(*I5R_K5GdbAz9KHax z^$iG5%x%@}GphgTxj~Znp3(%Byc)yzzIOlB=r1GjsRxnWx%ig=PNKc zI1Fny9E4qaPQu`@%?Cy3h2Omo-o8&_v_pME12i@^z=1;});dBC|*BKio5Qu>L8jgZ!|KAyocBD2W zAAqpD16MDd({=1zMTJ5kAHW}*KGmW_?`h`(@2L}H&u0OFXs1poe7i7q*qsK@!+6hu zGA5Pq6-)CFKfvZ;PYZl5{?(xA;5-Tf+}(CB#|MEB0qYyxYZd4F5SjpJqNDwLFphm4 zXq$g~-41AK?StEIn$LuL@$D%k=pn?#3&qXw*`ae7PT7t&VVVj36O|Hksw8jzFWq|; zhVfnQ*HO}cZzErVLh&Xrd{BXY3;+C$HSkZ5+y*=MorU*5+K0X!UE*{wz=uS#baQJP ze7tcB>_1?HRo7k%E0z_&oWfMVRNfdOIl(v#vO9BbXXzbg-eU}pq=ml=9czor+F;wZ zgHTpgil(c;-X}+$`1@vwE9A51#5ywC68qxsN%O$U26Q9}lbkRq+5c_)4#&Te1|S?< zwzW}iNYU0K-0H$i+^zc)0O*!>^!{fN0aML-j{*SOeL@gyLiHn=0G2GwVIm0VcRMrJ z0tGY7xP$J7ix_)PyL~bNuw*vb{0P|ZVjd|aF{OM}A`+p_ODLef_6LV2Ve76#aH+Ho zzI^{R%-GBp4H$6J_C{p)Ztog~78D##oUMbshfX4I2T*NI9CA*+-tMRKG~wHbj>3uo zxiiw>kq53t2$e#n^L6=h;rw63os^Wlp4FGNJNH-$-n$(%Z)z#I^+&$CN!_Wg_$s-#uj;|Q_nr6Zo^ucU55SWe5F~j?^bON5%UR7n>DoE&EccxrLO+G6pgC+`)v zY$@|}T1+3KfUvOzf)W3Fq*>&e&ElD?UsEFf&))Nc&AcC5?nIM1xFXbIEq+xbdHKEHEp8$%3(1& zIj^nyZJb!#frmXxxpZZ1@Nil1hn#d9y&}TKX%&c+;8!;T0H7o4v#||72Mh%u{5+OD zI$&f+bc_tE_JoHI1zzDPdkt)XB-IA_@OEb4^Z; z!~Z;rYHM@2Uqrym4g;K|ajlGBfKm~Fd~vwd8@H|$U;OOdV)MEZhy8(laTc0RvwC2S zy1mM}B2;}k_2q}`RaSv~Ko)=VFP;$|9$`J2o-+W=y1ot$ulL zt463jT30RsHQsP;SgngL>51WE}qC!w8{`o#Bn01XX}Hs(inJPL34(fB1!a#rl=uOlMTW+4&~@ z=RsVYRb^Tfm}cFL4%nBq-9UvGubYEaAR{wXeC>}uDZc#V!{U{<4vP1YMi@vDB96u# z2b?+L?K{o+@K}j)nd)eFo2h&DcD=91t?w0eRvTk|y)@wSlxg2p1JPLd%W;@Y)2JBs zo~tn#`@+KkLjed6U){A@otc@^+|xajU4-($-V^|u^oat<;yt8XSOGc$C<{!b;R34( z)^>`GYYL?zpjQA0GrR&;flF6g0YJ1k(9qq@1hStv=bZ3R#1)Xh39vlqC)Pk(jNCQ! zF`ELAE;k~T?~A_|sujQU{rW2M^aH1$u?riye$|A%UV6$i?58<*jPF*!8;_^BxXRrw z%D*%yx?lOi!{W)$+y@}2qhi;dp5f}*1u#EnREdISZj>N?OZtx6Qe9P%PXE6_$g zT*toTYM&pRi1SS}S%>KVOS@nC!%4&c@AP28_|;GV!pL0brjh?%m6wxF1>lBalm|Y4 z$@z@M-+K@TpcH^S__za60930~1TJ1_lVyc(xoL$hzJSM<0d;U$VY%o4^4{f&HqqVV zJG`)J-5WwLgDfro5J+SXcO}P4@P?o*IWOF z0uaF`o|PU~zq}-;p|YxNAv-h0ln9f)x6;0Z1Xkn>2&DG=yQRPX{H0c#0zlKFTsfg3 z)+;E^-zd5f-Fykbg5InY{`Nf%1%N<6M4t;q9Du+52Yf!}N4jDbIAMEPkd`8<|4Z=k z-*d8%w&a%_fapPF%IbbLN zq35aF7Gc_&=^w7GNlHrSo|u>_EXd734!-An=w76&Dau9gAb-%In2;v{$}<7HbRr1O z!ij#lvR!OMoPZL_v^sqJu63dhKtjY3Xh&h5Q1nYMAWqZtFQ;Oax0HycQ?k3i_d`=jixtYxPZR(B4<8r5^Tb`^SHKZq z%>n0NK`OCIk&TscdmV>nisA1X!4O_g_w@w27AW{_8j-GP>1vQ2T3ZXn@7mXKxjoNh z8k?OSK4h5x-Et@_2Mh%uENu1A=mKDYv(lQW02IOO?o9!hK*|vTbSRkVw@(itavy^X zldmVSCd&HVm`C`+$0tpq&yI}Rf`|n&U27n)M2{;0+P*{SQFo^#@i^wW~ zA}uMf8_u7yUb_o3EmJ9<{LG!=)4=Ly`DTMc&jIz$6&Gcp=D?@Le`>i0FvI)BX}}EI z(g!I_DM)p~F7zV_ZHqGXj&2qF7m64Fo8Tue6w|aJ&jAlZnfl}6t$FDUYtq5@dz7W#^SCnMSQ}X}|94fy#VSN_)OU+DWqHVjE)p5qDxIJx_ujD@=!7M(7IXqvy$|zCU^6Y<002c= zS_%N}m9jmKt16As(vrpRJbs7x>?dzUnwWjRI&N%3&jG%2NW~YW2wMH-pFb&@+j_;b zzuqg(0!Kg!KRhHa9-7b(Du@>CDS(0fmQ>eqa7|J9qy@y*5?A?9SoMOR=W{pT)>x9Z z@c$oB_hM`##{okDh#aqFPly9(DOr|Ncd4>v4#L$;K%7wsUQeV~0CICvC6ukF^L;%w zsDlJ%4FMHkNON#)LyxR1xN=3V*u0@gWI{n85t;DLTUUy$<;AiVK^g0Ja+BYI>>Mx^!9Ko$~iLr0y>ciYEm>l^+L%9PjHKd^l>wug>RI z#REtsAXc;hQ`{T+F&dvE!vRA9hzy@?y}-vck2rv;l;ni=(b0)g!UB&%*m|OH1Ol?Z zL136G%7UrinK+>|K)^RP_lo7qa>Uk+#j@BC&m({ks}F7|FA-O(Iz=7s!5grVYM*6+ zIq^|=az$-3;i?m2aLbivg2f_s+*K|<^XM%=GIS3;9D~EZW`{&YU~+C&6c^@*Z~w*T zL^BkDAN>4XVDm!}2%%3sTnv;J0Js4BHN{i#$8*ImP5su<@i)bG=Aan468yG%&>5@f zdWbri%>8>_`L7oZ|9|A4grNXLp6AR|s?N?#ZJ`1{JOBz$Pt=FGec0;My0_C8hrbATMK@spW zHd;L7>H`xNYumG+Q7jXclt=F0Ccg6I-I^stH-pCVz^|jRNkw3ORun-I_~&nZNi?)} ziU0V;yP~41K4!Uq*I%y2`P=r@0uav?zZtm$xY#DV(GIUCLgBoZ-=1U!uH}EW>!Zve z;Qt?w@(`}u?hOSX3XeWH(NtAfnAz0Q)_Z3m;s7`iP{_I?3et!m=waF5W2r8y3!b zZ`4o#yz?mJXKY!&rh9avwg#H~G{Eo@fDR}akmredfd@bZfW&mbPyzMyBNKprefkjT zm-jXfHAdnC@LvJavo>!m5hZYCkdgqNena^()K@Q()&QapI>Q%_k55W-2Epf2WmQ0D z=w8)J^tN~2zCnEDbN7lB@G%>-G&n%zSn1Ies1s8&qO_zyJoVi_5;YBN;)lQ3C2CPS zB34LY0B}666aWhQc&zs0k^LiA{F=1pek$)iulRL0%xhz5^)K&T%)hA{qVX|;9557s z2r^sW6@G}->1jzVgF|C$3K5C#P4KcZpg@WqiKT&s=#rm<{WAp1f`mOO>2g!bhVcnxp_%6F^a(W_DNKgV_!u4nUE$G;p8#>erSv1P&do71;&PN z31x}bttpUM1y`y%pa@LMr5a7_gaeKR+O&ST_`}cNhd{?=UWUUprhVe}a{q9#qMP;-2{?B(J^CdDxuw1c0r2tak)0EGx1%jU#8Tu7JuVqC44Dd4o z75ujT^?Gacdx0>z8&H;b-O4;s-_noD zebW+bC@sngKq{j7|NKuL75Cm24cOjbxouppI6&d)3sDi6g&}I)no{w@fBA3X7ccA; z@9aM6%cUVdhrbIFgYgm)EV4SL{I2;is00x9I3$8!_Jlt=M#Vx9peZiSsQ#y(O%%3z zeJ~2$U%y?#z19-Z^w_^nVnbypSnITXBRIdap zI(DL7?A&u1fmpr3l()`mT=lFi%e5g5rlm#C0>FI!@BRJn8^T{bwCij;H@hJI_zxZx zYgVm^%ALm{jZYHN04?!Yi)rx8l+hIY6O&V@6*3e2s$X|^`UzhPDbO8__{W8Xx!GM2 z@73tg#sU5NVB2YYhztiH6KfF%(9GVpZYq)KeV%$|AfO@!0)~P(2@Gj~K(s6*pg>V5 zi4p!M&oqiRcV7~fND-XX6alqhV-Vmy+t8GBOIu(0{Jn@-FoOJi;nlTz&iulh z_~fJ8fk&a<$nf7O`=aux6ad?z9DqQxP{zk6G{GMR90L^t5EH9^b3!fPfBOvofB4@y zcRd&ifIH4aV1Ht~sWLA+qlH}&=l|wyD?Ii}L_kIPKm-gG4ImRhQFcan0B-26ICH*9 zy!HM?1O#^3ECTGe6fnn+>?jvI?pZIh3e;G_xutpau^rn2=t8s425_ZuNo;b!f`V9y z@V$qd2;Rdo$EhjFA`yK`(xg9E{1p6?aE*Bc@d;v(vMWu`i2?UNl&*w>LEMgUX$UxA zC;%Z~VxUc=rkLuMm1H-n>({T!6Qw}hb4PuHb_+lbfOr^07YZ6=CV)nn8KF}wRGa!O zGXc(BY!RpN&w2DLMf~x5Hi!rB-zbWKGhka8=^F^qXSyOrMy8N{6$?{gh;GVg#%~T- z;^M`HOKnK2oAvuzUoRkd4`WR-Aovqt>Ua3r!UQ)xGaFXHug0lX|MG_y1NMK6%P9Y= z9!`|Dh5`^J#as%Qvmeq!0*9zg zbwBR5`aYXl24uxUb#6Y~8>~S1$OGk4Aqs~gz&z^L-@OPW!}tj(zAxo1l}TrX(%bzc zVHIAD6xF)g3lPGgx9l*DFD*4$uxOM87CuGvf-$g95?=0zL18O52X8gndaubeGe0-I zPw~XqMu!8&>=zxr>v~Uyp9)B*;YiT< zxD^wCllH*ynArLLC2{&f3orugnGLK!c=zqA#3vryB37X~VW=z~>i#}Fd`28RROi!(WQ`{o+8qKt{Ia%dDfww;-gA`sY%Tz;KB0*n0# z`1XfK#;sBPFuyw}vtbDKbM+)`E&$wav^@Z(xcL39#%Sz|EC<3L`4ZXtS#rH4Cz+Z{ zOLFQetJ)U0R$#gu{nM*a@0_W0S1LLj?x}CUt^nj~1$i7!2=L1w0s+$ykx$^BG)!P4 zBX)qG0Gsr98G%p6pmea$G$;UyxZhTRup~#615GeTb4w63=w=iiD&J5jDp6~o9_fWM zGny$?HQ?vAsl8vkv1`BR>>d^m-@ieu2UdZ13XQ<<-j8vHUmW1d*3F}dx0v92H{;#v z6YuW3Dz02UEP4heVEXr~hean-S%wQwBm>jitU32N1}Yo#NHgS$e;Sa)&f~I{@P*Qk z_gyLeiy9C04xLUC>Zbi|4|oiQ|KDGxh2eZd0SE(A{b>>=u-UHjQ$;c67dhL6{FQX)p$W82(J#bH>{Dr+;-WD+4YsvVLPtfN(1S>}d>c z04e}12;@whFk4W4uoVKjs4x@t28scJY_)hKV#2*)v$ftpO?@|T-g^S(0p&%{;EdRP z;IL?J>lKgQzgcX*brmLbS3eCJ%%bf)b>@`u-4h2Wi0OZ)mFwuK8u8JwtD?5*lo*~s z8EQAmf~WS>F$#XB@=@?pVYdr-2y_bmDF}XO7xw+?O6!*6B1Os+FF}XcCk7PiKDB18 zH|EP zlNSr`khntb2P|-b1_zv*l?pgh>oIvkfPWlP0TBn#+GdMO46sG-mnkTb6!i_gz!~Va!VG)wdeIqEvvK0W)hf~5)gs!v z??e^AwPJZm=A!dBW!rocE{+Gh zDN+i6QaG%g%D*A|!~5}vOmXJD{+w@|8$J#g7J%^a)}NL;mzCsHU%c8f2a~v&6U?zw z4H7Jbx$*SN-@ARCIPg&w;DE=4qXH0`K)_-6{OOD2M3IGpK9QhtT`dE;0j7}V=nM6O z2S-lUi43?ZSax{DGFBjjj~A2bYPe5Ut;m7vU{Fr}LpqQnU)tt5W0P^>^qKRbv$bA4 z@z}lMuG?3OoUGvMB>39Dab6%Cuv%uY;Fs^g=*YAIrPwNEl zNli*b?G4MK%VwtL5yvv=cN_`>0QsLZ#?!NXeV=iXAEjcs%} zU|0a6%YWHBd|?X-a08?yC$w`%Q9*``ia&I$R;Vg~EU)_jKyqjapbJ0|A%O$^A*wdG zrE6hm*c$kkj-WgrZ!XGZn+zK!!;bAZo%9*<~na8f+~ z>S^)nuU{1P&FujRf9CLK0TinsFI}XfUWHX;TbH_2R0jvf<%&O$AXKx4J;633767PO z9ApeYh;#G);zr`ZYHIr@5>!K?3W?>1+YeAM@uk3mTmTvf#UW zuZT*-PY_R?M1fEYW$M1|>&1?{*NY-Q(FHkA4vZ~`kB*-d|NQU25hu?zqZYd%_$6~= z=ntPWDAO(Sa?|1GcU<+41I4l*aNGp3vH|%7 z!UrXcaqU6~{qgRGLjK@?D}`A2t7d)O!vB91`TtQKg|DN%_RljEfEankfCWZ*;LJu2 zr!YT5rV;Tom4Lk;R@y89bO-D}fZBwn0Pvhp#Q_ZIAc3$cV6`w9izDn7y zU#lKpIx)@(ngj3ds}Mi=>GPtlv0n6#`4{ES%#vIL*5*_2Bf6hLQ_@+a$H=eB>Mnr1^dKG}SFB%KASZQRmjTNLL50S~i$oQ$1ey_S z=?fGq1%7AWq&Ri)Dpr*fNJ}ge8`o-ilimho91j}@=<^=}pwsKSPKf&Y%c8A!45f5^ z?~bW_d?#3rmzgDs=HL>eg^E7@P#5Q6&?~g(KoM&h92#3w1S^120C)<8+QB0=V*72YMA@=z-BrSt z66mlqfG_$~z$^sVMS_TPkW2+Y2L=?XeyL#8A~v87%FzlGS=sed7G3I zrWw&aIO~(}r?5&!%n1d5yxA;MP1V5W;YVNq;rO^UC|2zcJ1;#=N zf1Up1l3gaVr4#1=2E+gFl08w`X(#|uVY};26B0yqR(7gY0RZ^T)v8X16DgpMoTwM$ zTbJ8#0(b&f>r8zM7QAN?L3vY9G#FQqt+OyV3?jr|#>S^aUUo`Y6aY0;1~@jg^oi!y zK2#aZlDx~*&;=6{5Cdh&rJFk@~dFY<+Wkg69a}X*Efb+P0;hD)4|B>@`#`oxQz)%39%YTR76D_9N zlESRU#>Q^=BGV-1z4NM+W=aOUjlh3?+l?!vZco?S!}o6zhfmasuI>?sHvRoF0COr+ z4>`#a@PtZ1IB85v1!R9jY@?+kT*OJLy<<@RV>ZS5HHFgWOP;dtjq8fUx>W_D5onAx z4FnZhj1xPH@o@rYmPFt1v>3g7RSXUEhzIVw1vmud5}YVp?S3m{S! z0L~92b>G%+f&Y~XjCMKW0DO)sFvaZ(yjSDOaB#p-0K&o50NaFVY;0t_?ozxZehgv8 zsd*^&<4&Nna87YyhA1u0!VeIzqzPv6aij@eLhXRd2!IRp$3BRR11bO{7A5M4kOIK| zi1i;x-afDOm%6$~!P`+pe5YXbUMP9VHYnIvugpR9!5lyl4@wt;-m)@-3-qziDQ_bF#-U+GAgns7z#jS`Rz+@H5qBiE&T%{8wq*qP61#;1iPUE zpfr4Lx51pvG(rLijj9>amtM7VSQJQ6R%TL2V1`wDuD1G20N@}*OaV}189+YwabOA1 zg+fT-WyQ?EkoH*#Fhr1{4p>!KbAab^#mH&U0^hMBPJo?#Z6EyH1)(LnF zu(;uO@MIIxX!|m=AkA)xW~a&{%Ih`U8zbCYggurm9PLv z*D2&iF%&BP>xGc-?z6gB;x%)$eq3X~QMy=JD+cUB4s zQ0zC^Vh`M3r;B{=Mvsd) zfNZ43<;!wvc_0_&X;r^`(TUhW{PAfm=ZV7q=;=n8R>yPs{dU9%5IxWzZ2Up^L#=Bi zA#&(j4Kt}20CtlK3qZ7K0x=2164?Dgg-jD<(4R7~q+?U%c?zX<1J|f86-&odc9L6#gw81LDQk&xoCGzlC7SS}}+Uegyk*rg#8k zk+O5rkWR`~KN*WE7n*}5VMOzbW+F{WfrxmyEa1ajk}vG)U472;3_<}fm^0fHH3wwd)Sc{;|@CH)`dY`d& z9Zmk0xOj278by45bx0ik@Ey_8J1!uQ~RWDNwxPu0Fn_;VWZK9#7;s6fE@ zFn7Sd!}t^{9PsBc#>$wl`EkIyTn+PkGjEirfS#zY-)^!eA|P?f4IJ#T&Jk24jJl$siS3xIxH{H(A9_5h9X?8P<& zjoC8;SRiKWrV`W~*dVJB()X;VY51wD8oI2kl2QK8 zJpYEMyn0gfjly@WOAKLnMtXXRC@akqB}nN^LU5o`4fw1EhDH&**$WH}00L=>rP~gp zaKL5(fX?fyKn`@6G=wMxURm`#wPh3hSou2$hB+%>k;8a7ejhCu@c&9%b3g=)h#Npt0FVU`tHjk$OIyD63O;Lr$7qgLC4IclL>=e{}+;Y+6zv{L9}2b{A2JoT@R!T-NseKxkS#Q{SBh^^aqe+ zn3oC2Ub~fr?xRjzrv%iTs{nx*R5p(wv&)G|mL47`_TUDf$$wZg zp|em>mW#UeJ-wsiy?s|@HMzP2<2@ zSCr~O*-__2drK?;63Zu_gt_`4qSV_uhHMieC*Bo+Yrb#$I&ljEF%{|{GtcUjS4G9egQ5o}{~5fZbf++)f8A<= zHDw^e-!Ay+CTIhoQ&USXg8U|=@S@*8)>%mztHf+1QULXf0o%)|J!Y=>fj1&EC~Qw2 z&w@%6T_;MZvK><&l*I7$wKbWi@Qxof%>Rmkv9&eQ0AuTU*hXWqh^nltwC2u<{(^OY znI%@eJ7OSUhL-x2mz^f_)t%2}5D;_O8{rx#-%t$HyF%nw!`Fv@G+(b?nI}u>R@QWi z(a|Yc_Ests5wrks%qcLR>w$Ni?;{qz&m!0|F%hdK2qgxX4MSlfI%7p`m$U$s0y{vh zzF5tJ8Vh$A{xMKE%>qQ} z{G~SW!b>lSKE$1jOqc|eUhpGLWNNM_J)jk1Mfy;VcS4sssw_tO^TI#+zZN@>tBy7+LFf+ z83*+oFcbhiH)7~>g2hxb_1FcmSJ`O}19Rb;3A^n=2$e!uRo^96mF9|-r8%CDY4LpH!9%GLHsfTa(Q z92eca!{Vz?ehkQix#1da%(WYDG9y5BR_s1dDR%FE6Tm(*!W5q8^bPqb|XF%*;^-MFLeXSVpQ00FF2kxIudiiCEO6ZSF?6GDeTB(vn&X6V+qN>dSN3CaRFtm^ z1XTN6ZHaS#>THwPx#zNIXzjP1!peNNqDr1V`k#n^;iOTm9sv7@rBpz7_pH(uEf&{5 zAI4r()pUt{F!NU+ooy1Zvx*2yd&A}uasS=xM1FoQ3O-eeAN*psICY^_opQ}?DP)oo zEV7W#GrvA5-rxPUn3%PQqGbhQ6=EmSS*a1k_nj-&0le3BsEg4KJOFpdeMAAIP;;)C zAsdRZKeV=~RXGK3IEixF8f}1H@R_j_s&Dp7C14JV0h;@L5&pKZ`gk(?Cn5dsUF#>~ zZ!B`a^$Zq^BfZYX1fSVLQlhycB`L9;w_aA7E%NiyTymj*aKg5t$Vf}_2p;6z0L}eJ zQDN}i{Z~a>mpx65=6`JghSh04CJ*N;L-(DNyIK(~i5LJ>pCjo5(p+(am?59kQ|kb#_#GDYwI@Vc zc8(}2&Vk#zG)Yf&1&h8;G151G?QfYPiRjsG^(10rpP>N6#&d8viX^}S=VYa|@F3;y z$8K3AtC=~U%+(TosETsn2Jn|y1@J=%s=bFQ0e5S)C@7h~7Utd==l zhx#0@{3~m^#cOXK6$?`h!jhVfKtDVGD3m(721RvUheYk0g+JOCQgr~Boe}F~Q&uog zzlE=lH^+2Iy6hBGwXy>69i;1u3RNI|`?>&7=W!bsZ#jzme;ta0k2hhQ7BLPO3P8kI z?SDs5;;T!FGwaorObNUJ>1D2n!DWi_;BO9;x_d^&UVz>ZNJvjPs0;AGUF*a{_isdW zwrfj-{(OHwk7=IshP(B+uW%y-HaL2l9VXAdRmaS zSs{kU?3ozO{V%2Ogp{H1zX*8cLx;~GfUiT0PJ?fnd1(q+DQ)fjQt%VJ$KRE)O#$$< zB#jYN~ z2L;pFXh}iVS)@SOq`@Xc@4H%w_h9l=(@I{hwVZD-oFS0VIP=y(s$G6&p`nNF-H2*; z>qSmZ;ED#@(vzxO$a(;JU#0$>Sou6NgOb1)j5G3|KjS0X;yGjvFom!g07Fa*BcF%I zXGL98BMK8W0vKlydt!fO>+Zf8?3b8eLOp~Qas0#?(b>}`Mi}pjjE51po*LmEse!4V zya?3jK}$CUPb7eK+})?I`w`D0Pn`>)0>A*?M7UNc`1PrhN*`jdp0$FducLH*o-Lq| zhkbb^5$^#1zo7uQW@pUoH57oDc@(Y(Vm6zuW@Mx^>W^**fR7$+j>iU2D_s$@1{P`K z>YyK6RT`Mn*10RKVpJEcPlUq9c9e^IQ1OnXSA!*0mz@)u^SuSzcBrpc5VOnz2$q;R zaJ8~i96Vl&G{Igm4O95U)V#P-T`NwUYZmQY4A7(>+?QE5*wKXMdcJ!}2|$%I&q`R| z!yg?J?cF_sv7SQ!nWBHc0l|ID;SZK*R;RF2F(kpZ7q08c6I;eYpxTaZa5ljYh1m=@ zk|M@J(4Ar<@J1;EwtYcgUyvbx~g!#YG@c#$N>@Z($e{;gzkZw#;SEf z7nlXaLh-~yn)GnHup$KTeRiWS= zg&&10ecZ833Il*+4Mthv_4M&41NVa~e!aDf@8u-QCo?00=gGc8CH>i$M$S#@4#D04 zluY~=XNot{{}jVwcpJJIEQW7#DGv=AbbEXEjK(K&1gL+6{APf?Hjp=Q*4LyRch>h!tL?RH|I5iMea24|2Yp{yF zT-h#|X6O$&paCW)iC$s1X!z4kB+j+s0ANL@L#GQ>znr{VAn+M9SO_c25bzkLC&$F} zEgJ#Wl#5j)1s$yc%K)7KWg+MW4 z-+arY4N7&Yb--h`Ktnu#MR+VPsL#eWb~#`u0I~aQY$HodOgIHMK-19B*eVA56+uk8+53G z8&>CwHHc`ZqHwjg(=B+B^H9dLdH~!X>lOfIVzt1`9WJ6Nx3hCd{v&8pcW=LVaK~+8 zJ@}hQAs{DPz6G3gxeeb~(6~#A!)iYcf{21a_6}*#jxp~!R3pxvJSb{g#~}FaLcwJt zLGDy$bysHTAe#>9n-%Aa@-nMgh$5yhC;3hd+H&96i+{^zIU? z0>JS^#y!AEpMpO=eohL1-aq}nq^8zEM%^UJ002M$NklQ_%x z9uCVNCG27y2uNiBkX61GEwVt-OHE0Z0e%{bB@Y?YHx3ue6ac2w?^DlQ^*rRhPy`%m z!HcpzO#|R~WMH7~C!iDsb9~Z!U;g~vdR;+3JmYf=aX|O@VrW#xP#idL*DY&XN=kB% zBZ_#ILT4Q2b%ox?AgFskwpNz+?FZhxB3AL!2M4MB{%pBY8J1z&sKJhy0(b4v+<)f0 z6;-ef<%iX@PxrtL<;%pUAKNCa1n#cqnAjKn#-vzB160L;60j6a>|tuUoy4P?i=M)4&&lglDbx%kQSRAIq)rC+0bzf3`6{*z0+K3nt5v z?94O?6-N{Mu~QAA3pD^p3GhYlK#o3Zq${&MR{_~5G7~Hfu#PQ=KXq@F0U3yR969d0 z_FfT9@L?-b@WM|#vRSNMMN~oiq^8c{c3cYtOpEb~fC>o6UmO7ay$1SuU@Ttj+OuE$ z{Dsq^rDF`~Z?0ORV!#8m3MgfO-(8-J{f>kx)Obby1pqRwG(9a@q#}xv08xtOu;QPX zoRYzl%<1<;{AsD*SE$etxw7Yf?3qdhF$W}R{s;#p!<I_e z&X|mmxZprVPYj4J?yQKd%Y z4GP%t?n3u%WWtQ}x>MrE&mIO={s1Or{*3w0iJ`Jfr3`TT=D%6+oxXdn_#0O{mmUZ(~&120YI}Fnhbu&2i;UP%C^^6IAdV!304|9SoZy9d9uW=izI~N=?7_`qS#g$45f~psqBw|k z2oh2KFqVm>Y#D2%5AKTOc*iD9;?k9?;-}9XKx!t-MewIg{CD!roicz!k{st}XGKh% zW!qBd&&P?mSu;}1PzKp-5mSijfBB7z;;lDcmL`7~f1*OxAV35ndNHd{_R835MkP7cjFIKEkmDEw&z zknax<#F=KG$oz+~`d1u^{jL6a#{Oup;}IVI0(SL zMf3$?R6DV>RTHzjihz+AD}fp->YK!KFYOZtKC01m$)B}e8IZFx_Hh!|q>S!t$B!`K zL)b6Oo8`Z`*?1}R7v||xUO+^8oXj@)@h^{x!ymmXP_GA;0LK<0^-cM@=RzUq!Z>KI z{*{tN7XV#H6#VIU|Cvdmo(Lg=x!NZ{lC&}fFeZnXflw^O)xa?pB`l;X0KO;917M3& zh>K6&``RzQ+7-aCjZ31$0rgCy)VVv~r-6+aO@uCOs*&x;?(+<&6)->+ebrkyDpS_;X98fd3f0X#G6DV8-fR_iqluA1;Lj z}7~iK6i5K$ZJN`JvqoKFSC5fKk%akMVF} zO&|RPGH8JBAy^Vy3DsbRYYU1*W5Kqk6fFICsBG!mRTc+xeDbATFMjoiVgA>1EA~Gd z3P9{XBZm<_|IXOO(q%>a5$QY6G`kIJ?W<-g0ee2Ia)9dHvawhO+&MJj=a+#&J0x%x zRxhC-t|;qK42I=zPhDuS6&_M;mjhfVRCNYao1xsQRKQTw0|>>qI4QSwkBi+0j)YXFB1Mdrnv|#2^s$v z-(ujc@a67|a});*1we6PiEPIv7DmRVli$nDNtadt!tqLYP;LB-31Et#BK}4haJLO* zgxyW@>RfkQ7I0CNiGZb05?624wOvpIE{U_3TO90%s?NanJ6126L_;}-K-wY#8%bY* zEs%CXeNA^iF!&E25l_E(3RMPWyr@rIP$8Rh0`4KCEZ>=T_FWZk?b;(6nrg(r7y*A{ zgVbs_)-V91;0JC7gK(2m;-!qFoaDbrtn>!~*hOD|D5CWBPlEj_3Z`wgqt8ZQo6NFs zR#a@Oe{*8p7yk3(&l#(K#r>tTH57oQ^Q`1Z84URBuCa5ar3L#iIWN!(P`;tq;VwG6 zhvA!bRK~Z$5^x`|0+h?Z;Q}vT1~n`Io|XfDeK?5l!WC6rLRBDC6Bwto)ys3lCy_dM z`z#3l`iVJ|NW)|UMIc}!1DutVZlmECX9~+wtx!_k1n5c-h zKGj222YuW!K+&vfEURnqBXs7mv^Hq(iX|oId!Ku($f^;mYmDuZ;6QBsqL&2AqCd!q z$pu&sQ{T?XNx#a`&<&s$jQp`u32>%ms3Gv^gXIw7{+aQa3aD0nIT`+4>pTW;-&JrR z1?*)AFBU%{al%=Lc!uA4WUIL4#uY)wB6u5zx8tfHP_(cByp4|@fRpFXiRWKAin78@ zZck^**)*z1a`n%D6d)A-{aB^G@SC$@&+a!xYY$ee)ArS++r}b%0Hv~JkGzLy65q{K zo}(0KPv`58QI>OYpxi3*7DB1vGXfRi{1o0@Eo!CZ`iWT zegZyq=m2SaInTt-LDYr;Hn_ZZbdHvHUIzd-JWlgtu?v*p*!vk-KuEBX6i#_4b8&M|jXej{v z{V2hoft9~r08gP#Mt}dPOzERi#dG;P3MVPFLH6MfE&}<;N`uOJ!0Q)uN|kd#Au2rT ztxrrV{Kvoj(=Aa#f#66D3WfvfIR?{)acy)sFx=js^mbuE_BnRH2FQ8^`S!ejy#*ix ze*1j~D}fSdO}*npbt_5<-+$M7tfYN2srPAd02BrKX#G&%`lzM=u-#;gjdcdjUTl#u z0#)@rQb6$-p;F6HaOl%e1Xyq=5Z4CV36zKfzyLZ6(z;@q7^+a)(j#7cbr;O}=cP-a ze{fPF%TcGYz|WtYXnh9;`p3oo!!_dBm-dP?CwGg!Q43s2u_^q?bG2Fx_sZJ&`BIpt zK#|S|=oVvQ%vvBN&zl0nDfoxrHY0kU6qed7>+h=ExnED;IVL5*edPzQ;qQj;u)3CN z_8f9M)pyya4rGbfD8&nT{}*q)<1WMZ@9DwB__5fYy<(hs(HtJE0P}Z$`Ap^i_*~J8 zy*)#lQ5rP))*H*jdkEO#DsBV-J~XFuWx`qk1PyxsgRA1^Z7an3Ro2wN61W7OcxbaY zgDCt4MC5y-{{AuPi|08x*=Z6C#?$$M9#a+oUY`m-Dq)356Hp9J!<1cF(wU za^-%S;J0on5$l18P*IH%#i&Xc?v)lhRxtpvk_8}{;pPu>^;>(U#h!h8#Mv|1V)=@V zV$I5Yk&~4ql^A9WG&T>2hPq18-%~FpX3Sz}G9K@dn+9WKw_5Q!`=`S-JPN;Lplv!7 z#?qKH1we+8k;MQ$ges;m^a!HTLV67SuYe&nj3$}8?k`S&JU|sp z<|IaeZ^0^=w7eu+5Qyj0*+$XbGc2eqBq6t(ruI8;T`5)qS@0Ap`Z1Nz74>0NqbMKI z&DA12fL~)eT!1vJvYd(IJAr~j71f<`*toVxT*a|kSPQ}l5HmY# zjd^0_wxvLnQ~hHJVq|Ju^bVX6SFg?j3J4}?;Qh}5)^`$d0JA^<9GkLWyAjv@_eyI9fq{6PB`It7$F|@0rs4ZnoLZV&`;%Up=2Er1?9ML%#FWLqZMB6};=p0TFeW)@xG=a$f zB_jNJPs-;$AI3sJ6#PX+nXr1LO5v|~$skZF#eIDvZiPS3RcWHq3ZTWYs5U~mRmu?C zbf;+0T83I8QuwRB`J0|bNk=98xnGa|r(gZs`!Elu=WYDP&;IU#C__Q1yD+waa==gk z0_9wcTn8m!;+JoY9W5;_dKHt*0t@Nffpj^_`hFDS8PkB;StFnjLS7Ml>j#h2ile6+ zZNNbcCcJ6$a`8Bd^pv1TkDlg{R>)vN5{nM$vkvs<1^am#!BZ+=F(uW#0-*XC0GP^0 z$LaupR3X}6;ZVd{>Ng|K;c;X=tVZ^LekG>P31&O1Vcp9mKqQ7gHMCvg?e zvev}2(4Wr3{H@5da6nlT^j3kSjKZJ(`g=Rh%Z7|UOPT|Q0qEDPZ?8auh6<{Uu>79wb|6O}8i`u3>+aXr#+kqlIJMLZ&vuCnxe@~xP z0pO0pymZ_CP=8Qy0IpmB?)vIO0NbIXwPN2#Rid+ZM4rZ%n=826ZbAuS#5!0@5L+ie zckTGPpC(gMtfm3_dLQyUT@agJ*G2hcC0s+91&{>Ak%B)zFGB{$Dd9Ma=iNUrD!P$g zOY}ZpNyRC!46uGR?d$cvoF|1dYB!Mdyr8g8#EL%&L7r+HJcls?(@q{VCg?Y!L-2lk51O+<}d$unD_>GOEiOLqk3Os zOCNv7l1kw(cSiW)0j|2~SKocGLRRZj=S#N$R?xTHutK5@b|H2^QDK4InxOWDYCAe^ z%~l|oioev;J1VOq?mt{5dT8s`h%34~ZYe_v;&M$9P)7pW&S)-xSOfwJ?2eY+Rm=n- zg`(&USIEVMS*S{xArccPMZPHpvy0@452maUCA(?mwz#{c)gFCVx24%LOmc1d!;{^l&n z;TwZi9Douq_VT`&4@#F6zDQR9S4VdPLU#n207F{(Ujl-AN)Zg`j(U5?P_3^*oIKlP za|=-W--y7%Cmz`%qo36(rbw4^=&rp$_m7QRO{=VC7^vxQihoE; zO%%7^TqYjgQ7)E4A<`B$eh-xsEhqI@CPV;)x1~xrCIMR{V=ObK!3J#rShgaKKOiu8GHJy@}p|xt@mJtREH>W*_DrrU2ds zVOQ7GW4p_>s37QxI9b#-^oX7BTLE~Kz-J}E+ioZovsz9t&GRbQ&{KPX92?Ut0KC>y zAfPX8cXf@3{ZIrxI$j4w$eL0}p-*gt?YFLiBCtsm!{i%?BA~#4j5q*aoml4$|f zi+EdbBzeR8q054}72e3ENZ>>Q0ZG>ag+JPfPe?yiPz@q^d9X>g%<} zdBJf&Wp}9e+Sk_T6Q|%WDb5keN%k~T2Iz6c-`>$LM#lhst^8oVHtga&y+93Drh>{c zklkx>0Q^kr)a2B(M{~b^$W$cAQ(DUSk5XQ_159!8eOX1@{_NoMkJ|%^^cNeSmn;YD zv-y&}1!LfP4nPT*dS>VN(c^Dv6SEQ+!+lf^x7ne4b{J^aAF z1vpN_pGq>~02CJ^w>4{K=~)OmUx*bmY35(PEC))Zy`m*uIfIB5X>038KL2<~1iu=O z%+1$~>iXb3bv)QD?}-6^6yA!c5Yg1ncm;ppfc3$DwE9u_Yt7E4X086ti~n}dG1Z5$ zy>>WYC;->av$Wj_lz@@9j#^$USyu48j0eCB!GJ(TYk&mkuh1rl(5}5##FlD(WD z2sHErD)p@{L#mx_B2s+?vK`f10N}F^HAzLFZNL`qFgj)jKgvbE_dOq5i^_>>MP8og z%7=C|z!2SdsBhHg_LkoGr3C=PR%vSPZk0lkEB+PB^CWJ+I*%sqfx$7++S&(*?6I&! z=sQ>dLg)fu<_W6~CKAF~_oHKq;>5(1Pv(Bb2es7)67p?OZ$wgh@iQyeZ~3ti^{+T} zZEXz&;M#hgw!7)+pX;gZ%>Li`1=;(#lSp~WTuNwQ=EnO=oNOzryF^oqOf;}vo&taB z_FGqqM;<6g`jovMf$ao8KTN@Asm2;eCV;Qq(MQ^ZEC~iao;lxwU@v=KcRotG-nV_d zxcAOAZWRH#Nk~Q@psySc-FeD9?Jl=ot@z7I^F(@TvQ19T;T>i=Uvq1(7=!|#Vq8Ku zGJ8vU1t1g_0Q%%ve2A&7>i$)4bTJT@fXc1E#7qShY|Gz-3Yd*g%=oBe)ouUt?H@jB z|6%ysxN&|=bHGplV*2p}J~&tb7LHz+Zk#bK`(K%vsTFKV1tjq^Bm314o!|YTrdGQv zZ~_prigICPco@F)yKY~Di7N1wJ0~$!6evB2cGS4MZ8_=H0}tWd{a0k%gC5`|KQ}|% zyM3Ki5y*A42r!SDE2UU32;}Ylm*Vj_4ZA=ois~Mi{zg;($`$ztQl;Rxu4P$Ga$v|R z_y^$Y_s2Z0+V*!FuY&V%VKDtMI-hB*87axa!l-5 zPRWgbu*w_^&L@mTrMOBzIN{Z~gHe7_^v?o+7vEeB6>4Cm0c; z{uRw>z*OqHuUje?`h@{C+BmbNCnJB;=Z?Mlg+^c7G0t0>9557srTN@^9yOGJ>0j-h zK9ZBS>@O8akh*vas_LnUQtk6)%L%Pbt0O?+$M5g#!Rq;P3lsy*ERGb`gMLcA$(h6#-dB75fYqKq$EG zUmHre@~V=eEL1lv5Sf`NwiPU`I{kyAqQ1FX;`K*T@T)taa!eu$pufZz9SVL4xvkSL zCxP2PIyx>9|CAem?u9@t3}*CW&;G|KO{TGw?6NPO-uWk|{T;J${?g-sp#UtsXXf{a zp#)66uqW=#tek>xp%R!a9Uu$U%~rU82ll0dmR36$SlNmkOhEIZqN+>m+8FcK4?%4!;mZ^KAdXR?Day-CrpeTd(m#3iN5B1^AI%u2E@2KB3cwP6c>W9> zO2EV`A0$7Mn^*8%l^)3JiHfHJfc$B%9Tospv}Xm-1yxqTBvO!Aa`;`Wp1T0ct0)g< z^GEL6_!B>dpeOwED^F8B=pGA7) z`r5k4mbb>>zJ|tbS?)R*pLi(vul?-F7mSF1D^IS6zb2Z>47z?e@YsJloA$_U zLtpRj?fC{K6bXUWi>iUFrl$`o@`WRJ07-AHwhBIWx={wwIbTkf^GrOI;rIE#n%P_PB9Q~f`bjeDLmG}uu ziRNagtf{+LJaVSZ1xLYM&lNA@FmiJ2t9?#g{KshiUXDR+hDp)&PEA z`oL0WKJr{|*iK!gDFU_~+J_bZtn5O3z&ZHRmBoN%hgE#Q$`!e`iBEs+$moX^_Lx7kVi!Oh*;PwSZt=X;=h8@X*wXfBm{5kqZ<{^|C10-qAH6@%k4D{}A*Y zOcU`?5=|O1pQ;?`+#UlLvw%C11{ac5Fqx$Arxk4&Wv<;R4ZKcKAooq-Z$l6?nWmG| z^Zw*|5dMm1#@5aOLjkb!;=1|t&JS)Ld*f)Wbny^U_*g;AS7O1P zQEm0X>)q7`V0^&Us!qfQToN^ivRCU&#s}PWn-z~g&nd-@*NkXwXCj>|7HMK~PEOoI zBU93%p*O{r6hRRHn2@0e1oq_Un=UEN78}+TL-3~~QeB&>Ao&L%9LKfFeACn3G42B! zL2y?o0gLqfMR5pJ4l-ftC;FrXpB3?528VHPc<4*jkdpnE7EAmH zS&)qh>5&~9Y-w)Z*uqs<9R#|fwW~ZA8sf_47JgUXDa zfndd0B(A<_4&)?G7sYPW8mO%4#-yxN97L$YB5*$xfm?1|Ay;~_cptGeCMo#g@-R)}6!B22={CDup<11i@EBekgrFrI>nfPxCyzGLfI3iTB8w`$hrBsQ&8OPSFZ?l8xtF1!G04Z~!7XR&RZA zTuK3$n*mr9zSC!)3zW{=>xHAtNfD%9(>g)gK=|gQJLp{I7M6{AV`nzvm zEpp(WJP+T!r{KWJi5by~N_)%%NJ=n^G6?Yopz8TTj1M5508t4y1CxN22HE#E6d7W@ zf;P0PC=*ryS`cE17z@A!7=5aGQi=dUg$TL44wexKw~CWWN>GUo*z_IU!=f5-1j@>x zzy5mqOy#Kn0-phXDnLsGSuwb;t9!_$FI)w%#i9iu9?A<#>G0iHz<)V8>4>jE>0$_O zekYBO&A`-;$bMg!@VJJie+8_?Qq)AE^hS&qP$`o<7a%`1+1Y6i9o_X0P2R3Gj$iv6 zFf0Jq{xkOJ4*%jS_nuh0cExr~7)R9wTK?%Ph+K-qN} zEJ(r6%6wbOOGP0t6^QJ|_ePrfA^59m+C_6quS?-i9us7TK5%_%oSyg5{H!kzor0CO z68;<${rvs?h@C;nVSj`_uciN9(e#XGWtb*^!Urenl+*Zxq}?crbc-SU6&H=Ip#T`~ zfQQ&hB*W!k)@l*p^;}3uN_*;4kKFKy-7kMj#`}2a z$~bf#alpU*}yVKXaj3qLQh7YD@n=D*}?* zyN1OFhpXl1K>lzgxV*%UA3&oi6#}BXRlvti`~a@5V}b6y6J>)D)O7&&JBZi@Ux-4G z>5psS=FlUt(HczG0AYxSh9Zk25N;d*OF|rAlrvE+5`leuz#y8#Da`t?BJOl!>UYIY zDqHK;7D*+cw%!9igdgU0SN-_2kGI51ed+iogY>k%XW{6lpPyxVgBcEE2oeC?L!z=g z$yxwT#4!Nw8?H1vzz+s9oRpIGHXGY+ob89`-ud|V8399E4_#B-zZpR zP*1EtNJKsg%4q)>KX&^nQ3Ans_(bg@p6JKy>a$V=)*w!RnFLBeaN7kKTe=M*r3i#e z0iX}MxG+-`}cZ4^|`=jvhg{7ynYtXHk-xbfekEZR_s|tXQHzgXI>{t|w&Q}aw zbRe`l7+s&4k!%CjQLWFSKu<5MWB!`@)%9vit3n3S1|@-a!>}Ycw@C{@a-tUKr}5aF zka#&WEBjMN-umkX)z{qY9)9r8;Rr@-Dz85WG zR6jE&PMm3U2wL=KJW3Iu6~#djz#Vr3PB{{aK&TY}CHM;w;KxaKL0HZ8T=DnzkBANg zf4T#@@y0u*(v<;tiUE`@t$prK53>Tmdt@$uB2x3%m9sKBGBPPfMuWHHaP^<4R{wB~ zOlTMg>c&|ykm-RoT3DE~q-6fAFhA#OdtUmw=U@TbMc2m%LjiDn&V~Zu$PeS28xH*a z|9$z<>Dh&!&(E2bsZ$1Erap42-nL3*Al#>61z_49DHK)!77$_$0n(+)4v~w@gp$Gx z`Ca{KgUjJ8;DVzOQO}gcb*uBmIVd8&Wt7QI9B5p!$L-;HL zp;7=S6iW)T5C_5>en!4qZ-5FwPw#M0g5Sys6aYd%C<6!?GoZ%=P{(cCFew0BZ4;a- zHAUf5Sgjf}$jhG?pRuKRa(u2#^|tZ(@PVs;f(DVu$zcEmslZ1bOHN9(xsk|fb9{V9 zLUPs@&%FQbz49XCj{^q`1;BwF$W}4P;D8St`0M}id+%>qU$n(!ntRgB?Z3c=$KmdijoJ-~`O z9?mU;07(f*fee-y*H8zy#(|Hj5$n(?rWxm;nLG-SLw+Yv6PY!T4d~`rBMSbq6?x*O zt!1L9ILnrE&T;ktr~l%WW@)Yubk(oFUn-guhLH{eWb6WkF%_IN`1%!b9K~b)t!cQvl<+6N(_sm*Xm|*v0W zI}LyXpZ(PRgEwuf|LqrFJMkr~HlCWFH)S(#|JH4#0J_N%#}FG(Ti+`-tuK~VfKouY zas}VpI~G81)=Q{V48Z4^gH>OF1{d4`lm9zd^{>Usduw@#?2D80#x?n36-pCdf@^}Q ziE8y3aOXZ;$TUEbKHbWP%RHXK>Vw1(Xlm(~?ge5ABp?TzQ~-YFgAaXJT`~rOg=V5j z6hzDLoC^^L5r=m{`KHPH9+>*MLbpcrhc05DXm-?#7+@DEVt^n0{I*DZX|)1BMI08;6YJE;;c1U%tHpfq*YAEX3WdmZ?)%v7SEPEGI201tg~Q?L!`X zFvL|ag*)lhcg~CHN&Cu*6XBLkB{*In1rX2SWXWPeG|RUl79pHe3{ao#sEXKveomcj z3QJc6y&=F73oa>3gVpCOq$1FOK)8M=0>N0k{dED*?_3PX-Xg4qDfpHDn}Vi= ztTa#h*&A`c1k`a$b_TgU#oYm-?qvf5BSm|MtxYY6sp%PMb#dj&pf#ibX^!Oo@bIMP zvAOG5+t2*j0fPQ^^XX4)l@^7b{xPu! zRrfp*O|h#GkhXPmF>nd&<&6Pq1f;nx{Ob%EkPwCOZ+&7Lin+|ma>t&=73yQ`y97eH z3~>)>DG4@#tOUg<(#3jV0qGkUg}HjtqXMQ^0LZ^&@GTocKO5$6rn*rSv0Y8(!@s>C zar)akm5<-s-Gxp8*tot#7Tc*pz$q&-x+Omp0N!&(I!g57e(BO6@m?1YXFxZ@@Q8O8 zOKxtOV9*}<)B(Qu(Y@?X{h`~z9%N|cINL(IbC$&PAKZGw^1uAazx)>?y-zXBkFB8q zI5Nvn037*Yd<&8T-~N{u@55s08CU~WDr*3noP`XO|@5! z!(mAa^VZVGQ&V%WFbs&Ut|8CD*Ls=mulMJrJK5vM&E?CbLUN^&g{nN9ss``j6dq!p zJ&O@Bo&>L{B=Uv9eFO0Gdn?gvy#j@v5Su^&L;m)`0uTr>n3b*v>x!Q(SO;W^v$Q6q zq<-Pp&hH!wq+R10D+de(!0}Bm6aYtl7~ev`ffrxjpL(gH?I-c^md_zf$(CwLSHLc~ z0s=|jVkG?bTUN@^?MEDdKj1$}$riB{WoFkRh|ao{wcg|)Cirug+x%+F6?-1uu}R{; zzX$ZgiSfk{t*)*K*FN4WkqHY>V;~KJO_ENi@w@g_3V*2_VBh>K(k(TCsgCnZ?qYs> zKLlE5&k!sLSP6R)y0Sx8e*(H_QA2b`Y}&X?M%Y(Wd5L@Tq5v?EF&8k-Hb1|{nQ^#I zdSNy3wL~umN&i2K8nH{_Q3MJAlm)8`@6kT=g$1)E^`+${8GroNGk+C$I?WYANBan<%fwc4_ zk*c{vlz^84U;8qrQvjxCVJR4$7DE7?8N>>h0d4az>jX!kQ2;2k*P{*qqvC7oIz11` zE=3*AW3ilEl<3U>n2#c!Dp&4EU35m;Q|EZvGGad~%i3NKvLIXq33|F-feUeLYS^Z@ z_`Zb1)IWayhhH>s{5>(xk7I@c;K(dP0dVAp@h!w0cn5HtvY?__R}!6r(#t*2n51+_g(Sj5|`%`A$9L&WDI0ND3U0eIF7o;K5?b8T?!X@ zz{fwg-CQP0k$rINRD;V51s|IU`)Q0BU|xB0Qamcf#UX{v>g%S&nVJ|CQz$Vzg%nFy zmFDOR4~~Mp-r75TWn4*cdjy^A_Cb_C1;LjiDP zodE>o$PeRNh&k}}KlyD=V2}URKRowG@#ckpfi)nLe$JaVFBi+Mxw>Ulv_OdNgZI!Di|}mTR3rnpI5Ds~Us-9ksI2Z3RVdr*Yu3V~hD(r))oCmf zT`zP|xN=kIg8*as9^x%lkJ7BC;6E2Pz^M9MpeZs|Ln)oy_79GV0R)yRARiZ(dfmnM zIl;nbZ<1LUPI`yi>#2}rq^5`j z*Wds-@K4|TgP$STYTNAW^ic#eOQz%_*y*v4mjg?`KnAjT<5UNPwxVMAxq~52{zTGy zckfkM*k~RrT<)X)mB2!ucyKe6fn1-)Mbj}UDcR7cgLx*tT+12>Bwx!xGr(_USw7&2 zGf`;CE@+3b+NTA9EB|1GKgYrnyw;T^EGgQ%!2OG*{7Z#{V5rjLF2`ga;VU2hySI1ow>7%>2$j6&pY(;Zx_DBnG~_h910cqhMj2X_$at+j&0Zc>hH9BcfnFa=J-Y zbM68#?T5w1iZ++-3W)}I_j+01{?O4{cSDcq{oY&vu@VEo)jYw7>Kl4pBJFXN&w$KK z`1<+qtj~`CNwcB{3ZzHmzyO~R)BjlcaCUCiV@3%vkVJ>BR`C95#qq#|J8rK0&BoclT%Yq!5V zE$+H~4g8|6&FRG4C$TsVQU5|sjN<z+T@pi=MVByErY7o%;5`h)Tf3%6g88UpjC<(n9u$p@U11UaO#dU^1QzxP z9(5d(8Uv4|17s)ftT2jY*6cw|&;hO0hf#R9MeAV6^l@X=e}`A}YH{D~L`3=4oGtCxn_?>J`T+cn04|NX5m9WBhy+ze~L zv#lX3AUF!r>?Bv9*-7_-=hP14K<<)(MQxokM?k4meLY3aDKqwIqq<4@e z2#87-r3iw8)C2^iH>HapRhl#bK_CG{x}egF6anca^p@P9-^{(gKjCI}GLuR2=H#^J zIcN7h%b(%3#31fdRp3t5OD?UHTUt*6?uu>_Bp|}~|GPMZm_#jL#zTanKp@I;b>gCJ zo@L}`S?Cuajd({-{FBhLHXO7Y{#dwSj?*^vgMq1 z7UP0)P=^Xc(WjK-)8TEdj}=RO$PJ6DA>qGpb0W9>=S%kgD1LHG$8QVRNfT>G4K&9t z*>RC$ytTBTD)BB5imH8JDm9b{d4j<=_xr#q`>WE7$UuunK@Ox}H#S&45}dpzSv==b z-_PD9?%ct>iaeKv*#>P*8_C(|H;=@1?q>9-f@1jR6S$5Mba`8G55RLsJA59`#6Ml4 zRxcj4JW$QjaQGv-a*&5azqe)`msDc!!A?+?qbG+&$bkEW>9O~F(Nuz8vT9$>Pi{H0 zc4F1dYtAJs{P=i!om?Kwz1bM*;?&}B3Azd>k&cP^YV`D@dcJLaBmHSdQXEW)GJh2*habNPw$FNE!Y8-mjM$5j zDB>5yX2Ck0L|xH<=V;U?U#3u^s6|=wb@bG>O--W(ucY{hwj_RRUrO4zNJ9@s!7@oI zdq>Zl1R0%Y&7>0dGtK>~H9(qvD?LpZ!);mOZFK`D1S7IgVOpcxCuo>;3JQf;1A5Ul zj#NM<&WbInELfB)@tWEzzpLNePWqf%aZ8+6a+O+}!{?Q|ssK66_&-`3BS{_jn=?mL z)qhT3mn=14o)Nub0R2=*^1!EvAo(!JEJ|nF*r*U$DJ@R4#ilw@QGBx4$+U z0tL8y4eFboj9xp4qC?Wh3oI=aMxWsus;BwS`i4tNtV}=;=Oa>E6CD`CF!;f5z0JE>;tkQM|Blb+*y>Y!ZR( z@;DD6+2o^qA(gp*xf{jPG3h^nI@pf)Jd|6g<;PHZAHU3&mV?-~uJW|wQ~bBoWTrUl zIr5!b36ItJj>{ivSA^hCsK`???=tFc!kK9LhhO#xf8ggAbPYC%UtY>d9~4 zY>JOH7_EI#R}0TgFQ5Wg~2FM2gL4gKqTFr+IeTN1YjQHe<7+xw2%% zY3MHbcT)9#6>6+Eaum6ZbHmLR6bfJ&UL*M!SYec)U2fy!K(VXz@l)q~r3_NwKtX$G zauP>9Q3knYjaKuBU_a`8tdO|H)8l*8@@&6H8m_n#A%*-jhWm&t<9WzObXAV(>!>1= zs>!O|^)sI0?l%2cocvQMZZ-uI0>k=KY>7bL?eUz+|0FFH$D$%k~yTeEsN5Q zNy<%hZ9Mu~fmrWrxI1j$uECc&pUtb7+8vh6dvDs}BRya*LeJn7(;iBx`X-RSmh52q z*LRxmM;hb-`Zm8O#aOWrxCaV?}ay6 z(lpZXJY=rh^B>!FCwnp{IEZp>y*qI@h@bq1i2j5mByygj2GW14&^|Rky!NKSw=d4g zp5Fi`ICw3Ju|Py5^sc&HCR?>1fgX)(_@noP=eW&mvZr@P{JOSdAAF3J$^70rnJQ*= zUr0fiEm!bKX6H>B8TlLZI;2FC1XKf{Nr;d&8bZpSSOn=crGDq0Y~O32Neo4+Q1aQb z?Sxh*xn1p>9Q%Gf`N29aK-%a>9lxJeW|CGWPCq?EVei4OEBXQovlSsQbLXPafl@R~7cN3-3S zXze$INDc1pFB06rHNO(rRGoAY&F|4qX7-GJ$+@)l{8@SRfViyXlLsVj@+kJt){ft)!XbLHB;5QRzOL_e6!7B3q#^q! zrrunLc20w=j1QJ?EF*}h9CYm`iznf$&O;iOIO7Tp-X_{-l>cJmHNZ_^U&aRZJFC`v{spTE>l`d&$w^g~O7X6eP7kmAqn z;9?gI;n}S%A$6qz3Ikb)bhE@1Ezx*uPv;G7J?gN*`foagglM0OH4`8BJis-{nczPL*xZyjl9$U&= z{Cssgr<11UR?AY~-~)a5d(68?sFCnD_OmTl{l1UDkVVl0@#U2GAobrlNgo0}EZ%$rGH(m^DR!wkH<($!S-=n!|uO8uALjjHkxA}y~wE{VS?IaW*$ zV%1!D^R>v(L0K77zdK<-BbXhTXFT_Lo!1N$>yyQ&1+x8cI@6f+1g#RbNYiD1X5KX5 zzbZO7tu#L_`EvkMTfl#5ZK}W4=GT}j-B$AA@Y+|lYnX`X7zGL=Bu^sYxs4;Q65j^z zdb`F=0+r2;63dAiQOuS!+N!n9<M*LxD``GxrSaj6`V60w zE;cV&>b5=N$b)3w$$NTRdQ0e>i%>V_~6nT{~w7=jK|FX~GxZ+@>atgF~tk^_SNLg1p|O zEmQS-;&=EDdf!~+gm|049H(6BBE%<7Uqiiz0f3HPS3}ioqp1E$>$e2PDNPc1!x7y!x8ePo~sU~JaJDzfvgKmZAbVldXGk^zA`b9=! zoA$RKE7Px!CCJ;DQtGeR!m+PzczT;CEK@MCx{Qj5d`4>A-ozbeng@M~Vil;Pi)5>}b>!0pcK?@dqOTpFfk7%d4L-!g0UTIq5O z|5Abpf)`3!clXvuAc`Z1mV@8&7o&?3`rvnyYxZ@N?;@l^GQ6{o_~tqoRzrU)VN&tF&#B_O=DEd`XF1$UxD+fd}Laiff^C zi2^}fSBUzO{9wEt@xD%zX+O(<<<`ucpVl^9j6Z)tqn`>8A(ik*d!443>}3Z~cP%Q)QIj5V{We9qN2@LF=0J?03(jXRI-yN-b-2dgL&p`E&g8$z zK6{Dc*OQh;HcNWX8XJF3zm`(9KJ z)2p;UFgBtYQf@Y77%fdZW=Ggyn}^W8N94I8D3PL1i$D38-VU0geig!_c#ehGpD0jK zeCdCCQA-CT%RF><&tOr5cfNN8(p7jKK)pvKZWG>O3s+06R_XTn6Y@grNGAB0)W3t5 zffS$i^9?|`d$yuPWL$Tv`XAIj#+T7X?L1s=RPWVYeq{$_+28NXiCj>&|1euysX4+f zca7h(8Pca$&zy>c!)r(qHvE%xt%7G?w(C^r@bTde4*k0XTUS5aNCCS0Rn*?t3o4$? z^h$ChalJwe?JRq!nW;&;a2y+d`g7d1L5N(0B;EzHlZL;Bv|Ca?H()Nq+7Gh>?spndZ9W%{dck@B z1pCC!L329z&O<_u^V|FZXX0+spQ;;j;>M46^pq7>`?gQcgGZ(pR3mu>g3q<~FC~`) z^td@D?nhoHf<4wdJT7dm;?+MqePVm4eZY}3nC63PQx|LS9O3+$?2;cbEq{C+9NjUgU%8oWk#}-Jy~r)3QrN<_ z@ytkr%Ag$KB$bA1U!ULM70_00J}6XsC(O}5$SWA>`B2O6zOIpCmSu28u(S7@s#a5{ zHt6p6R)Jw}xnQApSCWPB$oiYGkaG^QUp#UK4xvcw^&NBDdNFIoa+$E66kH}OO)zl? zs&%<--Gu1zE3tuP*dB3Y{>&i_tC_{vuKM?HXP79hy3e?PXM>@yPg8cxDDF973^_J*u(_x4lZ7=HB7x4%I)yds>EM|d_y%P6KLKN-f_;<1EfKiP z5C~#B)?6%yHJ=Jtn|Azs{NR~HOVj%BDD|K~jt4#KveK^DtuhOSk~r2;Ude9-_EN`> zEd~=eKCueBIZ0$`yAe8u4kkqsUtJ|3(fTOSSBk1{>VGdM6cxG%ktN=aJm{I9|IT8F zWf8xoV|xeu5ip2};7mjh6JjyZXQOSZJCBN`*ZE>J$e5GB+|TBW(@tP$Hqq`q*tQ&`{**b3`pjRYUd3yFSuw3ZpZ-SGe@SA2L#< z;(mK4*1-zOAD^dm>LD3{pv6xz-!J{Yi+^YOmF&g~_}uH|`Yna8wp64<9aH`A?l{~M z)%w>85W7x{D2Wsm$C0dGs38|q8A6^9qeg9YP+1}@8wl=4d~q64UaV7s%dJ!_FM8N6 z5RiS*Kr$lJJ$D_ULuBWjPDdZaLg46>te_eVa?2_MhT1wyg2|+oEjKgbyOhBIUVhji z?jH3s;0$N_p`n@0{Ebtk4VI);DW~vC30Z67W_Z@@kaif}^ly>E9L37Nk8{$o0$s|| zCxf|w4pwr3T|ho$sW`aL>(C0`kV4<+Vc0Mw-EIwLKTn!)9&)|dZf$KvP3-n6uYUvekf$A`$Xq)5J3NKF zR8+0N&L2F%ylGY9DxF6}>57cjAn%TbU%`O-hpQc-02GIMe6vyRFq{Sy>G0H`?uaTv zJm{>``aT%~tqqNg!^8Nun12!7ABB8Gg{);M2d!RVbfd_{yh&2W-5Vx~EG$C5dCd1! zLNEsC)=1{bINyYw_0`xE1zQ@csdqdY+&`qTY5hIs`wh--7wh=^)RgD4AQbo)8Icqr zaZEv|`sV%?`PppmXtiHJHF5XTQKV_hX8}#}FH81Mj+4R}0sBJVT1twQVt!i$An*4cDkI)n;M(PgRK3vBIa4SMDGYg z_uIC3OIvu^_#q7x-{xO3ngWhag=OZVCO?>q5Dp_WI^BxQENt~kQ_^!Nw=VsLH7n%u zkWnza?sE0tMwj>-L%3k5QVwz|tRh})W3df^t4NXBB`X9Lbak_GE%`H@T9Sge%CMLp zfxI6FZFwM7812l$Z*Q?fDcjA@f2M{nx35cxT_%U;rk6Ow6A}i2Re+o{8ykw89i3P- zU0-X)#hwn61Z0|7Tid98VBJh04tybJzCm}3bN2($9MTza^Vy>>9m;_-wkqMo5ut-m zeGcwo=SMlcDiLe%=01CK;q}|`m)dHA$@sZOk;x0w#S|ECqdI1O|4v<4LFS#aIo6?q zuUd>n_M3rZfNQ3v`Rkrio5RyMBGh->_axgGXWPXe?$+1bZ>|74UAHnX>D>8$x}C@C z*7LN-T((_zC%}`cTdJPO(|m$_{KTZz#bx#8@eQ`1kP`?Xue1HRsYu4OQKET`29F-- zBMn~<96WYi+23qDUKU>@WDEV59c95eXOl;z##@K03*fN~?))MZIcZ;3DblSk_2@`n ztjL6bDS%(X&#Bdo~2X|LW)OpouNgh^uYb z*6%M#UN(=RF8a0;`0Jg|pFz{{`h>Nq-8cDXE+THgRYks@FxceoBDUO&nX*mQIR3*2 zyE<J{Gy(k2_&OqB=fG7h-;wC{oK(g}luO-|9R;a2 z`Tjuwm8*)4jUr|Vt+CCjh8i$Gt0pbdCvTR(nCxUV>CV%4B!59 zGd{5pI@v(MfP>zJu3o)q8>8A{>(`6a=)3?3EjU@Ts@T~F?ybh1Lw?HN%XJ-O+fZz` zAYsn0S@aSWIGA^Os(sj9ip#e7)gt?d8}s#lHN8rP*EB_+C`FFfXD<;-T3b1?h~fF7 z`*oEc2D*iq^#$`7fCoLlGS{F^4Q=GK-zh8)5`3N=&BmGIW-Jf14?*^g1aI}hd+PYo z@z3ANA%>8VDC8Dy|Fcz}2swwwM>K;^Km`0Mp_&UNS=bu2PiX`%>6I^xsxsO_auLSr zJzdflCm*H_ct1*vWjKW$^lbMk7qz@ix+ms-gUJmy&~otoZp@!VwAv-_Tm4I#c|tTb z=ce}_-)N|j+D1iVX3QDOuJ~3OI}KG*Uwc~eb(v#hd$3uEhFFW6kx#~k2{0`el#sBl zu)O^qb8mtrElComqwPGKeL$kH)mdtT%kvRn=xzn^(T zW9aPDQ~*s?H`ot$oO`5NGCxY4uojVkHoF~?%el?UaX#-ios{xHPycS|$KgUR<3`JP zu5z+@nO41LPpMmHrbjOZW;_&!Yy-PVpq6}>KNOPp4?1H~(UY~6_sq@B_dPXrv0BcIxzX8+l}7)GdvV&P?!cKnAXj5k1}AwNxmwp$mH&#AVi*_bihga&&` zj3>*k8MEs4*aU-pj3V~Bnw?v}gedmg;fIi$Gc!R6lJ5rdaCAu|UzbxRnA03dOa!B{zMZ z_xLJmOy3^e4&h5y_g@`KV> z(ZpBZ%hA`*#>W95Ywu<2K&R?q{U}GQfa?C*j06=-`VqoHHqNyQf=jFj=^B0E8 z-@_Y;4FE_;`+M8ixjFdK**ZA6cuF#yboDXNx!6lG7z=9hXnM;#IJ>C4@o~_9qh(pLNBMIw0+@w9XJ@7^xR zz5jzN^#8>bllO73@%8dC@bYs1uNLSyd--}noV~p1Or4`>*=^KkD?~DWs169sZATARqq6yc|4{Hsymfp0~uTZ2({}jjDpI zf&Y)g9&{*^@hh&N=e~ISv+3vC`F^ckYPH>QD$lUPV01R%fnn<>IBmR>A!vsPN>-Wg zXoqW)OW&2iC}gS_1kXywMYSgQ3*@8xChKdC_xJaCT%0O)XsX*PYirnl2KJrg2ZAbV zp7YoYL}C)NXk=!2CjkEQ@IM0$3ZV7BUuW}xVE@2n^Voo)e-V&_0Sv%@29bn-e`g{w z83D7M&ywuCqW=LhNaRqfbNC+&Fcb+m&s0axo6~%jCkbLfZUutbyM#%Mi~`e50X<(- zfW%4wn4-%v{f z386I;6iGCakR0XHX4l(czn|NIq71MX{Ra(XwF7d_yXV0it)r#cQW*>i&{P0&Sq>(k zHD+QB#qIf%NYZZsU&9Z;U;RP~>wxFL-E%BBS5`bkte|_f%~!18NTJ$5t(t*IhwUZ; z^)yj?Ne-Zv3bd23MVa|Ut^V&ph|oiOF>bJj|Jh8ZJ%&1qhvc4 z98KSqG)3=<)#ZX2Bs#O6?f#tfRzr5!aj zdouP#hc+LfZepyrv;k_oG6I{H$7YZxN1uq9W#h1Zgjz`HEkDRG(y8_H>v?bue#0hF zoG!{g`v`2 z5`Pw~_D|>Q;BmVb^CAjT=n46l-wdAt6UboELAZ-JAFoC>{v_F9Hvk-YkWmX+|3`;nG&)Ub; zfZkmrzv-$bPN4ICj~*OF&rl4wmXg>(< z3?e`2xc`~CJ<)?;`2?b`l&90M46jxx-Suy*LZjF5sMo=zr(G42pyI@mt0^FJ3o@Qd zBhXhvX+_yBcfHK*oX==}ob{K!njG9(?XJo&VL{S;y0h_Pv&G1A1BR<}${?lXi zSeU%kLoE?lW`3EI5vV~{e|kvBV>-C*CA?OIim2FjTto4?T^|uK-mI@`DqDQ5y+#7` zaCyBL4u+M#w95xse~ffUIuqK3$QrA2-ncGH&3!y7@ZxP()_)@S!(H|mGbtxMn+F@| zfJr0zOz$W0+WM`h&KLYbZWc+LW>J92>0)m#aU^0}Ux}V9%bF{}j3z6dhBf00fIrO;9rxL{8Xj>>)L=UHY-NWd zTJ{6lgoMJoWZETY-xn1C(zR#vu;IfO4nrJgLuqcL&%=_XS-Qm6%s0=c*6@=C>8Lfw zb1II{M5<=8ZSkP`?A_rQ_n;av{|+zI1UFa2zf5z{}biLK7vBeA3}M@i^37JCy$p;BG;pn z(I2H;J9D$u21cFTwHh@^{JllMDGnLY#C+9pXu^5!3J&G<^nk=yaO)R~tyXv~$9pr& zwas8mp7y|h4a0sZJk+#V8n5c)&0?V6UGD3L)XANZe$s&rCLBD)?T5P+PY)yawp6eN zSrMwWly2KoRT&S>;LUyUzC*W_AE2mdpd*oW8yXK0>~GY?d58}~MBq!dwe>;}MO~7X z+`3crRO<3;*7lZqb)6N1pOC85Q`>y8d8-z`P)CovK`Wu)7Pq{OH zPrf=$>RLAT>fAJq8L?u=0u2%qC0-VBuT{H0gzD84q9@LyI(>v{(%tI`04z$*F(M1v zqA@V5Fg=}DPDh=d7U}?eq<{>6H9hacGXRUR**!PezH#v_ZO2CRaKSxIP$uLqub|Xu zWk>IXN%JN`kJwu`Iryhp$%Nye@_^1*2pSM$K0uA;Yu)WqeLGyp_{AjLf7sRRe7wxLrQJ&S-RPT?HTZ*9?=Is;98a*q|F4yWAi-LRbma~?;aJG)-U%a59yaJ6etz6b<-s~3)b(f&16SSyQ*-XbX2!2x=%s?ZQu zKzS%=TkMnRwotP`MH{u>=X&1$DuY=!>6YDzfr->*c>&^m`#V~+%z`f~a6A93?&5yC zZZhHPQMzPuQ)4St+1{7x{pPo+rlu?EC90i6r)Ldss-@n^?w>|qK4d94sjZFU1UBRJU4{*~{ zt4<$>^|1D!)5U(uPpIg+)F|ARg^?OcK-THHPvWisPEd40;s{bLUCLO2BIPS7)ov3t zq&U+xQ|o1h0Oes@0&V+`BXtVTSChxW+mx~3H7-9Kds$Y_I)IJXVo!ivY+r~yx_$~z zBkp#JTIm7XgR*u01b1go+rVQpl0C6fP)ZI&1)3^*jqp6rY*&s>f zd5E0Hwf0eri~{m?>aAO~bR<8EJ9d(~iA5JNUT#F~=DNEt3}f3pz$aeEmkD&^sU@>{ z^AkcoyhHM!D`7|mA9=6vyQkj3e+9q$-+*)y-C#8Vs;s`8rou$ zb^=350cKntzWo!cCI^ZK5g7a1eLC+0i>SrVTa95q*-}XHZT~6)GdBEp-2D~|5kcD} zi_>r$Ie9?%GFu+KyV{#*p$;c3eqziGoti3kfX8&(?SlUof;blE18>_GMMH9q1w2*g z`zkISzhKL;!8P8OXI&$jbY9FTXqcm7=zRZrx%j6KxY*G74Q~90HAJt~@(HE~yk+s= z<;G34;UmK|xX#oN9W*lAq@zo^yH|kBZhU?=(C)wsw35WgDrQ}-ZH#KT7vu3h~dSY-}Yd7#XBPhWa%gj$+5o%a}O;fN!k-1nW$| z4LRpA zh*6@E4R7~;+Z*mQXzQP0Wocpmo3AHYBhdpXC)n0I#4r^=x+tL#D$E|~E?EF*m^>QU z0YkwJJ;y(WYrXuxI@wRR53v17`7$cdl$^Ri9@Es@&(^Ub954i>I2kxv_yf0lOHa&V ztOqWPFp>l9yM}k=w)bvo2TX5dgxxb z>#2mwN1Rd^aMDc;m+ZFRR(d0#@PvC8P%3k*tj_iR83%BR&vod7$^Nmm<3VQ16Oz^| zz+uQ5YoHapc}>r-GaLAM0jWNJa~U$Mhk6Ji;2fU4hJeQu-HtQScK_TYa;6E<9i=Qv zW5b+yL0w*CY{r8^hh_96U%e@lhU*Updz+rYjc1%EKgaWo{AQD|QZ@TenNM0Rn2TI! z%k)mIEPlPjn$lBNff9XE=BBw&{m@7Wi$mu{M%A%W5TZ0U)Xl9H5$lIo2-LF}&y!F% zN6#H(HGYaG2tUsnu12*M3fIRx;uJKM5jJk;k7#mbt94pQizi5*XWu_>BRA@tB_c3o zMOj%sJWeMC&su&^m$c*5GUY^x($eV@O3j$H8C0{&k-gY^N~{>qWS?*imvn z{dy37G@9~9;yqkp46II6kul0;Q_Iq#2eQT_0VdOwUs%qYP$tu4G_E#yl_i{LY&+}p zZI4Y>>I4iSY`^M&KL%J5g8w|H&ik{N#jc+(8M8Ftan5)Ua*K8*cP?Tq5EBwr;X5H0 zbm$3|znGu-oqN<^_o+PZ{Mt%FAL!34)s9iG0@|u@GHa6-74_d$M*xP>!PWoaXqZnq zUwVHD#tk|4b=msK*&E4|$MumD{f;~QfK>)3I{v3aBpG6Ni3y5Mk2oI$PQNth7@Zq? zJn!9Di>v$Ynwqbr#?$kGqxNDpsQ%XM;}!a`@rPu^<`IaAI@p|6GGx4<@tsivSu5Vw zYO|RoN=3}a+1_#V4?^s%AAEh7B}TRKjQfAq=&9K?8t`*3wqOfS2G!U}F|U)h+RM7` zjO4aU@Cy1~AVPY+%@)2sE57xiw%>|C=4ZxuTb+Cd2I9CiGZStQ&x8CvjHuWU3%r=m zk6~*MgR&W3aF1m5Em2|}+niO%jxrsQ=J?%`H9Q0yYQ|tPPkqaEl`!oOS1Xs=iWq() zoj`~zH__PPdInrnQ!ARj_cl~lftGf3hP=9DeIhJDL@9dfCH?G+Wn2mrD-o}JI4$mF z-%0=_@85#SxBf!*X`9X*-?=?s%TfIEW)wvCehBT|=tv{6#naGrA8|#kywl3cezm|# z%$v$tqTOQz|6`C9GdYvwQ^lrLuce&XdIY~y&A59~P zus$^f$31s{;8%Pzn{MaS2X~Eyo6CTnQ_DN@RlK8CK9m}_!)8Q5CSPp05BcM)#-+1J zpJ0%OS4(!=MFM=5vxY$FJDZl&THBX?E%L(bg;h_Zw&w80$`J4PQgU9wCtc%%sEIbS zDUWM`sFNu5O@X}i7DZhFT0pmlqD|t<90I`M%LTUFp=Y=u=+8bb4^@lz6{}!!(D=?( zB&$_~)a+s#15>$$+caXp&qx#46L9|4o^p$=+?x)%e~Z<=P;TWKYC(6a3kBZv?et8# z*SUTFLpiZ=0a+@mnvxbl?NM^UV-rpWnbb6T7S3s@maB8mJ2J zd{GZ=_|EPz^}_;1GZH`%lO4yj?sGC&Cm?FtCTp7aKly7}B`vGHg@E?!D|8DIiBuYp`{v>wSi=1|;cGO;v{N)cE<~d+TW` zoYP6jUEtP6j8WZk(18=v&sBz9C#CVbtsEP(xsjwl6vI%gB3j>;dD-9s;XvIoJKfzU z*})j}maH^je$dC4F{S{r=BZN|MURM9ue%u;mMaCHKLgng5-A#wOV7K@X(5Wk@6<4$ z8Q^C^MR5-9y3Z-xvJIhx&>HlRE|6HhX3Q45Nyp`rg|0S_H9~hc&#U_&V9}SraC<>* z0W-l=4fSHCfiVLPAzofe>5Jhfsb$JZOzgW=uffpvX?TI(N=V?ygyeOQSbwLn$VH_1 znF5toK4rMC85gx;$3yCSYD^i{zKqpN0-TQKvwakK8wzT?$uV)h2efZFm^lW4ePENC zsTs@Ujj#`9OE;tBXn2SMT?vdQ+|j+oQCe;E0E=qgp-u0&`d7F##*Yi0^Rq2#1d^J3 zCw1srb%tZo_7o5{>JxyWYWQF(16JI$f;4+S{@o7z~TOb&Rp+1jk zRdhb-*LfAAjNNx0kz2F3{;Sq++K1A;`QlwDuPtE$J{~;nxs9re$ly6XKE=96VP3A&S$`{H5LkA-2CQPAfJw~ zh(55(HVHwB07FC8r>-~x{xcRfFG#PX$Xqd`G z4CLXi*=znz5*CnOG_T(Ew}xp~sX|6G%2*QW#uO8@({eIqPeIhgTb$*Ni5s$a>*ORW zK~BHG2m0^~Dt%zOwlx3y&6bDu$yv-NS|5>>MKwMF%=a;IpQ3ZcMJnAsn5n+NN&~OF zw4|0kbHR9j#$ZbD6eKeC>!rB!ummK;@5lWpoNXq6KQ8~QBa)ws{ydKQyQQt|?k6HF8KFkKPva}TS0`=3u)eY=ZE6qRP}4#+Y2SF&U^=hRxHS69 z?`ixXZyzIU@_^}@N2`(>S)lts!lZh42vZxR}T=NeI0~3++nDv)z7luRn=sU$bn#9*&{8xRFb?w>MD+6ovOj z-#w4g0awP3iD+mHxv)rr5szKQu3+flRXn34j|9;;x*_(I?T&J~*hKXrc$MIytA7XL zfW1gZsp2#>SZUwwc^rumo5g~r-F@2H2sb`i;&Psl5I_#)uZnnGs9F`j`uF(E9`FDHue99)YD?Ez8 z<&B3}VHJwUOAUKB_}Shf+=L<6L`zv}`TB=bpB)3R zoC5^0#kB_0S!ENiuY^f^m0e;h!mL)>Lqks+VnS{)EBSA@R5z%+4ScYE;4@A$U0i6& za8IkS-7IB)?fM}PevoY8ey>NAFEX!8m7Ruu7f|v#e+V)04pmTI03KGN`cXp{8yIfG z4^;e?o}AlJ>g_ob=Ep8KdyF$#o&B?D(?BGUra!c1j`+)yDm4dE7gr2e+{B$bkP?R$ zX<=`-uZz6K>;-D8<+or`;c=>hgwTh~_Q8`F4k>ckr1o(5R^W^dKhOu{t*nz1V3=Wu&E)zE8xyyLpyF)-dXw`j{t*fpi7m zTjAaLw_266XQ^wC7h%>OJIcg-eMXAwi}Am46Qf;zQA08$Cbrg{9%5M@F5U8)@2Mr1 zv?1%}pYwc}o#K;GhJ&i6E2r2){FyY!FP=Jk)kkH#xTGUuIhIUIjX-It5^u^;IVy8wB8o7FFFm*9&H;@f=U&M27GIU-#{?~vm;F4TNc#EKVzXiSePN<-zt|Jd0e%JcF_vJ)i?}RdtSa5N(61^G4 zTy!xuh`&3yiN_*dW^G z?UwQykqyxNna!%a(_@Zb7h~{TUVh8=jN~z4IE{x?Ysx=#0&T0FiZH8oMLsNG_LGsb zXWdvKdKtd=4`3+Gsi+Mfbvr%D?|Q&66_en>Vw+BK=q9yfPuV*FN2JO}M^j%Be`_g# z;aWQTM=f497BgC17d^T^Y+w0(eze6!q!*V@pla#9I~+Noh!E|SGE&nK4gwW@2!^A=# zX4*O}7+UFyXA7HXY1pYWr&Ic4t0@w+huL(Wb+=P*$h`jO0b!j3`@mL~rDdN!Ub&xC ztnu0tBpbi+Zl$$;iEGJ9gwr@uK7^C*8F)NEkf5btQ}U2;2Ir@w1nb797P>gdvc&h$ z?%X5$)v=?d>%xT_7Lw}B1<__jDkNaw@Xe3w_7ixByjH_%U&c+ZFZABiAS2{qvh?XQE*i3`#m{aVg4K0jpqy zH4H_(%~ua0!@&YdUdGH@XxA+*nW3;j)heKj zWNUfmJ^O`7Vkd1vWu35P$9WFjTCbiAkU}I zn8AGc#jq?Dwa$oHlev6hZgjJ7+HZR84tA_d72~>nFI&i?l`4H(7IoG($@))b)9&Wb zBz4R;%b{*cc1$Q8Jzb0nv!Kn}R06v7debF^FdEd>4aSkc=3J@pP=Ug5fT&bq8Ntdt z-vWTMe{%ShN6S9VQU6x{i1y|08dNT(yE4l%x5wR38z(j!y{GGq^MTgdwm;UnpFkeZ z5vM8E9jHL62!;gcr^c9$16J(&dj&3=8z27k zn=I01xy9YpuNYNGml9U_e4oaq&p>Ng4D}4c>Q<3`0!(9?1zQ#wf$N=51fbwF zf`qRrE$;_$wTEHaZ{LnEgYKBA&Th*e0WG0Z(_SdRP&J;>OBM_mjr5XwJ&jTC2OSBz z#FODoK#-Hjci!qRT8W~Zf8^$~4T>`kV4=)Kl+oB?qxY^XOx7lbLW1>uCnwR`!IuSN zW7v*FI__$8H(_;e}W5!6y4_E67Qa5Zl;y~ z3i|2s-Gq3R`&SPcFX*dkU+Q}gfAb&F#&rdhm$56KKX#1{iMS|d{3dy(7tMmI#xl#p z^ERHW@#M9*a~drZ%H^vN*JI&8>ZAn4x7%xWn-Um2q_OgOX0>kTTogrbooJKHEH=U` zqNz;UKZ%M1A`RQJPqvXm%CWd$o&m6LAz;;z(!tvnI0zJH{A$8)ruYlL{)1h zx}DrFr}9ryG8Hp9@$S+8l^{>!da`TlLDR_E%&Ym<>&GJJ+_h(?&&OXNB8u`OO(g2e zvq@sNIYih_^P0D69Ao$=aG=ix9~-S!Ra$~PCLT{?fnvt>5HQR=$Z=yjeEk$UApSPG z5X@Prbo^SUc7#XVLT4&}tvEeQg#*)R5UD+JNsKNQm zg4K`&dUn&)!kCsIVKM3lkUh^4>sM5r-`0*n&taA?O?wb=IS*fZ_^B87AHQ=CY3}C+ z_6fY%?yOAHCs*buL;uG^Qr}oX*2jzRSZ_J2rphYd$Wzd*a|2n$`sqJCJ0(YvXz&)C zH+t>v$@pIO>)fI^59;T#+o+HPg5El9no4EL6o@FbWl8{{y&S0J(449TQbDX__wtj) zJ|$Po?=8!;mglt5?4_v1A112dkj!&s#Kx?i)PoH7rORP3(x{(|meXtGzd89k=6H94xM3pb22bxXuAfvu?xUAdhbJ8oMt{hev($)ZPkuc}h6nO-_BW-Y zbc%+aH@#a~tJ(8XuxSwyv9Kg~>bV-GGp>kV!@S#U)}ckeNS@^C7@9!ALrq|)UcwcW zfQ(PNM{6R+h;;?BhxqABRNeb?45$W#{B0>r>@!~zn5PLPl+EqRibFjKJgRd-4%;DX zn``XcX)Ne<%un}~n*fTNzaahdwZTa_lQv{CS@bG12i1B47K*^VM%akpi3^U1P=;L0 zP^vhBL7y0$W6wL?AEb;+ejQwk+I-HfI8=Fc<1ft_|20RMXTtJ+f`;P&d-OmbpP{o? z;F(?=5r!vP`jH5(&XbE79w`mEcM~cBqlpV~<4?qj8zzc+uHv(lkyR#VhcEDOGXRsC zBM%nT6swl(u33~Xo5Xc#+qXB~5UdrJQiQHZ)+XrgzuTy!9r9ap@4HTCDN{fu^d$5s zeWl(D{&WF@0+?--RwNN4{o)+4Wni(Ih!<+2u!AlwiayP>%giFU+- z9E3S1?mp`ahPbt1{r>&)CB=YGmqiJg-D_9AHDIL=i@WizSbjKxB5M_h9<;}lNG(%~ zGWjyfEP&95hcZdFlBzTm9mGZPRNX1`fFaW9U|Z`WO+CsF8K1iq01lp}hF^kZN1`(k=J$9 zzou`BZM|-V_b^@^O!%;hFa1Ylq!s=6e$D{&8oQfJ9%&9lr`mMISf4OgG1LXg>@3G! zg(j`c^1=nw>C_|&qsyG{M%GL#uoLWAlv-r2q}9YU?!&KQ3-ZfmK?;F>oqKaoI+5Ks z9ut!S+&b^$oSB}C19XT_9k2U$!wZ&;UDxr=enQJw-&k9FGvw72Q!H?^lwd+dTI@f# zK=%(0L;*Hwvv&@nWqA+3nP06kaO;l96zPAT{Q&RZ(?GZ9J-J2PSbYrdfNBP z@Wui!%;@ej?O?jguJU=OD%+%GTlsg(w$1IKsh2_rIH`MYx*aF>25tq`ndLz5aBj_K z3doL%o`M3OZB-^gvpu};Gf9_b1~H1gs5>#5Og^UUk^nbC(R;3zZDs7RCf3k1w6x$sV#9rFWD5GpAu8)nD(w6sNLqU{nP z)D#+eU*IS8Fv5sw(_S2Lvq2oe!;?SNZ-wFqar(t^QyY#tu3>m{%D8(SuFL7{%mfP1 z&nW)_uIbzd<>CXjhR&B=&VR58PWmzO653BOq*OS{Zf-58S#78!5CCXjfmS*_qIzGH z+l0p?SrqFMG}D$?%jN_5^`Z+``sIMNb0EOdUh{9vhyApigT{wKsN+f9`E*I3a6246 ziTf0>Q~BFK#I^D+`zjxgy^7Rp`Av+~yli!BVbPZE zt@!7%rUE4%kCkN^V5t_S0n>qyeCw;`$-SDl7^Q`i6`OLDSet%d-ao7taw8Cu72ReYw2dUIf4~_*%JwVcNZ%Q`q+vB zzqbmB@7g&-G}lp$_*?>cBBYJU9!~skW*J{v3g4q7B#dkjOv9@StDXnIEK}x`TVoN; zvVytM3*L`AEmW7g+SXUZFeV8t-C{tLqhADu&S~0Pp=8J@rRhCqr6Nss&c__EJtuLd zz+gx6u6HU#ROSoQ?mNJn5tJS>8dukD7PoLLMmrl3pF_Q~fO$TlhanQYw>F8{>S2V? zeXxv9mJ^jV*2CB0-YA*jDlzH3E;@_Vk7udeIE6Rum9L0khH9YRGwr_q)jQZomL`g+ z5E%CU5t6C+3ntXRsQC$l>E@3pG8lu~5rbNTWZo>LeFxH%$ku#m#QHzlKY-43d2;- zg>4PP=A+V}Cp~I90VSdRLGsP%ANPo`V-Xh+Z(=Q z1_!=(giY>HL$%wmhyk|s=PVN6Nk||z zn+X!jn{`~!+PiO2xk4|-HtKSncx4iMOMc=fUILw6F=VC;3MQ+708~OS3&=+42gBO9 z`P4T_A;NM8=*URf+!BvhLW|u(I3RC9NVaOCe!OH~L77Ei6P=r8!q2BA7*3in?qFnz z30i-RjJluj)N-{7_8f{$HFJ{+H+0i#iU3t+RVYVAUkyLdeDPO^Hc$!(IKAoXRn}6T zVgL-17 zR`S6Ix(KAu{8c%Ttb1yV+br$GcP@YSK|0cP{vB-X=%tLgI;U?6D*O4F1s*dt%m9%z z6w_dzo2*ygasef)kVRB;e^N>OM2KoHioTW8{m=mTOpZ??zn=<@FED z^~OVY=yrt+{r5d+OaXZqXXnAAB=Q=djh?#VdXrW@ge$9$Pz0KjZ&cye9wo z>LEmUE=4-#A$6@{&O_=71R2yQ*AETfEic?cnElM>Zf}edNa1hTz4h}cAsl|s>SLZ zp1BzM+&$K7GOyC;JpVX>T}Cr#{EL#w{3^gRi?P=9N!Krl$8{BoXyvzLTCny`dVqNQ z>uC=WNo39B9lCT9w~R`6i-A(uoIPl`l4wcOE`Qy`L@Oq1(nppwTG3MtNWfAKlRT9SARP1_Jm6u(jv^-N9 z8kW>%#&s2aZs>0#!LY<}^TL~W7Zsr!PEH$Fd2O?UBCns;eR$_J!42pS z`J9Wqw-KH~dmR%MJe3Q@c3s*2jA#2Zbk(7+yb}$tXqO$Brt~YQyqIkVm9{FSob@Ba z0Sn@-k`6V)rL1!mzKr~+wkDjq5;u+HK^CI*FtnjQ7n>5IU6yW>E5@=gzW`mSw~Kbd2@K({g?uQeBJ>Z0kN}a~vm^pQ=QA{pDypl7csL z9ZkAwVqKRVDH(F%o$+NPTcnDuuV^&hBmW^o$_m>@DIy=g_QX)uN8QmqlEW(*Agz!l zT)9VQ-3w2~;{+u-qte;atzW0yYwDhAGtC4PQztQvLS91GcIb1PQ7p3czK7|Ps!FXaG~BAdORU!rhz3(`zXoNX~0Z|a=wOEvA*VJyzqG;v3@JK zkums}S#HUnm2%#Wpj*BKocwT&=NXLP_|CU8pv>IDlk3U%K12k6#I12U4)WT&AJ65L z5%3;G6{tl57?v@vHGu)+fUT+g(I`!DC^J%cRQc-jU^7 zrvw9lKh*#qm~VQtzW3B)n^8psEOVs=Cl}E`DW(hS^-=RNr~eGI{3#E8hLg9@>H;oq zicUjkT%;8?wzDAA5e(HPRX?b8B?HKVLfSJs31|h#D2P@CTtTtW{8(Kg1*=JFz+h0) zX6||UryC5Y1I+JJ&(z9CJfr$u2KBcDvvdLr_q*X_RXG@$&24YKFcQiPbv6vklRtlA zWWy+1UHzN9S&VIj4x0JB{{NV&)zvL zjbz{mfLC~iUoH|7ogS`laQ9S#6qy^}c?+gOUFiYkX%8f*h#1;{MK63!ws!qf)(AMZ zw6wZFNa@l3da_Ta7pTq4v(oY*5jW&CAYWlw(^32Fdux1Ps6agxA0JKBV)BKINIuG# z&yP2SuQfwrSp1SlWaCjv6zOx);!tdP4LCj!8K4!t9%+Hk)1pRF{BXfev7dCfA?lIf{JI4VqD67Mo%%nA9*zLofD_=0}dy*@0d&1{ze zyDileXqWTN-}lRkfXuJJuY}=fpBlWZvF>6P<)rCR9zWac9z20Il|WM7YW^OD>E&{D z49Iw04xQe(!pNP`V!ooFWZ`LkjZIfYwWen+DNHaWjs&;3hyLM4#}D2!e$E68YDIE} z4lpZpf3n)%2B|g#i)D0qaHbk{q7=3TT4Or0d`JLd5!#VQZ_DUycja zzMc+G({#)U7y8+h3#8drElLZZ)*1^;d5kB@cFc)eriw{$viEZkknhCVossxUa-H6< zppF>7ClP3^j|s#(E_K}9n0|(Xr)*>U9GJfGZ18g#L2{D3gjB;g`#}iGMKuLv&JorD zI+P)3)riMd1blk|Q5nhFL0AP37*GA$7gJwTn3!nhR^&~vf{~YHKY1^U?tk{F zb^>u-xJUnltIFXGTL;}p&_m@f;AC+)TYua*U>zPPR|2yvK>tQ*1wda?h%N7jTxfh6 zoTb#=y6<+c_UQDSyw7vD5vOM-0X$eTu`_b*4f%RiDJAZ|8-G$1&0OqSfs!c(%qF~% zzTD!cWwx7LO+iIKc!cPI+*<;n{68&rDJO}bq~O!(me3=^+Twq zT^5k%U>jbdng}3p@u;B(TjM;>L3<_=fGS-@VqSl5V*ni~D}O#5w#uXVi;ld`B^-Ut zm-KLT+R&RiUs;KdM}nqY%du?pr5ui}ft|)M>m{>>io&oAWuP9<5l4K!711>W6pwKuCZW zf8WO<(e}R74Q^~WiVd7*g*JZMy2{GveCH7IBi


H4Z(0R9wpcCXY}&;vCND<AFE*6z8P4#-?n(9 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 literal 0 HcmV?d00001 diff --git a/app/resources/dev/brave_win.ico b/app/resources/dev/brave_win.ico new file mode 100644 index 0000000000000000000000000000000000000000..befc37cd24d5f413bed4e423a084e80d3d7ac0c3 GIT binary patch literal 89003 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

&IC|bXpwZx1Q6!4 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%0A#VZXb}mtZ*5Q;oII;D)~^bB(`p7Bv0c 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

>Cmas|LO` ze^=vK4m0A7Q_71w@eHWPOZ-z|Bg6({oBQVeK5Y#?SMww+N(U0CC1?U!=oajymaoB5 zde8qi`6@^1_MF}QMS^%nJwdEq!dG?Cr)%7?>xNgjrgCGJp=>VkEwOK~DuXUDPFbJDLTuq;Uct&b~*_Y+ptjL}HH z(w()5PXA-+4ZK(hQ|A&A(!$7wj0BNCXElYrrX=*s8k$B?*T|yyCkGC_Y=1aOdyetfl)mERtfyA|;3~e&T zb2e}dORe3RJ>DuGmnLia)G(ZTXqYon4!hV8XISq3^v$On2@#w6UcM@|U>yw1+74feY1pw~+I^$XGeLfbMtg^q8VJvZh;ud5_w|J7(zr zH78=g{)`r?q1i!3xYN0cK)&vidZeXAep9$q4F0Xo#;I}r+h>)#^;>^TQiE5R-^wrp z>?D}xwc)nAEa|kx{h>acw$yaLHeE)j%!Hiqc0etg2>Hc(qN~e!b#tJscb@`3rM8fjE+NVdC ztGOIdb)i0Ltc6hv^{tLV54lOd3<31ts-{AU;)3UK2;=5cEET_UqJ90&ijx zmjw=v#U1b%$lbole@+Po|Dw@3j&9rmyO8H8?oNBxC?w8)tv@=r1qgM?-IR2BcYB<+ zhDApe*zvyr=R``TKKf23T*i8F6v!8F~0*cVfgO= ze@`1LNzt^rzN+aHfPem1@zc4CX7n^o{`%YQfS;jWk>cO4)?dcJe~Jd!!@%-uhtIV9 ze@_DRagW^%*zSwo*#GmW<8JSqOYgret%O{BFQ-_O*o*|?{Z3TOS$$P4xS@WX0lwez zewkFIM2)an(ymM83 z767R^^FEXqd?r5ke){BK-P0aT9O(hr@n4K*=0DL8$U3S5S!>)h9!YyY+lHG~E8jLB zAOCp>mspQB+C~cYU5k1u=-3PcCFIXszdQ(-Z5C0v-2N_~2EMnE#8&bfaru#V1h6EN zh{QXZy%73~{}$Yi&s(zn3{>hsQw;DPLWVTD(tvo`jTt1>Y1TDtY9JvpK)q>~?(psk zJ0WBBruPjO7+wbxDhTxN-V!s9w{##Ew~N1{Slv7A`sI9*jyqrs0?AGPKkFadJo^t> zW5EaBBQ*S4*zcUEnm%81$bT2li}E@As~x?lSHDUx1HrnpR(hyRq1(B|n-ygq*|C_s ziPy>hvQ@a12*Owq5Y4kZ>roir+_lZG1h<&abHSuOxFn)ld7Nwo47kfu6JUExV9||% z$B;)TSuNjccePXJ9i!l}!|JmL8dHnn>Wv@Xn7iL(68UA*%gJ@8Y?*~!Qe)LMec3X0w9al5?n=~)DV)7wDOJyY zuu7ssKveLCj*tF*_4#7HS#3a zMH<_5d063r z-AVs37!O3^!$Fn5%QpxtV56Q+dm_5Ix>UgP@ENIh5$Y9WFh{MOWWrVy=^Q8CN*!SZ z1yB?#4Kj#Q?lUdVRBck&uK1+m2Va$E6dKxNmiDRugMhrD6jXJoYe`UxN)%@TFLLPf^%CC0^{c%WJPbo3K!d+M=7;5@5&n1ka+jyAxHRjYwqc|w zTR@@V?JXvZju0}7LF1y}HJtnuH_vDI0Vng2Y(m^9C9B93MJTQ3&y{CzE{#GlJQp0a z?d5oz0$y9=#EFx4Ym zLieL@K~~cy_-4UQGKx@EwMmmiUS!tG*Vpku+X$}XM@eG&NzPS#-k z-TyDr5nbm5Q?V5BA&rsl2ecgaE+Uk9jiUI>VC8H?7|OHpjN*;=0hDelD;P76RZYHdEo|!eD0I$ zsThkatcXw3is92MA+|ffD+Cr*VWy~3)K)>HHUo{zAnO4sxU745I~G18_j0VTvT2?bSvw6SF}#$&^=--j^2s2; zGF4ULyQEIdhy@F!#mXHtpOkJP0rE2> z=NpEm#sRN$fJI4(f&x7N#XC^V-@inSrX^x1Ytx~Vv^L+*&96n&#CsN*+kCb9 ztUl>b1jF|yBIXV5hqpdz@M2GQ)56YCcpf?g-??4C@Klyk^@RARz;^O-kvvoz40M`Q zAZ2Yx50&7_s^uG9tb2YjVjojdSgo&?3>urwO&`xRF2p|7U!J>o2_};Sr+pLCxaL{A zb{WAlwDZI3TP3uF)O_dC{ZD&QDjv^-%z4bBcmveSoUJ1KcIvH;yDM%xjOBMVoP$jw zi|8Sy@PTn^r(Oy1OnmYi?WC~?1+g@vmTqk2WLJ$iNBN)lZj!u72^00#+-!tqZgBh; zUXdj?MfaS`&LH+SP-u|^6ht3gj10Wfe3&)4#cU(b*U95OnXV>TmWykJb&fl-1{N{U?bJv+hcQa@{_gi)ZxthXB zQN@dFCLmWqhVf=MLVr6GyEDDm+%r+{QUEDIiN3VAxBO4m&$EVmHE_hE%hPYBQ2OsZ z_kVHEZHN2+Md^Lb(`73l3%BCInk;>;Iy-9>VP?oltE7b4pJgZgAwEv}b2@AVDZpl* zm=I2{x+lp*@!}1Ia1Y)?n9OxO zt_c|#rRX#^wV3-&yjg0(ee&qHQ(YSPsyeWEesiGh^(qHkmZB%Bo$2G(uAjyB2a@XM zd~j7cIdAOt(Ov$LNqQwk<;)T3L~0PBb>vkEIavD6{P&JzuiF#|nbk70yYHX<{iH!M zz8v_{#y7Zx6h}`w?+rE1|Fv_b(Dsc+j#ZyjojoUQi=Q%?x<7s9WiXX+zyOv0dE#wo zNyVeV6w`Flg1OnH2oD@6eePdC9nfVTiMaeS^T&>h-C*e3q=W}2F|Y7(^L(+!7yu33 zeg{#JOU8#hXLGt2L^b`B0@LLc5}-kTfd}jPja3wHnEU)VPtZ};KS1Dbx9FE&MqTn( zBwa5oF1UDpnB!YyraE3WWh%ItxZY?`&>;bQ8nFC5W9#g)h$(#-Fqt$DsYyBE#W`#} zDjAGh39*lKbRJ4n^V#1`_zc~eTR-eCNzwHI1#}XQA92$>P`;p0@#hcM?yIzQ=YnbB z1Zl%m9NFhc_Tyhl#=Febe_FCUqw9&wyQ!l(fJw$s8wn`loaxvRat;N4AVe~`>ScIhb|ua!)e{&;c19j#A- zHvV6m;QT;*OCY|z#JHj)XCg$2-jfQ>n7c6BK>QDv5cd=4HkFQ%8(;-E!l&OF7@i+v zWDRgEW>6VK;Q*f3S2F0CIJmL%gCL{ORr#9$bZlwsVAe1!Xv05bV*kq2`Z}mN_Mbwj zIMJa>C(Q6l{EZ#ZGZNF)&dx>-sE0sMs2QZ_7M)qBbCaLnqk!Jud!T!jZEYX0a9;UN zLe$LvDS0cIlof-CuacnMEXm33ilcHlw?o;TV%{&<9uN?ZJ{o^kp(N&L^ndx0lZqI} z0_HplwtBXjPd3Pryebfm{dZ)nFW}I;dNSSI-}$Y|GUV!IH9a~D;(XzmCUZFuuF8^` z0H#XC#3Zz^eXXNK7BA$24qD>~*?_gn7z!q+x3`HAjfXOHUI(%0xIT3Z<-{3F1Oxs0 zqQ1BH6SDY`eFru|c*ub2QY=0}C1qGRHedvg;yBhcaP;@Hjy+R%E~f1s$zj>0r9oV# zOLRvd2#fwltkkcJ^hh7YE~S9Ojyba9squ$%Hay2r@Txdk>u-Ky^Vw;=u`03T!Ai>i z-&WWK|G8=GFg*{?&RK~$_i3>IVQ|!)j~#DWt8xqu-%OpXRXHBdq`9Dvu69RIx31Zb z<9Ldu^2V=X7di4RiX)D{I}-Y4I>QW-_-JO!ldsT-NjKJ5GHVnX&ft}1YaYj6p|M!J zb>(j!M1SZ%LI6w1YdF#qwItqtH>%zuR`AhJxSEECH#zYQnJ={@2(l)NElh&pXz(p& z-6klUkrnv2r3s->=(B&dRH2t8yv|ZS#CzsR$X^P)(n4$KvaUtTy}eca?K|n|e{572 z6B9o3g)xs32aQ(S9Ic{O`@Bf^Cui_LE&tA*SHYUq&qMBNA?nIfKhFXyIZ%zyzqYQct!G8f;nWrxG`3k zp^gCA0QS%rw&mt)X}K|bpcbloK@G3yDhUW)TgOpPVXBqCo8s|d_A%k_QI>cfvXoP6 z&*`85ydoF;3ddgItgL1I>_s+uZoec4*PeAUuP&RMd%|2vQq2XcW#nL{?cX8Be*D^%pTI7jGVH%KY@;6VUYhCxC^et|_*Df%i{gN9^{0-craC;I zm*8-Pg9jAbwmAPh-`<8pZSZTjYhO-duVpSm>wK*hW>~E*$jR;>D<(%N;$`lw3Al1X z*F0<&g&+*}hwT&=O5~KT{9tamM{!tj^5=by>t_SRwr6n?v2hQ^{`NdYkp-ORVkZ+R zbc&R*G0^JH?gUE>;T+55s&ebPc`|u#pqdQ|h{sZ0@VDZ`A5RI(p0p2|Um-=3w$1tY z-nh!CJ?_2s*d#}Y^MX1uA#%c0j3q8i`=Ke5!rI^3cWobeiFEefFu)97MhYTVH9p+G zL@{+dgBJuiAOKGmxG+^~jQ3xbj=82S?$lSeuk9%NeA*>whGS){rLQ^JcT3vU^J-R( z|Kd5>qW)~wHnI`zt2#w%1Be-7T4v`{sDpF}iis zZ=?fYGIe5DoGnd{VAq)SRxbl4JgvZm0Xb_+@-fOa9kc}Wa37>)FH^zbyTq2i{P%a6 z#Y>0Rnu{xmxU7}QNG~1DukB*^*)+L+u5J!}j44_5(Xbl|?lJFATB1>5jI;|IB_)O{ zMvgywso#9szvu7x6r^VJq{i~-UL;fY-rk&QH7ChsGT+yr3Z2EQa+C%y%Y%$_PKIi?rK3!wR01>Q8>Sk-@;_c8lz$T}6 zqC}}_LeK|^wrW|~F;vMkF1=~mUGmabv;#wD7s}s1P^=B?7}qj(T|Bqn=3QXa{&S(Q zT>u|F`-Eo*8I-p3t9SP(QvCOiaE{&CgiL`$%gZfiBsr0?b;n9o;agd^B!OqoGBiE1 zecC6Ui)i|Ij;OFcUa@fHmH?mSn+JkQ4w7K5cBOpeMQN>rL$1`%hhxKC7bWCXw9Uso zNAs>r$lVA>C9|>fiwGABj#59O`tDOSLX|o!!WS7=Uv#{%G12w6jl!P$lWxBb@6Sr- z>-pTOu3834(_e&cv9S+-a(H3bw*L{k#ZE0`cY^yEwG=c@>h`w30PTTM1moLw&kB(DqpSSBB z@nZjzA0J5o^l!nLt=KcdZ?&iF)ysE!B}`z0lltU9;&++X<3vZ-&GwRJ6fcgB_;J!K z07uu@eYvV9tPgXsv}?zx5)YAUJUWlPhY;JGdxcaBpeih1@GjOb0uz;PaG~s|S{H zAmg|Eo5O9J6tlQJWTSfn#}ENzevp-xZBzUrs%gs4agNNr_~q9ZQ{scfM-m9^|0hqM zLF&jWLfZ~=r@P<5iW=>?-@6j0U|_QR=O26{vk111wN0p5|q;yYw=5D@@>FxpTQy&VGi@MQa47?cYX@4`m_^E5XC zg#y|vioN_i-^Wpae>j=Kg}!o%OIB%iq2?JP>4(340$8Yy$t^bT+UWd`rLXXdvVXe% z?k?RS-JnQHDImFkfJ%#WhlI4!C@j*A2ug=YcO$uy(jeWXba(H&_wRZCf&FaFTxZUl zIS0#&+ymNwLJeXiftUbnNf+q)-7?koi7e`UCw465%%DO}qeXG9zHjs)z|qO!njjCw zYnA5X=aDgs(k{BSlR?V`j9?geGg^C#jXnn<(x`}7jT6^G|LVrcj1J8&izC zYbKN7A-{L&?3RZM3CE+6;df_u!YQ6QLNI202g`WbQzZIL2^k!R(9CvRY zJYEH)jf^ZMxz* zzIcEA_Zn};Ic|30yb6{>*JJZMS)F5K3VKYA^e3Rqp|_=!X$sg^wYlal)VgUuO2w+l zC!VLKZVAXViQ(mY4BY@54@@z?4za40M|OU3__!-1g;fJlUUEE z{s3$2>WY5_91^fJLIdNm<-`3;jzM>x8WNH>2xBOqLCfyj%M#kUr_6GmF{^Q;W-wS8 zVZ5p>%tI@5+@E!1V3NR-m_P3feQr-(=_X;sz>yKfmEiXxo|fv`%iaEQ_!ApGG>+0j z=XD4|piAK$h5JhUx>p+$Pf6$W@!sApE_@u25@}IyTdu+UmRGyOG)nCfqpREyJG6cp z%Kai?7KOKk`mdQh1aP!(#M$ZQX!k4?>gW~X`?SFD8UJlTtzk=kT-t#0Ot^3l_3gQ| zR9=eC+J7psoh+H{(T7aM{D(_viar!}*S@3(I1(!Zl0P~7KpoZ`Hig~fzcV#CQe0<@ z#jK<)7}t|o))-utu5RO*V}W$^a;!$$;JhbW_$ewk`KUj(n=b*!RA+9`i?F0m5W6E& zdN9(v{{iRSy}lE>tP5^ylsD!3%4x=X;ExNx!=!B2fJ5BKoz6h&`YQ3D&{D3Iov}Ur zaC5i8L%|QH7)bAJly=o%RZYb@T2k_Vq8+0?jC<83bIl)T4?X|p4c;bbe)4)LeQns|<=MTwYC>~m_)W~9?TY&KaFXw@Z$4EidXgNDqcsbpslik^F<*_J6vAKCmCo*DW%} z{SRz^vqrsUx#m+Z+uPgQG18Ur2#Ee2snS9VmUsXFeR%T2O9iGG5iPDZF!VK1d*G)j zg2SnF1F|Z9QhKztP$~G-DxGNBz-Otnn!US?0n(xxq%jc9Hduk%wr_tuH?=piaccJ; z!rA=}To3G)eE3SKdc{SZnUDs^Wjr^DSqh8-Y(do}zSE9d_(%f>r&vgtLSBlcX2_hzyV&N}-DJC@y|K0tQX zyx7c2E+4@!++hLw6jz_n1a1R*AoPcDxF0fSn0)53tK0Pz{zUzYdTaGi*aK?H!@sfo zD_scI`y>s*fgN}DMu1ri8~GOyTav!scqOy+O~}ssaz-X5V}GJw zLf}ks;_;X<>f452k)E@(Uyat=oB!Dsp?M@RB51D)j-@$rzx8_XlId>LFN$dM)#Y5g z3f#2RsxhqP-M_44`ohZ}OO1qx!~f(CJ`U-RGE&UpfKG%OLVrLr98uA4B~+C;QCf`q z1|Xt;F}lJ!1ks^0`BV*%;cS`ZZ(r%;^lfAtK7;p5rx;rRdl;D!V^nP!Cdv6=eIaa#xb>$S>w!~~ z?QX`ay08H@>ZBh z{v8&6vH9nJTKXghZ>qcm)bSrnSe7<=FiQKJk1&$sFVssYPCioRW))5(IDClhV3YW4 z>JdOzJ`o5tYu}^zMfoY5*)g_D7>Zc&fpmY-ArdBs^8DEuBe8Jtq{P-XeI@KJa}bhs zPXTVBPO8xVoluDLQ}{b1xkt4g!A>H%KkcIr?4}g>6w**BsvJsGN-U1>8H4YP_(9!# zPiZV9)*j#AJAo+#A=+f0N;8$5xItWtW80al6N?5-3V`HH4l-BI0etxyDkAX;( zNBjwx&W~BE+`QP`cWT`HvGISBA#NJ*oz@K)IEkD)8Tmd`#;r7km0^MMFt(I-g^X{v zpzt;&E)s!*u_ryBXHBQt;9~o=G!%}o!*KqS;alGuXcc23N}V(J79d5i?LQQ#J9+*+ z&NP3#A@0qnEa^vWj!l;;D0%;rzs3X?mp}RDR}33^CI(Z*Luh{gzxTy#5Q98bWrps!`38 zKh^hJ{gGF8riQtquFX$Nu+#FXK!f)UY#0R`t;jJMh(gF0IA0sxs_CDhWg?CMjJtx~ zft!Qn=6Pbrj~f#~??xlu*Gxkjn>4ThMjZWLS_r^|1B)OLqRmxI+u^pZP%G`ni!box&}XVm>NdyTa0>{)g0&_j9zSwzLUfDe}=1sxk1? z;dMjNxum9IY1?R@l=w`c+TYwB;Y-JX&LmRA{4}yukUbII!S6RP6824546OT=KY1+B z?m>Tc(x0KXGm)jFme&Q@dmaz}?kMxsKqza0a0Ub}$+-K0e_8|kkY;ZA+c{t}P)=Q*&+%j9|G`m^9XEN}ArMEE_Y*0l6m#IW&y#HQxY3%NA?|}zn z#kx4{c@_zR-u5SE+>`SkgcAUXep;TGSZh*tg~n; zRAol?y-TM`LXqj+DUgzz2sJ)+{&-X9;|tu>wa@KqJcJV?6qeG>05l{)hQCVSs_*IT z(_q2(fDI*pOWw|VeC>ow6k1Ms8fz9*UwjbX=xj&}gC7d%ezy5;>pnWAc;U>FYjPz2 zR?NhTz-r`@cn4dR>&G2;&7H0P|3(?${C+F{#1I{*o#y3}E`55h@w~u+f8%GL5R1rx9EE7}<_W(&a_sP=K21sE- zj)?Er9*Ut=kh=KGzsutbkVywsF%whI%>g8mwbWx(_91vX+WZ$2Y5QhusAfC|Ajf!x2N{8NUuT{I;gXf2r&?s zQT}B2239B|V+vk2N&iO?zrh2sbUJJCwStz9GCtJ5L?0{Xt(OtYBbN%2UPe};y~xA< zl;fVXb%-PA>DjZ-e!FhJN(DZlXvHD9IL>)@{u>q_={hl$ePe^~BcFdk;Rr!!zmM>O zSReE_Y-CmPM-l;YXG|d5YlCR~VXJEpf;M{GRMqC4*rzg_0cK ziy+f}Wr3KXi=HnEz7he^QzPA!tn&rET$Yp?=7Uul?;CTpiN2v$^Eukdah=yc_bUk2 zdZFY7z!j4nlGXFvRY@`n&tTW4SjA@$aPJs z&N3CiOe{lge4y%aG4?Qu*66-_2RjZrnm{#@{t+eyzbg*f`^-K#@886;h)y`uoS|id z|2L;O0)lu=i=rw913m~4s5c!IMpJ9sXz9355$5oB0_DQ{07J>06b%(CaT0kk{jf1n z+18EKRqF+|8_^iRzPBd4BRTsaE!jDV6giYOQk^gjvmEsXHSw}kx}>$TDDDT3mN04r zphfWw?-Ix$g_}I?0n#6j9+TiGHm$DtD~mZjW-j-+P53Oj237uDBTfx_0t2R(P#1J$ zo`SHj{TuhH8XkhoU;gi9Xuw3*2ex1-5D+>cGJm3s0acVg>ujPRQFGzj{I5LAt#(9M zGBdX?dB-_Yz^Ohyv-KI8ZNs@iU-Jrdwskbmicj#E$-iI0Xn6nFN0L$PQ*Rwe zw?U2mM>KAT@xaIB5#dCptK$*3=VgZYp)vh93N3BEb%H>2uOQ8q6~MWf7fmzyGSj+z ziLG#MMn%e|$HdUBq+C95lFoI-N>~o9)f5n5S(`up`p_OUPvHFyh^lR{b8)fSPXIsm zvk&iE43(M~XL@wBTXH?_K|7G2{QW02&2XpLoZA4)EZ{|qc0-Z+|Cw+yl*f9kpAO25 zFzSWB82|6$T^+CgrE3veHNYCc!3h*0_A{muU8Z}%NhN6ys~_wKJ^iARUo7K9UF!bD)l<1?kfn4i`0O{smi*4K4G*9g@L2Ht`IRaqYjQk%X=TCZ zY}z{GqdhKUmyjRFI>KNo(Zty5-h=Lt4S&SJ-5B=e- zRY|^YX$uRBx4zy}6r&>m*br5sr_vaYU{GNLVl4Dk_!Tc?L zyRlA}-NG;$Uy?XlX-lsOyJ6rMQD@O`)%uiR=ry>4sNa|&y zkXK2;$ockqvP2lDi_jtl=h&e9SgMIHtz_`Y*i~1C%mAvZ@!!dE5N%_>tFUX8wMEC( zHi2Vmgz6uqXTeIspj7jAb$`z7e#*y^SH0aK!NFL0xw&6$V5@#jsQsoTUNHF@Ra*@d zvXqP*-1dLnKp&reTH^Qc^dQR~D(OT==U`nnRZ#$NBrm?!|B3eUil82&NjJdzVJc~R~z`&8)9s95g^p%`=%JdxdVjxa?QG1;9Sb~ zk79K-@vApRR4U4YKX=Zragi`%+`-~IQc^N(q`%u0D$xdq@a#=cNSVIytJx{`8Pw6C z^dOkihQq^+2fjfoy8lIdyIOlw$zn1@!Qg0|B#!>q35;!hX(Q9+p;5)Wh2Z@ zNE-hd(IjGU6zt@_T4oVyBs7mdSg@cuLH6)q;aCcuU#U+kz>^{y(ZR5Qq#MkuW8m~& zJ7#Tt{M#a18$@g^XHintLaFi?$0w5Mue;^X;S z`vwE38*4Af*AgNwE|{=E9lT4#0Y)-i!QgCZY1!iET+XN4Tv^;o^Lwg|;bPYyJV-uj zhbd%D8=) zbEA4#5n|qkfF$g&<;%~Lr2=Sp%s9*^wNJm~>r7+t1MtC0$vCDOCB5=v5!a#^-YzLu zWRzvbO$SDV8_zw7$fO&GI3*;lo+SU9Ug6dW=DF|3dw3{K1RPzkkjZSBuL<%Uoa{DF z&*FxPjn&g#QHKOp=XRelV{ss9|FHSllWvJ!$ntghtgOC8zHirka?>lG;JT|oHSod^ zIueccAw6j^e5r!xM!UL<17b;?AoUMnOFdx6-3~avwCLE)^1<6UF}k#$j=K0*{gA6r zd^1O+Omh(#_*Y+dE^ZPF3*NMr`VT^4c7pm=@z<&6#l6;}JvMR9`<|!gMfWNz{Qd9l z9!Q#3d0_Grqu+GWUneS@{3fq##ez&ySi>7Ce6DM9YRS3{p@R_Yn92B0_0Nmy?S``A_5; zlUKMq1N^R{oCvW!d)<}aN=Q^gKb?_q%~${6*QUufc|?c?#QfkPvIDvB$+Nd_jE}LDWgxt0Gtn#4ts z__af`!V0Dd%MNM>!Nr&SzZH*5L;uNRhrfzSkUmSsF;!V#eUMV`yQ@#EXNVd^^3tBXsE)Q!zgPLeZ_)rme;Sx|9kS0)JE#_Vx?nIA}}AEHfuI#oO(&o{gn z@tSMsiScy`ZZ%|SA#agj!y6!wtgMlYS9<)gKTgc_IcIws;Zb;&*I4ELB>tUp@;@u= zxmk1r^p}?-Eli0u+pfOhsiyv9gCUC%b)jf8mqFzybN6N`EBA@vP59L0oRsO4kv7a& zBbbnZ9LnOabHbg6>S;{gmr}s^7!9wm_nv;Wu%9>kgGSc{2Z_e|`l*k2IT={CTTo3` z>tN|CHki-_yi~2VoRP8JxA!gZl7aD-)wy!lbY@u9;D8S%+#B* z2c3b)ct%YU77?;=_anXGN?ZPQqC?l2j19(84vFcT@HRbWFuv%HPnJd_I9=jL2RHA^ z$s1{~GAE!rIlFg%^b7HR_Wb1w_8z8SNhYKGtjR^*?1|_sw=b=}*umim!_F&n=t64p z&TvM5;TwJ~@uZD-kw|7_N|Z$5_?09Ro5kqK-ZWQu6ww49IM%0m`_?|*5mogD)fXTH z>Ui+smU;vncn}(V^>?>d2V*xY&okTPvX8aBSp7eG7KAardZVFi3FJodVyyq9FilkE zz?E?l3+jlX2|S{SOh3F`;ONDlX9fHgbN-Jk91xh|wjX#HuP)vR&58glW=>;2i(gR` zqtJ5Ny&W3sh!BNt>$Iz60bH;K!Nu+;AQs8LlRQlUR8(mwFT_HKmx}kxzqE(8PAJi$ zi=3XJh4uSrN6r!mNc=@*00wErBg1hl3DOoI>S`!C>x0efh)5qI_Pn)?fpe}zELL_w zoXPwNgI$4-%Qu(?$EYUM5Dj7$3!^v>bJ(UvU&Y5-F)yR;VIv&^eYCljRz=uVA>E%Y z{ede*0HTazQ;l){T++%Bdq)8Pc6jyM@tsIGh0h5btLUO4Stt2M#=7;? z7qj`{>l!+hyQ}x}eaNN_H-3XQK&_AVrAxyUL!y1bSEVo@%+b>p#Jgrt#ltJ9V^tcv zQyK0|l08nYobHa3u@letnxl`CuI^Is!Bc!B1nKbkX3~JPpHU@>P*TeH9ai@8rx3)( z4sX^L)%7ue5U2N>TS@8`L;yENn_Q^{`$*~;&@Uu0e9D9D--$yDxYm)cnCG5F^oehb zdvFyLnj!oj3YfFObn!d&_CECmudihz_Q<(%Wch%rEJ|7#j2#rB`eBV|;QHxVz@xE@ zf!zStwOzrh4f7JMb~+yj!iDgFk{_7THxcmSTaTb@8iRr^BF3M>Ohm0rj}9hcPllQ_ z-`cx$bO)A8kD&JVwzk-|SN+6R&RPv9n=qHc`&;ismy)0S(us)hVe=N|h0oDs8 zzfnCi=A{~sqD@WN(->cAJM7ecOG%&pp)%?-Xej?d!W&cB`TQEMs1Hj~x7*(f@o`_xR452*@PQVAW=8b5s?ym zDoOq0b&jp}y_fdga;+x47^za9S>`gKxb~KbW^~Mtws0U|td8I*CI}U2jpa23%u{VX zu!!aLjvAz=s`9I`$3*JhFGc_TP!w#1U`)kXT*npgKU4sCu(3ITN-Kp~cC;i4;RC7H z;t<}1AndRcY<=CKK_QnZK(-wBbl~G_twj}l)d}y5Yp<%mt*kdr?)ds3tKVM@0}Sd_ zVoefx{rjud%gedT_v_w0mGTWDR?UYu47S@yeICAT9 z&)MVBoS`4KZ|UqJqfDcCzVtoMiB*e?iXu8cJslV+#F>e^h&e|+1@$FytzLAdDo5Sf zwYd#>{MM84%DL!)m&nw$_Ju4t*_h8d-RAI~eD6@Wp8SQ3RB@%-SQB;+b+B0Gk!+na zIzNfdQ31oo>G?b+x%sE*zd(V(Wfdw^F3uNy6uAHEHI6Z~Ryk-&0nTb+=ihyXErox} z2xf8VWLr++Iam(6X@Wd?4!`)aVy;ON^4`*!2MUw-W2&>KeR6Tw@Bq~C7IJ;SZ}g|^ zvGM%c^vqM`{fv@Ij*dvQn{{)uWxGQZ!@Q;5P4_j59?U5u`H_nu`o29n|G0^7Lao!| zS}{Tj5yWxmUr+uOY{X@o*RzQ!)*zB}QY5dlaftnHbN)Ty*r@a-H9o(T;>hPa0t{x( zMsJO<{7+|Oj$D~|Mz;UL@$$2+BPBDd;D2HSBQ;pVLUU!EEsEc6)Y)*|*G@uLu>t|& z)#8F)^}9V&OCcfNkrIY)nt0v~_e&NlZ|E5F|qvlw>*!wz0XFzDY;D zL#U4RgRhk1wR6)K+J)!Yg)JF+=j>frRT20vHx&1#U*sf6y53q*In+7`iwX1rP(%-H zHX;H)v`9KMUMb4zC=4O*`GU#;?}U!i{awCZ_*`|@{i}ItKx`lgA!#Zkp5eJArt0_-Qe~rPevzC}<3==lQ|B_m zQY^^^5MfzaS^fnD1#c=HQQW(z<0Y_E`|{3x7c0M{bW_s%U!4OWE#is{!4;HbPqyRk21OuaxDU9o>lro>PQ0m16iYV(3_W1tqn-(G2 zMbtMMWZ5wYG z=hW(-%FdM8+CNNF%6?4v>CLuY(Y(B$C*68ZwU-$m=)AiADc#j#8#Et#A~`?cu7`i-%kdnoZHOHs@xCw<=FnR87#`FG#D3|!FN+xs%-4z+!TdV2dG z;t9iKrWx;#vbQoV&Pk+-j zVn|&(LlU&yHN8~!8}O_{1Fftmrwyg_L1w!VXY{8P##VA*B~Am5k;b1Yw-_7r7Q2;$ z-u=mr)FPIWEjIUE`6ZFtiLg=6ZVqx&s$-7*rKB24m~*q^iAQ`Ya!E5)-jFLLJSfsA z|I+6gg5;uJL>s*2XJKa6U8PrT*RNT1jrh+Oup97Q2R=ig*_or01O6FP7;;Li1#-7g z%#eK&kE<2rzV|060%qxT8PjOm-n3pI3 zBxM696upd}_U?U-o5$w!B~g^*<8U)3?vBs5zL`m*Q*jBYzJAGrA2N4uo23!>F!Ow7 zi2cQN;oP2F`^E!;fUV>Z8%0^1lzSvaqV-ga9Lhi1QgyAwF!=mC)GM#jAf5@=qq}UphZNH3>#UE=sQuVr;VlSa92xa@=N*WAVE7X zd!>@HFn`N5gj^O*%2%p$+ugl!`hqLS-afOn)k#k}k8@$We?JHfV9^*a5No&|nccC>Ph^f=WMnnj{tfVQ=atn=WWSB|2|GJzM>se{;=)+1~rt4R>;!%-$ z3-Q<~X7|ONJ#1k0^q`+tp!QdSF^I-W{GyQc$7z#3hflJ0_JDM2KQKQYK#v5o^NmIK z0PL#*j+ba`zl0=c zaND}N#tcvQoj!h2)%*HcP}xEZR!E-xoiG1moPN}|dHcPrnr%NX6nfg7l{hvyhw)=W zSj8rco19&i%I>zxa&IJR^pA9W5YZXwdeyjqq@0*ivj0>oIJjgWNadj7BWD+;v<K=E*A(E=XJQv9;jh6VNKKMewD(UZC4g{)13b2)CpN6$7*2omn%l8aj|P7X4k)WLKd7BiuaWE zUYUwN7VPY|Zty-WsUXL$(p@xJUrQEHKVl6{T8Mi9k0XVnV+t_(N>eeGKidcn;t^_W z4hSop8@yFpT72OUgzcaV03+z;B%Z@jChXga$wa1+!?PKi}C z697iT^~2D`nPNqLnC7AVjk5C9%FmHe=`OO?>LUy}=2PIjzh&aEli{R-Z|VjGydd>U z{+Y&UkyepC9zxT>g3pN8PN0;g?p{C2BzMtVR6^{HW_#=x8jFh$mf6jc&CT1d@BUYY z48$198x@t6e-_jvPYwb@>^b%6u?@>v`L~&pzgPcNX9M8Wo|ieg;nl>i+JCzP=sXXP z`d{C>@zg%_Bfn{#mjApcCo%aN`+38#h8k1giNzMy#vaQ|L2?S@m**y$%}&rc=T};` zU!DpNx7`r>-ZpA0D_WXBc-!CcqUc{_9`y*Rzx`8Udy5BbXr~%6tR%0P2edwJq_Gpb zi`k-U;pcS}fSIQwrr`A&)i)Z3)>fPDeFi&6! zF_j@-S}iH7{{$Vk4{6_<(=BwQH@%AdXTa?Blhs-NerJ%z-C#}j2cSq;a{F3q)`3;l z00!c(&X9lNu7$vzZxJIX#R4{2p;lVpJTKZRh5n*6U8CXEOm%6j8tp5{5;FGm8ZyCc zAq`^>9XCEI=%eC0ypzTT62DtsfC=dE+nVIT!L#VZ;lMO<+9M?;B8!%juVjY@Q#^E& zkR0^-V}SB9TkvK#ICP8r^X8BmZ=GPS|A<7*8VI*r>AHq_~QG1@39d7n^xEWvOx^7wUs>h7kjkZCjBPX zS(QOGd$HBHCHFk|t*xi)gzoL?&|rF$R~qbD)2RmUY+PL{zlEaWVO86T2cmUR;P5dU95T zK$IfL53f61nVC`Iv z5j~p)(=1jJAt_;*yL(&7j7&@ncn2r?^!l$V`gLO&boD=#*33C>je_&GsP-iHyn+?2be+w(@7 z+XYnkiBwM&CN}VpJuPF~mW8wFmlhQ`@^VdOs3}j2o>)n$t4h|%-4#vpIN zZM7U32#Oz;3~juh`U@hC`Azo%&Ot%`_hb70RUFb2=bh9ULhXaQV2qpFW;B7eebcTIT*K60ZrHPDEq zK9Tk0ou!%}-KeDx?ws>FaJ-^k4){f&%W7*=Gu1meM27ks|K1@diWM$m4L*E4x%H!e zsAve9o9*AJr$m1S z1wF_=ASPYdgS*%|(3Rw%9Kyi=FGxFHu!{Nq-%-M^pFgKRn+B~PZG_80{O<|B>wJb$ z?o#<4J+GsqAVzsuj^jutpAXo>k{jJKbSuaUj8eZhA7$xgG8N~ z-UF0w1^d>vcs6p+j{R`1O(4ckbKWb#LA7T@^wAdDZscrTF)n{_9+*nGpy7GM+j!c! z=cL=)j@)C13s#fM|9tV98SvO{{&*OrZq~pE`u?Y9wNPz~t zm6BcM7;{ZnX0ePN-;`cK8^LowOG`&--16%CSHQF3$S`Z|jPxut3NQ1fnELGDC?ifQ zS|F?|UgsWNF=R3Woe4(>-xuNy_d2Pn9WGQ3TwD7Z__jJ_i}qUh$HwA4pzgJm2ENTT zVlG+hLIbCM<1o9jOns=&V-8C4e^C^2wIflvMxy`X9BKDf+?LfjLZ3Xm zBN|hRe_v(}O}gT4WL}AkLoUQ`Si9NR(J3z6W6O9pj5be^S_cXaOuIcX7LxXJf7LN} zW@!564ovw7^nIG8?w=6U-?#`JzCV5ebgF^9yc!D4*xV!U3wZ^Orv8G$ef2Y-Tjap_ zBlpa#i?yC>cyUNI#!9=sI7$J_j|8!vr|6E5h6l8NyW=B_Bpytb5v!@H5Mgp_i3jW4;~H*J-wbe9^xN|% zPde$HMVsn}o^W&coUG8lz^V_E`$0*cZ}ri}ZV0;pbu+HL##PTJo&G0ivXjd)C=U>< zOwak)z9G&oF1ZX@^%%e#THP*b!UTw}i@RFE@)nv|hO%o{feFkEX({rLE& z)Zy`gBwtC?bAGjgW;>kuw<%b>(U)ctcmTOLFb`Oeu+a)F0)!U;=IijDfk#--%0H|p zJ)ce_y{5$4y-0envI9Y|T=r<&Pl2Jkl9DZYR-q4r`3LY@1$+^|xq}L*UPk7LtP0L` z@+?+7QFt;hMQ$g?U;lIVLx09v^{Efo8UO@XVm=;?D zHnEnta2oWz{o3xs?_d5zpBrn{C}(`Ay>^3#Xv|LyJ2U8%GZ7PECqbOf$gw@fzoSroc+X?B_3+F%b3@r@?cDbWZ0mG-iLx6&AZbUK{?|_*Vm-ooTH=6d)6ewrWAkW9i33BAhCrBP z)e*Qqa?bboPtti|P)9~LhxZAdJfELo_1da#3T`Bn*69(QAt6CkiimWvzzn`zD*|5Q zJv8FV)r$`Of$q+G3sE;yAtE`Wvph3QoEKBXx&~uEsJl3w+e<|=(9<8nm6!#D6Ui`! zDX1uZM`wEho~wM7uzZU7qh!k=RrM}bWeuN6{u=x+n-A{>OQf(9+P*+2J&f~zOx6^; zK``1sg-c7ZPX%saJH!xvRB^bab1Zm19bop*llM6TIAm;UPUt#D)zm2^b~f~SXC%??qtHOA;>J3ucoem!2_!^8{qb)gCu8*j*tpOx&DWuHPpSM4zil$Up8@Kv zR7x{6IYrozp9H{D$?;-@G+G=Q9Xa%(5I;jjGQ@^2vLkQ;t{Zyi#jgjXVdZ$8{}|c^ zTx8x=Y5aT&h6Mpa7&+H;&7YKMYapgDC5K_ae-DooNmot3L$ARdx%&RQ0ezt;y;FaF zy95@}NjD!p;uIX`C%^7%&pU+&%H>7Q!GU(sZ#lX1Q2M} zgy%RV8b&rPfEU`GbIjx>efQV;y6pp$3~NntJV@MP15Ig>Z#5seQ%D}d8Q+hP!$f@4 zV^W^1n^8m@ILSP*IGaM3kplFrGp*SoKKWjk@G{F&fn3)9m}&eN+7H;y`vF8qJ>m98 z5Ma-iX&i_}&g4OuN_X{7*3Q=Qw!b#=>$k7Z8zp@Dj1;}8;O8QcqjQ5C1F}_FTPGQ` z4S#EWz4D|}wT70%H?7Y*R@CTNTp$oeVp01VM;YJyZsjq&E83=ZL}QsVd=fc(dS0JNr~ z8}j58$w8OhkVtf-+DVWX`f^n|05ewGhYxj_i|f|%kS|@YPYeTeSxplH)T})n5{loJ z3rUyM9Bew!kXL$2nn;hH$G+gw&kJ-!A)WP;s8f7`B=;lg-d{o#OblIu&?a`#Sm(STMQ=7A5p+X4z zQb0shmu0y0zWCQ)T?hRM^K=k!MfFqGOh!solBm7{X?!s)^u{%(c=RIq5K*+e&bT0d z&Zr_1{qrX=7=Fm$LdOu}06_4uuggfYKELw|iAuh-mYXp%s|0TC8&6YX#xT@N+p5t# zj*cx8zX8XVkHP78=p8>x&^lK=rY4KgQlLpd4`yISoubqDKuS#j3?^imbSI*AhwcGO zX4v?quWlo#CX#g0iyi(fGU9c9akFwA&iU#ltpq5Y23=BP(!UmBsKa&c6zRyT4J32eu z>BPnfU?}imEZjnCd&sM!k&zcvjMJUZJm}2PIW$e?rrXajTZ_#9FzW^$H5C_jZ%E)& z&^GP2Ug(lC%x0JymNYm3(-+mSHL)273lNXz?&ws94X*zB;Kc=nqj0MBI{kzMiX4Ns zlCNyq_{D5{V8|p=76_1IqqBoxU|tNbGfBdqCb>|S_IS}|7*d3pU#%Q0xSqKo%B*#4 zU-S&1^*_wfryG7Pci&)uaT`DkWRY(US$&TCk56|cn;Fcc8lD=)W?tlsj0id2(5gBs z^?54*Ux=Diyqf&e3QH$NF#c2+e`P=WVVSepI6fbE&0s40lDkpQ4n4gsNj!x5H}!K| z#i4WF(|HGeI?0zuAq0s)Z3<*Kpkj9fEH%K4qC5PY)e3PNC&t9!tv)4*QldHo)j%{a z&zjQk%A2(fHuW?NYU&Cj2l;Cm=o5p_GDsZO4@{BgagRoE2utlGCbf4*)0HBBuT;Mu0G6wJuX{8uonBgLv5-EFB*R2SITZ!k=)bjRwEfl11JV~2wCfwF$ zY(J>YvvrkyzL5XthbU`0o_omjO(n9bWj<~>i)DL7y7X=xAiBlITTkqic)_8p!PUmC z$Sk)V9c}yVzsP;X7{hy4S?1ls(%PK36yg$R8GhSXS|UW$ew=eF>37PcZ}Li{xkPO$ zZHL|1y#L&u{ULmNaW}m}d3C$}?=||0aHoh1;{>J#0T5AquG;*EWloUah7Dwi-B$pt zU!xt!5Q{sm5LYR$L{-K?!548x%@!wrQYJSC&bgJZ_P@iJ&EL(qJqrIEyu~EIU)^%+ z@lICs<7E$9d=jn04dcY+-PylBtSVMXIz*5x!+a3|GAuw(nVtg?a@aYY6})(NCGx~- z92KW`gcg|mC!%K5Mj&c5;G3AWzwZqHA5&M^5Y_i}?=W--0s@MJQX(Kos?y0Bhua7-8C>X&-MR%^DrNPd(S;*pSAa1YwaPPhSM7ti=OG= zC&nr%?1F!0%(+!R21=W}B+a`a=M^3jwW5FT=Trr9-rwI7_cH#hob8Jpd<=w!ow`p! zVt&x*P7{Z|mFK?iwI4-nW5e_`{Oh5_EgH7L?@SAQZe)m5R|{@#Yo&Zu#+feQcO4$0 zJzVBW_Q6rwi|RPdmd>4b9|CHZTd0g*JhZyqUI!{N@139OdN908XE*{mxYd0FU8Ry5F0m26>EbkD9iiJOym-(9HbV zRDJt!kGjbnqsLX{_W7RieV}FV54X!^Us6NJ0b z6}fh_lY3!pYCqV2B%Q9x<7gh3dDT}R4uLs$OR zeFB`1S~`FWS##!PLQ-NfdaneoUZ_!g>d{^GLiLo7u+`#|jWMa>XGE1U^fXEP^{*W3 z!OKP%oY(78L)exi=vDHi7lF2S#H8THYZ^erfYZ0mC|%aSFR-LhU$zmd-bpVU6vRRP zli!VnOX%H*(}556tuc&x~;iOE9^NQ!3dllnn~3KxB-(;}2U0k5;?G~! z*PMot0{yhwMJk`EVHm(H*k5wyPw%fX32d;<2GRDhe= zOJEg{u1$1$w&42Gk8#g2)$^NrT0PpANgc&N6uwH+q%+{I8c zg_qmX)(sfG@K{;>(uPUNs@rUAL`XE~Vd9xy1))HMg-XjR z$hAB~H2-R|E@X2C`7}_u&!Z2P>C*p&^)C=rIvK=gj&Yrg*AfjPQJJmE{(J4C_p?y+ zdMIJnCR~^{OB-SO&L%b}L3L#!MQZrdEAl2>Z#TBM+^+2_BFn|yH7}c;`{gLmD49B| zxa|8D?!IV74et4w7TMXy(Rk$fb^%W;PP^Du1i8`w?l1@qf)xJuk28I98!4{G&2jF) zS98msU2?deP`BRn+^D4Wn!kGaKn6v}5jR(^XSj*dGWW^z;u0N#}bJmVi7Xm-fkN@dxy zdzUT);3)64CDoO!2(=_ND|YZ^?3n@gCxCW2ynif)*0h!!W{p>q6MyxCSSM??#u^CW zZPd*_Nv^R+We*BOK0dhmhqRP~!s&GJLODyeOFel^V}W@LVI?69E9q?ffZj#pvAES8 zLCuR^_)V9E!m`K#nwM(f2JEtu=avHxK^f!|zAr=+5X5al?kByTVkb-slGoALQx*TF z)N)c{Rj&OPUtd>AwMv%DJ5qm5x@}S6OaLp%KgjK@HjRHqQvOaSnkS(htU0ST!|SkW!U%aY8mDr}9cdj`?iSSZ zVfU$6>mxDT%B~Fn>;Vv|NnBNIyIGrg z1A`=zzr6`|Y!#uH8#a-dy!`4OYjqZ|V`)|ezHv^r%+q+%r3ZI?J6=#xa`|U6HhYbMl*Sb$@(&3` zG~)}fi`A~T)rUUc6=$Yo`~{x4I5kiV9E9(|J)+(_HNSeqC6s)=+M$j>GQNiih8Bf2 zqT?cOBl19WgP@4n<5~PZ6b}VN<$=K9_GqW3(N)5gPoiHMIU6}yG25SoGIgwE5$!s* zf5RR&Gbr4);|8@BGppBYntOJYj#*}3o^ekaJ;Kv2<2fISV9%Yp=# z!QwmMJ(X^|D*x?NyHvawaK5&!KE(OtCodk9m^5X1|5fUyhzK0Q&=t6KJW=Y@E-vwq^5PbX3nKjC+ zyT7n2mKK+I0%>e(S|YkdY;hHrJ56C`()I7p5YN}bHJHB zf{qtPM$8`kk=MueUcfJx76H+#?CymI6bMW>{)@#=DNK|p9$Das75e+{7k@5GyV)dQ z!gOFSZG%g4`E5U$caC}Ht#a9TSqn^9n(dT=VvIiC3W;YA#o9wsJ@}$EC%|Hsi0??7 zVgLoj!#2=~5#XfAv)QzI*S%XwI6h55+t7Xw0nkArrOsc2Dwi7Qe6^}xBG9Zi;~U)F zTc#94FtaOZ6=j2hNTkS>% zZR-FyfLDd9(uHyBL@zO<>BZYJ)+a*55k{96-%{rzBqlUZyX^OW_FRF%G&jLyFcml?2cuA)!r^?2{}1&Y`+2M}Lm z>*&Ma2gT-pUq=`Tc~c_h%H>|&2pLB1hYX;#K272zB>z~@uq7qcdHDv1;n8c0uMKn* z+(u3FuVk;fLG^)4S|6?)cG*m;i}1`+@_3zVV?h#&N)+e=V+$d`O@#G0NCVS_Cnk{4 z@oF4%^J(HzV#0ZJ+fLu@+e=OEbH2!2H?1Z!h1?52+3cCgYA5OYq1n^*^17MwqNci8 zSqlw%ojQ1898DodKN|-L2=AP&4d2SQollSe{=yT;UP4QCc@cg1^9xbaM%D759NFE7 z+YEQ`AfyCf1aRzJpSnY5<73#{y`wiu!MR0&gfCM`eNdR+} zqGOOuu=u$?MFz{wD$luW0I z8!{exE69%-`AB;}hg6!C&=>I9wo8N6HC==YwNDBsq}}N|Cpn$|MV=rSi4$(uak+XE z&-9U#qIqW_h1N<*EUc`c^XYImA)`K3=1^aMBpsEv6)Wa%okRSkl@)FE-dT&2dSO3D zZ^M;6Ue2At4V7?mfh!3m&wgWTKGcn({5f9*GLew{ESDkd_Pb+aqQh@B7nKtR*m-ws z)nv?N5gYbQ3>&5m^0^N`Fp@Cy1I~I}q?Ne9DorzhJ`KSdEwh=Iwq~M_%kJvNx&@oN zGri8vsWxS=n|W19Ok`{a-F-!Bsws&oOy?*ozOXc861WvE6v-&}>1sHRevsgm6*ds3 zp#~3lQB9Go!zg;@XoFp8!Zlb=eu;UGW2wE zLw1t?q7;y0O0)WP9vpdPJ{sEoj8j}y%*T3OGnK4 zfta&1i3JYfE7stU-CrTZ3HJMP@7<#0 z)^x92n~7QZa$n7Kr7BLx!n`skioI(~PZeFs;j!hIR8YnpJbQg`uYU+;8YU zUPL+k?m*Oc2j&vfy)ylVjBfD#m|k_mgc-ng{AL>bn|v2)qIfm%>)(E~yG_V90S)ao zV!8V9M~(}YJJQ{B!HfNHRf5^Tj^EjvNAka@ie=T<;J3qg@3c*mJ)aKC_rx`TWP>E| zQWE==ER9DReWi02tuNm)G2bL9X76b%l)Kg611GG6xHH_}+|x=6A;gVYWoi@&seF<0 zfn~z1Nc6|1B*tcu= z7qjS(enb20rj}otHqw_Oba?I5u+pUS$&K}Rx>tAaCRxz5QoNL+2t5dc0n>zZ#7@km zWvbqs*2bpcPmdy4>FB2lcP#I?DQU!fXzHt7+aZsQ1l}kQ6qyoyX87~-tBktna&@uk z3-{X3opyZq@9&uJhVmO|>)A}huuL24EElwD1|JDO=RWzPJux~r^kL-yyN^v4(ZHN& z<-_$q!^N(^8k-r3E^r(7m8^d}IJo>CK(Uf|06&=i-h`?OR?pp^Q0n*02@1aS@Y`3% zJ6#W{5`4yQ-(~wS-+?Y28cei{JmEi+k}M!{@e3~kf!g{QBW!nO}cSd_+_s3Ihg*|J5ws5Zc69H z-SKR_LMD8TPB9&KkYs&mHvzZw>n}Gg!Y2Ka?JD}l`kHUg9T%?Wg^IMeve89-H@4gw zST0LhB9NT@Z?98;3(yJeCZ*Q#{kw3p{avK1OU{zX`=+96HNwbxHA_SMN|n*~Ls@t6 z7NBuUIGWkL+z1R_C^DkH)*vRdEH(QpvM7D&t&zO9!lJ} zJGk9H+3UI%OqFc4MXF6Ji4=(L89h%%3bb6FMg4eFk0H||+q?=CKRo@)gL`eW&6|wQ zyZnwlBXJs4(K{Q6bL}x!{Dyqv1z)z}2V+)sJQ1@1O|#h13^-S=;R;2soW+uLVvO3> zwt`YO>Bjv$mh=@1d?=09k#(?@0`_pKEmU638c98D*LmQEGf6dm3b}Ug5U`!E-XH7p zF>R3;o!T)10Cn=;PTs$icv(0p9?jn6YLG-{-tn;ka6C*zA2^aFoL`EIiz`t|cFu$c z<64JNY8RaLAZ9`HEj$$68S#6F7`pud3rq78t))iIakrthSaz1sN)F0*(vi*YJU-gY zDZ}lw)P0addZgJ0c8yeSHrwW5(DsyHI1D^UYXyR!VJ+-;#~q#i@O3`XqVF5P;%Rg#Ib| z_8EkNx>b@{F_ZjHU#K9aQ57HPoZGK}m0NN7lbOY=OSaXSSBix@n@z1Eemg1Kk(neP z+gzm(;Kp2D&}?>H^-{srX8@{}sL%S6>_$>-Q3}-BJjYI~i&MOv{gFCxX|5lm`RbT@ zo8__|IgXXnUX$A4(N&DNuT^ZPF*x#WUkoY>g88xJbA2tnajW@tFmV z$#R_=j|BfDX>K14y`+CLZbCV4g+fTY@M8{s_wYv9@3X8l4+D#^{~tHp@opR-uaR1- zhHLr20MuHQ=QDP?ty;(x+2AfqPp-Uy;EX@^lQFZ!)`c0T-E?R+->dP4d)U_pUsy5H zZ=L896B5Kxwxj74HLRr3dJ=UuUiG;UG4I7uLUh$4> z|4#e#d-I^0G*Q&gPtB@?jI~|#piMwrHU1pR+_U(kfKO}?p>dbd=#%8wr;^6@k~QaA zl#B7D&$?mmK97NsnnnL+L&DFy`X>^#!*)-HEOKS}yCk*NP9sOab~}TIF<7Y0K>@hc zcLTJLepx;C4j8cMG0^ZBH|7SDdNcg5?z5TC@v|W$81j^B|veEQi_q5 zyjAw6o_7zSL9ZMAi@Lwz#}k{jlNb0?nFT%)Yj+k6Q|QvEVHArqdy5>crmxNqsgBN38)M-`AnIn;MhgGkQU)aVst67E#nub zhMxFEEySMQo}~Ha>QP9{SjG2#zZ(UP#242*v1`xa^>LEXGb|uqpaRw$4%S9009>7c zlETWun%ZAibB@@nw7vd6fBTa4Rdc?iy}r8K63ZEur=*&L9$Cq{l0zY(=wPB=oj)1* z8Xj+UX~3$|ds5uL=;A<89TjwImGA@I&-P3J-|XyIZ2rJq7&GUSQ1R({M`h;HAD1gr zov)5YU09DI9z5O>d6@1Z%e>WEV_c^^fJ-fMAu9Vbbc@i*j)IYJ*qY9Oy#6F^DhL9^ z1F_$68nmZTb@y!i4toVUz>cT7a4a(6l9o)tnEIwD>E<~|ssf_*NpN5B>ZE+zUhwIa z7kU2t*Y$TeYNO{u@9lZ?RB-HQ2v@D-)J+|@ML*R% zq|iyaWGK1plNKAFsahjHI69mcT>rxnw`rfz+qH@l`}Cdox28rUNtYU&)K$YPYwofF zkQAEZ)j*fxTlR*kN$ltTu=T;FxECW2Z%)X}6W6~#Ie(ga!{d2#vik3(W&qq6-7^(X ziAN2*XCrr$_PE${*K_!)9eHzQj9Ht{n_L~Om0i0GVruK@$B8d?mQr{bX=z^BE?x}<7$)TP zN>87%a#m&hdX3<`Rq9RE|A5fSIX+}(2pf+8uKnO}Bgaxdz! ztD7u?$46s+whjV;J6L5Rl9*TurJ>sxb{Zr-8G;yrIHX`YHwZo7e>%<*@&f~V`|#S( z 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 literal 0 HcmV?d00001 diff --git a/app/resources/nightly/brave_win.ico b/app/resources/nightly/brave_win.ico new file mode 100644 index 0000000000000000000000000000000000000000..986af1b433f15ca1a55dc0e6b755bac8491fadb2 GIT binary patch literal 84653 zcmV)kK%l<>00966000000096X0H*{209F7105C8B0096X0B9xv0R03303aX$0096X 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�|vnN@KiLpG^n? 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 ct+FKW_&%gKrIUc>I=pDTKfBW};02cd8p#T5? literal 0 HcmV?d00001 diff --git a/app/resources/release/brave.icns b/app/resources/release/brave.icns new file mode 100644 index 0000000000000000000000000000000000000000..73b982127d2b8888ea0f4e8c069a20e42a0f4a18 GIT binary patch literal 181413 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 literal 0 HcmV?d00001 diff --git a/app/resources/release/brave_linux.png b/app/resources/release/brave_linux.png new file mode 100644 index 0000000000000000000000000000000000000000..03d59895876303e5000fa72e35b6df47d925dca6 GIT binary patch literal 55757 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 literal 0 HcmV?d00001 diff --git a/app/win/BUILD.gn b/app/win/BUILD.gn new file mode 100644 index 0000000000..4fc8a7e6ba --- /dev/null +++ b/app/win/BUILD.gn @@ -0,0 +1,17 @@ +executable("electron_app") { + + # This is just a stub and doesn't work yet + sources = [ + "//electron:main", + ] + + defines = [] + + deps = [ + "//electron:electron_dll", + "//build/config/sanitizers:deps", + "//build/win:default_exe_manifest", + ] + + deps += [ "//sandbox" ] +} diff --git a/atom/app/BUILD.gn b/atom/app/BUILD.gn new file mode 100644 index 0000000000..b4c661edc4 --- /dev/null +++ b/atom/app/BUILD.gn @@ -0,0 +1,42 @@ +import("//build/buildflag_header.gni") +import("//build/config/chrome_build.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/features.gni") + +source_set("app") { + public_configs = [ + "//electron/build:electron_config", + ] + + sources = [ + "atom_content_client.cc", + "atom_content_client.h", + "atom_main_delegate.h", + "atom_main_delegate.cc", + "uv_task_runner.cc", + "uv_task_runner.h", + ] + + if (is_mac) { + sources += [ + "atom_main_delegate_mac.mm", + ] + } + + deps = [ + "//third_party/kasko", + ] + + # TODO(bridiver) - this doesn't belong here + if (is_debug) { + sources += [ + "//electron/atom/browser/node_debugger.cc", + "//electron/atom/browser/node_debugger.h", + ] + + deps += [ + "//v8:v8", + "//electron/chromium_src:debug", + ] + } +} diff --git a/atom/app/atom_content_client.cc b/atom/app/atom_content_client.cc index 439dd8fb1b..4bfc33d849 100644 --- a/atom/app/atom_content_client.cc +++ b/atom/app/atom_content_client.cc @@ -8,23 +8,35 @@ #include #include "atom/common/atom_version.h" -#include "atom/common/chrome_version.h" #include "atom/common/options_switches.h" #include "atom/common/pepper_flash_util.h" #include "base/command_line.h" +#include "base/debug/crash_logging.h" #include "base/files/file_util.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/common/crash_keys.h" +#include "chrome/common/chrome_version.h" +#include "chrome/common/extensions/extension_process_policy.h" +#include "chrome/common/secure_origin_whitelist.h" #include "content/public/common/content_constants.h" #include "content/public/common/pepper_plugin_info.h" #include "content/public/common/user_agent.h" +#include "gpu/config/gpu_info.h" +#include "third_party/widevine/cdm/stub/widevine_cdm_version.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" #include "url/url_constants.h" #if defined(ENABLE_EXTENSIONS) #include "content/public/common/url_constants.h" #include "extensions/common/constants.h" +#include "extensions/common/features/feature_util.h" #endif namespace atom { @@ -50,6 +62,25 @@ AtomContentClient::AtomContentClient() { AtomContentClient::~AtomContentClient() { } +void AtomContentClient::SetGpuInfo(const gpu::GPUInfo& gpu_info) { + base::debug::SetCrashKeyValue(crash_keys::kGPUVendorID, + base::StringPrintf("0x%04x", gpu_info.gpu.vendor_id)); + base::debug::SetCrashKeyValue(crash_keys::kGPUDeviceID, + base::StringPrintf("0x%04x", gpu_info.gpu.device_id)); + base::debug::SetCrashKeyValue(crash_keys::kGPUDriverVersion, + gpu_info.driver_version); + base::debug::SetCrashKeyValue(crash_keys::kGPUPixelShaderVersion, + gpu_info.pixel_shader_version); + base::debug::SetCrashKeyValue(crash_keys::kGPUVertexShaderVersion, + gpu_info.vertex_shader_version); +#if defined(OS_MACOSX) + base::debug::SetCrashKeyValue(crash_keys::kGPUGLVersion, gpu_info.gl_version); +#elif defined(OS_POSIX) + base::debug::SetCrashKeyValue(crash_keys::kGPUVendor, gpu_info.gl_vendor); + base::debug::SetCrashKeyValue(crash_keys::kGPURenderer, gpu_info.gl_renderer); +#endif +} + std::string AtomContentClient::GetProduct() const { return "Chrome/" CHROME_VERSION_STRING; } @@ -57,48 +88,81 @@ std::string AtomContentClient::GetProduct() const { std::string AtomContentClient::GetUserAgent() const { return content::BuildUserAgentFromProduct( "Chrome/" CHROME_VERSION_STRING " " - ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING); + PRODUCT_SHORTNAME_STRING "/" ATOM_VERSION_STRING); } -base::string16 AtomContentClient::GetLocalizedString(int message_id) const { - return l10n_util::GetStringUTF16(message_id); +bool AtomContentClient::IsSupplementarySiteIsolationModeEnabled() { +#if defined(ENABLE_EXTENSIONS) + return extensions::IsIsolateExtensionsEnabled(); +#else + return false; +#endif } void AtomContentClient::AddAdditionalSchemes( std::vector* standard_schemes, std::vector* referrer_schemes, std::vector* savable_schemes) { - standard_schemes->push_back({"chrome-extension", url::SCHEME_WITHOUT_PORT}); + standard_schemes->push_back( + {extensions::kExtensionScheme, url::SCHEME_WITHOUT_PORT}); + standard_schemes->push_back( + {extensions::kExtensionResourceScheme, url::SCHEME_WITHOUT_PORT}); + savable_schemes->push_back(extensions::kExtensionScheme); + savable_schemes->push_back(extensions::kExtensionResourceScheme); } void AtomContentClient::AddSecureSchemesAndOrigins( std::set* schemes, std::set* origins) { -#if defined(ENABLE_EXTENSIONS) schemes->insert(content::kChromeUIScheme); schemes->insert(extensions::kExtensionScheme); schemes->insert(extensions::kExtensionResourceScheme); + GetSecureOriginWhitelist(origins); +} + +void AtomContentClient::AddServiceWorkerSchemes( + std::set* schemes) { +#if defined(ENABLE_EXTENSIONS) + if (extensions::feature_util::ExtensionServiceWorkersEnabled()) + schemes->insert(extensions::kExtensionScheme); +#endif +} + +bool AtomContentClient::AllowScriptExtensionForServiceWorker( + const GURL& script_url) { +#if defined(ENABLE_EXTENSIONS) + return script_url.SchemeIs(extensions::kExtensionScheme) || + script_url.SchemeIs(extensions::kExtensionResourceScheme); +#else + return false; #endif } +content::OriginTrialPolicy* AtomContentClient::GetOriginTrialPolicy() { + if (!origin_trial_policy_) { + origin_trial_policy_ = base::WrapUnique(new ChromeOriginTrialPolicy()); + } + return origin_trial_policy_.get(); +} + void AtomContentClient::AddPepperPlugins( std::vector* plugins) { AddPepperFlashFromCommandLine(plugins); } -void AtomContentClient::AddServiceWorkerSchemes( - std::set* service_worker_schemes) { - std::vector schemes; - ConvertStringWithSeparatorToVector(&schemes, ",", - switches::kRegisterServiceWorkerSchemes); - if (!schemes.empty()) { - for (const std::string& scheme : schemes) - service_worker_schemes->insert(scheme); - } - service_worker_schemes->insert(url::kFileScheme); -#if defined(ENABLE_EXTENSIONS) - service_worker_schemes->insert(extensions::kExtensionScheme); -#endif -} +// void AtomContentClient::AddServiceWorkerSchemes( +// std::set* service_worker_schemes) { +// std::vector schemes; +// ConvertStringWithSeparatorToVector(&schemes, ",", +// switches::kRegisterServiceWorkerSchemes); +// if (!schemes.empty()) { +// for (const std::string& scheme : schemes) +// service_worker_schemes->insert(scheme); +// } +// service_worker_schemes->insert(url::kFileScheme); +// #if defined(ENABLE_EXTENSIONS) +// service_worker_schemes->insert(extensions::kExtensionScheme); +// #endif +// } } // namespace atom diff --git a/atom/app/atom_content_client.h b/atom/app/atom_content_client.h index b08aef7fec..f04569c0e9 100644 --- a/atom/app/atom_content_client.h +++ b/atom/app/atom_content_client.h @@ -10,6 +10,7 @@ #include #include "brightray/common/content_client.h" +#include "chrome/common/origin_trials/chrome_origin_trial_policy.h" namespace atom { @@ -22,7 +23,8 @@ class AtomContentClient : public brightray::ContentClient { // content::ContentClient: std::string GetProduct() const override; std::string GetUserAgent() const override; - base::string16 GetLocalizedString(int message_id) const override; + void SetGpuInfo(const gpu::GPUInfo& gpu_info); + bool IsSupplementarySiteIsolationModeEnabled() override; void AddAdditionalSchemes( std::vector* standard_schemes, std::vector* referrer_schemes, @@ -31,10 +33,13 @@ class AtomContentClient : public brightray::ContentClient { std::vector* plugins) override; void AddServiceWorkerSchemes( std::set* service_worker_schemes) override; + bool AllowScriptExtensionForServiceWorker(const GURL& script_url) override; void AddSecureSchemesAndOrigins(std::set* schemes, std::set* origins) override; + content::OriginTrialPolicy* GetOriginTrialPolicy() override; private: + std::unique_ptr origin_trial_policy_; DISALLOW_COPY_AND_ASSIGN(AtomContentClient); }; diff --git a/atom/app/atom_library_main.mm b/atom/app/atom_library_main.mm index 7ee7522934..8b318fbf9c 100644 --- a/atom/app/atom_library_main.mm +++ b/atom/app/atom_library_main.mm @@ -5,7 +5,7 @@ #include "atom/app/atom_library_main.h" #include "atom/app/atom_main_delegate.h" -#include "atom/app/node_main.h" +// #include "atom/app/node_main.h" #include "atom/common/atom_command_line.h" #include "base/at_exit.h" #include "base/i18n/icu_util.h" @@ -14,6 +14,12 @@ #include "brightray/common/mac/main_application_bundle.h" #include "content/public/app/content_main.h" +extern "C" { +__attribute__((visibility("default"))) +int AtomMain(int argc, const char** argv); +} + + #if defined(OS_MACOSX) int AtomMain(int argc, const char* argv[]) { atom::AtomMainDelegate delegate; @@ -24,15 +30,15 @@ int AtomMain(int argc, const char* argv[]) { return content::ContentMain(params); } -int AtomInitializeICUandStartNode(int argc, char *argv[]) { - base::AtExitManager atexit_manager; - base::mac::ScopedNSAutoreleasePool pool; - base::mac::SetOverrideFrameworkBundlePath( - brightray::MainApplicationBundlePath() - .Append("Contents") - .Append("Frameworks") - .Append(ATOM_PRODUCT_NAME " Framework.framework")); - base::i18n::InitializeICU(); - return atom::NodeMain(argc, argv); -} +// int AtomInitializeICUandStartNode(int argc, char *argv[]) { +// base::AtExitManager atexit_manager; +// base::mac::ScopedNSAutoreleasePool pool; +// base::mac::SetOverrideFrameworkBundlePath( +// brightray::MainApplicationBundlePath() +// .Append("Contents") +// .Append("Frameworks") +// .Append(PRODUCT_SHORTNAME_STRING " Framework.framework")); +// base::i18n::InitializeICU(); +// return atom::NodeMain(argc, argv); +// } #endif // OS_MACOSX diff --git a/atom/app/atom_main.cc b/atom/app/atom_main.cc index 9c255db5fb..473045c9be 100644 --- a/atom/app/atom_main.cc +++ b/atom/app/atom_main.cc @@ -27,7 +27,7 @@ #include "atom/app/atom_library_main.h" #endif // defined(OS_MACOSX) -#include "atom/app/node_main.h" +// #include "atom/app/node_main.h" #include "atom/common/atom_command_line.h" #include "base/at_exit.h" #include "base/i18n/icu_util.h" @@ -135,9 +135,9 @@ int main(int argc, const char* argv[]) { #else // defined(OS_LINUX) int main(int argc, const char* argv[]) { - if (IsEnvSet(kRunAsNode)) { - return AtomInitializeICUandStartNode(argc, const_cast(argv)); - } + // if (IsEnvSet(kRunAsNode)) { + // return AtomInitializeICUandStartNode(argc, const_cast(argv)); + // } return AtomMain(argc, argv); } diff --git a/atom/app/atom_main_delegate.cc b/atom/app/atom_main_delegate.cc index 0cf53a9fc3..8760c8697d 100644 --- a/atom/app/atom_main_delegate.cc +++ b/atom/app/atom_main_delegate.cc @@ -14,17 +14,62 @@ #include "atom/common/google_api_key.h" #include "atom/renderer/atom_renderer_client.h" #include "atom/utility/atom_content_utility_client.h" +#include "base/base_switches.h" #include "base/command_line.h" #include "base/debug/stack_trace.h" #include "base/environment.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/path_service.h" +#include "base/trace_event/trace_log.h" +#include "brave/common/brave_paths.h" +#include "brightray/browser/brightray_paths.h" +#include "brightray/common/application_info.h" +#include "browser/brightray_paths.h" +#include "chrome/common/crash_keys.h" +#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/trace_event_args_whitelist.h" +#include "components/component_updater/component_updater_paths.h" +#include "components/content_settings/core/common/content_settings_pattern.h" +#include "components/crash/content/app/crashpad.h" #include "content/public/common/content_switches.h" +#include "extensions/common/constants.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" +#if defined(OS_WIN) +#include +#include +#include +#include "base/debug/close_handle_hook_win.h" +#include "chrome/browser/downgrade/user_data_downgrade.h" +#include "chrome/common/child_process_logging.h" +#include "chrome/common/v8_breakpad_support_win.h" +#include "components/crash/content/app/crashpad.h" +#include "sandbox/win/src/sandbox.h" +#include "ui/base/resource/resource_bundle_win.h" +#endif + +#if defined(OS_MACOSX) +#include "base/mac/foundation_util.h" +#include "chrome/app/chrome_main_mac.h" +#include "chrome/browser/mac/relauncher.h" +#include "chrome/common/mac/cfbundle_blocker.h" +#include "components/crash/content/app/crashpad.h" +#include "components/crash/core/common/objc_zombie.h" +#include "ui/base/l10n/l10n_util_mac.h" +#endif + +#if defined(OS_POSIX) +#include +#include +#include "chrome/app/chrome_crash_reporter_client.h" +#endif + #if defined(ENABLE_EXTENSIONS) #include "brave/browser/brave_content_browser_client.h" #include "brave/renderer/brave_content_renderer_client.h" @@ -42,11 +87,102 @@ bool IsBrowserProcess(base::CommandLine* cmd) { } #if defined(OS_WIN) +// If we try to access a path that is not currently available, we want the call +// to fail rather than show an error dialog. +void SuppressWindowsErrorDialogs() { + UINT new_flags = SEM_FAILCRITICALERRORS | + SEM_NOOPENFILEERRORBOX; + + // Preserve existing error mode. + UINT existing_flags = SetErrorMode(new_flags); + SetErrorMode(existing_flags | new_flags); +} + +bool IsSandboxedProcess() { + typedef bool (*IsSandboxedProcessFunc)(); + IsSandboxedProcessFunc is_sandboxed_process_func = + reinterpret_cast( + GetProcAddress(GetModuleHandle(NULL), "IsSandboxedProcess")); + return is_sandboxed_process_func && is_sandboxed_process_func(); +} + void InvalidParameterHandler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t) { // noop. } + +bool UseHooks() { +#if defined(ARCH_CPU_X86_64) + return false; +#elif defined(NDEBUG) + return false; +#else // NDEBUG + return true; #endif +} + +#endif // defined(OS_WIN) + +struct MainFunction { + const char* name; + int (*function)(const content::MainFunctionParams&); +}; + +void InitLogging(const std::string& process_type) { + logging::OldFileDeletionState file_state = + logging::APPEND_TO_OLD_LOG_FILE; + if (process_type.empty()) { + file_state = logging::DELETE_OLD_LOG_FILE; + } + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + logging::InitChromeLogging(command_line, file_state); +} + +base::FilePath InitializeUserDataDir() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + base::FilePath user_data_dir; + + // first check the env + std::string user_data_dir_string; + std::unique_ptr environment(base::Environment::Create()); + if (environment->GetVar("BRAVE_USER_DATA_DIR", &user_data_dir_string) && + base::IsStringUTF8(user_data_dir_string)) { + user_data_dir = base::FilePath::FromUTF8Unsafe(user_data_dir_string); + command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir); + } + + // next check the user-data-dir switch + if (user_data_dir.empty() || + command_line->HasSwitch(switches::kUserDataDir)) { + user_data_dir = + command_line->GetSwitchValuePath(switches::kUserDataDir); + if (!user_data_dir.empty() && !user_data_dir.IsAbsolute()) { + base::FilePath app_data_dir; + PathService::Get(base::DIR_APP_DATA, &app_data_dir); + user_data_dir = app_data_dir.Append(user_data_dir); + } + } + + // On Windows, trailing separators leave Chrome in a bad state. + // See crbug.com/464616. + if (user_data_dir.EndsWithSeparator()) + user_data_dir = user_data_dir.StripTrailingSeparators(); + + if (!user_data_dir.empty()) + PathService::OverrideAndCreateIfNeeded(brightray::DIR_USER_DATA, + user_data_dir, false, true); + + // TODO(bridiver) Warn and fail early if the process fails + // to get a user data directory. + if (!PathService::Get(brightray::DIR_USER_DATA, &user_data_dir)) { + brave::GetDefaultUserDataDirectory(&user_data_dir); + PathService::OverrideAndCreateIfNeeded(brightray::DIR_USER_DATA, + user_data_dir, false, true); + } + + return user_data_dir; +} } // namespace @@ -59,110 +195,87 @@ AtomMainDelegate::~AtomMainDelegate() { bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { auto command_line = base::CommandLine::ForCurrentProcess(); - logging::LoggingSettings settings; -#if defined(OS_WIN) - // On Windows the terminal returns immediately, so we add a new line to - // prevent output in the same line as the prompt. - if (IsBrowserProcess(command_line)) - std::wcout << std::endl; -#if defined(DEBUG) - // Print logging to debug.log on Windows - settings.logging_dest = logging::LOG_TO_ALL; - settings.log_file = L"debug.log"; - settings.lock_log = logging::LOCK_LOG_FILE; - settings.delete_old = logging::DELETE_OLD_LOG_FILE; -#else - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; -#endif // defined(DEBUG) -#else // defined(OS_WIN) - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; -#endif // !defined(OS_WIN) - - // Only enable logging when --enable-logging is specified. - std::unique_ptr env(base::Environment::Create()); - if (!command_line->HasSwitch(::switches::kEnableLogging) && - !env->HasVar("ELECTRON_ENABLE_LOGGING")) { - settings.logging_dest = logging::LOG_NONE; - logging::SetMinLogLevel(logging::LOG_NUM_SEVERITIES); - } - - logging::InitLogging(settings); - - // Logging with pid and timestamp. - logging::SetLogItems(true, false, true, false); - - // Enable convient stack printing. - bool enable_stack_dumping = env->HasVar("ELECTRON_ENABLE_STACK_DUMPING"); -#if defined(DEBUG) && defined(OS_LINUX) - enable_stack_dumping = true; +#if defined(OS_MACOSX) + // Give the browser process a longer treadmill, since crashes + // there have more impact. + const bool is_browser = !command_line->HasSwitch(switches::kProcessType); + ObjcEvilDoers::ZombieEnable(true, is_browser ? 10000 : 1000); #endif - if (enable_stack_dumping) - base::debug::EnableInProcessStackDumping(); - chrome::RegisterPathProvider(); + base::trace_event::TraceLog::GetInstance()->SetArgumentFilterPredicate( + base::Bind(&IsTraceEventArgsWhitelisted)); -#if defined(OS_MACOSX) - SetUpBundleOverrides(); +#if defined(OS_WIN) + v8_breakpad_support::SetUp(); #endif #if defined(OS_WIN) + if (UseHooks()) + base::debug::InstallHandleHooks(); + else + base::win::DisableHandleVerifier(); + // Ignore invalid parameter errors. _set_invalid_parameter_handler(InvalidParameterHandler); #endif - return brightray::MainDelegate::BasicStartupComplete(exit_code); -} + chrome::RegisterPathProvider(); -void LoadExtensionResources() { - // create a shared resource bundle if one does not already exist - if (!ui::ResourceBundle::HasSharedInstance()) { - std::string locale = base::CommandLine::ForCurrentProcess()-> - GetSwitchValueASCII(::switches::kLang); - ui::ResourceBundle::InitSharedInstanceWithLocale( - locale, nullptr, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); - } + ContentSettingsPattern::SetNonWildcardDomainNonPortScheme( + extensions::kExtensionScheme); - std::vector pak_resource_paths; #if defined(OS_MACOSX) - pak_resource_paths.push_back( - GetResourcesPakFilePathByName("extensions_resources")); - pak_resource_paths.push_back( - GetResourcesPakFilePathByName("extensions_renderer_resources")); - pak_resource_paths.push_back( - GetResourcesPakFilePathByName("atom_resources")); - pak_resource_paths.push_back( - GetResourcesPakFilePathByName("extensions_api_resources")); -#else - base::FilePath pak_dir; - PathService::Get(base::DIR_MODULE, &pak_dir); - - // Append returns a new FilePath - pak_resource_paths.push_back( - pak_dir.Append(FILE_PATH_LITERAL("extensions_resources.pak"))); - pak_resource_paths.push_back( - pak_dir.Append(FILE_PATH_LITERAL("extensions_renderer_resources.pak"))); - pak_resource_paths.push_back( - pak_dir.Append(FILE_PATH_LITERAL("atom_resources.pak"))); - pak_resource_paths.push_back( - pak_dir.Append(FILE_PATH_LITERAL("extensions_api_resources.pak"))); -#endif - - ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - - for (std::vector::const_iterator - path = pak_resource_paths.begin(); - path != pak_resource_paths.end(); - ++path) { - bundle.AddDataPackFromPath(*path, ui::GetSupportedScaleFactors()[0]); - } + SetUpBundleOverrides(); +#endif + + return brightray::MainDelegate::BasicStartupComplete(exit_code); } void AtomMainDelegate::PreSandboxStartup() { brightray::MainDelegate::PreSandboxStartup(); -#if defined(ENABLE_EXTENSIONS) - LoadExtensionResources(); + auto command_line = base::CommandLine::ForCurrentProcess(); + std::string process_type = command_line->GetSwitchValueASCII( + ::switches::kProcessType); + +#if defined(OS_POSIX) + // crash_reporter::SetCrashReporterClient(g_chrome_crash_client.Pointer()); #endif + +#if defined(OS_MACOSX) +#ifdef DEBUG + // disable os crash dumps in debug mode because they take forever + base::mac::DisableOSCrashDumps(); +#endif + InitMacCrashReporter(command_line, process_type); +#endif + +#if defined(OS_WIN) + child_process_logging::Init(); +#endif + + base::FilePath path = InitializeUserDataDir(); + if (path.empty()) { + LOG(ERROR) << "Could not create user data dir"; + } else { + if (!PathService::Get(component_updater::DIR_COMPONENT_USER, &path)) { + base::FilePath component_path = + path.Append(FILE_PATH_LITERAL("Extensions")); + PathService::Override(component_updater::DIR_COMPONENT_USER, component_path); + } + // TODO(bridiver) - do we need this for component updater?? + // why does chrome set DIR_COMPONENT_USER to the user data dir? + // component_updater::RegisterPathProvider(chrome::DIR_COMPONENTS, + // chrome::DIR_INTERNAL_PLUGINS, + // chrome::DIR_USER_DATA); + + } + +#if !defined(OS_WIN) + // For windows we call InitLogging when the sandbox is initialized. + InitLogging(process_type); +#endif + // Set google API key. std::unique_ptr env(base::Environment::Create()); if (!env->HasVar("GOOGLE_API_ENDPOINT")) @@ -170,9 +283,19 @@ void AtomMainDelegate::PreSandboxStartup() { if (!env->HasVar("GOOGLE_API_KEY")) env->SetVar("GOOGLE_API_KEY", GOOGLEAPIS_API_KEY); - auto command_line = base::CommandLine::ForCurrentProcess(); - std::string process_type = command_line->GetSwitchValueASCII( - ::switches::kProcessType); +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + if (process_type == switches::kUtilityProcess || + process_type == switches::kZygoteProcess) { + AtomContentUtilityClient::PreSandboxStartup(); + } +#endif + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // Zygote needs to call InitCrashReporter() in RunZygote(). + if (process_type != switches::kZygoteProcess) { + breakpad::InitCrashReporter(process_type); + } +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) // Only append arguments for browser process. if (!IsBrowserProcess(command_line)) @@ -183,29 +306,32 @@ void AtomMainDelegate::PreSandboxStartup() { // https://github.com/brave/browser-laptop/issues/715 command_line->AppendSwitch(::switches::kNoSandbox); #endif +} -#if defined(OS_MACOSX) - // Enable AVFoundation. - command_line->AppendSwitch("enable-avfoundation"); -#endif +#if defined(OS_POSIX) && !defined(OS_MACOSX) +void AtomMainDelegate::ZygoteForked() { + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch( + switches::kEnableCrashReporter)) { + std::string process_type = + command_line->GetSwitchValueASCII( + switches::kProcessType); + breakpad::InitCrashReporter(process_type); + // Reset the command line for the newly spawned process. + crash_keys::SetCrashKeysFromCommandLine(*command_line); + } } +#endif content::ContentBrowserClient* AtomMainDelegate::CreateContentBrowserClient() { -#if defined(ENABLE_EXTENSIONS) browser_client_.reset(new brave::BraveContentBrowserClient); -#else - renderer_client_.reset(new AtomBrowserClient); -#endif return browser_client_.get(); } content::ContentRendererClient* AtomMainDelegate::CreateContentRendererClient() { -#if defined(ENABLE_EXTENSIONS) renderer_client_.reset(new brave::BraveContentRendererClient); -#else - renderer_client_.reset(new AtomRendererClient); -#endif return renderer_client_.get(); } @@ -217,22 +343,98 @@ content::ContentUtilityClient* AtomMainDelegate::CreateContentUtilityClient() { int AtomMainDelegate::RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) { - if (process_type == kRelauncherProcess) - return relauncher::RelauncherMain(main_function_params); - else + static const MainFunction kMainFunctions[] = { + #if defined(ENABLE_PRINT_PREVIEW) && !defined(CHROME_MULTIPLE_DLL_CHILD) + { switches::kServiceProcess, ServiceProcessMain }, + #endif + + #if defined(OS_MACOSX) + { switches::kRelauncherProcess, + relauncher::RelauncherMain }, + // mac_relauncher::internal::RelauncherMain }, + #endif + }; + + for (size_t i = 0; i < arraysize(kMainFunctions); ++i) { + if (process_type == kMainFunctions[i].name) + return kMainFunctions[i].function(main_function_params); + } + return -1; } +void AtomMainDelegate::SandboxInitialized(const std::string& process_type) { +#if defined(OS_WIN) + InitLogging(process_type); + SuppressWindowsErrorDialogs(); +#endif +} + #if defined(OS_MACOSX) bool AtomMainDelegate::ShouldSendMachPort(const std::string& process_type) { - return process_type != kRelauncherProcess; + return process_type != switches::kRelauncherProcess && + process_type != switches::kServiceProcess; } bool AtomMainDelegate::DelaySandboxInitialization( const std::string& process_type) { return process_type == kRelauncherProcess; } + +void AtomMainDelegate::InitMacCrashReporter( + base::CommandLine* command_line, + const std::string& process_type) { + // TODO(mark): Right now, InitializeCrashpad() needs to be called after + // CommandLine::Init() and chrome::RegisterPathProvider(). Ideally, Crashpad + // initialization could occur sooner, preferably even before the framework + // dylib is even loaded, to catch potential early crashes. + + const bool browser_process = process_type.empty(); + const bool install_from_dmg_relauncher_process = + process_type == switches::kRelauncherProcess && + command_line->HasSwitch(switches::kRelauncherProcessDMGDevice); + + const bool initial_client = + browser_process || install_from_dmg_relauncher_process; + + // TODO(bridiver) - unresolved symol issue + // crash_reporter::InitializeCrashpad(initial_client, process_type); + + if (!browser_process) { + std::string metrics_client_id = + command_line->GetSwitchValueASCII(switches::kMetricsClientID); + crash_keys::SetMetricsClientIdFromGUID(metrics_client_id); + } + + // Mac is packaged with a main app bundle and a helper app bundle. + // The main app bundle should only be used for the browser process, so it + // should never see a --type switch (switches::kProcessType). Likewise, + // the helper should always have a --type switch. + // + // This check is done this late so there is already a call to + // base::mac::IsBackgroundOnlyProcess(), so there is no change in + // startup/initialization order. + + // The helper's Info.plist marks it as a background only app. + if (base::mac::IsBackgroundOnlyProcess()) { + CHECK(command_line->HasSwitch(switches::kProcessType) && + !process_type.empty()) + << "Helper application requires --type."; + } else { + CHECK(!command_line->HasSwitch(switches::kProcessType) && + process_type.empty()) + << "Main application forbids --type, saw " << process_type; + } +} +#endif // OS_MACOSX + +void AtomMainDelegate::ProcessExiting(const std::string& process_type) { + brightray::MainDelegate::ProcessExiting(process_type); + logging::CleanupChromeLogging(); +#if defined(OS_WIN) + base::debug::RemoveHandleHooks(); #endif +} std::unique_ptr AtomMainDelegate::CreateContentClient() { diff --git a/atom/app/atom_main_delegate.h b/atom/app/atom_main_delegate.h index 952ad9a098..a2d62caee9 100644 --- a/atom/app/atom_main_delegate.h +++ b/atom/app/atom_main_delegate.h @@ -10,6 +10,10 @@ #include "brightray/common/content_client.h" #include "brightray/common/main_delegate.h" +namespace base { +class CommandLine; +} + namespace atom { base::FilePath GetResourcesPakFilePathByName(const std::string resource_name); @@ -17,7 +21,7 @@ base::FilePath GetResourcesPakFilePathByName(const std::string resource_name); class AtomMainDelegate : public brightray::MainDelegate { public: AtomMainDelegate(); - ~AtomMainDelegate(); + ~AtomMainDelegate() override; protected: // content::ContentMainDelegate: @@ -29,6 +33,11 @@ class AtomMainDelegate : public brightray::MainDelegate { int RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) override; + void SandboxInitialized(const std::string& process_type) override; +#if defined(OS_POSIX) && !defined(OS_MACOSX) + void ZygoteForked() override; +#endif + #if defined(OS_MACOSX) bool ShouldSendMachPort(const std::string& process_type) override; bool DelaySandboxInitialization(const std::string& process_type) override; @@ -40,10 +49,13 @@ class AtomMainDelegate : public brightray::MainDelegate { void OverrideChildProcessPath() override; void OverrideFrameworkBundlePath() override; #endif - + void ProcessExiting(const std::string& process_type) override; private: #if defined(OS_MACOSX) void SetUpBundleOverrides(); + void InitMacCrashReporter( + base::CommandLine* command_line, + const std::string& process_type); #endif brightray::ContentClient content_client_; diff --git a/atom/app/atom_main_delegate_mac.mm b/atom/app/atom_main_delegate_mac.mm index 7a3ed397a6..d03f43a38d 100644 --- a/atom/app/atom_main_delegate_mac.mm +++ b/atom/app/atom_main_delegate_mac.mm @@ -14,6 +14,8 @@ #include "base/strings/sys_string_conversions.h" #include "brightray/common/application_info.h" #include "brightray/common/mac/main_application_bundle.h" +#include "chrome/common/chrome_version.h" +#include "chrome/common/chrome_paths_internal.h" #include "content/public/common/content_paths.h" namespace atom { @@ -36,8 +38,10 @@ } // namespace void AtomMainDelegate::OverrideFrameworkBundlePath() { + base::FilePath* version_path = new base::FilePath(GetFrameworksPath()); + chrome::SetOverrideVersionedDirectory(version_path); base::mac::SetOverrideFrameworkBundlePath( - GetFrameworksPath().Append(ATOM_PRODUCT_NAME " Framework.framework")); + GetFrameworksPath().Append(PRODUCT_SHORTNAME_STRING " Framework.framework")); } base::FilePath GetResourcesPakFilePathByName(const std::string resource_name) { @@ -49,7 +53,7 @@ void AtomMainDelegate::OverrideChildProcessPath() { base::FilePath frameworks_path = GetFrameworksPath(); base::FilePath helper_path = GetHelperAppPath(frameworks_path, - ATOM_PRODUCT_NAME); + PRODUCT_SHORTNAME_STRING); if (!base::PathExists(helper_path)) helper_path = GetHelperAppPath(frameworks_path, brightray::GetApplicationName()); diff --git a/atom/app/node_main.cc b/atom/app/node_main.cc index 040b4b6157..698cb0554b 100644 --- a/atom/app/node_main.cc +++ b/atom/app/node_main.cc @@ -6,7 +6,9 @@ #include "atom/app/uv_task_runner.h" #include "atom/browser/javascript_environment.h" +#ifndef NDEBUG #include "atom/browser/node_debugger.h" +#endif #include "base/command_line.h" #include "base/feature_list.h" #include "base/threading/thread_task_runner_handle.h" @@ -43,13 +45,15 @@ int NodeMain(int argc, char *argv[]) { node::Init(&argc, const_cast(argv), &exec_argc, &exec_argv); node::Environment* env = node::CreateEnvironment( - gin_env.isolate(), loop, gin_env.context(), argc, argv, + node::CreateIsolateData(gin_env.isolate(), loop), gin_env.context(), argc, argv, exec_argc, exec_argv); +#ifndef NDEBUG // Start our custom debugger implementation. NodeDebugger node_debugger(gin_env.isolate()); if (node_debugger.IsRunning()) - env->AssignToContext(v8::Debug::GetDebugContext()); + env->AssignToContext(v8::Debug::GetDebugContext(gin_env.isolate())); +#endif node::LoadEnvironment(env); diff --git a/atom/atom_resources.grd b/atom/atom_resources.grd index 06e11f80af..8b9b516c8b 100644 --- a/atom/atom_resources.grd +++ b/atom/atom_resources.grd @@ -8,10 +8,6 @@ - - - - @@ -21,11 +17,7 @@ - - - - - + @@ -33,9 +25,16 @@ + + + + + + + diff --git a/atom/browser/BUILD.gn b/atom/browser/BUILD.gn new file mode 100644 index 0000000000..0c1d746e92 --- /dev/null +++ b/atom/browser/BUILD.gn @@ -0,0 +1,332 @@ +import("//build/config/ui.gni") +import("//build/config/features.gni") + +source_set("browser") { + public_configs = [ + "//electron/build:electron_config", + ] + + include_dirs = [ + # make sure chromium_src comes before the chrome src root + "//electron/chromium_src", + ] + + sources = [ + "api/atom_api_app.cc", + "api/atom_api_app.h", + "api/atom_api_autofill.cc", + "api/atom_api_autofill.h", + # "api/atom_api_auto_updater.cc", + # "api/atom_api_auto_updater.h", + "api/atom_api_content_tracing.cc", + "api/atom_api_cookies.cc", + "api/atom_api_cookies.h", + "api/atom_api_debugger.cc", + "api/atom_api_debugger.h", + "api/atom_api_download_item.cc", + "api/atom_api_download_item.h", + "api/atom_api_dialog.cc", + "api/atom_api_global_shortcut.cc", + "api/atom_api_global_shortcut.h", + "api/atom_api_importer.cc", + "api/atom_api_importer.h", + "api/atom_api_menu.cc", + "api/atom_api_menu.h", + "api/atom_api_power_monitor.cc", + "api/atom_api_power_monitor.h", + "api/atom_api_power_save_blocker.cc", + "api/atom_api_power_save_blocker.h", + "api/atom_api_render_process_preferences.cc", + "api/atom_api_render_process_preferences.h", + "api/atom_api_protocol.cc", + "api/atom_api_protocol.h", + "api/atom_api_screen.cc", + "api/atom_api_screen.h", + "api/atom_api_session.cc", + "api/atom_api_session.h", + "api/atom_api_system_preferences.cc", + "api/atom_api_system_preferences.h", + "api/atom_api_tray.cc", + "api/atom_api_tray.h", + "api/atom_api_user_prefs.cc", + "api/atom_api_user_prefs.h", + "api/atom_api_web_contents.cc", + "api/atom_api_web_contents.h", + "api/atom_api_web_contents_mac.mm", + "api/atom_api_web_request.cc", + "api/atom_api_web_request.h", + "api/atom_api_window.cc", + "api/atom_api_window.h", + "api/event.cc", + "api/event.h", + "api/event_emitter.cc", + "api/event_emitter.h", + "api/trackable_object.cc", + "api/trackable_object.h", + "api/frame_subscriber.cc", + "api/frame_subscriber.h", + "api/save_page_handler.cc", + "api/save_page_handler.h", + "auto_updater.cc", + "auto_updater.h", + "atom_access_token_store.cc", + "atom_access_token_store.h", + "atom_browser_client.cc", + "atom_browser_client.h", + "atom_browser_context.cc", + "atom_browser_context.h", + "atom_download_manager_delegate.cc", + "atom_download_manager_delegate.h", + "atom_browser_main_parts.cc", + "atom_browser_main_parts.h", + "atom_javascript_dialog_manager.cc", + "atom_javascript_dialog_manager.h", + "atom_permission_manager.cc", + "atom_permission_manager.h", + "atom_quota_permission_context.cc", + "atom_quota_permission_context.h", + "atom_resource_dispatcher_host_delegate.cc", + "atom_resource_dispatcher_host_delegate.h", + "atom_security_state_model_client.cc", + "atom_security_state_model_client.h", + "atom_speech_recognition_manager_delegate.cc", + "atom_speech_recognition_manager_delegate.h", + "autofill/atom_autofill_client.cc", + "autofill/atom_autofill_client.h", + "autofill/autofill_observer.h", + "autofill/personal_data_manager_factory.cc", + "autofill/personal_data_manager_factory.h", + "bridge_task_runner.cc", + "bridge_task_runner.h", + "browser.cc", + "browser.h", + "browser_context_keyed_service_factories.cc", + "browser_context_keyed_service_factories.h", + "browser_observer.h", + "common_web_contents_delegate.cc", + "common_web_contents_delegate.h", + "importer/profile_writer.cc", + "javascript_environment.cc", + "javascript_environment.h", + "lib/bluetooth_chooser.cc", + "lib/bluetooth_chooser.h", + "login_handler.cc", + "login_handler.h", + "native_window.cc", + "native_window.h", + "native_window_observer.h", + "net/asar/asar_protocol_handler.cc", + "net/asar/asar_protocol_handler.h", + "net/asar/url_request_asar_job.cc", + "net/asar/url_request_asar_job.h", + "net/atom_cert_verifier.cc", + "net/atom_cert_verifier.h", + "net/atom_network_delegate.cc", + "net/atom_network_delegate.h", + "net/atom_ssl_config_service.cc", + "net/atom_ssl_config_service.h", + "net/atom_url_request_job_factory.cc", + "net/atom_url_request_job_factory.h", + "net/http_protocol_handler.cc", + "net/http_protocol_handler.h", + "net/js_asker.cc", + "net/js_asker.h", + "net/url_request_async_asar_job.cc", + "net/url_request_async_asar_job.h", + "net/url_request_string_job.cc", + "net/url_request_string_job.h", + "net/url_request_buffer_job.cc", + "net/url_request_buffer_job.h", + "net/url_request_fetch_job.cc", + "net/url_request_fetch_job.h", + "relauncher.cc", + "relauncher.h", + "render_process_preferences.cc", + "render_process_preferences.h", + "ui/accelerator_util.cc", + "ui/accelerator_util.h", + "ui/atom_menu_model.cc", + "ui/atom_menu_model.h", + "ui/drag_util.h", + "ui/file_dialog.h", + "ui/message_box.h", + "ui/tray_icon.cc", + "ui/tray_icon.h", + "unresponsive_suppressor.cc", + "unresponsive_suppressor.h", + "web_contents_permission_helper.cc", + "web_contents_permission_helper.h", + "web_contents_preferences.cc", + "web_contents_preferences.h", + "web_dialog_helper.cc", + "web_dialog_helper.h", + "window_list.cc", + "window_list.h", + "window_list_observer.h", + ] + + if (is_posix) { + sources += [ + "atom_browser_main_parts_posix.cc", + ] + } + + if (is_mac) { + sources += [ + "api/atom_api_system_preferences_mac.mm", + "common_web_contents_delegate_mac.mm", + "api/atom_api_menu_mac.h", + "api/atom_api_menu_mac.mm", + # "auto_updater_mac.mm", + "atom_browser_main_parts_mac.mm", + "browser_mac.mm", + "mac/atom_application.h", + "mac/atom_application.mm", + "mac/atom_application_delegate.h", + "mac/atom_application_delegate.mm", + "mac/dict_util.h", + "mac/dict_util.mm", + "native_window_mac.h", + "native_window_mac.mm", + "relauncher_mac.cc", + "ui/accelerator_util_mac.mm", + "ui/cocoa/atom_menu_controller.h", + "ui/cocoa/atom_menu_controller.mm", + "ui/drag_util_mac.mm", + "ui/file_dialog_mac.mm", + "ui/message_box_mac.mm", + "ui/tray_icon_cocoa.h", + "ui/tray_icon_cocoa.mm", + ] + } + + if (use_aura) { + sources += [ + "api/atom_api_menu_views.cc", + "api/atom_api_menu_views.h", + "common_web_contents_delegate_views.cc", + "native_window_views.h", + "ui/accelerator_util_views.cc", + "ui/views/frameless_view.cc", + "ui/views/frameless_view.h", + "ui/views/menu_bar.cc", + "ui/views/menu_bar.h", + "ui/views/menu_delegate.cc", + "ui/views/menu_delegate.h", + "ui/views/menu_layout.cc", + "ui/views/menu_layout.h", + "ui/views/menu_model_adapter.cc", + "ui/views/menu_model_adapter.h", + "ui/views/native_frame_view.cc", + "ui/views/native_frame_view.h", + "ui/views/submenu_button.cc", + "ui/views/submenu_button.h", + "ui/views/win_frame_view.cc", + "ui/views/win_frame_view.h", + ] + } + + if (is_win) { + sources += [ + "browser_win.cc", + "native_window_views_win.cc", + "relauncher_win.cc", + "ui/file_dialog_win.cc", + "ui/message_box_win.cc", + "ui/tray_icon_win.cc", + "ui/win/atom_desktop_window_tree_host_win.cc", + "ui/win/atom_desktop_window_tree_host_win.h", + "ui/win/jump_list.cc", + "ui/win/jump_list.h", + "ui/win/message_handler_delegate.cc", + "ui/win/message_handler_delegate.h", + "ui/win/notify_icon_host.cc", + "ui/win/notify_icon_host.h", + "ui/win/notify_icon.cc", + "ui/win/notify_icon.h", + "ui/win/taskbar_host.cc", + "ui/win/taskbar_host.h", + ] + } + + if (is_linux) { + sources += [ + "browser_linux.cc", + "relauncher_linux.cc", + "ui/file_dialog_gtk.cc", + "ui/message_box_gtk.cc", + "ui/tray_icon_gtk.cc", + "ui/tray_icon_gtk.h", + "ui/views/global_menu_bar_x11.cc", + "ui/views/global_menu_bar_x11.h", + "ui/x/event_disabler.cc", + "ui/x/event_disabler.h", + "ui/x/window_state_watcher.cc", + "ui/x/window_state_watcher.h", + "ui/x/x_window_utils.cc", + "ui/x/x_window_utils.h", + ] + } + + public_deps = [ + "//electron/vendor/brightray:browser", + "//ui/events", + "//components/security_state", + "//components/autofill/content/browser:browser", + "//third_party/protobuf:protobuf_lite", + "//third_party/WebKit/public:blink_headers", + ] + + deps = [ + "//electron:common", + "//storage/browser", + "//storage/common", + ] + + if (use_aura) { + sources += [ + "native_window_views.cc", + "ui/drag_util_views.cc", + ] + } + if (enable_webrtc) { + sources += [ + "api/atom_api_desktop_capturer.cc", + "api/atom_api_desktop_capturer.h", + ] + deps += [ + "//third_party/webrtc/modules/desktop_capture", + ] + } + + if (enable_extensions) { + sources += [ + "api/atom_api_extension.cc", + "api/atom_api_extension.h", + "extensions/api/atom_extensions_api_client.cc", + "extensions/api/atom_extensions_api_client.h", + "extensions/atom_browser_client_extensions_part.cc", + "extensions/atom_browser_client_extensions_part.h", + "extensions/atom_extension_api_frame_id_map_helper.cc", + "extensions/atom_extension_api_frame_id_map_helper.h", + "extensions/atom_extension_host_delegate.cc", + "extensions/atom_extension_host_delegate.h", + "extensions/atom_extensions_network_delegate.cc", + "extensions/atom_extensions_network_delegate.h", + "extensions/atom_extension_system.cc", + "extensions/atom_extension_system.h", + "extensions/atom_extension_system_factory.cc", + "extensions/atom_extension_system_factory.h", + "extensions/atom_extension_web_contents_observer.cc", + "extensions/atom_extension_web_contents_observer.h", + "extensions/atom_extensions_browser_client.cc", + "extensions/atom_extensions_browser_client.h", + "extensions/atom_process_manager_delegate.cc", + "extensions/atom_process_manager_delegate.h", + "extensions/shared_user_script_master.cc", + "extensions/shared_user_script_master.h", + "extensions/tab_helper.cc", + "extensions/tab_helper.h", + ] + } +} diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 5a721d147c..421f965c86 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -44,6 +44,7 @@ #include "content/public/browser/navigation_details.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" +#include "chrome/browser/browser_process.h" #include "content/public/common/content_switches.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" @@ -51,6 +52,8 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/image/image.h" +#include "base/threading/thread_restrictions.h" + #if defined(OS_WIN) #include "atom/browser/ui/win/jump_list.h" #include "base/strings/utf_string_conversions.h" @@ -325,6 +328,47 @@ struct Converter { return dict.GetHandle(); } }; + +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + content::CertificateRequestResultType* out) { + std::string item_type; + if (!ConvertFromV8(isolate, val, &item_type)) + return false; + + if (item_type == "continue") + *out = content::CertificateRequestResultType::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE; + else if (item_type == "cancel") + *out = content::CertificateRequestResultType::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL; + else if (item_type == "deny") + *out = content::CertificateRequestResultType::CERTIFICATE_REQUEST_RESULT_TYPE_DENY; + else + return false; + + return true; + } + + static v8::Local ToV8(v8::Isolate* isolate, + content::CertificateRequestResultType val) { + std::string item_type; + switch (val) { + case content::CertificateRequestResultType::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE: + item_type = "continue"; + break; + + case content::CertificateRequestResultType::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL: + item_type = "cancel"; + break; + + case content::CertificateRequestResultType::CERTIFICATE_REQUEST_RESULT_TYPE_DENY: + item_type = "deny"; + break; + } + return mate::ConvertToV8(isolate, item_type); + } +}; + } // namespace mate @@ -459,8 +503,9 @@ App::App(v8::Isolate* isolate) { static_cast( brave::BraveContentBrowserClient::Get())->set_delegate(this); Browser::Get()->AddObserver(this); - content::GpuDataManager::GetInstance()->AddObserver(this); + // content::GpuDataManager::GetInstance()->AddObserver(this); Init(isolate); + g_browser_process->set_app(this); #if defined(ENABLE_EXTENSIONS) registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, @@ -475,7 +520,6 @@ App::App(v8::Isolate* isolate) { void App::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { -#if defined(ENABLE_EXTENSIONS) switch (type) { case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { content::WebContents* web_contents = @@ -483,11 +527,13 @@ void App::Observe( auto browser_context = web_contents->GetBrowserContext(); auto url = web_contents->GetURL(); +#if defined(ENABLE_EXTENSIONS) // make sure background pages get a webcontents // api wrapper so they can communicate via IPC if (Extension::IsBackgroundPageUrl(url, browser_context)) { WebContents::CreateFrom(isolate(), web_contents); } +#endif break; } case chrome::NOTIFICATION_PROFILE_CREATED: { @@ -498,7 +544,6 @@ void App::Observe( break; } } -#endif } App::~App() { @@ -582,58 +627,6 @@ void App::OnLogin(LoginHandler* login_handler, login_handler->CancelAuth(); } -bool App::CanCreateWindow(const GURL& opener_url, - const GURL& opener_top_level_frame_url, - const GURL& source_origin, - WindowContainerType container_type, - const std::string& frame_name, - const GURL& target_url, - const content::Referrer& referrer, - WindowOpenDisposition disposition, - const blink::WebWindowFeatures& features, - bool user_gesture, - bool opener_suppressed, - content::ResourceContext* context, - int render_process_id, - int opener_render_view_id, - int opener_render_frame_id, - bool* no_javascript_access) { - // just a reminder that we are on the IO thread - // and need to be careful about v8 isolate usage - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - *no_javascript_access = false; - - if (container_type == WINDOW_CONTAINER_TYPE_BACKGROUND) { - return true; - } - - // this will override allowpopups we need some way to integerate - // it so we can turn popup blocking off if desired - if (!user_gesture) { - return false; - } - - return true; -} - -void App::OnCreateWindow(const GURL& target_url, - const std::string& frame_name, - WindowOpenDisposition disposition, - int render_process_id, - int render_frame_id) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - content::RenderFrameHost* rfh = - content::RenderFrameHost::FromID(render_process_id, render_frame_id); - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(rfh); - if (web_contents) { - auto api_web_contents = WebContents::CreateFrom(isolate(), web_contents); - api_web_contents->OnCreateWindow(target_url, frame_name, disposition); - } -} - void App::AllowCertificateError( content::WebContents* web_contents, int cert_error, @@ -643,24 +636,21 @@ void App::AllowCertificateError( bool overridable, bool strict_enforcement, bool expired_previous_decision, - const base::Callback& callback, - content::CertificateRequestResultType* request) { + const base::Callback& + callback) { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); - bool prevent_default = Emit("certificate-error", - WebContents::CreateFrom(isolate(), web_contents), - request_url, - net::ErrorToString(cert_error), - ssl_info.cert, - ResourceTypeToString(resource_type), - overridable, - strict_enforcement, - expired_previous_decision, - callback); - - // Deny the certificate by default. - if (!prevent_default) - *request = content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY; + // TODO(bridiver) handle CertificateRequestResultType + Emit("certificate-error", + WebContents::CreateFrom(isolate(), web_contents), + request_url, + net::ErrorToString(cert_error), + ssl_info.cert, + ResourceTypeToString(resource_type), + overridable, + strict_enforcement, + expired_previous_decision, + callback); } void App::SelectClientCertificate( @@ -748,6 +738,7 @@ void App::SetLocale(std::string locale) { bool App::MakeSingleInstance( const ProcessSingleton::NotificationCallback& callback) { + base::ThreadRestrictions::SetIOAllowed(true); // ugh electron if (process_singleton_.get()) return false; diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index b809b98184..fea70e7e3a 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -89,23 +89,6 @@ class App : public AtomBrowserClient::Delegate, const base::DictionaryValue& user_info) override; #endif - // content::ContentBrowserClient: - bool CanCreateWindow(const GURL& opener_url, - const GURL& opener_top_level_frame_url, - const GURL& source_origin, - WindowContainerType container_type, - const std::string& frame_name, - const GURL& target_url, - const content::Referrer& referrer, - WindowOpenDisposition disposition, - const blink::WebWindowFeatures& features, - bool user_gesture, - bool opener_suppressed, - content::ResourceContext* context, - int render_process_id, - int opener_render_view_id, - int opener_render_frame_id, - bool* no_javascript_access) override; void AllowCertificateError( content::WebContents* web_contents, int cert_error, @@ -115,8 +98,8 @@ class App : public AtomBrowserClient::Delegate, bool overridable, bool strict_enforcement, bool expired_previous_decision, - const base::Callback& callback, - content::CertificateRequestResultType* request) override; + const base::Callback& + callback) override; void SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, diff --git a/atom/browser/api/atom_api_autofill.cc b/atom/browser/api/atom_api_autofill.cc index 8ad5425e3b..600b8cfbc7 100644 --- a/atom/browser/api/atom_api_autofill.cc +++ b/atom/browser/api/atom_api_autofill.cc @@ -166,6 +166,10 @@ Autofill::Autofill(v8::Isolate* isolate, Autofill::~Autofill() { } +Profile* Autofill::profile() { + return Profile::FromBrowserContext(browser_context_); +} + void Autofill::AddProfile(const base::DictionaryValue& profile) { std::string full_name, company_name, street_address, city, state, locality, postal_code, sorting_code, country_code, phone, email, language_code, guid; @@ -351,8 +355,7 @@ void Autofill::RemoveCreditCard(const std::string& guid) { void Autofill::ClearAutocompleteData() { scoped_refptr web_data_service = - static_cast(browser_context_) - ->GetAutofillWebdataService(); + profile()->GetAutofillWebdataService(); if (web_data_service.get()) { base::Time delete_begin; @@ -379,8 +382,7 @@ void Autofill::ClearAutocompleteData() { void Autofill::ClearAutofillData() { scoped_refptr web_data_service = - static_cast(browser_context_) - ->GetAutofillWebdataService(); + profile()->GetAutofillWebdataService(); if (web_data_service.get()) { base::Time delete_begin; diff --git a/atom/browser/api/atom_api_autofill.h b/atom/browser/api/atom_api_autofill.h index fcb467e035..849a6c3c77 100644 --- a/atom/browser/api/atom_api_autofill.h +++ b/atom/browser/api/atom_api_autofill.h @@ -59,10 +59,7 @@ class Autofill : public mate::TrackableObject, // PersonalDataManagerObserver void OnPersonalDataChanged() override; - brave::BraveBrowserContext* browser_context() { - return static_cast(browser_context_); - } - + Profile* profile(); private: void OnClearedAutocompleteData(); void OnClearedAutofillData(); diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc index d11e5a3add..29ff56e9fe 100644 --- a/atom/browser/api/atom_api_download_item.cc +++ b/atom/browser/api/atom_api_download_item.cc @@ -10,8 +10,8 @@ #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h" -#include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #include "native_mate/dictionary.h" #include "net/base/filename_util.h" @@ -80,7 +80,7 @@ void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) { Emit("done", item->GetState()); // Destroy the item once item is downloaded. - base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, GetDestroyClosure()); } else { Emit("updated", item->GetState()); } diff --git a/atom/browser/api/atom_api_extension.cc b/atom/browser/api/atom_api_extension.cc index dbdc508867..99ae4dbef6 100644 --- a/atom/browser/api/atom_api_extension.cc +++ b/atom/browser/api/atom_api_extension.cc @@ -78,6 +78,8 @@ scoped_refptr LoadExtension(const base::FilePath& path, const extensions::Manifest::Location& manifest_location, int flags, std::string* error) { + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); + scoped_refptr extension(extensions::Extension::Create( path, manifest_location, manifest, flags, error)); if (!extension.get()) @@ -113,6 +115,62 @@ Extension::~Extension() { } } + +void Extension::LoadOnFILEThread(const base::FilePath path, + std::unique_ptr manifest, + extensions::Manifest::Location manifest_location, + int flags) { + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); + + std::string error; + if (manifest->empty()) { + manifest = extensions::file_util::LoadManifest(path, &error); + } + + if (!manifest || !error.empty()) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Extension::NotifyErrorOnUIThread, + base::Unretained(this), error)); + } else { + scoped_refptr extension = LoadExtension(path, + *manifest, + manifest_location, + flags, + &error); + + if (!extension || !error.empty()) { + content::BrowserThread::PostTask( + content::BrowserThread::FILE, FROM_HERE, + base::Bind(&Extension::NotifyErrorOnUIThread, + base::Unretained(this), error)); + } else { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Extension::NotifyLoadOnUIThread, + base::Unretained(this), base::Passed(&extension))); + } + } +} + +void Extension::NotifyLoadOnUIThread( + scoped_refptr extension) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + extensions::ExtensionSystem::Get(browser_context_.get())->ready().Post( + FROM_HERE, + base::Bind(&Extension::AddExtension, + // GetWeakPtr() + base::Unretained(this), base::Passed(&extension))); +} + +void Extension::NotifyErrorOnUIThread(const std::string& error) { + node::Environment* env = node::Environment::GetCurrent(isolate()); + mate::EmitEvent(isolate(), + env->process_object(), + "extension-load-error", + error); +} + void Extension::Load(mate::Arguments* args) { base::FilePath path; args->GetNext(&path); @@ -127,40 +185,14 @@ void Extension::Load(mate::Arguments* args) { int flags = 0; args->GetNext(&flags); - std::string error; std::unique_ptr manifest_copy = manifest.CreateDeepCopy(); - if (manifest_copy->empty()) { - manifest_copy = extensions::file_util::LoadManifest(path, &error); - } - - if (!manifest_copy || !error.empty()) { - node::Environment* env = node::Environment::GetCurrent(isolate()); - mate::EmitEvent(isolate(), - env->process_object(), - "extension-load-error", - error); - } else { - scoped_refptr extension = LoadExtension(path, - *manifest_copy, - manifest_location, - flags, - &error); - if (!extension || !error.empty()) { - node::Environment* env = node::Environment::GetCurrent(isolate()); - mate::EmitEvent(isolate(), - env->process_object(), - "extension-load-error", - error); - } else { - extensions::ExtensionSystem::Get(browser_context_.get())->ready().Post( - FROM_HERE, - base::Bind(&Extension::AddExtension, - // GetWeakPtr() - base::Unretained(this), base::Passed(&extension))); - } - } + content::BrowserThread::PostTask( + content::BrowserThread::FILE, FROM_HERE, + base::Bind(&Extension::LoadOnFILEThread, + base::Unretained(this), + path, Passed(&manifest_copy), manifest_location, flags)); } void Extension::AddExtension(scoped_refptr extension) { diff --git a/atom/browser/api/atom_api_extension.h b/atom/browser/api/atom_api_extension.h index 8a00453439..59bd86dede 100644 --- a/atom/browser/api/atom_api_extension.h +++ b/atom/browser/api/atom_api_extension.h @@ -60,6 +60,12 @@ class Extension : public mate::TrackableObject, Extension(v8::Isolate* isolate, AtomBrowserContext* browser_context); ~Extension() override; + void NotifyLoadOnUIThread(scoped_refptr extension); + void NotifyErrorOnUIThread(const std::string& error); + void LoadOnFILEThread(const base::FilePath path, + std::unique_ptr manifest, + extensions::Manifest::Location manifest_location, + int flags); void Load(mate::Arguments* args); void AddExtension(scoped_refptr extension); void OnExtensionReady(content::BrowserContext* browser_context, diff --git a/atom/browser/api/atom_api_importer.cc b/atom/browser/api/atom_api_importer.cc index 1208260b74..8396346edd 100644 --- a/atom/browser/api/atom_api_importer.cc +++ b/atom/browser/api/atom_api_importer.cc @@ -18,10 +18,8 @@ #include "brave/browser/brave_content_browser_client.h" #include "chrome/browser/importer/external_process_importer_host.h" #include "chrome/browser/importer/importer_list.h" -#include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" #include "native_mate/dictionary.h" -#include "native_mate/object_template_builder.h" using content::BrowserThread; diff --git a/atom/browser/api/atom_api_importer.h b/atom/browser/api/atom_api_importer.h index 090a8085db..70aa2d8069 100644 --- a/atom/browser/api/atom_api_importer.h +++ b/atom/browser/api/atom_api_importer.h @@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/values.h" #include "chrome/browser/importer/importer_progress_observer.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/importer/importer_data_types.h" #include "native_mate/handle.h" diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index a579c77417..e821dbe7f0 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -9,6 +9,7 @@ #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/image_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" +#include "base/threading/thread_task_runner_handle.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" @@ -160,7 +161,7 @@ void Menu::MenuDestroyed() { if (!is_destroyed_) { is_destroyed_ = true; FOR_EACH_OBSERVER(MenuObserver, observers_, MenuDestroyed()); - base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, GetDestroyClosure()); } } diff --git a/atom/browser/api/atom_api_menu_mac.mm b/atom/browser/api/atom_api_menu_mac.mm index 9e901d1098..8b8592d062 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -35,7 +35,7 @@ [[AtomMenuController alloc] initWithModel:model_.get() useDefaultAccelerator:NO]); NSMenu* menu = [menu_controller menu]; - NSView* view = web_contents->GetView()->GetNativeView(); + NSView* view = web_contents->GetWebContents()->GetNativeView(); // Which menu item to show. NSMenuItem* item = nil; diff --git a/atom/browser/api/atom_api_power_save_blocker.cc b/atom/browser/api/atom_api_power_save_blocker.cc index e407225094..acba7a44e6 100644 --- a/atom/browser/api/atom_api_power_save_blocker.cc +++ b/atom/browser/api/atom_api_power_save_blocker.cc @@ -6,6 +6,7 @@ #include +#include "chrome/common/chrome_version.h" #include "content/public/browser/browser_thread.h" #include "native_mate/dictionary.h" @@ -75,10 +76,10 @@ void PowerSaveBlocker::UpdatePowerSaveBlocker() { new device::PowerSaveBlocker( new_blocker_type, device::PowerSaveBlocker::kReasonOther, - ATOM_PRODUCT_NAME, - content::BrowserThread::GetMessageLoopProxyForThread( + PRODUCT_SHORTNAME_STRING, + content::BrowserThread::GetTaskRunnerForThread( content::BrowserThread::UI), - content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::GetTaskRunnerForThread( content::BrowserThread::FILE))); power_save_blocker_.swap(new_blocker); current_blocker_type_ = new_blocker_type; diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index a13d08a9c2..a10ec09c45 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -29,6 +29,7 @@ #include "base/guid.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "brave/browser/brave_content_browser_client.h" #include "brave/browser/brave_permission_manager.h" @@ -60,6 +61,11 @@ using content::BrowserThread; using content::StoragePartition; +class ScopedAllowWaitForLegacyWebViewApi { + private: + base::ThreadRestrictions::ScopedAllowWait wait; +}; + namespace { struct ClearStorageDataOptions { @@ -223,7 +229,7 @@ class ResolveProxyHelper { // Start the request. int result = proxy_service->ResolveProxy( - url, "GET", net::LOAD_NORMAL, &proxy_info_, completion_callback, + url, "GET", &proxy_info_, completion_callback, &pac_req_, nullptr, net::BoundNetLog()); // Completed synchronously. @@ -620,6 +626,11 @@ mate::Handle Session::FromPartition( browser_context = AtomBrowserContext::From( partition == "default" ? "" : partition, true, options); } + DCHECK(browser_context.get()); + // TODO(bridiver) - this is a huge hack to deal with sync call + ScopedAllowWaitForLegacyWebViewApi wait_allowed; + static_cast( + browser_context.get())->ready()->Wait(); return CreateFrom(isolate, static_cast(browser_context.get())); } diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index 3f0386ab70..291f7f92e9 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -13,6 +13,7 @@ #include "atom/common/native_mate_converters/image_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/node_includes.h" +#include "base/threading/thread_task_runner_handle.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" #include "ui/gfx/image/image.h" @@ -71,7 +72,7 @@ Tray::Tray(v8::Isolate* isolate, v8::Local wrapper, Tray::~Tray() { // Destroy the native tray in next tick. - base::MessageLoop::current()->DeleteSoon(FROM_HERE, tray_icon_.release()); + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, tray_icon_.release()); } // static diff --git a/atom/browser/api/atom_api_user_prefs.cc b/atom/browser/api/atom_api_user_prefs.cc index bc20e602f1..7aa4c6fc37 100644 --- a/atom/browser/api/atom_api_user_prefs.cc +++ b/atom/browser/api/atom_api_user_prefs.cc @@ -5,15 +5,14 @@ #include "atom/browser/api/atom_api_user_prefs.h" #include "atom/common/native_mate_converters/value_converter.h" +#include "atom/common/native_mate_converters/v8_value_converter.h" #include "base/values.h" #include "content/public/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" -#if defined(ENABLE_EXTENSIONS) #include "components/pref_registry/pref_registry_syncable.h" #include "components/syncable_prefs/pref_service_syncable.h" -#endif -#include "atom/common/native_mate_converters/v8_value_converter.h" namespace mate { @@ -54,122 +53,127 @@ UserPrefs::UserPrefs(v8::Isolate* isolate, UserPrefs::~UserPrefs() { } +Profile* UserPrefs::profile() { + return Profile::FromBrowserContext(browser_context_); +} + void UserPrefs::RegisterStringPref(const std::string& path, const std::string& default_value, bool overlay) { - browser_context()->pref_registry()->RegisterStringPref(path, default_value); + profile()->pref_registry()->RegisterStringPref(path, default_value); if (overlay) - browser_context()->AddOverlayPref(path); + profile()->AddOverlayPref(path); } void UserPrefs::RegisterDictionaryPref(const std::string& path, const base::DictionaryValue& default_value, bool overlay) { std::unique_ptr copied( default_value.CreateDeepCopy()); - browser_context()->pref_registry()-> + profile()->pref_registry()-> RegisterDictionaryPref(path, copied.release()); if (overlay) - browser_context()->AddOverlayPref(path); + profile()->AddOverlayPref(path); } void UserPrefs::RegisterListPref(const std::string& path, const base::ListValue& default_value, bool overlay) { std::unique_ptr copied( default_value.CreateDeepCopy()); - browser_context()->pref_registry()-> + profile()->pref_registry()-> RegisterListPref(path, copied.release()); if (overlay) - browser_context()->AddOverlayPref(path); + profile()->AddOverlayPref(path); } void UserPrefs::RegisterBooleanPref(const std::string& path, bool default_value, bool overlay) { - browser_context()->pref_registry()->RegisterBooleanPref(path, default_value); + profile()->pref_registry()->RegisterBooleanPref(path, default_value); if (overlay) - browser_context()->AddOverlayPref(path); + profile()->AddOverlayPref(path); } void UserPrefs::RegisterIntegerPref(const std::string& path, int default_value, bool overlay) { - browser_context()->pref_registry()->RegisterIntegerPref(path, default_value); + profile()->pref_registry()->RegisterIntegerPref(path, default_value); if (overlay) - browser_context()->AddOverlayPref(path); + profile()->AddOverlayPref(path); } void UserPrefs::RegisterDoublePref(const std::string& path, double default_value, bool overlay) { - browser_context()->pref_registry()->RegisterDoublePref(path, default_value); + profile()->pref_registry()->RegisterDoublePref(path, default_value); if (overlay) - browser_context()->AddOverlayPref(path); + profile()->AddOverlayPref(path); } std::string UserPrefs::GetStringPref(const std::string& path) { - return browser_context()->user_prefs()->GetString(path); + return profile()->GetPrefs()->GetString(path); } const base::DictionaryValue* UserPrefs::GetDictionaryPref( const std::string& path) { - return browser_context()->user_prefs()->GetDictionary(path); + return profile()->GetPrefs()->GetDictionary(path); } const base::ListValue* UserPrefs::GetListPref(const std::string& path) { - return browser_context()->user_prefs()->GetList(path); + return profile()->GetPrefs()->GetList(path); } bool UserPrefs::GetBooleanPref(const std::string& path) { - return browser_context()->user_prefs()->GetBoolean(path); + return profile()->GetPrefs()->GetBoolean(path); } int UserPrefs::GetIntegerPref(const std::string& path) { - return browser_context()->user_prefs()->GetInteger(path); + return profile()->GetPrefs()->GetInteger(path); } double UserPrefs::GetDoublePref(const std::string& path) { - return browser_context()->user_prefs()->GetDouble(path); + return profile()->GetPrefs()->GetDouble(path); } void UserPrefs::SetStringPref(const std::string& path, const std::string& value) { - browser_context()->user_prefs()->SetString(path, value); + profile()->GetPrefs()->SetString(path, value); } void UserPrefs::SetDictionaryPref(const std::string& path, const base::DictionaryValue& value) { - browser_context()->user_prefs()->Set(path, value); + profile()->GetPrefs()->Set(path, value); } void UserPrefs::SetListPref(const std::string& path, const base::ListValue& value) { - browser_context()->user_prefs()->Set(path, value); + profile()->GetPrefs()->Set(path, value); } void UserPrefs::SetBooleanPref(const std::string& path, bool value) { - browser_context()->user_prefs()->SetBoolean(path, value); + profile()->GetPrefs()->SetBoolean(path, value); } void UserPrefs::SetIntegerPref(const std::string& path, int value) { - browser_context()->user_prefs()->SetInteger(path, value); + profile()->GetPrefs()->SetInteger(path, value); } void UserPrefs::SetDoublePref(const std::string& path, double value) { - browser_context()->user_prefs()->SetDouble(path, value); + profile()->GetPrefs()->SetDouble(path, value); } double UserPrefs::GetDefaultZoomLevel() { - return browser_context()->GetZoomLevelPrefs()->GetDefaultZoomLevelPref(); + return profile()->GetZoomLevelPrefs()->GetDefaultZoomLevelPref(); } void UserPrefs::SetDefaultZoomLevel(double zoom) { - if (!browser_context()->IsOffTheRecord()) - browser_context()->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(zoom); + if (!profile()->IsOffTheRecord()) + profile()->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(zoom); } // static mate::Handle UserPrefs::Create( v8::Isolate* isolate, content::BrowserContext* browser_context) { + DCHECK(browser_context); return mate::CreateHandle(isolate, new UserPrefs(isolate, browser_context)); } diff --git a/atom/browser/api/atom_api_user_prefs.h b/atom/browser/api/atom_api_user_prefs.h index 023a53ac6f..dca3efb4f6 100644 --- a/atom/browser/api/atom_api_user_prefs.h +++ b/atom/browser/api/atom_api_user_prefs.h @@ -17,9 +17,7 @@ class DictionaryValue; class ListValue; } -namespace brave { -class BraveBrowserContext; -} +class Profile; namespace atom { @@ -78,10 +76,7 @@ class UserPrefs : public mate::TrackableObject { double GetDefaultZoomLevel(); void SetDefaultZoomLevel(double zoom); - brave::BraveBrowserContext* browser_context() { - return static_cast(browser_context_); - } - + Profile* profile(); private: content::BrowserContext* browser_context_; // not owned diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 228bddd9c4..57f42162e5 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -21,13 +21,9 @@ #include "atom/browser/lib/bluetooth_chooser.h" #include "atom/browser/native_window.h" #include "atom/browser/net/atom_network_delegate.h" -#include "atom/browser/osr/osr_output_device.h" -#include "atom/browser/osr/osr_render_widget_host_view.h" -#include "atom/browser/osr/osr_web_contents_view.h" #include "atom/browser/ui/drag_util.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" -#include "atom/browser/web_view_guest_delegate.h" #include "atom/common/api/api_messages.h" #include "atom/common/api/event_emitter_caller.h" #include "atom/common/color_util.h" @@ -45,14 +41,15 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "brave/browser/brave_content_browser_client.h" +#include "brave/browser/guest_view/tab_view/tab_view_guest.h" #include "brave/browser/renderer_preferences_helper.h" #include "brightray/browser/inspectable_web_contents.h" #include "brightray/browser/inspectable_web_contents_view.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" -#include "chrome/browser/printing/print_preview_message_handler.h" -#include "chrome/browser/printing/print_view_manager_basic.h" +// #include "chrome/browser/printing/print_preview_message_handler.h" +// #include "chrome/browser/printing/print_view_manager_basic.h" #include "components/autofill/content/browser/content_autofill_driver_factory.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/zoom/page_zoom.h" @@ -82,6 +79,7 @@ #include "native_mate/object_template_builder.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request_context.h" +#include "printing/print_settings.h" #include "third_party/WebKit/public/web/WebFindOptions.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/base/l10n/l10n_util.h" @@ -94,17 +92,10 @@ #include "atom/common/node_includes.h" #if defined(ENABLE_EXTENSIONS) +#include "atom/browser/api/atom_api_extension.h" #include "atom/browser/extensions/tab_helper.h" #endif -namespace { - -struct PrintSettings { - bool silent; - bool print_background; -}; - -} // namespace namespace mate { @@ -131,14 +122,20 @@ struct Converter { }; template<> -struct Converter { +struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, - PrintSettings* out) { + printing::PrintSettings* out) { mate::Dictionary dict; if (!ConvertFromV8(isolate, val, &dict)) return false; - dict.Get("silent", &(out->silent)); - dict.Get("printBackground", &(out->print_background)); + + bool silent = false; + dict.Get("silent", &silent); + // out->set_should_print_backgrounds(silent); + + bool should_print_backgrounds = false; + dict.Get("printBackground", &should_print_backgrounds); + out->set_should_print_backgrounds(should_print_backgrounds); return true; } }; @@ -175,7 +172,6 @@ struct Converter { case Type::BROWSER_WINDOW: type = "window"; break; case Type::REMOTE: type = "remote"; break; case Type::WEB_VIEW: type = "webview"; break; - case Type::OFF_SCREEN: type = "offscreen"; break; default: break; } return mate::ConvertToV8(isolate, type); @@ -191,8 +187,6 @@ struct Converter { *out = Type::WEB_VIEW; } else if (type == "backgroundPage") { *out = Type::BACKGROUND_PAGE; - } else if (type == "offscreen") { - *out = Type::OFF_SCREEN; } else { return false; } @@ -281,11 +275,11 @@ void OnCapturePageDone(base::Callback callback, WebContents::WebContents(v8::Isolate* isolate, content::WebContents* web_contents) : content::WebContentsObserver(web_contents), - embedder_(nullptr), type_(REMOTE), request_id_(0), - background_throttling_(true), - enable_devtools_(true) { + enable_devtools_(true), + is_being_destroyed_(false), + guest_delegate_(nullptr) { web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent()); Init(isolate); @@ -296,14 +290,12 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options, const content::WebContents::CreateParams* create_params) - : embedder_(nullptr), - type_(BROWSER_WINDOW), + : type_(BROWSER_WINDOW), request_id_(0), - background_throttling_(true), - enable_devtools_(true) { + enable_devtools_(true), + is_being_destroyed_(false), + guest_delegate_(nullptr) { // Read options. - options.Get("backgroundThrottling", &background_throttling_); - // FIXME(zcbenz): We should read "type" parameter for better design, but // on Windows we have encountered a compiler bug that if we read "type" // from |options| and then set |type_|, a memory corruption will happen @@ -314,8 +306,6 @@ WebContents::WebContents(v8::Isolate* isolate, type_ = WEB_VIEW; else if (options.Get("isBackgroundPage", &b) && b) type_ = BACKGROUND_PAGE; - else if (options.Get("offscreen", &b) && b) - type_ = OFF_SCREEN; // Whether to enable DevTools. options.Get("devTools", &enable_devtools_); @@ -333,46 +323,28 @@ WebContents::WebContents(v8::Isolate* isolate, session_.Reset(isolate, session.ToV8()); content::WebContents* web_contents; + content::BrowserContext* browser_context = session->browser_context(); + + content::WebContents::CreateParams params(create_params + ? *create_params + : content::WebContents::CreateParams(browser_context)); + if (IsGuest()) { - scoped_refptr site_instance = - create_params ? create_params->site_instance : - content::SiteInstance::CreateForURL( - session->browser_context(), GURL("chrome-guest://fake-host")); - content::WebContents::CreateParams params( - create_params ? *create_params : content::WebContents::CreateParams( - session->browser_context(), site_instance)); - guest_delegate_.reset(new WebViewGuestDelegate); - params.guest_delegate = guest_delegate_.get(); - web_contents = content::WebContents::Create(params); - } else if (IsOffScreen()) { - bool transparent = false; - options.Get("transparent", &transparent); - - content::WebContents::CreateParams params( - create_params ? *create_params : content::WebContents::CreateParams( - session->browser_context())); - auto* view = new OffScreenWebContentsView( - transparent, base::Bind(&WebContents::OnPaint, base::Unretained(this))); - params.view = view; - params.delegate_view = view; - - web_contents = content::WebContents::Create(params); - view->SetWebContents(web_contents); - } else { - content::WebContents::CreateParams params( - create_params ? *create_params : content::WebContents::CreateParams( - session->browser_context())); - web_contents = content::WebContents::Create(params); + if (!params.guest_delegate) { + params.guest_delegate = brave::TabViewGuest::Create( + HostWebContents()); + } + guest_delegate_ = + static_cast(params.guest_delegate); } + web_contents = content::WebContents::Create(params); std::string delayed_load_url; if (options.Get("delayedLoadUrl", &delayed_load_url)) { - const GURL url(delayed_load_url); - delayed_open_url_params_.reset( new content::OpenURLParams( - url, content::Referrer(), CURRENT_TAB, - ui::PAGE_TRANSITION_LINK, false)); + GURL(delayed_load_url), content::Referrer(), CURRENT_TAB, + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false)); } Observe(web_contents); @@ -383,12 +355,12 @@ WebContents::WebContents(v8::Isolate* isolate, // Save the preferences in C++. new WebContentsPreferences(web_contents, options); + // Initialize zoom + zoom::ZoomController::CreateForWebContents(web_contents); // Intialize permission helper. WebContentsPermissionHelper::CreateForWebContents(web_contents); // Intialize security state client. AtomSecurityStateModelClient::CreateForWebContents(web_contents); - // Initialize zoom state controller - zoom::ZoomController::CreateForWebContents(web_contents); brave::RendererPreferencesHelper::CreateForWebContents(web_contents); // Initialize autofill client autofill::AtomAutofillClient::CreateForWebContents(web_contents); @@ -402,21 +374,6 @@ WebContents::WebContents(v8::Isolate* isolate, autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER); web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent()); - if (IsGuest()) { - guest_delegate_->Initialize(this); - - NativeWindow* owner_window = nullptr; - if (options.Get("embedder", &embedder_) && embedder_) { - // New WebContents's owner_window is the embedder's owner_window. - auto relay = - NativeWindowRelay::FromWebContents(embedder_->web_contents()); - if (relay) - owner_window = relay->window.get(); - } - if (owner_window) - SetOwnerWindow(owner_window); - } - Init(isolate); AttachAsUserData(web_contents); @@ -427,15 +384,10 @@ WebContents::WebContents(v8::Isolate* isolate, WebContents::~WebContents() { // The destroy() is called. if (managed_web_contents()) { - // For webview we need to tell content module to do some cleanup work before - // destroying it. - if (type_ == WEB_VIEW) - guest_delegate_->Destroy(); // The WebContentsDestroyed will not be called automatically because we // unsubscribe from webContents before destroying it. So we have to manually // call it here to make sure "destroyed" event is emitted. - RenderViewDeleted(web_contents()->GetRenderViewHost()); WebContentsDestroyed(); } } @@ -445,7 +397,7 @@ bool WebContents::AddMessageToConsole(content::WebContents* source, const base::string16& message, int32_t line_no, const base::string16& source_id) { - if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) { + if (type_ == BROWSER_WINDOW) { return false; } else { Emit("console-message", level, message, line_no, source_id); @@ -463,18 +415,7 @@ bool WebContents::ShouldCreateWebContents( const GURL& target_url, const std::string& partition_id, content::SessionStorageNamespace* session_storage_namespace) { - - ProtocolHandlerRegistry* registry = - ProtocolHandlerRegistryFactory::GetForBrowserContext(GetBrowserContext()); - GURL translated_url = registry->TranslateUrl(target_url); - - if (IsGuest()) { - return true; - } else { - Emit("-new-window", translated_url, frame_name, NEW_FOREGROUND_TAB); - } - - return false; + return true; } void WebContents::WebContentsCreated(content::WebContents* source_contents, @@ -482,10 +423,22 @@ void WebContents::WebContentsCreated(content::WebContents* source_contents, const std::string& frame_name, const GURL& target_url, content::WebContents* new_contents) { + ProtocolHandlerRegistry* registry = + ProtocolHandlerRegistryFactory::GetForBrowserContext(GetBrowserContext()); + GURL translated_url = registry->TranslateUrl(target_url); + + if (guest_delegate_) { + guest_delegate_->WebContentsCreated(source_contents, + opener_render_frame_id, + frame_name, + translated_url, + new_contents); + } + v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); - content::NavigationController::LoadURLParams load_url_params(target_url); + content::NavigationController::LoadURLParams load_url_params(translated_url); CreateFrom(isolate(), new_contents)->delayed_load_url_params_.reset( new content::NavigationController::LoadURLParams(load_url_params)); } @@ -499,64 +452,48 @@ void WebContents::AddNewContents(content::WebContents* source, v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); - std::string target_url = ""; - auto url_params = CreateFrom(isolate(), new_contents)-> - delayed_load_url_params_.get(); + auto new_api_contents = CreateFrom(isolate(), new_contents); + + std::unique_ptr options(new base::DictionaryValue); + options->SetBoolean("userGesture", user_gesture); + + auto url_params = new_api_contents->delayed_load_url_params_.get(); if (url_params) { - target_url = url_params->url.spec(); - CreateFrom(isolate(), new_contents)-> + options->SetString("delayedLoadUrl", url_params->url.spec()); + new_api_contents-> delayed_load_url_params_.reset(nullptr); } - // set webPreferences - base::DictionaryValue* web_preferences = - WebContentsPreferences::FromWebContents(new_contents)->web_preferences(); - std::unique_ptr options(new base::DictionaryValue); - options->Set(options::kWebPreferences, web_preferences->CreateDeepCopy()); - if (target_url != "") - options->SetString("delayedLoadUrl", target_url); - - if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { - // fire off the tab open event - auto new_tab_event = v8::Local::Cast( - mate::Event::Create(isolate()).ToV8()); - mate::Dictionary(isolate(), new_tab_event).Set("sender", - GetWrapper()); - node::Environment* env = node::Environment::GetCurrent(isolate()); - // the url will be set in ResumeLoadingCreatedWebContents - mate::EmitEvent(isolate(), - env->process_object(), - "ELECTRON_GUEST_VIEW_MANAGER_TAB_OPEN", - new_tab_event, - "about:blank", - "", - disposition, - *options); - } else if (disposition == NEW_POPUP || disposition == NEW_WINDOW) { - // Set windowOptions - std::unique_ptr browser_options( + brave::TabViewGuest* guest = + brave::TabViewGuest::FromWebContents(new_contents); + if (guest) { + options->SetInteger(atom::options::kGuestInstanceID, + guest->guest_instance_id()); + } + + if (disposition == NEW_POPUP || disposition == NEW_WINDOW) { + std::unique_ptr window_options( new base::DictionaryValue); - browser_options->SetInteger("height", initial_rect.height()); - browser_options->SetInteger("width", initial_rect.width()); - browser_options->SetInteger("x", initial_rect.x()); - browser_options->SetInteger("y", initial_rect.y()); - options->Set("windowOptions", std::move(browser_options)); - - // fire off the window open event - auto window_open_event = v8::Local::Cast( - mate::Event::Create(isolate()).ToV8()); - mate::Dictionary(isolate(), window_open_event).Set( - "sender", GetWrapper()); - node::Environment* env = node::Environment::GetCurrent(isolate()); - // the url will be set in ResumeLoadingCreatedWebContents - mate::EmitEvent(isolate(), - env->process_object(), - "ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN", - window_open_event, - "about:blank", - "", - disposition, - *options); + window_options->SetInteger("height", initial_rect.height()); + window_options->SetInteger("width", initial_rect.width()); + window_options->SetInteger("x", initial_rect.x()); + window_options->SetInteger("y", initial_rect.y()); + options->Set("windowOptions", std::move(window_options)); + } + + // the url will be set in ResumeLoadingCreatedWebContents + bool blocked = + Emit("new-window", "about:blank", "", disposition, *options); + + if (was_blocked) + *was_blocked = blocked; + + if (blocked) { + if (guest) { + guest->Destroy(); + } else { + new_api_contents->DestroyWebContents(); + } } } @@ -566,20 +503,11 @@ bool WebContents::ShouldResumeRequestsForCreatedWindow() { bool WebContents::IsAttached() { if (guest_delegate_) - return guest_delegate_->IsAttached(); + return guest_delegate_->attached(); return owner_window() != nullptr; } -void WebContents::OnCreateWindow(const GURL& target_url, - const std::string& frame_name, - WindowOpenDisposition disposition) { - if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) - Emit("-new-window", target_url, frame_name, disposition); - else - Emit("new-window", target_url, frame_name, disposition); -} - void WebContents::AutofillSelect(const std::string& value, int frontend_id, int index) { auto autofillClient = @@ -598,33 +526,19 @@ void WebContents::AutofillPopupHidden() { content::WebContents* WebContents::OpenURLFromTab( content::WebContents* source, const content::OpenURLParams& params) { - if (params.disposition == SUPPRESS_OPEN) - return nullptr; - ProtocolHandlerRegistry* registry = ProtocolHandlerRegistryFactory::GetForBrowserContext(GetBrowserContext()); GURL translated_url = registry->TranslateUrl(params.url); if (params.disposition != CURRENT_TAB) { - if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) - Emit("-new-window", translated_url, "", params.disposition); - else - Emit("new-window", translated_url, "", params.disposition); + Emit("new-window", translated_url, "", params.disposition); return nullptr; } - // Give user a chance to cancel navigation. - if (Emit("will-navigate", params.url)) + if (guest_delegate_ && !guest_delegate_->OpenURLFromTab(source, params)) return nullptr; - auto api_web_contents = CreateFrom(isolate(), source); - if (api_web_contents->IsAttached()) { - CommonWebContentsDelegate::OpenURLFromTab(source, params); - } else { - api_web_contents->delayed_open_url_params_.reset( - new content::OpenURLParams(params)); - return nullptr; - } + CommonWebContentsDelegate::OpenURLFromTab(source, params); return source; } @@ -632,7 +546,7 @@ content::WebContents* WebContents::OpenURLFromTab( void WebContents::BeforeUnloadFired(content::WebContents* tab, bool proceed, bool* proceed_to_fire_unload) { - if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) + if (type_ == BROWSER_WINDOW) *proceed_to_fire_unload = proceed; else *proceed_to_fire_unload = true; @@ -646,7 +560,7 @@ void WebContents::MoveContents(content::WebContents* source, void WebContents::CloseContents(content::WebContents* source) { Emit("close"); - if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window()) + if ((type_ == BROWSER_WINDOW) && owner_window()) owner_window()->CloseContents(source); } @@ -676,9 +590,11 @@ bool WebContents::IsPopupOrPanel(const content::WebContents* source) const { void WebContents::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { - if (type_ == WEB_VIEW && embedder_) { + if (type_ == WEB_VIEW && HostWebContents()) { // Send the unhandled keyboard events back to the embedder. - embedder_->HandleKeyboardEvent(source, event); + auto embedder = + atom::api::WebContents::CreateFrom(isolate(), HostWebContents()); + embedder->HandleKeyboardEvent(source, event); } else { // Go to the default keyboard handling. CommonWebContentsDelegate::HandleKeyboardEvent(source, event); @@ -710,13 +626,13 @@ void WebContents::ExitFullscreenModeForTab(content::WebContents* source) { void WebContents::RendererUnresponsive(content::WebContents* source) { Emit("unresponsive"); - if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window()) + if ((type_ == BROWSER_WINDOW) && owner_window()) owner_window()->RendererUnresponsive(source); } void WebContents::RendererResponsive(content::WebContents* source) { Emit("responsive"); - if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window()) + if ((type_ == BROWSER_WINDOW) && owner_window()) owner_window()->RendererResponsive(source); } @@ -803,6 +719,10 @@ void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { // there are two virtual functions named BeforeUnloadFired. } +void WebContents::RenderViewReady() { + Emit("render-view-ready"); +} + void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) { Emit("render-view-deleted", render_view_host->GetProcess()->GetID()); } @@ -904,7 +824,8 @@ void WebContents::DidGetRedirectForResourceRequest( void WebContents::DidStartNavigation( content::NavigationHandle* navigation_handle) { - if (navigation_handle->IsSamePage() || navigation_handle->IsErrorPage()) + if (navigation_handle->HasCommitted() && + (navigation_handle->IsSamePage() || navigation_handle->IsErrorPage())) return; auto url = navigation_handle->GetURL(); @@ -1031,11 +952,33 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) { // be destroyed on close, and WebContentsDestroyed would be called for it, so // we need to make sure the api::WebContents is also deleted. void WebContents::WebContentsDestroyed() { + if (is_being_destroyed_) + return; + + is_being_destroyed_ = true; + + // For webview we need to tell content module to do some cleanup work before + // destroying it. + if (type_ == WEB_VIEW && guest_delegate_) + guest_delegate_->Destroy(); + guest_delegate_ = nullptr; + + if (managed_web_contents()) { + managed_web_contents()->SetDelegate(nullptr); + static_cast(managed_web_contents())->WebContentsDestroyed(); + } + + RenderViewDeleted(web_contents()->GetRenderViewHost()); + memory_pressure_listener_.reset(); // clear out fullscreen state if (CommonWebContentsDelegate::IsFullscreenForTabOrPending(web_contents())) { ExitFullscreenModeForTab(web_contents()); } + + if (g_browser_process->IsShuttingDown()) + return; + // This event is only for internal use, which is emitted when WebContents is // being destroyed. Emit("will-destroy"); @@ -1050,7 +993,7 @@ void WebContents::WebContentsDestroyed() { Emit("destroyed"); // Destroy the native class in next tick. - base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, GetDestroyClosure()); } void WebContents::NavigationEntryCommitted( @@ -1095,9 +1038,8 @@ void WebContents::ResumeLoadingCreatedWebContents() { web_contents()->GetController().LoadIfNecessary(); } else { OpenURLFromTab(web_contents(), *delayed_open_url_params_.get()); - delayed_open_url_params_.reset(nullptr); } - return; + delayed_open_url_params_.reset(nullptr); } GetWebContents()->ResumeLoadingCreatedWebContents(); } @@ -1145,11 +1087,6 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { } else { view->SetBackgroundColor(SK_ColorTRANSPARENT); } - - // For the same reason we can only disable hidden here. - const auto host = static_cast( - view->GetRenderWidgetHost()); - host->disable_hidden_ = !background_throttling_; } void WebContents::DownloadURL(const GURL& url) { @@ -1303,10 +1240,6 @@ std::string WebContents::GetUserAgent() { return web_contents()->GetUserAgentOverride(); } -void WebContents::InsertCSS(const std::string& css) { - web_contents()->InsertCSS(css); -} - bool WebContents::SavePage(const base::FilePath& full_file_path, const content::SavePageType& save_type, const SavePageHandler::SavePageCallback& callback) { @@ -1394,7 +1327,7 @@ void WebContents::InspectElement(int x, int y) { OpenDevTools(nullptr); scoped_refptr agent( content::DevToolsAgentHost::GetOrCreateFor(web_contents())); - agent->InspectElement(x, y); + agent->InspectElement(static_cast(managed_web_contents()), x, y); } void WebContents::InspectServiceWorker() { @@ -1444,20 +1377,20 @@ bool WebContents::IsAudioMuted() { } void WebContents::Print(mate::Arguments* args) { - PrintSettings settings = { false, false }; + printing::PrintSettings settings; if (args->Length() == 1 && !args->GetNext(&settings)) { args->ThrowError(); return; } - printing::PrintViewManagerBasic::FromWebContents(web_contents())-> - PrintNow(settings.silent, settings.print_background); + // printing::PrintViewManagerBasic::FromWebContents(web_contents())-> + // PrintNow(); } void WebContents::PrintToPDF(const base::DictionaryValue& setting, const PrintToPDFCallback& callback) { - printing::PrintPreviewMessageHandler::FromWebContents(web_contents())-> - PrintToPDF(setting, callback); + // printing::PrintPreviewMessageHandler::FromWebContents(web_contents())-> + // PrintToPDF(setting, callback); } int WebContents::GetContentWindowId() { @@ -1590,9 +1523,9 @@ mate::Handle WebContents::Clone(const mate::Dictionary& options) { int guest_instance_id = -1; if (IsGuest()) { cloneOptions.Set("isGuest", true); - cloneOptions.Set("embedder", embedder_); - guest_instance_id = guest_delegate_->GetNextInstanceId(); - cloneOptions.Set(options::kGuestInstanceID, guest_instance_id); + // cloneOptions.Set("embedder", embedder_); + // guest_instance_id = guest_delegate_->GetNextInstanceId(); + // cloneOptions.Set(options::kGuestInstanceID, guest_instance_id); } mate::Handle session; @@ -1618,9 +1551,9 @@ mate::Handle WebContents::Clone(const mate::Dictionary& options) { auto handle = mate::CreateHandle( isolate(), clone); - if (IsGuest()) { - guest_delegate_->RegisterGuest(handle, guest_instance_id); - } + // if (IsGuest()) { + // guest_delegate_->RegisterGuest(handle, guest_instance_id); + // } return handle; } @@ -1698,30 +1631,6 @@ void WebContents::SendInputEvent(v8::Isolate* isolate, isolate, "Invalid event object"))); } -void WebContents::BeginFrameSubscription(mate::Arguments* args) { - bool only_dirty = false; - FrameSubscriber::FrameCaptureCallback callback; - - args->GetNext(&only_dirty); - if (!args->GetNext(&callback)) { - args->ThrowError(); - return; - } - - const auto view = web_contents()->GetRenderWidgetHostView(); - if (view) { - std::unique_ptr frame_subscriber(new FrameSubscriber( - isolate(), view, callback, only_dirty)); - view->BeginFrameSubscription(std::move(frame_subscriber)); - } -} - -void WebContents::EndFrameSubscription() { - const auto view = web_contents()->GetRenderWidgetHostView(); - if (view) - view->EndFrameSubscription(); -} - void WebContents::StartDrag(const mate::Dictionary& item, mate::Arguments* args) { base::FilePath file; @@ -1810,73 +1719,16 @@ void WebContents::OnCursorChange(const content::WebCursor& cursor) { } void WebContents::SetSize(const SetSizeParams& params) { - if (guest_delegate_) - guest_delegate_->SetSize(params); + LOG(ERROR) << "set size!!"; + NOTIMPLEMENTED(); + // if (guest_delegate_) + // guest_delegate_->SetSize(params); } bool WebContents::IsGuest() const { return type_ == WEB_VIEW; } -bool WebContents::IsOffScreen() const { - return type_ == OFF_SCREEN; -} - -void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) { - mate::Handle image = - NativeImage::Create(isolate(), gfx::Image::CreateFrom1xBitmap(bitmap)); - Emit("paint", dirty_rect, image); -} - -void WebContents::StartPainting() { - if (!IsOffScreen()) - return; - - auto* osr_rwhv = static_cast( - web_contents()->GetRenderWidgetHostView()); - if (osr_rwhv) - osr_rwhv->SetPainting(true); -} - -void WebContents::StopPainting() { - if (!IsOffScreen()) - return; - - auto* osr_rwhv = static_cast( - web_contents()->GetRenderWidgetHostView()); - if (osr_rwhv) - osr_rwhv->SetPainting(false); -} - -bool WebContents::IsPainting() const { - if (!IsOffScreen()) - return false; - - const auto* osr_rwhv = static_cast( - web_contents()->GetRenderWidgetHostView()); - return osr_rwhv && osr_rwhv->IsPainting(); -} - -void WebContents::SetFrameRate(int frame_rate) { - if (!IsOffScreen()) - return; - - auto* osr_rwhv = static_cast( - web_contents()->GetRenderWidgetHostView()); - if (osr_rwhv) - osr_rwhv->SetFrameRate(frame_rate); -} - -int WebContents::GetFrameRate() const { - if (!IsOffScreen()) - return 0; - - const auto* osr_rwhv = static_cast( - web_contents()->GetRenderWidgetHostView()); - return osr_rwhv ? osr_rwhv->GetFrameRate() : 0; -} - - v8::Local WebContents::GetWebPreferences(v8::Isolate* isolate) { WebContentsPreferences* web_preferences = WebContentsPreferences::FromWebContents(web_contents()); @@ -1963,9 +1815,10 @@ v8::Local WebContents::Session(v8::Isolate* isolate) { } content::WebContents* WebContents::HostWebContents() { - if (!embedder_) - return nullptr; - return embedder_->web_contents(); + if (guest_delegate_ && guest_delegate_->attached()) { + return guest_delegate_->embedder_web_contents(); + } + return nullptr; } v8::Local WebContents::DevToolsWebContents(v8::Isolate* isolate) { @@ -2018,7 +1871,6 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("isCrashed", &WebContents::IsCrashed) .SetMethod("setUserAgent", &WebContents::SetUserAgent) .SetMethod("getUserAgent", &WebContents::GetUserAgent) - .SetMethod("insertCSS", &WebContents::InsertCSS) .SetMethod("savePage", &WebContents::SavePage) .SetMethod("openDevTools", &WebContents::OpenDevTools) .SetMethod("closeDevTools", &WebContents::CloseDevTools) @@ -2051,18 +1903,9 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("tabTraverse", &WebContents::TabTraverse) .SetMethod("_send", &WebContents::SendIPCMessage) .SetMethod("sendInputEvent", &WebContents::SendInputEvent) - .SetMethod("beginFrameSubscription", - &WebContents::BeginFrameSubscription) - .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) .SetMethod("startDrag", &WebContents::StartDrag) .SetMethod("setSize", &WebContents::SetSize) .SetMethod("isGuest", &WebContents::IsGuest) - .SetMethod("isOffscreen", &WebContents::IsOffScreen) - .SetMethod("startPainting", &WebContents::StartPainting) - .SetMethod("stopPainting", &WebContents::StopPainting) - .SetMethod("isPainting", &WebContents::IsPainting) - .SetMethod("setFrameRate", &WebContents::SetFrameRate) - .SetMethod("getFrameRate", &WebContents::GetFrameRate) .SetMethod("getType", &WebContents::GetType) .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) @@ -2128,9 +1971,19 @@ void WebContents::OnRendererMessageSync(const base::string16& channel, EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args); } +// static +mate::Handle WebContents::FromTabID(v8::Isolate* isolate, + int tab_id) { + return CreateFrom(isolate, + extensions::TabHelper::GetTabById(tab_id)); +} + // static mate::Handle WebContents::CreateFrom( v8::Isolate* isolate, content::WebContents* web_contents) { + if (!web_contents) + return mate::Handle(); + // We have an existing WebContents object in JS. auto existing = TrackableObject::FromWrappedClass(isolate, web_contents); if (existing) @@ -2169,6 +2022,7 @@ void Initialize(v8::Local exports, v8::Local unused, mate::Dictionary dict(isolate, exports); dict.Set("WebContents", WebContents::GetConstructor(isolate)->GetFunction()); dict.SetMethod("create", &WebContents::Create); + dict.SetMethod("fromTabID", &WebContents::FromTabID); dict.SetMethod("fromId", &mate::TrackableObject::FromWeakMapID); dict.SetMethod("getAllWebContents", &mate::TrackableObject::GetAll); diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index c8f33738ac..7e072b868f 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -29,6 +29,14 @@ namespace blink { struct WebDeviceEmulationParams; } +namespace brave { +class TabViewGuest; +} + +namespace guest_view { +class GuestViewBase; +} + namespace brightray { class InspectableWebContents; } @@ -62,9 +70,17 @@ class Dictionary; namespace atom { -struct SetSizeParams; +struct SetSizeParams { + SetSizeParams() {} + ~SetSizeParams() {} + + std::unique_ptr enable_auto_size; + std::unique_ptr min_size; + std::unique_ptr max_size; + std::unique_ptr normal_size; +}; + class AtomBrowserContext; -class WebViewGuestDelegate; namespace api { @@ -77,13 +93,16 @@ class WebContents : public mate::TrackableObject, BROWSER_WINDOW, // Used by BrowserWindow. REMOTE, // Thin wrap around an existing WebContents. WEB_VIEW, // Used by . - OFF_SCREEN, // Used for offscreen rendering }; // For node.js callback function type: function(error, buffer) using PrintToPDFCallback = base::Callback, v8::Local)>; + // Get the webcontents by tabId. + static mate::Handle FromTabID( + v8::Isolate* isolate, int tab_id); + // Create from an existing WebContents. static mate::Handle CreateFrom( v8::Isolate* isolate, content::WebContents* web_contents); @@ -133,7 +152,6 @@ class WebContents : public mate::TrackableObject, bool IsCrashed() const; void SetUserAgent(const std::string& user_agent, mate::Arguments* args); std::string GetUserAgent(); - void InsertCSS(const std::string& css); bool SavePage(const base::FilePath& full_file_path, const content::SavePageType& save_type, const SavePageHandler::SavePageCallback& callback); @@ -224,25 +242,11 @@ class WebContents : public mate::TrackableObject, void SetSize(const SetSizeParams& params); bool IsGuest() const; - // Methods for offscreen rendering - bool IsOffScreen() const; - void OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap); - void StartPainting(); - void StopPainting(); - bool IsPainting() const; - void SetFrameRate(int frame_rate); - int GetFrameRate() const; - // Callback triggered on permission response. void OnEnterFullscreenModeForTab(content::WebContents* source, const GURL& origin, bool allowed); - // Create window with the given disposition. - void OnCreateWindow(const GURL& target_url, - const std::string& frame_name, - WindowOpenDisposition disposition); - void AutofillSelect(const std::string& value, int frontend_id, int index); void AutofillPopupHidden(); @@ -344,6 +348,7 @@ class WebContents : public mate::TrackableObject, // content::WebContentsObserver: void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; + void RenderViewReady() override; void RenderViewDeleted(content::RenderViewHost*) override; void RenderProcessGone(base::TerminationStatus status) override; void DocumentAvailableInMainFrame() override; @@ -396,6 +401,7 @@ class WebContents : public mate::TrackableObject, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); private: + friend brave::TabViewGuest; AtomBrowserContext* GetBrowserContext() const; uint32_t GetNextRequestId() { @@ -418,11 +424,6 @@ class WebContents : public mate::TrackableObject, v8::Global devtools_web_contents_; v8::Global debugger_; - std::unique_ptr guest_delegate_; - - // The host webcontents that may contain this webcontents. - WebContents* embedder_; - // The type of current WebContents. Type type_; @@ -434,12 +435,13 @@ class WebContents : public mate::TrackableObject, std::unique_ptr delayed_load_url_params_; - // Whether background throttling is disabled. - bool background_throttling_; - // Whether to enable devtools. bool enable_devtools_; + bool is_being_destroyed_; + + guest_view::GuestViewBase* guest_delegate_; // not owned + // When a new tab is created asynchronously, stores the OpenURLParams needed // to continue loading the page once the tab is ready. std::unique_ptr delayed_open_url_params_; diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc index 2120cfad1a..892cb483ac 100644 --- a/atom/browser/api/atom_api_web_request.cc +++ b/atom/browser/api/atom_api_web_request.cc @@ -14,6 +14,7 @@ #include "content/public/browser/browser_thread.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" +#include "net/url_request/url_request_context.h" #include "v8/include/v8.h" using content::BrowserThread; @@ -135,7 +136,7 @@ void WebRequest::Fetch(mate::Arguments* args) { if (!path.empty()) fetcher->SaveResponseToFileAtPath( path, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); + BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE)); fetcher->Start(); fetchers_[fetcher] = FetchCallback(callback); } @@ -152,6 +153,17 @@ void WebRequest::SetResponseListener(mate::Arguments* args) { &AtomNetworkDelegate::SetResponseListenerInIO, type, args); } +template +void WebRequest::SetListenerOnIOThread( + const scoped_refptr& getter, + Method method, Event type, URLPatterns patterns, Listener listener) { + auto delegate = static_cast( + getter->GetURLRequestContext()->network_delegate()); + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(method, base::Unretained(delegate), + type, patterns, listener)); +} + template void WebRequest::SetListener(Method method, Event type, mate::Arguments* args) { // { urls }. @@ -168,16 +180,19 @@ void WebRequest::SetListener(Method method, Event type, mate::Arguments* args) { return; } - auto delegate = browser_context_->network_delegate(); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(method, base::Unretained(delegate), type, - patterns, listener)); + base::Bind(&WebRequest::SetListenerOnIOThread, + base::Unretained(this), + scoped_refptr( + browser_context_->GetRequestContext()), + method, type, patterns, listener)); } // static mate::Handle WebRequest::Create( v8::Isolate* isolate, AtomBrowserContext* browser_context) { + DCHECK(browser_context); return mate::CreateHandle(isolate, new WebRequest(isolate, browser_context)); } diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h index 932c44b388..c6323d1306 100644 --- a/atom/browser/api/atom_api_web_request.h +++ b/atom/browser/api/atom_api_web_request.h @@ -103,6 +103,11 @@ class WebRequest : public mate::TrackableObject, template void SetResponseListener(mate::Arguments* args); template + void SetListenerOnIOThread( + const scoped_refptr& request_context, + Method method, Event type, + URLPatterns patterns, Listener listener); + template void SetListener(Method method, Event type, mate::Arguments* args); private: diff --git a/atom/browser/api/atom_api_web_view_manager.cc b/atom/browser/api/atom_api_web_view_manager.cc deleted file mode 100644 index 1586c3a10d..0000000000 --- a/atom/browser/api/atom_api_web_view_manager.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/web_contents_preferences.h" -#include "atom/browser/web_view_manager.h" -#include "atom/common/native_mate_converters/content_converter.h" -#include "atom/common/native_mate_converters/value_converter.h" -#include "atom/common/node_includes.h" -#include "content/public/browser/browser_context.h" -#include "native_mate/dictionary.h" - -using atom::WebContentsPreferences; - -namespace { - -void AddGuest(int guest_instance_id, - int element_instance_id, - content::WebContents* embedder, - content::WebContents* guest_web_contents, - const base::DictionaryValue& options) { - auto manager = atom::WebViewManager::GetWebViewManager(embedder); - if (manager) - manager->AddGuest(guest_instance_id, element_instance_id, embedder, - guest_web_contents); - - WebContentsPreferences::FromWebContents(guest_web_contents)->Merge(options); -} - -void RemoveGuest(content::WebContents* embedder, int guest_instance_id) { - auto manager = atom::WebViewManager::GetWebViewManager(embedder); - if (manager) - manager->RemoveGuest(guest_instance_id); -} - -void Initialize(v8::Local exports, v8::Local unused, - v8::Local context, void* priv) { - mate::Dictionary dict(context->GetIsolate(), exports); - dict.SetMethod("addGuest", &AddGuest); - dict.SetMethod("removeGuest", &RemoveGuest); -} - -} // namespace - -NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_web_view_manager, Initialize) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 729244c222..3d01d60e80 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -16,7 +16,9 @@ #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/options_switches.h" #include "base/command_line.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/common/content_switches.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" @@ -71,7 +73,9 @@ v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { Window::Window(v8::Isolate* isolate, v8::Local wrapper, - const mate::Dictionary& options) { + const mate::Dictionary& options) + : is_window_ready_(false), + is_blocking_requests_(false) { // Use options.webPreferences to create WebContents. mate::Dictionary web_preferences = mate::Dictionary::CreateEmpty(isolate); options.Get(options::kWebPreferences, &web_preferences); @@ -81,13 +85,12 @@ Window::Window(v8::Isolate* isolate, v8::Local wrapper, if (options.Get(options::kBackgroundColor, &value)) web_preferences.Set(options::kBackgroundColor, value); - v8::Local transparent; - if (options.Get("transparent", &transparent)) - web_preferences.Set("transparent", transparent); // Creates the WebContents used by BrowserWindow. auto web_contents = WebContents::Create(isolate, web_preferences); web_contents_.Reset(isolate, web_contents.ToV8()); api_web_contents_ = web_contents.get(); + // TODO(bridiver) - should we always do this? + SuspendRenderFrameHost(api_web_contents_->GetWebContents()->GetMainFrame()); // Keep a copy of the options for later use. mate::Dictionary(isolate, web_contents->GetWrapper()).Set( @@ -132,7 +135,16 @@ Window::~Window() { // Destroy the native window in next tick because the native code might be // iterating all windows. - base::MessageLoop::current()->DeleteSoon(FROM_HERE, window_.release()); + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, window_.release()); +} + +void Window::SuspendRenderFrameHost(content::RenderFrameHost* rfh) { + DCHECK(rfh); + // Don't bother blocking requests if the renderer side is already good to go. + if (is_window_ready_) + return; + is_blocking_requests_ = true; + content::ResourceDispatcherHost::BlockRequestsForFrameFromUI(rfh); } void Window::WillCloseWindow(bool* prevent_default) { @@ -165,7 +177,7 @@ void Window::OnWindowClosed() { RemoveFromParentChildWindows(); // Destroy the native class when window is closed. - base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, GetDestroyClosure()); } void Window::OnWindowBlur() { @@ -200,6 +212,15 @@ void Window::OnWindowMinimize() { Emit("minimize"); } +void Window::OnWindowReady() { + is_window_ready_ = true; + if (is_blocking_requests_) { + is_blocking_requests_ = false; + content::ResourceDispatcherHost::ResumeBlockedRequestsForFrameFromUI( + api_web_contents_->GetWebContents()->GetMainFrame()); + } +} + void Window::OnWindowRestore() { Emit("restore"); } @@ -880,6 +901,7 @@ void Window::BuildPrototype(v8::Isolate* isolate, &Window::SetVisibleOnAllWorkspaces) .SetMethod("isVisibleOnAllWorkspaces", &Window::IsVisibleOnAllWorkspaces) + .SetMethod("notifyReady", &Window::OnWindowReady) #if defined(OS_WIN) .SetMethod("hookWindowMessage", &Window::HookWindowMessage) .SetMethod("isWindowMessageHooked", &Window::IsWindowMessageHooked) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index d2c41c2881..c0c395ce39 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -20,6 +20,10 @@ class GURL; +namespace content { +class RenderFrameHost; +} + namespace gfx { class Rect; } @@ -88,6 +92,9 @@ class Window : public mate::TrackableObject, void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) override; #endif + void OnWindowReady(); + void SuspendRenderFrameHost(content::RenderFrameHost* rfh); + private: // APIs for NativeWindow. void Close(); @@ -211,6 +218,8 @@ class Window : public mate::TrackableObject, std::unique_ptr window_; + bool is_window_ready_; + bool is_blocking_requests_; DISALLOW_COPY_AND_ASSIGN(Window); }; diff --git a/atom/browser/atom_access_token_store.cc b/atom/browser/atom_access_token_store.cc index 7a90b9b64b..e172264d83 100644 --- a/atom/browser/atom_access_token_store.cc +++ b/atom/browser/atom_access_token_store.cc @@ -10,7 +10,7 @@ #include "atom/common/google_api_key.h" #include "base/environment.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/geolocation_provider.h" +#include "device/geolocation/geolocation_provider.h" using content::BrowserThread; @@ -30,7 +30,7 @@ const char* kGeolocationProviderURL = class TokenLoadingJob : public base::RefCountedThreadSafe { public: explicit TokenLoadingJob( - const content::AccessTokenStore::LoadAccessTokensCallback& callback) + const device::AccessTokenStore::LoadAccessTokensCallback& callback) : callback_(callback), request_context_getter_(nullptr) {} void Run() { @@ -56,7 +56,7 @@ class TokenLoadingJob : public base::RefCountedThreadSafe { // Equivelent to access_token_map[kGeolocationProviderURL]. // Somehow base::string16 is causing compilation errors when used in a pair // of std::map on Linux, this can work around it. - content::AccessTokenStore::AccessTokenMap access_token_map; + device::AccessTokenStore::AccessTokenMap access_token_map; std::pair token_pair; token_pair.first = GURL(kGeolocationProviderURL); access_token_map.insert(token_pair); @@ -64,14 +64,14 @@ class TokenLoadingJob : public base::RefCountedThreadSafe { callback_.Run(access_token_map, request_context_getter_); } - content::AccessTokenStore::LoadAccessTokensCallback callback_; + device::AccessTokenStore::LoadAccessTokensCallback callback_; net::URLRequestContextGetter* request_context_getter_; }; } // namespace AtomAccessTokenStore::AtomAccessTokenStore() { - content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices(); + device::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices(); } AtomAccessTokenStore::~AtomAccessTokenStore() { diff --git a/atom/browser/atom_access_token_store.h b/atom/browser/atom_access_token_store.h index 7c18b5ab22..4398308de8 100644 --- a/atom/browser/atom_access_token_store.h +++ b/atom/browser/atom_access_token_store.h @@ -6,11 +6,11 @@ #define ATOM_BROWSER_ATOM_ACCESS_TOKEN_STORE_H_ #include -#include "content/public/browser/access_token_store.h" +#include "device/geolocation/access_token_store.h" namespace atom { -class AtomAccessTokenStore : public content::AccessTokenStore { +class AtomAccessTokenStore : public device::AccessTokenStore { public: AtomAccessTokenStore(); ~AtomAccessTokenStore(); diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index f679170e9f..a65a4cb4d7 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -27,23 +27,26 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "chrome/browser/printing/printing_message_filter.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h" -#include "chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h" +#include "chrome/browser/plugins/plugin_info_message_filter.h" #include "chrome/browser/speech/tts_message_filter.h" #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/client_certificate_delegate.h" -#include "content/public/browser/geolocation_delegate.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/common/web_preferences.h" +#include "device/geolocation/geolocation_delegate.h" #include "net/ssl/ssl_cert_request_info.h" #include "ppapi/host/ppapi_host.h" #include "ui/base/l10n/l10n_util.h" #include "v8/include/v8.h" +#include "v8/include/libplatform/libplatform.h" + #if defined(ENABLE_EXTENSIONS) #include "extensions/common/constants.h" #endif @@ -59,11 +62,11 @@ bool g_suppress_renderer_process_restart = false; std::string g_custom_service_worker_schemes = ""; // A provider of Geolocation services to override AccessTokenStore. -class AtomGeolocationDelegate : public content::GeolocationDelegate { +class AtomGeolocationDelegate : public device::GeolocationDelegate { public: AtomGeolocationDelegate() = default; - content::AccessTokenStore* CreateAccessTokenStore() final { + scoped_refptr CreateAccessTokenStore() final { return new AtomAccessTokenStore(); } @@ -95,7 +98,7 @@ AtomBrowserClient::~AtomBrowserClient() { content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID( int process_id) { // If the process is a pending process, we should use the old one. - if (ContainsKey(pending_processes_, process_id)) + if (base::ContainsKey(pending_processes_, process_id)) process_id = pending_processes_[process_id]; // Certain render process will be created with no associated render view, @@ -106,10 +109,10 @@ content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID( void AtomBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { int process_id = host->GetID(); - host->AddFilter(new printing::PrintingMessageFilter(process_id)); - host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext())); - host->AddFilter( - new WidevineCdmMessageFilter(process_id, host->GetBrowserContext())); + Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); + host->AddFilter(new printing::PrintingMessageFilter(process_id, profile)); + host->AddFilter(new TtsMessageFilter(host->GetBrowserContext())); + host->AddFilter(new PluginInfoMessageFilter(host->GetID(), profile)); } content::SpeechRecognitionManagerDelegate* @@ -117,11 +120,6 @@ content::SpeechRecognitionManagerDelegate* return new AtomSpeechRecognitionManagerDelegate; } -content::GeolocationDelegate* -AtomBrowserClient::CreateGeolocationDelegate() { - return new AtomGeolocationDelegate(); -} - void AtomBrowserClient::OverrideWebkitPrefs( content::RenderViewHost* host, content::WebPreferences* prefs) { prefs->javascript_enabled = true; @@ -149,67 +147,6 @@ std::string AtomBrowserClient::GetApplicationLocale() { return l10n_util::GetApplicationLocale(""); } -void AtomBrowserClient::OverrideSiteInstanceForNavigation( - content::BrowserContext* browser_context, - content::SiteInstance* current_instance, - const GURL& url, - content::SiteInstance** new_instance) { - - // TODO(bridiver) this seems to cause some a timing issue when loading - // non component extensions for some reason - // content::WebContents* web_contents = - // GetWebContentsFromProcessID(current_instance->GetProcess()->GetID()); - // // processes with no disabled may not have a web_contents with the - // // id that GetWebContentsFromProcessID looks for - // if (!web_contents) - // return; - // WebContentsPreferences* web_contents_prefs = - // WebContentsPreferences::FromWebContents(web_contents); - - // if (web_contents_prefs) { - // auto web_preferences = web_contents_prefs->web_preferences(); - // bool node_integration = true; - // web_preferences->GetBoolean(options::kNodeIntegration, - // &node_integration); - // if (!node_integration) { - // // only renderers with node integration need to be restarted - // return; - // } - // } -#if defined(ENABLE_EXTENSIONS) - if (url.SchemeIs(extensions::kExtensionScheme)) { - g_suppress_renderer_process_restart = false; - } -#endif - - if (g_suppress_renderer_process_restart) { - g_suppress_renderer_process_restart = false; - return; - } - - // Restart renderer process for all navigations except "javacript:" scheme. - if (url.SchemeIs(url::kJavaScriptScheme)) - return; - - scoped_refptr site_instance = - content::SiteInstance::CreateForURL(browser_context, url); - *new_instance = site_instance.get(); - - // Make sure the |site_instance| is not freed when this function returns. - // FIXME(zcbenz): We should adjust OverrideSiteInstanceForNavigation's - // interface to solve this. - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::Bind(&Noop, base::RetainedRef(site_instance))); - - // Remember the original renderer process of the pending renderer process. - auto current_process = current_instance->GetProcess(); - auto pending_process = (*new_instance)->GetProcess(); - pending_processes_[pending_process->GetID()] = current_process->GetID(); - // Clear the entry in map when process ends. - current_process->AddObserver(this); -} - void AtomBrowserClient::AppendExtraCommandLineSwitches( base::CommandLine* command_line, int process_id) { @@ -253,7 +190,7 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( void AtomBrowserClient::DidCreatePpapiPlugin( content::BrowserPpapiHost* host) { host->GetPpapiHost()->AddHostFactoryFilter( - base::WrapUnique(new chrome::ChromeBrowserPepperHostFactory(host))); + base::WrapUnique(new ChromeBrowserPepperHostFactory(host))); } content::QuotaPermissionContext* @@ -270,13 +207,12 @@ void AtomBrowserClient::AllowCertificateError( bool overridable, bool strict_enforcement, bool expired_previous_decision, - const base::Callback& callback, - content::CertificateRequestResultType* request) { + const base::Callback& callback) { if (delegate_) { delegate_->AllowCertificateError( web_contents, cert_error, ssl_info, request_url, resource_type, overridable, strict_enforcement, - expired_previous_decision, callback, request); + expired_previous_decision, callback); } } @@ -297,39 +233,6 @@ void AtomBrowserClient::ResourceDispatcherHostCreated() { resource_dispatcher_host_delegate_.get()); } -bool AtomBrowserClient::CanCreateWindow( - const GURL& opener_url, - const GURL& opener_top_level_frame_url, - const GURL& source_origin, - WindowContainerType container_type, - const std::string& frame_name, - const GURL& target_url, - const content::Referrer& referrer, - WindowOpenDisposition disposition, - const blink::WebWindowFeatures& features, - bool user_gesture, - bool opener_suppressed, - content::ResourceContext* context, - int render_process_id, - int opener_render_view_id, - int opener_render_frame_id, - bool* no_javascript_access) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - if (delegate_) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&api::App::OnCreateWindow, - base::Unretained(static_cast(delegate_)), - target_url, - frame_name, - disposition, - render_process_id, - opener_render_frame_id)); - } - - return false; -} - void AtomBrowserClient::GetAdditionalAllowedSchemesForFileSystem( std::vector* additional_schemes) { auto schemes_list = api::GetStandardSchemes(); @@ -341,7 +244,6 @@ void AtomBrowserClient::GetAdditionalAllowedSchemesForFileSystem( brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( const content::MainFunctionParams&) { - v8::V8::Initialize(); // Init V8 before creating main parts. return new AtomBrowserMainParts; } diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 5a43dbad30..2d3a235337 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -49,15 +49,9 @@ class AtomBrowserClient : public brightray::BrowserClient, void RenderProcessWillLaunch(content::RenderProcessHost* host) override; content::SpeechRecognitionManagerDelegate* CreateSpeechRecognitionManagerDelegate() override; - content::GeolocationDelegate* CreateGeolocationDelegate() override; void OverrideWebkitPrefs(content::RenderViewHost* render_view_host, content::WebPreferences* prefs) override; std::string GetApplicationLocale() override; - void OverrideSiteInstanceForNavigation( - content::BrowserContext* browser_context, - content::SiteInstance* current_instance, - const GURL& dest_url, - content::SiteInstance** new_instance) override; void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override; void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) override; @@ -71,29 +65,12 @@ class AtomBrowserClient : public brightray::BrowserClient, bool overridable, bool strict_enforcement, bool expired_previous_decision, - const base::Callback& callback, - content::CertificateRequestResultType* request) override; + const base::Callback& callback) override; void SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, std::unique_ptr delegate) override; void ResourceDispatcherHostCreated() override; - bool CanCreateWindow(const GURL& opener_url, - const GURL& opener_top_level_frame_url, - const GURL& source_origin, - WindowContainerType container_type, - const std::string& frame_name, - const GURL& target_url, - const content::Referrer& referrer, - WindowOpenDisposition disposition, - const blink::WebWindowFeatures& features, - bool user_gesture, - bool opener_suppressed, - content::ResourceContext* context, - int render_process_id, - int opener_render_view_id, - int opener_render_frame_id, - bool* no_javascript_access) override; void GetAdditionalAllowedSchemesForFileSystem( std::vector* schemes) override; diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index cf1b9430a4..c4ccd4b599 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -15,9 +15,7 @@ #include "atom/browser/net/atom_ssl_config_service.h" #include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/http_protocol_handler.h" -#include "atom/browser/web_view_manager.h" #include "atom/common/atom_version.h" -#include "atom/common/chrome_version.h" #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/files/file_path.h" @@ -28,6 +26,7 @@ #include "base/threading/sequenced_worker_pool.h" #include "base/threading/worker_pool.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_version.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "content/public/browser/browser_thread.h" @@ -73,12 +72,12 @@ AtomBrowserContext::AtomBrowserContext( Browser* browser = Browser::Get(); std::string name = RemoveWhitespace(browser->GetName()); std::string user_agent; - if (name == ATOM_PRODUCT_NAME) { + if (name == PRODUCT_SHORTNAME_STRING) { user_agent = "Chrome/" CHROME_VERSION_STRING " " - ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING; + PRODUCT_SHORTNAME_STRING "/" ATOM_VERSION_STRING; } else { user_agent = base::StringPrintf( - "%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING, + "%s/%s Chrome/%s " PRODUCT_SHORTNAME_STRING "/" ATOM_VERSION_STRING, name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING); @@ -90,7 +89,7 @@ AtomBrowserContext::AtomBrowserContext( options.GetBoolean("cache", &use_cache_); // Initialize Pref Registry in brightray. - InitPrefs(); + // InitPrefs(); } AtomBrowserContext::~AtomBrowserContext() { @@ -169,12 +168,6 @@ AtomBrowserContext::GetDownloadManagerDelegate() { return download_manager_delegate_.get(); } -content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { - if (!guest_manager_) - guest_manager_.reset(new WebViewManager); - return guest_manager_.get(); -} - content::PermissionManager* AtomBrowserContext::GetPermissionManager() { if (!permission_manager_.get()) permission_manager_.reset(new AtomPermissionManager); @@ -207,15 +200,4 @@ void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { pref_registry->RegisterDictionaryPref(prefs::kDevToolsFileSystemPaths); } -// static -// scoped_refptr AtomBrowserContext::From( -// const std::string& partition, bool in_memory, -// const base::DictionaryValue& options) { -// auto browser_context = brightray::BrowserContext::Get(partition, in_memory); -// if (browser_context) -// return static_cast(browser_context.get()); - -// return new AtomBrowserContext(partition, in_memory, options); -// } - } // namespace atom diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index cf7c87b5db..06b3c5685e 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -8,14 +8,13 @@ #include #include +#include "atom/browser/net/atom_network_delegate.h" #include "brightray/browser/browser_context.h" namespace atom { class AtomDownloadManagerDelegate; -class AtomNetworkDelegate; class AtomPermissionManager; -class WebViewManager; class AtomBrowserContext : public brightray::BrowserContext { public: @@ -41,13 +40,12 @@ class AtomBrowserContext : public brightray::BrowserContext { // content::BrowserContext: content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; - content::BrowserPluginGuestManager* GetGuestManager() override; content::PermissionManager* GetPermissionManager() override; // brightray::BrowserContext: void RegisterPrefs(PrefRegistrySimple* pref_registry) override; - virtual AtomNetworkDelegate* network_delegate() const { + virtual AtomNetworkDelegate* network_delegate() { return network_delegate_; } protected: @@ -57,7 +55,6 @@ class AtomBrowserContext : public brightray::BrowserContext { private: std::unique_ptr download_manager_delegate_; - std::unique_ptr guest_manager_; std::unique_ptr permission_manager_; std::string user_agent_; bool use_cache_; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 7ee84c8f73..97153fda38 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -11,7 +11,7 @@ #include "atom/browser/browser.h" #include "atom/browser/browser_context_keyed_service_factories.h" #include "atom/browser/javascript_environment.h" -#include "atom/browser/node_debugger.h" +// #include "atom/browser/node_debugger.h" #include "atom/common/api/atom_bindings.h" #include "atom/common/node_bindings.h" #include "atom/common/node_includes.h" @@ -21,11 +21,19 @@ #include "base/memory/memory_pressure_monitor.h" #include "base/path_service.h" #include "base/threading/thread_task_runner_handle.h" +#include "browser/media/media_capture_devices_dispatcher.h" #include "brightray/browser/brightray_paths.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" #include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/web_ui_controller_factory.h" #include "v8/include/v8-debug.h" +#include "v8/include/v8.h" +#include "gin/public/v8_platform.h" +#include "v8/include/libplatform/libplatform.h" + + #if defined(USE_X11) #include "chrome/browser/ui/libgtk2ui/gtk2_util.h" #include "ui/events/devices/x11/touch_factory_x11.h" @@ -51,8 +59,8 @@ AtomBrowserMainParts::AtomBrowserMainParts() DCHECK(!self_) << "Cannot have two AtomBrowserMainParts"; self_ = this; // Register extension scheme as web safe scheme. - content::ChildProcessSecurityPolicy::GetInstance()-> - RegisterWebSafeScheme("chrome-extension"); + // content::ChildProcessSecurityPolicy::GetInstance()-> + // RegisterWebSafeScheme("chrome-extension"); } AtomBrowserMainParts::~AtomBrowserMainParts() { @@ -97,31 +105,55 @@ void AtomBrowserMainParts::PreEarlyInitialization() { #endif } +int AtomBrowserMainParts::PreCreateThreads() { + fake_browser_process_->PreCreateThreads(); + + // Force MediaCaptureDevicesDispatcher to be created on UI thread. + brightray::MediaCaptureDevicesDispatcher::GetInstance(); + + return BrowserMainParts::PreCreateThreads(); +} + void AtomBrowserMainParts::PostEarlyInitialization() { brightray::BrowserMainParts::PostEarlyInitialization(); +} + +void AtomBrowserMainParts::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { + if (atom::Browser::Get()->is_shutting_down()) + return; + + base::allocator::ReleaseFreeMemory(); - // Temporary set the bridge_task_runner_ as current thread's task runner, - // so we can fool gin::PerIsolateData to use it as its task runner, instead - // of getting current message loop's task runner, which is null for now. - bridge_task_runner_ = new BridgeTaskRunner; - base::ThreadTaskRunnerHandle handle(bridge_task_runner_); + if (js_env_.get() && js_env_->isolate()) { + js_env_->isolate()->LowMemoryNotification(); + } +} - // The ProxyResolverV8 has setup a complete V8 environment, in order to - // avoid conflicts we only initialize our V8 environment after that. +void AtomBrowserMainParts::IdleHandler() { + base::allocator::ReleaseFreeMemory(); +} + +void AtomBrowserMainParts::PreMainMessageLoopRun() { + brightray::BrowserMainParts::PreMainMessageLoopRun(); + content::WebUIControllerFactory::RegisterFactory( + ChromeWebUIControllerFactory::GetInstance()); js_env_.reset(new JavascriptEnvironment); + js_env_->isolate()->Enter(); + node_bindings_->Initialize(); // Support the "--debug" switch. - node_debugger_.reset(new NodeDebugger(js_env_->isolate())); + // node_debugger_.reset(new NodeDebugger(js_env_->isolate())); // Create the global environment. node::Environment* env = node_bindings_->CreateEnvironment(js_env_->context()); // Make sure node can get correct environment when debugging. - if (node_debugger_->IsRunning()) - env->AssignToContext(v8::Debug::GetDebugContext()); + // if (node_debugger_->IsRunning()) + // env->AssignToContext(v8::Debug::GetDebugContext(js_env_->isolate())); // Add atom-shell extended APIs. atom_bindings_->BindTo(js_env_->isolate(), env->process_object()); @@ -131,31 +163,6 @@ void AtomBrowserMainParts::PostEarlyInitialization() { // Wrap the uv loop with global env. node_bindings_->set_uv_env(env); -} - -void AtomBrowserMainParts::OnMemoryPressure( - base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { - if (atom::Browser::Get()->is_shutting_down()) - return; - - base::allocator::ReleaseFreeMemory(); - - if (js_env_.get() && js_env_->isolate()) { - js_env_->isolate()->LowMemoryNotification(); - } -} - -void AtomBrowserMainParts::IdleHandler() { - base::allocator::ReleaseFreeMemory(); -} - -void AtomBrowserMainParts::PreMainMessageLoopRun() { - js_env_->OnMessageLoopCreated(); - - // Run user's main script before most things get initialized, so we can have - // a chance to setup everything. - node_bindings_->PrepareMessageLoop(); - node_bindings_->RunMessageLoop(); #if defined(USE_X11) ui::TouchFactory::SetTouchDeviceListFromCommandLine(); @@ -181,8 +188,10 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { browser_context_ = AtomBrowserContext::From("", false); brightray::BrowserMainParts::PreMainMessageLoopRun(); - bridge_task_runner_->MessageLoopIsReady(); - bridge_task_runner_ = nullptr; + + js_env_->OnMessageLoopCreated(); + node_bindings_->PrepareMessageLoop(); + node_bindings_->RunMessageLoop(); #if defined(USE_X11) libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess()); @@ -214,6 +223,7 @@ void AtomBrowserMainParts::PostMainMessageLoopRun() { js_env_->OnMessageLoopDestroying(); + js_env_->isolate()->Exit(); #if defined(OS_MACOSX) FreeAppDelegate(); #endif diff --git a/atom/browser/atom_browser_main_parts.h b/atom/browser/atom_browser_main_parts.h index e236012a05..699de8205a 100644 --- a/atom/browser/atom_browser_main_parts.h +++ b/atom/browser/atom_browser_main_parts.h @@ -51,6 +51,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { protected: // content::BrowserMainParts: + int PreCreateThreads() override; void PreEarlyInitialization() override; void PostEarlyInitialization() override; void PreMainMessageLoopRun() override; @@ -92,7 +93,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { std::unique_ptr js_env_; std::unique_ptr node_bindings_; std::unique_ptr atom_bindings_; - std::unique_ptr node_debugger_; + // std::unique_ptr node_debugger_; base::Timer gc_timer_; std::unique_ptr memory_pressure_listener_; diff --git a/atom/browser/atom_permission_manager.cc b/atom/browser/atom_permission_manager.cc index 7fc5fa0d03..13ee393cb6 100644 --- a/atom/browser/atom_permission_manager.cc +++ b/atom/browser/atom_permission_manager.cc @@ -51,6 +51,7 @@ int AtomPermissionManager::RequestPermission( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, + bool user_gesture, const ResponseCallback& response_callback) { int process_id = render_frame_host->GetProcess()->GetID(); @@ -81,6 +82,7 @@ int AtomPermissionManager::RequestPermissions( const std::vector& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, + bool user_gesture, const base::Callback&)>& callback) { // FIXME(zcbenz): Just ignore multiple permissions request for now. diff --git a/atom/browser/atom_permission_manager.h b/atom/browser/atom_permission_manager.h index d0fcdc6076..bd719e3e19 100644 --- a/atom/browser/atom_permission_manager.h +++ b/atom/browser/atom_permission_manager.h @@ -37,11 +37,13 @@ class AtomPermissionManager : public content::PermissionManager { content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - const ResponseCallback& callback) override; + bool user_gesture, + const base::Callback& callback) override; int RequestPermissions( const std::vector& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, + bool user_gesture, const base::Callback&)>& callback) override; diff --git a/atom/browser/atom_security_state_model_client.cc b/atom/browser/atom_security_state_model_client.cc index 77a91bdec9..fc8144f3c1 100644 --- a/atom/browser/atom_security_state_model_client.cc +++ b/atom/browser/atom_security_state_model_client.cc @@ -81,7 +81,7 @@ void AtomSecurityStateModelClient::GetVisibleSecurityState( return; } - state->initialized = true; + state->connection_info_initialized = true; state->url = entry->GetURL(); const content::SSLStatus& ssl = entry->GetSSL(); state->initial_security_level = @@ -90,22 +90,20 @@ void AtomSecurityStateModelClient::GetVisibleSecurityState( state->cert_status = ssl.cert_status; state->connection_status = ssl.connection_status; state->security_bits = ssl.security_bits; + state->pkp_bypassed = ssl.pkp_bypassed; state->sct_verify_statuses.clear(); - state->sct_verify_statuses.insert(state->sct_verify_statuses.end(), - ssl.num_unknown_scts, - net::ct::SCT_STATUS_LOG_UNKNOWN); - state->sct_verify_statuses.insert(state->sct_verify_statuses.end(), - ssl.num_invalid_scts, - net::ct::SCT_STATUS_INVALID); - state->sct_verify_statuses.insert(state->sct_verify_statuses.end(), - ssl.num_valid_scts, net::ct::SCT_STATUS_OK); + state->sct_verify_statuses.insert(state->sct_verify_statuses.begin(), + ssl.sct_statuses.begin(), + ssl.sct_statuses.end()); state->displayed_mixed_content = - (ssl.content_status & content::SSLStatus::DISPLAYED_INSECURE_CONTENT) - ? true - : false; + !!(ssl.content_status & content::SSLStatus::DISPLAYED_INSECURE_CONTENT); state->ran_mixed_content = - (ssl.content_status & content::SSLStatus::RAN_INSECURE_CONTENT) ? true - : false; + !!(ssl.content_status & content::SSLStatus::RAN_INSECURE_CONTENT); + state->displayed_content_with_cert_errors = + !!(ssl.content_status & + content::SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS); + state->ran_content_with_cert_errors = + !!(ssl.content_status & content::SSLStatus::RAN_CONTENT_WITH_CERT_ERRORS); } } // namespace atom diff --git a/atom/browser/autofill/atom_autofill_client.cc b/atom/browser/autofill/atom_autofill_client.cc index 3ac6ec7347..403a0197b9 100644 --- a/atom/browser/autofill/atom_autofill_client.cc +++ b/atom/browser/autofill/atom_autofill_client.cc @@ -23,21 +23,21 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::AtomAutofillClient); // stubs TODO - move to separate files -#include "net/http/http_request_headers.h" -namespace variations { -void AppendVariationHeaders(const GURL& url, - bool incognito, - bool uma_enabled, - net::HttpRequestHeaders* headers) { -} -} -#if !defined(OS_LINUX) -namespace rappor { -void SampleDomainAndRegistryFromGURL(RapporService* rappor_service, - const std::string& metric, - const GURL& gurl) {} -} // namespace rappor -#endif +// #include "net/http/http_request_headers.h" +// namespace variations { +// void AppendVariationHeaders(const GURL& url, +// bool incognito, +// bool uma_enabled, +// net::HttpRequestHeaders* headers) { +// } +// } +// #if !defined(OS_LINUX) +// namespace rappor { +// void SampleDomainAndRegistryFromGURL(RapporService* rappor_service, +// const std::string& metric, +// const GURL& gurl) {} +// } // namespace rappor +// #endif // end stubs namespace mate { diff --git a/atom/browser/autofill/atom_autofill_client.h b/atom/browser/autofill/atom_autofill_client.h index 679ccaddbf..4b5906bbae 100644 --- a/atom/browser/autofill/atom_autofill_client.h +++ b/atom/browser/autofill/atom_autofill_client.h @@ -86,6 +86,14 @@ class AtomAutofillClient void OnFirstUserGestureObserved() override; bool IsContextSecure(const GURL& form_origin) override; + // TODO(bridiver) + void ConfirmCreditCardFillAssist( + const CreditCard& card, + const base::Closure& callback) override {}; + bool ShouldShowSigninPromo() override { return false; }; + void StartSigninFlow() override {}; + + // content::WebContentsObserver implementation. void WebContentsDestroyed() override; private: diff --git a/atom/browser/autofill/personal_data_manager_factory.cc b/atom/browser/autofill/personal_data_manager_factory.cc index e69a7c47c3..0c54f92d04 100644 --- a/atom/browser/autofill/personal_data_manager_factory.cc +++ b/atom/browser/autofill/personal_data_manager_factory.cc @@ -5,7 +5,6 @@ #include "atom/browser/autofill/personal_data_manager_factory.h" #include "base/memory/singleton.h" -#include "brave/browser/brave_browser_context.h" #include "brave/browser/brave_content_browser_client.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/incognito_helpers.h" @@ -43,7 +42,7 @@ KeyedService* PersonalDataManagerFactory::BuildServiceInstanceFor( PersonalDataManager* service = new PersonalDataManager( brave::BraveContentBrowserClient::Get()->GetApplicationLocale()); - service->Init(static_cast(context) + service->Init(static_cast(context) ->GetAutofillWebdataService(), user_prefs::UserPrefs::Get(context), nullptr, diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index b227b6b815..1e576dfe62 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -10,8 +10,8 @@ #include "atom/browser/native_window.h" #include "atom/browser/window_list.h" #include "base/files/file_util.h" -#include "base/message_loop/message_loop.h" #include "base/path_service.h" +#include "base/threading/thread_task_runner_handle.h" #include "brightray/browser/brightray_paths.h" namespace atom { @@ -84,9 +84,9 @@ void Browser::Shutdown() { FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit()); - if (base::MessageLoop::current()) { - base::MessageLoop::current()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + if (base::ThreadTaskRunnerHandle::Get()) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::MessageLoop::QuitWhenIdleClosure()); } else { // There is no message loop available so we are in early stage. exit(0); @@ -149,11 +149,6 @@ void Browser::WillFinishLaunching() { } void Browser::DidFinishLaunching(const base::DictionaryValue& launch_info) { - // Make sure the userData directory is created. - base::FilePath user_data; - if (PathService::Get(brightray::DIR_USER_DATA, &user_data)) - base::CreateDirectoryAndGetError(user_data, nullptr); - is_ready_ = true; FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching(launch_info)); diff --git a/atom/browser/browser_context_keyed_service_factories.cc b/atom/browser/browser_context_keyed_service_factories.cc index 9a39230608..805fa4fa9b 100644 --- a/atom/browser/browser_context_keyed_service_factories.cc +++ b/atom/browser/browser_context_keyed_service_factories.cc @@ -4,7 +4,10 @@ #include "atom/browser/browser_context_keyed_service_factories.h" +#include "chrome/browser/content_settings/cookie_settings_factory.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" +// #include "chrome/browser/plugins/plugin_prefs_factory.h" #if defined(ENABLE_EXTENSIONS) #include "atom/browser/extensions/atom_extension_system_factory.h" #include "extensions/browser/api/alarms/alarm_manager.h" @@ -32,7 +35,6 @@ namespace atom { void EnsureBrowserContextKeyedServiceFactoriesBuilt() { - ProtocolHandlerRegistryFactory::GetInstance(); #if defined(ENABLE_EXTENSIONS) // keep AtomExtensionsBrowserClient::RegisterExtensionFunctions in sync extensions::AlarmManager::GetFactoryInstance(); @@ -60,6 +62,13 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { extensions::WebRequestAPI::GetFactoryInstance(); extensions::AtomExtensionSystemFactory::GetInstance(); #endif + CookieSettingsFactory::GetInstance(); + HostContentSettingsMapFactory::GetInstance(); + // autofill::PersonalDataManagerFactory::GetInstance(); +// #if defined(ENABLE_PLUGINS) +// PluginPrefsFactory::GetInstance(); +// #endif + ProtocolHandlerRegistryFactory::GetInstance(); } } // namespace atom diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index c0bed05285..26c1e29af9 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -18,13 +18,15 @@ #include "atom/browser/web_dialog_helper.h" #include "atom/common/atom_constants.h" #include "base/files/file_util.h" +#include "base/strings/utf_string_conversions.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" -#include "chrome/browser/printing/print_preview_message_handler.h" -#include "chrome/browser/printing/print_view_manager_basic.h" +// #include "chrome/browser/printing/print_preview_message_handler.h" +// #include "chrome/browser/printing/print_view_manager_basic.h" #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/common/custom_handlers/protocol_handler.h" #include "chrome/common/pref_names.h" +#include "chrome/grit/generated_resources.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" @@ -37,6 +39,9 @@ #include "content/public/browser/security_style_explanation.h" #include "content/public/browser/security_style_explanations.h" #include "storage/browser/fileapi/isolated_context.h" +#include "net/ssl/ssl_cipher_suite_names.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "ui/base/l10n/l10n_util.h" #if defined(ENABLE_EXTENSIONS) #include "atom/browser/api/atom_api_window.h" @@ -178,6 +183,74 @@ content::SecurityStyle SecurityLevelToSecurityStyle( return content::SECURITY_STYLE_UNKNOWN; } +void AddConnectionExplanation( + const security_state::SecurityStateModel::SecurityInfo& security_info, + content::SecurityStyleExplanations* security_style_explanations) { + + // Avoid showing TLS details when we couldn't even establish a TLS connection + // (e.g. for net errors) or if there was no real connection (some tests). We + // check the |cert_id| to see if there was a connection. + if (security_info.cert_id == 0 || security_info.connection_status == 0) { + return; + } + + int ssl_version = + net::SSLConnectionStatusToVersion(security_info.connection_status); + const char* protocol; + net::SSLVersionToString(&protocol, ssl_version); + const char* key_exchange; + const char* cipher; + const char* mac; + bool is_aead; + uint16_t cipher_suite = + net::SSLConnectionStatusToCipherSuite(security_info.connection_status); + net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead, + cipher_suite); + base::string16 protocol_name = base::ASCIIToUTF16(protocol); + base::string16 key_exchange_name = base::ASCIIToUTF16(key_exchange); + const base::string16 cipher_name = + (mac == NULL) ? base::ASCIIToUTF16(cipher) + : l10n_util::GetStringFUTF16(IDS_CIPHER_WITH_MAC, + base::ASCIIToUTF16(cipher), + base::ASCIIToUTF16(mac)); + if (security_info.obsolete_ssl_status == net::OBSOLETE_SSL_NONE) { + security_style_explanations->secure_explanations.push_back( + content::SecurityStyleExplanation( + l10n_util::GetStringUTF8(IDS_STRONG_SSL_SUMMARY), + l10n_util::GetStringFUTF8(IDS_STRONG_SSL_DESCRIPTION, protocol_name, + key_exchange_name, cipher_name))); + return; + } + + std::vector description_replacements; + int status = security_info.obsolete_ssl_status; + int str_id; + + str_id = (status & net::OBSOLETE_SSL_MASK_PROTOCOL) + ? IDS_SSL_AN_OBSOLETE_PROTOCOL + : IDS_SSL_A_STRONG_PROTOCOL; + description_replacements.push_back(l10n_util::GetStringUTF16(str_id)); + description_replacements.push_back(protocol_name); + + str_id = (status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE) + ? IDS_SSL_AN_OBSOLETE_KEY_EXCHANGE + : IDS_SSL_A_STRONG_KEY_EXCHANGE; + description_replacements.push_back(l10n_util::GetStringUTF16(str_id)); + description_replacements.push_back(key_exchange_name); + + str_id = (status & net::OBSOLETE_SSL_MASK_CIPHER) ? IDS_SSL_AN_OBSOLETE_CIPHER + : IDS_SSL_A_STRONG_CIPHER; + description_replacements.push_back(l10n_util::GetStringUTF16(str_id)); + description_replacements.push_back(cipher_name); + + security_style_explanations->info_explanations.push_back( + content::SecurityStyleExplanation( + l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), + base::UTF16ToUTF8( + l10n_util::GetStringFUTF16(IDS_OBSOLETE_SSL_DESCRIPTION, + description_replacements, nullptr)))); +} + } // namespace CommonWebContentsDelegate::CommonWebContentsDelegate() @@ -195,8 +268,8 @@ void CommonWebContentsDelegate::InitWithWebContents( browser_context_ = browser_context; web_contents->SetDelegate(this); - printing::PrintViewManagerBasic::CreateForWebContents(web_contents); - printing::PrintPreviewMessageHandler::CreateForWebContents(web_contents); + // printing::PrintViewManagerBasic::CreateForWebContents(web_contents); + // printing::PrintPreviewMessageHandler::CreateForWebContents(web_contents); // Create InspectableWebContents. web_contents_.reset(brightray::InspectableWebContents::Create(web_contents)); @@ -408,83 +481,99 @@ bool CommonWebContentsDelegate::IsFullscreenForTabOrPending( content::SecurityStyle CommonWebContentsDelegate::GetSecurityStyle( content::WebContents* web_contents, - content::SecurityStyleExplanations* explanations) { + content::SecurityStyleExplanations* security_style_explanations) { auto model_client = AtomSecurityStateModelClient::FromWebContents(web_contents); - - const SecurityStateModel::SecurityInfo& security_info = - model_client->GetSecurityInfo(); + auto security_info = model_client->GetSecurityInfo(); const content::SecurityStyle security_style = SecurityLevelToSecurityStyle(security_info.security_level); - explanations->ran_insecure_content_style = + security_style_explanations->ran_insecure_content_style = SecurityLevelToSecurityStyle( SecurityStateModel::kRanInsecureContentLevel); - explanations->displayed_insecure_content_style = + security_style_explanations->displayed_insecure_content_style = SecurityLevelToSecurityStyle( SecurityStateModel::kDisplayedInsecureContentLevel); - explanations->scheme_is_cryptographic = security_info.scheme_is_cryptographic; - if (!security_info.scheme_is_cryptographic) + // Check if the page is HTTP; if so, no explanations are needed. Note + // that SECURITY_STYLE_UNAUTHENTICATED does not necessarily mean that + // the page is loaded over HTTP, because the security style merely + // represents how the embedder wishes to display the security state of + // the page, and the embedder can choose to display HTTPS page as HTTP + // if it wants to (for example, displaying deprecated crypto + // algorithms with the same UI treatment as HTTP pages). + security_style_explanations->scheme_is_cryptographic = + security_info.scheme_is_cryptographic; + if (!security_info.scheme_is_cryptographic) { return security_style; + } if (security_info.sha1_deprecation_status == SecurityStateModel::DEPRECATED_SHA1_MAJOR) { - explanations->broken_explanations.push_back( + security_style_explanations->broken_explanations.push_back( content::SecurityStyleExplanation( - kSHA1Certificate, - kSHA1MajorDescription, + l10n_util::GetStringUTF8(IDS_MAJOR_SHA1), + l10n_util::GetStringUTF8(IDS_MAJOR_SHA1_DESCRIPTION), security_info.cert_id)); } else if (security_info.sha1_deprecation_status == - SecurityStateModel::DEPRECATED_SHA1_MINOR) { - explanations->unauthenticated_explanations.push_back( + SecurityStateModel::DEPRECATED_SHA1_MINOR) { + security_style_explanations->unauthenticated_explanations.push_back( content::SecurityStyleExplanation( - kSHA1Certificate, - kSHA1MinorDescription, + l10n_util::GetStringUTF8(IDS_MINOR_SHA1), + l10n_util::GetStringUTF8(IDS_MINOR_SHA1_DESCRIPTION), security_info.cert_id)); } - explanations->ran_insecure_content = + security_style_explanations->ran_insecure_content = security_info.mixed_content_status == - SecurityStateModel::RAN_MIXED_CONTENT || + SecurityStateModel::CONTENT_STATUS_RAN || security_info.mixed_content_status == - SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT; - explanations->displayed_insecure_content = + SecurityStateModel::CONTENT_STATUS_DISPLAYED_AND_RAN; + security_style_explanations->displayed_insecure_content = security_info.mixed_content_status == - SecurityStateModel::DISPLAYED_MIXED_CONTENT || + SecurityStateModel::CONTENT_STATUS_DISPLAYED || security_info.mixed_content_status == - SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT; + SecurityStateModel::CONTENT_STATUS_DISPLAYED_AND_RAN; if (net::IsCertStatusError(security_info.cert_status)) { - std::string error_string = net::ErrorToString( - net::MapCertStatusToNetError(security_info.cert_status)); + base::string16 error_string = base::UTF8ToUTF16(net::ErrorToString( + net::MapCertStatusToNetError(security_info.cert_status))); content::SecurityStyleExplanation explanation( - kCertificateError, - "There are issues with the site's certificate chain " + error_string, + l10n_util::GetStringUTF8(IDS_CERTIFICATE_CHAIN_ERROR), + l10n_util::GetStringFUTF8( + IDS_CERTIFICATE_CHAIN_ERROR_DESCRIPTION_FORMAT, error_string), security_info.cert_id); if (net::IsCertStatusMinorError(security_info.cert_status)) - explanations->unauthenticated_explanations.push_back(explanation); + security_style_explanations->unauthenticated_explanations.push_back( + explanation); else - explanations->broken_explanations.push_back(explanation); + security_style_explanations->broken_explanations.push_back(explanation); } else { + // If the certificate does not have errors and is not using + // deprecated SHA1, then add an explanation that the certificate is + // valid. if (security_info.sha1_deprecation_status == SecurityStateModel::NO_DEPRECATED_SHA1) { - explanations->secure_explanations.push_back( + security_style_explanations->secure_explanations.push_back( content::SecurityStyleExplanation( - kValidCertificate, - kValidCertificateDescription, + l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE), + l10n_util::GetStringUTF8( + IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION), security_info.cert_id)); } } - if (security_info.is_secure_protocol_and_ciphersuite) { - explanations->secure_explanations.push_back( + AddConnectionExplanation(security_info, security_style_explanations); + + security_style_explanations->pkp_bypassed = security_info.pkp_bypassed; + if (security_info.pkp_bypassed) { + security_style_explanations->info_explanations.push_back( content::SecurityStyleExplanation( - kSecureProtocol, - kSecureProtocolDescription)); + "Public-Key Pinning Bypassed", + "Public-key pinning was bypassed by a local root certificate.")); } return security_style; diff --git a/atom/browser/common_web_contents_delegate_mac.mm b/atom/browser/common_web_contents_delegate_mac.mm index c766061379..07c4e814c0 100644 --- a/atom/browser/common_web_contents_delegate_mac.mm +++ b/atom/browser/common_web_contents_delegate_mac.mm @@ -9,6 +9,7 @@ #include "brightray/browser/mac/event_dispatching_window.h" #include "content/public/browser/native_web_keyboard_event.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/gfx/image/image.h" @interface NSWindow (EventDispatchingWindow) - (void)redispatchKeyEvent:(NSEvent*)event; @@ -16,6 +17,10 @@ - (void)redispatchKeyEvent:(NSEvent*)event; namespace atom { +gfx::ImageSkia CommonWebContentsDelegate::GetDevToolsWindowIcon() { + return gfx::ImageSkia(); +} + void CommonWebContentsDelegate::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { diff --git a/atom/browser/extensions/api/atom_extensions_api_client.cc b/atom/browser/extensions/api/atom_extensions_api_client.cc index d2280485b0..7634329cd2 100644 --- a/atom/browser/extensions/api/atom_extensions_api_client.cc +++ b/atom/browser/extensions/api/atom_extensions_api_client.cc @@ -8,6 +8,7 @@ #include "atom/browser/extensions/atom_extension_web_contents_observer.h" #include "atom/browser/extensions/tab_helper.h" #include "base/memory/ptr_util.h" +#include "brave/browser/guest_view/brave_guest_view_manager_delegate.h" #include "content/public/browser/resource_request_info.h" #include "extensions/browser/api/management/management_api_delegate.h" #include "extensions/browser/api/web_request/web_request_event_details.h" @@ -15,7 +16,6 @@ #include "extensions/browser/requirements_checker.h" #include "extensions/common/manifest_handlers/icons_handler.h" - namespace extensions { class AtomRequirementsChecker : public RequirementsChecker { @@ -52,9 +52,9 @@ class AtomManagementAPIDelegate : public ManagementAPIDelegate { AtomManagementAPIDelegate() {} ~AtomManagementAPIDelegate() override {} - bool LaunchAppFunctionDelegate( + void LaunchAppFunctionDelegate( const Extension* extension, - content::BrowserContext* context) const override { return false; } + content::BrowserContext* context) const override { } bool IsNewBookmarkAppsEnabled() const override { return false; } bool CanHostedAppsOpenInWindows() const override { return false; } GURL GetFullLaunchURL(const Extension* extension) const override { @@ -176,17 +176,18 @@ class AtomManagementAPIDelegate : public ManagementAPIDelegate { AtomExtensionsAPIClient::AtomExtensionsAPIClient() { } +std::unique_ptr +AtomExtensionsAPIClient::CreateGuestViewManagerDelegate( + content::BrowserContext* context) const { + return base::WrapUnique(new brave::BraveGuestViewManagerDelegate(context)); +} + void AtomExtensionsAPIClient::AttachWebContentsHelpers( content::WebContents* web_contents) const { extensions::TabHelper::CreateForWebContents(web_contents); AtomExtensionWebContentsObserver::CreateForWebContents(web_contents); } -WebViewGuestDelegate* AtomExtensionsAPIClient::CreateWebViewGuestDelegate( - WebViewGuest* web_view_guest) const { - return ExtensionsAPIClient::CreateWebViewGuestDelegate(web_view_guest); -} - std::unique_ptr AtomExtensionsAPIClient::CreateWebRequestEventRouterDelegate() const { return base::WrapUnique( diff --git a/atom/browser/extensions/api/atom_extensions_api_client.h b/atom/browser/extensions/api/atom_extensions_api_client.h index ccfbcedcb1..30e036163d 100644 --- a/atom/browser/extensions/api/atom_extensions_api_client.h +++ b/atom/browser/extensions/api/atom_extensions_api_client.h @@ -18,8 +18,9 @@ class AtomExtensionsAPIClient : public ExtensionsAPIClient { // ExtensionsAPIClient implementation. void AttachWebContentsHelpers(content::WebContents* web_contents) const override; - WebViewGuestDelegate* CreateWebViewGuestDelegate( - WebViewGuest* web_view_guest) const override; + std::unique_ptr + CreateGuestViewManagerDelegate( + content::BrowserContext* context) const; std::unique_ptr CreateWebRequestEventRouterDelegate() const override; ManagementAPIDelegate* CreateManagementAPIDelegate() const override; diff --git a/atom/browser/extensions/api/messaging/extension_message_port.cc b/atom/browser/extensions/api/messaging/extension_message_port.cc deleted file mode 100644 index 70e79f222b..0000000000 --- a/atom/browser/extensions/api/messaging/extension_message_port.cc +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "atom/browser/extensions/api/messaging/extension_message_port.h" - -#include "base/scoped_observer.h" -#include "content/public/browser/interstitial_page.h" -#include "content/public/browser/navigation_details.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/web_contents.h" -#include "extensions/browser/extension_host.h" -#include "extensions/browser/process_manager.h" -#include "extensions/browser/process_manager_observer.h" -#include "extensions/common/extension_messages.h" -#include "extensions/common/manifest_handlers/background_info.h" - -namespace extensions { - -const char kReceivingEndDoesntExistError[] = - "Could not establish connection. Receiving end does not exist."; - -// Helper class to detect when frames are destroyed. -class ExtensionMessagePort::FrameTracker : public content::WebContentsObserver, - public ProcessManagerObserver { - public: - explicit FrameTracker(ExtensionMessagePort* port) - : pm_observer_(this), port_(port), interstitial_frame_(nullptr) {} - ~FrameTracker() override {} - - void TrackExtensionProcessFrames() { - pm_observer_.Add(ProcessManager::Get(port_->browser_context_)); - } - - void TrackTabFrames(content::WebContents* tab) { - Observe(tab); - } - - void TrackInterstitialFrame(content::WebContents* tab, - content::RenderFrameHost* interstitial_frame) { - // |tab| should never be nullptr, because an interstitial's lifetime is - // tied to a tab. This is a CHECK, not a DCHECK because we really need an - // observer subject to detect frame removal (via DidDetachInterstitialPage). - CHECK(tab); - DCHECK(interstitial_frame); - interstitial_frame_ = interstitial_frame; - Observe(tab); - } - - private: - // content::WebContentsObserver overrides: - void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) - override { - port_->UnregisterFrame(render_frame_host); - } - - void DidNavigateAnyFrame(content::RenderFrameHost* render_frame_host, - const content::LoadCommittedDetails& details, - const content::FrameNavigateParams&) override { - if (!details.is_in_page) - port_->UnregisterFrame(render_frame_host); - } - - void DidDetachInterstitialPage() override { - if (interstitial_frame_) - port_->UnregisterFrame(interstitial_frame_); - } - - // extensions::ProcessManagerObserver overrides: - void OnExtensionFrameUnregistered( - const std::string& extension_id, - content::RenderFrameHost* render_frame_host) override { - if (extension_id == port_->extension_id_) - port_->UnregisterFrame(render_frame_host); - } - - ScopedObserver pm_observer_; - ExtensionMessagePort* port_; // Owns this FrameTracker. - - // Set to the main frame of an interstitial if we are tracking an interstitial - // page, because RenderFrameDeleted is never triggered for frames in an - // interstitial (and we only support tracking the interstitial's main frame). - content::RenderFrameHost* interstitial_frame_; - - DISALLOW_COPY_AND_ASSIGN(FrameTracker); -}; - -ExtensionMessagePort::ExtensionMessagePort( - base::WeakPtr message_service, - int port_id, - const std::string& extension_id, - content::RenderProcessHost* extension_process) - : weak_message_service_(message_service), - port_id_(port_id), - extension_id_(extension_id), - browser_context_(extension_process->GetBrowserContext()), - extension_process_(extension_process), - opener_tab_id_(-1), - did_create_port_(false), - background_host_ptr_(nullptr), - frame_tracker_(new FrameTracker(this)) { - auto all_hosts = ProcessManager::Get(browser_context_) - ->GetRenderFrameHostsForExtension(extension_id); - for (content::RenderFrameHost* rfh : all_hosts) - RegisterFrame(rfh); - - frame_tracker_->TrackExtensionProcessFrames(); -} - -ExtensionMessagePort::ExtensionMessagePort( - base::WeakPtr message_service, - int port_id, - const std::string& extension_id, - content::RenderFrameHost* rfh, - bool include_child_frames) - : weak_message_service_(message_service), - port_id_(port_id), - extension_id_(extension_id), - browser_context_(rfh->GetProcess()->GetBrowserContext()), - extension_process_(nullptr), - opener_tab_id_(-1), - did_create_port_(false), - background_host_ptr_(nullptr), - frame_tracker_(new FrameTracker(this)) { - content::WebContents* tab = content::WebContents::FromRenderFrameHost(rfh); - if (!tab) { - content::InterstitialPage* interstitial = - content::InterstitialPage::FromRenderFrameHost(rfh); - // A RenderFrameHost must be hosted in a WebContents or InterstitialPage. - CHECK(interstitial); - - // Only the main frame of an interstitial is supported, because frames in - // the interstitial do not trigger RenderFrameCreated / RenderFrameDeleted - // on WebContentObservers. Consequently, (1) we cannot detect removal of - // RenderFrameHosts, and (2) even if the RenderFrameDeleted is propagated, - // then WebContentsObserverSanityChecker triggers a CHECK when it detects - // frame notifications without a corresponding RenderFrameCreated. - if (!rfh->GetParent()) { - // It is safe to pass the interstitial's WebContents here because we only - // use it to observe DidDetachInterstitialPage. - frame_tracker_->TrackInterstitialFrame(interstitial->GetWebContents(), - rfh); - RegisterFrame(rfh); - } - return; - } - - frame_tracker_->TrackTabFrames(tab); - if (include_child_frames) { - tab->ForEachFrame(base::Bind(&ExtensionMessagePort::RegisterFrame, - base::Unretained(this))); - } else { - RegisterFrame(rfh); - } -} - -ExtensionMessagePort::~ExtensionMessagePort() {} - -void ExtensionMessagePort::RevalidatePort() { - // Only opener ports need to be revalidated, because these are created in the - // renderer before the browser knows about them. - DCHECK(!extension_process_); - DCHECK_LE(frames_.size(), 1U); - - // If the port is unknown, the renderer will respond by closing the port. - SendToPort(base::MakeUnique( - MSG_ROUTING_NONE, port_id_)); -} - -void ExtensionMessagePort::RemoveCommonFrames(const MessagePort& port) { - // Avoid overlap in the set of frames to make sure that it does not matter - // when UnregisterFrame is called. - for (std::set::iterator it = frames_.begin(); - it != frames_.end(); ) { - if (port.HasFrame(*it)) { - frames_.erase(it++); - } else { - ++it; - } - } -} - -bool ExtensionMessagePort::HasFrame(content::RenderFrameHost* rfh) const { - return frames_.find(rfh) != frames_.end(); -} - -bool ExtensionMessagePort::IsValidPort() { - return !frames_.empty(); -} - -void ExtensionMessagePort::DispatchOnConnect( - const std::string& channel_name, - std::unique_ptr source_tab, - int source_frame_id, - int guest_process_id, - int guest_render_frame_routing_id, - const std::string& source_extension_id, - const std::string& target_extension_id, - const GURL& source_url, - const std::string& tls_channel_id) { - ExtensionMsg_TabConnectionInfo source; - if (source_tab) { - source.tab.Swap(source_tab.get()); - source.tab.GetInteger("id", &opener_tab_id_); - } - source.frame_id = source_frame_id; - - ExtensionMsg_ExternalConnectionInfo info; - info.target_id = target_extension_id; - info.source_id = source_extension_id; - info.source_url = source_url; - info.guest_process_id = guest_process_id; - info.guest_render_frame_routing_id = guest_render_frame_routing_id; - - SendToPort(base::WrapUnique(new ExtensionMsg_DispatchOnConnect( - MSG_ROUTING_NONE, port_id_, channel_name, source, info, tls_channel_id))); -} - -void ExtensionMessagePort::DispatchOnDisconnect( - const std::string& error_message) { - SendToPort(base::WrapUnique(new ExtensionMsg_DispatchOnDisconnect( - MSG_ROUTING_NONE, port_id_, error_message))); -} - -void ExtensionMessagePort::DispatchOnMessage(const Message& message) { - SendToPort(base::WrapUnique(new ExtensionMsg_DeliverMessage( - MSG_ROUTING_NONE, port_id_, opener_tab_id_, message))); -} - -void ExtensionMessagePort::IncrementLazyKeepaliveCount() { - ProcessManager* pm = ProcessManager::Get(browser_context_); - ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id_); - if (host && BackgroundInfo::HasLazyBackgroundPage(host->extension())) - pm->IncrementLazyKeepaliveCount(host->extension()); - - // Keep track of the background host, so when we decrement, we only do so if - // the host hasn't reloaded. - background_host_ptr_ = host; -} - -void ExtensionMessagePort::DecrementLazyKeepaliveCount() { - ProcessManager* pm = ProcessManager::Get(browser_context_); - ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id_); - if (host && host == background_host_ptr_) - pm->DecrementLazyKeepaliveCount(host->extension()); -} - -void ExtensionMessagePort::OpenPort(int process_id, int routing_id) { - DCHECK(routing_id != MSG_ROUTING_NONE || extension_process_); - - did_create_port_ = true; -} - -void ExtensionMessagePort::ClosePort(int process_id, int routing_id) { - if (routing_id == MSG_ROUTING_NONE) { - // The only non-frame-specific message is the response to an unhandled - // onConnect event in the extension process. - DCHECK(extension_process_); - frames_.clear(); - CloseChannel(); - return; - } - - content::RenderFrameHost* rfh = - content::RenderFrameHost::FromID(process_id, routing_id); - if (rfh) - UnregisterFrame(rfh); -} - -void ExtensionMessagePort::CloseChannel() { - std::string error_message = did_create_port_ ? std::string() : - kReceivingEndDoesntExistError; - if (weak_message_service_) - weak_message_service_->CloseChannel(port_id_, error_message); -} - -void ExtensionMessagePort::RegisterFrame(content::RenderFrameHost* rfh) { - // Only register a RenderFrameHost whose RenderFrame has been created, to - // ensure that we are notified of frame destruction. Without this check, - // |frames_| can eventually contain a stale pointer because RenderFrameDeleted - // is not triggered for |rfh|. - if (rfh->IsRenderFrameLive()) - frames_.insert(rfh); -} - -void ExtensionMessagePort::UnregisterFrame(content::RenderFrameHost* rfh) { - if (frames_.erase(rfh) != 0 && frames_.empty()) - CloseChannel(); -} - -void ExtensionMessagePort::SendToPort(std::unique_ptr msg) { - DCHECK_GT(frames_.size(), 0UL); - if (extension_process_) { - // All extension frames reside in the same process, so we can just send a - // single IPC message to the extension process as an optimization. - // The frame tracking is then only used to make sure that the port gets - // closed when all frames have closed / reloaded. - msg->set_routing_id(MSG_ROUTING_CONTROL); - extension_process_->Send(msg.release()); - return; - } - for (content::RenderFrameHost* rfh : frames_) { - IPC::Message* msg_copy = new IPC::Message(*msg.get()); - msg_copy->set_routing_id(rfh->GetRoutingID()); - rfh->Send(msg_copy); - } -} - -} // namespace extensions diff --git a/atom/browser/extensions/api/messaging/extension_message_port.h b/atom/browser/extensions/api/messaging/extension_message_port.h deleted file mode 100644 index 2af8913485..0000000000 --- a/atom/browser/extensions/api/messaging/extension_message_port.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 ATOM_BROWSER_EXTENSIONS_API_MESSAGING_EXTENSION_MESSAGE_PORT_H_ -#define ATOM_BROWSER_EXTENSIONS_API_MESSAGING_EXTENSION_MESSAGE_PORT_H_ - -#include -#include -#include "atom/browser/extensions/api/messaging/message_service.h" -#include "base/macros.h" - -class GURL; - -namespace content { -class BrowserContext; -class RenderFrameHost; -class RenderProcessHost; -} // namespace content - -namespace IPC { -class Message; -} // namespace IPC - -namespace extensions { - -// A port that manages communication with an extension. -// The port's lifetime will end when either all receivers close the port, or -// when the opener / receiver explicitly closes the channel. -class ExtensionMessagePort : public MessageService::MessagePort { - public: - // Create a port that is tied to frame(s) in a single tab. - ExtensionMessagePort(base::WeakPtr message_service, - int port_id, - const std::string& extension_id, - content::RenderFrameHost* rfh, - bool include_child_frames); - // Create a port that is tied to all frames of an extension, possibly spanning - // multiple tabs, including the invisible background page, popups, etc. - ExtensionMessagePort(base::WeakPtr message_service, - int port_id, - const std::string& extension_id, - content::RenderProcessHost* extension_process); - ~ExtensionMessagePort() override; - - // Checks whether the frames to which this port is tied at its construction - // are still aware of this port's existence. Frames that don't know about - // the port are removed from the set of frames. This should be used for opener - // ports because the frame may be navigated before the port was initialized. - void RevalidatePort(); - - // MessageService::MessagePort: - void RemoveCommonFrames(const MessagePort& port) override; - bool HasFrame(content::RenderFrameHost* rfh) const override; - bool IsValidPort() override; - void DispatchOnConnect(const std::string& channel_name, - std::unique_ptr source_tab, - int source_frame_id, - int guest_process_id, - int guest_render_frame_routing_id, - const std::string& source_extension_id, - const std::string& target_extension_id, - const GURL& source_url, - const std::string& tls_channel_id) override; - void DispatchOnDisconnect(const std::string& error_message) override; - void DispatchOnMessage(const Message& message) override; - void IncrementLazyKeepaliveCount() override; - void DecrementLazyKeepaliveCount() override; - void OpenPort(int process_id, int routing_id) override; - void ClosePort(int process_id, int routing_id) override; - - private: - class FrameTracker; - - // Registers a frame as a receiver / sender. - void RegisterFrame(content::RenderFrameHost* rfh); - - // Unregisters a frame as a receiver / sender. When there are no registered - // frames any more, the port closes via CloseChannel(). - void UnregisterFrame(content::RenderFrameHost* rfh); - - // Immediately close the port and its associated channel. - void CloseChannel(); - - // Send a IPC message to the renderer for all registered frames. - void SendToPort(std::unique_ptr msg); - - base::WeakPtr weak_message_service_; - - int port_id_; - std::string extension_id_; - content::BrowserContext* browser_context_; - // Only for receivers in an extension process. - content::RenderProcessHost* extension_process_; - - // When the port is used as a sender, this set contains only one element. - // If used as a receiver, it may contain any number of frames. - // This set is populated before the first message is sent to the destination, - // and shrinks over time when the port is rejected by the recipient frame, or - // when the frame is removed or unloaded. - std::set frames_; - - // The ID of the tab where the channel was created. This is saved so that any - // onMessage events can be run in the scope of the tab. - // Only set on receiver ports (if the opener was a tab). -1 if invalid. - int opener_tab_id_; - - // Whether the renderer acknowledged creation of the port. This is used to - // distinguish abnormal port closure (e.g. no receivers) from explicit port - // closure (e.g. by the port.disconnect() JavaScript method in the renderer). - bool did_create_port_; - - ExtensionHost* background_host_ptr_; // used in IncrementLazyKeepaliveCount - std::unique_ptr frame_tracker_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionMessagePort); -}; - -} // namespace extensions - -#endif // ATOM_BROWSER_EXTENSIONS_API_MESSAGING_EXTENSION_MESSAGE_PORT_H_ diff --git a/atom/browser/extensions/api/messaging/message_property_provider.cc b/atom/browser/extensions/api/messaging/message_property_provider.cc deleted file mode 100644 index e0902d30c1..0000000000 --- a/atom/browser/extensions/api/messaging/message_property_provider.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2013 The Chromium 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 "atom/browser/extensions/api/messaging/message_property_provider.h" - -#include -#include -#include "atom/browser/atom_browser_context.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/strings/string_piece.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/values.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_thread.h" -#include "crypto/ec_private_key.h" -#include "extensions/common/api/runtime.h" -#include "net/base/completion_callback.h" -#include "net/cert/asn1_util.h" -#include "net/cert/jwk_serializer.h" -#include "net/ssl/channel_id_service.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" -#include "url/gurl.h" - -namespace extensions { - -MessagePropertyProvider::MessagePropertyProvider() {} - -void MessagePropertyProvider::GetChannelID( - content::BrowserContext* browser_context, - const GURL& source_url, - const ChannelIDCallback& reply) { - if (!source_url.is_valid()) { - // This isn't a real URL, so there's no sense in looking for a channel ID - // for it. Dispatch with an empty tls channel ID. - reply.Run(std::string()); - return; - } - scoped_refptr request_context_getter( - static_cast(browser_context) - ->GetRequestContext()); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&MessagePropertyProvider::GetChannelIDOnIOThread, - base::ThreadTaskRunnerHandle::Get(), - request_context_getter, - source_url.host(), - reply)); -} - -// Helper struct to bind the memory addresses that will be written to by -// ChannelIDService::GetChannelID to the callback provided to -// MessagePropertyProvider::GetChannelID. -struct MessagePropertyProvider::GetChannelIDOutput { - std::unique_ptr channel_id_key; - net::ChannelIDService::Request request; -}; - -// static -void MessagePropertyProvider::GetChannelIDOnIOThread( - scoped_refptr original_task_runner, - scoped_refptr request_context_getter, - const std::string& host, - const ChannelIDCallback& reply) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - net::ChannelIDService* channel_id_service = - request_context_getter->GetURLRequestContext()-> - channel_id_service(); - GetChannelIDOutput* output = new GetChannelIDOutput(); - net::CompletionCallback net_completion_callback = - base::Bind(&MessagePropertyProvider::GotChannelID, - original_task_runner, - base::Owned(output), - reply); - int status = channel_id_service->GetChannelID( - host, &output->channel_id_key, net_completion_callback, &output->request); - if (status == net::ERR_IO_PENDING) - return; - GotChannelID(original_task_runner, output, reply, status); -} - -// static -void MessagePropertyProvider::GotChannelID( - scoped_refptr original_task_runner, - struct GetChannelIDOutput* output, - const ChannelIDCallback& reply, - int status) { - base::Closure no_tls_channel_id_closure = base::Bind(reply, ""); - if (status != net::OK) { - original_task_runner->PostTask(FROM_HERE, no_tls_channel_id_closure); - return; - } - std::vector spki_vector; - if (!output->channel_id_key->ExportPublicKey(&spki_vector)) { - original_task_runner->PostTask(FROM_HERE, no_tls_channel_id_closure); - return; - } - base::StringPiece spki(reinterpret_cast(spki_vector.data()), - spki_vector.size()); - base::DictionaryValue jwk_value; - if (!net::JwkSerializer::ConvertSpkiFromDerToJwk(spki, &jwk_value)) { - original_task_runner->PostTask(FROM_HERE, no_tls_channel_id_closure); - return; - } - std::string jwk_str; - base::JSONWriter::Write(jwk_value, &jwk_str); - original_task_runner->PostTask(FROM_HERE, base::Bind(reply, jwk_str)); -} - -} // namespace extensions diff --git a/atom/browser/extensions/api/messaging/message_property_provider.h b/atom/browser/extensions/api/messaging/message_property_provider.h deleted file mode 100644 index 2c73b626e2..0000000000 --- a/atom/browser/extensions/api/messaging/message_property_provider.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2013 The Chromium 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 ATOM_BROWSER_EXTENSIONS_API_MESSAGING_MESSAGE_PROPERTY_PROVIDER_H_ -#define ATOM_BROWSER_EXTENSIONS_API_MESSAGING_MESSAGE_PROPERTY_PROVIDER_H_ - -#include - -#include "base/callback.h" -#include "base/macros.h" - -class GURL; - -namespace base { -class TaskRunner; -} - -namespace content { -class BrowserContext; -} - -namespace net { -class URLRequestContextGetter; -} - -namespace extensions { - -// This class provides properties of messages asynchronously. -class MessagePropertyProvider { - public: - MessagePropertyProvider(); - - typedef base::Callback ChannelIDCallback; - - // Gets the DER-encoded public key of the domain-bound cert, - // aka TLS channel ID, for the given URL. - // Runs |reply| on the current message loop. - void GetChannelID(content::BrowserContext* browser_context, - const GURL& source_url, - const ChannelIDCallback& reply); - - private: - struct GetChannelIDOutput; - - static void GetChannelIDOnIOThread( - scoped_refptr original_task_runner, - scoped_refptr request_context_getter, - const std::string& host, - const ChannelIDCallback& reply); - - static void GotChannelID( - scoped_refptr original_task_runner, - struct GetChannelIDOutput* output, - const ChannelIDCallback& reply, - int status); - - DISALLOW_COPY_AND_ASSIGN(MessagePropertyProvider); -}; - -} // namespace extensions - -#endif // ATOM_BROWSER_EXTENSIONS_API_MESSAGING_MESSAGE_PROPERTY_PROVIDER_H_ diff --git a/atom/browser/extensions/api/messaging/message_service.cc b/atom/browser/extensions/api/messaging/message_service.cc deleted file mode 100644 index 0ef3a42714..0000000000 --- a/atom/browser/extensions/api/messaging/message_service.cc +++ /dev/null @@ -1,907 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "atom/browser/extensions/api/messaging/message_service.h" - -#include -#include -#include - -#include "atom/browser/extensions/api/messaging/extension_message_port.h" -#include "atom/browser/extensions/atom_extension_system.h" -#include "atom/browser/extensions/atom_extensions_browser_client.h" -#include "atom/browser/extensions/tab_helper.h" -#include "base/atomic_sequence_num.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/json/json_writer.h" -#include "base/lazy_instance.h" -#include "base/macros.h" -#include "base/metrics/histogram.h" -#include "base/stl_util.h" -#include "build/build_config.h" -#include "components/prefs/pref_service.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/site_instance.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/child_process_host.h" -#include "extensions/browser/event_router.h" -#include "extensions/browser/extension_api_frame_id_map.h" -#include "extensions/browser/extension_host.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" -#include "extensions/browser/extension_util.h" -#include "extensions/browser/extension_web_contents_observer.h" -#include "extensions/browser/lazy_background_task_queue.h" -#include "extensions/browser/process_manager.h" -#include "extensions/common/extension.h" -#include "extensions/common/manifest.h" -#include "extensions/common/manifest_constants.h" -#include "extensions/common/manifest_handlers/background_info.h" -#include "extensions/common/manifest_handlers/externally_connectable.h" -#include "extensions/common/manifest_handlers/incognito_info.h" -#include "extensions/common/permissions/permissions_data.h" -#include "net/base/completion_callback.h" -#include "url/gurl.h" - -using content::BrowserContext; -using content::BrowserThread; -using content::SiteInstance; -using content::WebContents; - -// Since we have 2 ports for every channel, we just index channels by half the -// port ID. -#define GET_CHANNEL_ID(port_id) ((port_id) / 2) -#define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2) -#define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1) - -// Port1 is always even, port2 is always odd. -#define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0) - -// Change even to odd and vice versa, to get the other side of a given channel. -#define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1) - -namespace { - -content::WebContents* GetWebContentsByFrameID(int render_process_id, - int render_frame_id) { - content::RenderFrameHost* render_frame_host = - content::RenderFrameHost::FromID(render_process_id, render_frame_id); - if (!render_frame_host) - return NULL; - return content::WebContents::FromRenderFrameHost(render_frame_host); -} - -} // namespace - -namespace extensions { - -const char kReceivingEndDoesntExistError[] = - "Could not establish connection. Receiving end does not exist."; -const char kMissingPermissionError[] = - "Access to native messaging requires nativeMessaging permission."; -const char kProhibitedByPoliciesError[] = - "Access to the native messaging host was disabled by the system " - "administrator."; - -struct MessageService::MessageChannel { - std::unique_ptr opener; - std::unique_ptr receiver; -}; - -struct MessageService::OpenChannelParams { - int source_process_id; - int source_routing_id; - std::unique_ptr source_tab; - int source_frame_id; - std::unique_ptr receiver; - int receiver_port_id; - std::string source_extension_id; - std::string target_extension_id; - GURL source_url; - std::string channel_name; - bool include_tls_channel_id; - std::string tls_channel_id; - bool include_guest_process_info; - - // Takes ownership of receiver. - OpenChannelParams(int source_process_id, - int source_routing_id, - std::unique_ptr source_tab, - int source_frame_id, - MessagePort* receiver, - int receiver_port_id, - const std::string& source_extension_id, - const std::string& target_extension_id, - const GURL& source_url, - const std::string& channel_name, - bool include_tls_channel_id, - bool include_guest_process_info) - : source_process_id(source_process_id), - source_routing_id(source_routing_id), - source_frame_id(source_frame_id), - receiver(receiver), - receiver_port_id(receiver_port_id), - source_extension_id(source_extension_id), - target_extension_id(target_extension_id), - source_url(source_url), - channel_name(channel_name), - include_tls_channel_id(include_tls_channel_id), - include_guest_process_info(include_guest_process_info) { - if (source_tab) - this->source_tab = std::move(source_tab); - } - - private: - DISALLOW_COPY_AND_ASSIGN(OpenChannelParams); -}; - -namespace { - -static base::StaticAtomicSequenceNumber g_next_channel_id; - -static content::RenderProcessHost* GetExtensionProcess( - BrowserContext* context, - const std::string& extension_id) { - scoped_refptr site_instance = - ProcessManager::Get(context)->GetSiteInstanceForURL( - Extension::GetBaseURLFromExtensionId(extension_id)); - return site_instance->HasProcess() ? site_instance->GetProcess() : NULL; -} - -} // namespace - -void MessageService::MessagePort::RemoveCommonFrames(const MessagePort& port) {} - -bool MessageService::MessagePort::HasFrame( - content::RenderFrameHost* rfh) const { - return false; -} - -// static -void MessageService::AllocatePortIdPair(int* port1, int* port2) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - unsigned channel_id = static_cast(g_next_channel_id.GetNext()) % - (std::numeric_limits::max() / 2); - unsigned port1_id = channel_id * 2; - unsigned port2_id = channel_id * 2 + 1; - - // Sanity checks to make sure our channel<->port converters are correct. - DCHECK(IS_OPENER_PORT_ID(port1_id)); - DCHECK_EQ(GET_OPPOSITE_PORT_ID(port1_id), port2_id); - DCHECK_EQ(GET_OPPOSITE_PORT_ID(port2_id), port1_id); - DCHECK_EQ(GET_CHANNEL_ID(port1_id), GET_CHANNEL_ID(port2_id)); - DCHECK_EQ(GET_CHANNEL_ID(port1_id), channel_id); - DCHECK_EQ(GET_CHANNEL_OPENER_ID(channel_id), port1_id); - DCHECK_EQ(GET_CHANNEL_RECEIVERS_ID(channel_id), port2_id); - - *port1 = port1_id; - *port2 = port2_id; -} - -MessageService::MessageService(BrowserContext* context) - : lazy_background_task_queue_( - LazyBackgroundTaskQueue::Get(context)), - weak_factory_(this) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); -} - -MessageService::~MessageService() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end()); - channels_.clear(); -} - -static base::LazyInstance > - g_factory = LAZY_INSTANCE_INITIALIZER; - -// static -BrowserContextKeyedAPIFactory* -MessageService::GetFactoryInstance() { - return g_factory.Pointer(); -} - -// static -MessageService* MessageService::Get(BrowserContext* context) { - return BrowserContextKeyedAPIFactory::Get(context); -} - -void MessageService::OpenChannelToExtension( - int source_process_id, int source_routing_id, int receiver_port_id, - const std::string& source_extension_id, - const std::string& target_extension_id, - const GURL& source_url, - const std::string& channel_name, - bool include_tls_channel_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(source_process_id, source_routing_id); - if (!source) - return; - BrowserContext* context = source->GetProcess()->GetBrowserContext(); - - ExtensionRegistry* registry = ExtensionRegistry::Get(context); - const Extension* target_extension = - registry->enabled_extensions().GetByID(target_extension_id); - if (!target_extension) { - DispatchOnDisconnect( - source, receiver_port_id, kReceivingEndDoesntExistError); - return; - } - - bool is_web_connection = false; - - if (source_extension_id != target_extension_id) { - // It's an external connection. Check the externally_connectable manifest - // key if it's present. If it's not, we allow connection from any extension - // but not webpages. - ExternallyConnectableInfo* externally_connectable = - static_cast( - target_extension->GetManifestData( - manifest_keys::kExternallyConnectable)); - bool is_externally_connectable = false; - - if (externally_connectable) { - if (source_extension_id.empty()) { - // No source extension ID so the source was a web page. Check that the - // URL matches. - is_web_connection = true; - is_externally_connectable = - externally_connectable->matches.MatchesURL(source_url); - // Only include the TLS channel ID for externally connected web pages. - include_tls_channel_id &= - is_externally_connectable && - externally_connectable->accepts_tls_channel_id; - } else { - // Source extension ID so the source was an extension. Check that the - // extension matches. - is_externally_connectable = - externally_connectable->IdCanConnect(source_extension_id); - } - } else { - // Default behaviour. Any extension, no webpages. - is_externally_connectable = !source_extension_id.empty(); - } - - if (!is_externally_connectable) { - // Important: use kReceivingEndDoesntExistError here so that we don't - // leak information about this extension to callers. This way it's - // indistinguishable from the extension just not existing. - DispatchOnDisconnect( - source, receiver_port_id, kReceivingEndDoesntExistError); - return; - } - } - - WebContents* source_contents = GetWebContentsByFrameID( - source_process_id, source_routing_id); - - bool include_guest_process_info = false; - - // Include info about the opener's tab (if it was a tab). - std::unique_ptr source_tab; - int source_frame_id = -1; - if (source_contents && TabHelper::IdForTab(source_contents) >= 0) { - // Only the tab id is useful to platform apps for internal use. The - // unnecessary bits will be stripped out in - // MessagingBindings::DispatchOnConnect(). - source_tab.reset(extensions::TabHelper::CreateTabValue(source_contents)); - - content::RenderFrameHost* rfh = - content::RenderFrameHost::FromID(source_process_id, source_routing_id); - if (rfh) - source_frame_id = ExtensionApiFrameIdMap::GetFrameId(rfh); - } - - std::unique_ptr params(new OpenChannelParams( - source_process_id, source_routing_id, std::move(source_tab), - source_frame_id, nullptr, receiver_port_id, source_extension_id, - target_extension_id, source_url, channel_name, include_tls_channel_id, - include_guest_process_info)); - - pending_incognito_channels_[GET_CHANNEL_ID(params->receiver_port_id)] = - PendingMessagesQueue(); - if (context->IsOffTheRecord() && - !AtomExtensionsBrowserClient::IsIncognitoEnabled(target_extension_id, - context)) { - // Give the user a chance to accept an incognito connection from the web if - // they haven't already, with the conditions: - // - Only for spanning-mode incognito. We don't want the complication of - // spinning up an additional process here which might need to do some - // setup that we're not expecting. - // - Only for extensions that can't normally be enabled in incognito, since - // that surface (e.g. chrome://extensions) should be the only one for - // enabling in incognito. In practice this means platform apps only. - if (!is_web_connection || IncognitoInfo::IsSplitMode(target_extension) || - util::CanBeIncognitoEnabled(target_extension)) { - OnOpenChannelAllowed(std::move(params), false); - return; - } - - // If the target extension isn't even listening for connect/message events, - // there is no need to go any further and the connection should be - // rejected without showing a prompt. See http://crbug.com/442497 - EventRouter* event_router = EventRouter::Get(context); - const char* const events[] = {"runtime.onConnectExternal", - "runtime.onMessageExternal", - "extension.onRequestExternal", - nullptr}; - bool has_event_listener = false; - for (const char* const* event = events; *event; event++) { - has_event_listener |= - event_router->ExtensionHasEventListener(target_extension_id, *event); - } - if (!has_event_listener) { - OnOpenChannelAllowed(std::move(params), false); - return; - } - - return; - } - - OnOpenChannelAllowed(std::move(params), true); -} - -void MessageService::OpenChannelToNativeApp( - int source_process_id, - int source_routing_id, - int receiver_port_id, - const std::string& native_app_name) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(source_process_id, source_routing_id); - if (!source) - return; - - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(source); - ExtensionWebContentsObserver* extension_web_contents_observer = - web_contents ? - ExtensionWebContentsObserver::GetForWebContents(web_contents) : - nullptr; - const Extension* extension = - extension_web_contents_observer ? - extension_web_contents_observer->GetExtensionFromFrame(source, true) : - nullptr; - - bool has_permission = extension && - extension->permissions_data()->HasAPIPermission( - APIPermission::kNativeMessaging); - - if (!has_permission) { - DispatchOnDisconnect(source, receiver_port_id, kMissingPermissionError); - return; - } - - DispatchOnDisconnect(source, receiver_port_id, kProhibitedByPoliciesError); -} - -void MessageService::OpenChannelToTab(int source_process_id, - int source_routing_id, - int receiver_port_id, - int tab_id, - int frame_id, - const std::string& extension_id, - const std::string& channel_name) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK_GE(frame_id, -1); - - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(source_process_id, source_routing_id); - if (!source) - return; - - BrowserContext* browser_context = source->GetProcess()->GetBrowserContext(); - - WebContents* contents = TabHelper::GetTabById(tab_id, browser_context); - std::unique_ptr receiver; - if (!contents || contents->GetController().NeedsReload()) { - // The tab isn't loaded yet. Don't attempt to connect. - DispatchOnDisconnect( - source, receiver_port_id, kReceivingEndDoesntExistError); - return; - } - - // Frame ID -1 is every frame in the tab. - bool include_child_frames = frame_id == -1; - content::RenderFrameHost* receiver_rfh = - include_child_frames - ? contents->GetMainFrame() - : ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id); - if (!receiver_rfh) { - DispatchOnDisconnect( - source, receiver_port_id, kReceivingEndDoesntExistError); - return; - } - receiver.reset( - new ExtensionMessagePort(weak_factory_.GetWeakPtr(), - receiver_port_id, extension_id, receiver_rfh, - include_child_frames)); - - const Extension* extension = nullptr; - if (!extension_id.empty()) { - // Source extension == target extension so the extension must exist, or - // where did the IPC come from? - extension = ExtensionRegistry::Get(browser_context)-> - enabled_extensions().GetByID(extension_id); - DCHECK(extension); - } - - std::unique_ptr params(new OpenChannelParams( - source_process_id, source_routing_id, - std::unique_ptr(), // Source tab doesn't make - // sense - // for opening to tabs. - -1, // If there is no tab, then there is no frame either. - receiver.release(), receiver_port_id, extension_id, extension_id, - GURL(), // Source URL doesn't make sense for opening to tabs. - channel_name, - false, // Connections to tabs don't get TLS channel IDs. - false)); // Connections to tabs aren't webview guests. - OpenChannelImpl(contents->GetBrowserContext(), std::move(params), extension, - false /* did_enqueue */); -} - -void MessageService::OpenChannelImpl(BrowserContext* browser_context, - std::unique_ptr params, - const Extension* target_extension, - bool did_enqueue) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK_EQ(target_extension != nullptr, !params->target_extension_id.empty()); - - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(params->source_process_id, - params->source_routing_id); - if (!source) - return; // Closed while in flight. - - if (!params->receiver || !params->receiver->IsValidPort()) { - DispatchOnDisconnect(source, params->receiver_port_id, - kReceivingEndDoesntExistError); - return; - } - - std::unique_ptr opener( - new ExtensionMessagePort(weak_factory_.GetWeakPtr(), - GET_OPPOSITE_PORT_ID(params->receiver_port_id), - params->source_extension_id, source, false)); - if (!opener->IsValidPort()) - return; - opener->OpenPort(params->source_process_id, params->source_routing_id); - opener->RevalidatePort(); - - params->receiver->RemoveCommonFrames(*opener); - if (!params->receiver->IsValidPort()) { - opener->DispatchOnDisconnect(kReceivingEndDoesntExistError); - return; - } - - MessageChannel* channel(new MessageChannel()); - channel->opener.reset(opener.release()); - channel->receiver.reset(params->receiver.release()); - AddChannel(channel, params->receiver_port_id); - - int guest_process_id = content::ChildProcessHost::kInvalidUniqueID; - int guest_render_frame_routing_id = MSG_ROUTING_NONE; - - // Send the connect event to the receiver. Give it the opener's port ID (the - // opener has the opposite port ID). - channel->receiver->DispatchOnConnect( - params->channel_name, std::move(params->source_tab), - params->source_frame_id, guest_process_id, guest_render_frame_routing_id, - params->source_extension_id, params->target_extension_id, - params->source_url, params->tls_channel_id); - - // Report the event to the event router, if the target is an extension. - // - // First, determine what event this will be (runtime.onConnect vs - // runtime.onMessage etc), and what the event target is (view vs background - // page etc). - // - // Yes - even though this is opening a channel, they may actually be - // runtime.onRequest/onMessage events because those single-use events are - // built using the connect framework (see messaging.js). - // - // Likewise, if you're wondering about native messaging events, these are - // only initiated *by* the extension, so aren't really events, just the - // endpoint of a communication channel. - if (target_extension) { - events::HistogramValue histogram_value = events::UNKNOWN; - bool is_external = - params->source_extension_id != params->target_extension_id; - if (params->channel_name == "chrome.runtime.onRequest") { - histogram_value = is_external ? events::RUNTIME_ON_REQUEST_EXTERNAL - : events::RUNTIME_ON_REQUEST; - } else if (params->channel_name == "chrome.runtime.onMessage") { - histogram_value = is_external ? events::RUNTIME_ON_MESSAGE_EXTERNAL - : events::RUNTIME_ON_MESSAGE; - } else { - histogram_value = is_external ? events::RUNTIME_ON_CONNECT_EXTERNAL - : events::RUNTIME_ON_CONNECT; - } - EventRouter::Get(browser_context) - ->ReportEvent(histogram_value, target_extension, did_enqueue); - } - - // Keep both ends of the channel alive until the channel is closed. - channel->opener->IncrementLazyKeepaliveCount(); - channel->receiver->IncrementLazyKeepaliveCount(); -} - -void MessageService::AddChannel(MessageChannel* channel, int receiver_port_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - int channel_id = GET_CHANNEL_ID(receiver_port_id); - CHECK(channels_.find(channel_id) == channels_.end()); - channels_[channel_id] = channel; - pending_lazy_background_page_channels_.erase(channel_id); -} - -void MessageService::OpenPort(int port_id, int process_id, int routing_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!IS_OPENER_PORT_ID(port_id)); - - int channel_id = GET_CHANNEL_ID(port_id); - MessageChannelMap::iterator it = channels_.find(channel_id); - if (it == channels_.end()) - return; - - it->second->receiver->OpenPort(process_id, routing_id); -} - -void MessageService::ClosePort( - int port_id, int process_id, int routing_id, bool force_close) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - ClosePortImpl(port_id, process_id, routing_id, force_close, std::string()); -} - -void MessageService::CloseChannel(int port_id, - const std::string& error_message) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - ClosePortImpl(port_id, content::ChildProcessHost::kInvalidUniqueID, - MSG_ROUTING_NONE, true, error_message); -} - -void MessageService::ClosePortImpl(int port_id, - int process_id, - int routing_id, - bool force_close, - const std::string& error_message) { - // Note: The channel might be gone already, if the other side closed first. - int channel_id = GET_CHANNEL_ID(port_id); - MessageChannelMap::iterator it = channels_.find(channel_id); - if (it == channels_.end()) { - PendingLazyBackgroundPageChannelMap::iterator pending = - pending_lazy_background_page_channels_.find(channel_id); - if (pending != pending_lazy_background_page_channels_.end()) { - lazy_background_task_queue_->AddPendingTask( - pending->second.first, pending->second.second, - base::Bind(&MessageService::PendingLazyBackgroundPageClosePort, - weak_factory_.GetWeakPtr(), port_id, process_id, - routing_id, force_close, error_message)); - } - return; - } - - // The difference between closing a channel and port is that closing a port - // does not necessarily have to destroy the channel if there are multiple - // receivers, whereas closing a channel always forces all ports to be closed. - if (force_close) { - CloseChannelImpl(it, port_id, error_message, true); - } else if (IS_OPENER_PORT_ID(port_id)) { - it->second->opener->ClosePort(process_id, routing_id); - } else { - it->second->receiver->ClosePort(process_id, routing_id); - } -} - -void MessageService::CloseChannelImpl( - MessageChannelMap::iterator channel_iter, - int closing_port_id, - const std::string& error_message, - bool notify_other_port) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - MessageChannel* channel = channel_iter->second; - // Remove from map to make sure that it is impossible for CloseChannelImpl to - // run twice for the same channel. - channels_.erase(channel_iter); - - // Notify the other side. - if (notify_other_port) { - MessagePort* port = IS_OPENER_PORT_ID(closing_port_id) ? - channel->receiver.get() : channel->opener.get(); - port->DispatchOnDisconnect(error_message); - } - - // Balance the IncrementLazyKeepaliveCount() in OpenChannelImpl. - channel->opener->DecrementLazyKeepaliveCount(); - channel->receiver->DecrementLazyKeepaliveCount(); - - delete channel; -} - -void MessageService::PostMessage(int source_port_id, const Message& message) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - int channel_id = GET_CHANNEL_ID(source_port_id); - MessageChannelMap::iterator iter = channels_.find(channel_id); - if (iter == channels_.end()) { - // If this channel is pending, queue up the PostMessage to run once - // the channel opens. - EnqueuePendingMessage(source_port_id, channel_id, message); - return; - } - - DispatchMessage(source_port_id, iter->second, message); -} - -void MessageService::EnqueuePendingMessage(int source_port_id, - int channel_id, - const Message& message) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - PendingChannelMap::iterator pending_for_incognito = - pending_incognito_channels_.find(channel_id); - if (pending_for_incognito != pending_incognito_channels_.end()) { - pending_for_incognito->second.push_back( - PendingMessage(source_port_id, message)); - // A channel should only be holding pending messages because it is in one - // of these states. - DCHECK(!ContainsKey(pending_tls_channel_id_channels_, channel_id)); - DCHECK(!ContainsKey(pending_lazy_background_page_channels_, channel_id)); - return; - } - PendingChannelMap::iterator pending_for_tls_channel_id = - pending_tls_channel_id_channels_.find(channel_id); - if (pending_for_tls_channel_id != pending_tls_channel_id_channels_.end()) { - pending_for_tls_channel_id->second.push_back( - PendingMessage(source_port_id, message)); - // A channel should only be holding pending messages because it is in one - // of these states. - DCHECK(!ContainsKey(pending_lazy_background_page_channels_, channel_id)); - return; - } - EnqueuePendingMessageForLazyBackgroundLoad(source_port_id, - channel_id, - message); -} - -void MessageService::EnqueuePendingMessageForLazyBackgroundLoad( - int source_port_id, - int channel_id, - const Message& message) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - PendingLazyBackgroundPageChannelMap::iterator pending = - pending_lazy_background_page_channels_.find(channel_id); - if (pending != pending_lazy_background_page_channels_.end()) { - lazy_background_task_queue_->AddPendingTask( - pending->second.first, pending->second.second, - base::Bind(&MessageService::PendingLazyBackgroundPagePostMessage, - weak_factory_.GetWeakPtr(), source_port_id, message)); - } -} - -void MessageService::DispatchMessage(int source_port_id, - MessageChannel* channel, - const Message& message) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - // Figure out which port the ID corresponds to. - int dest_port_id = GET_OPPOSITE_PORT_ID(source_port_id); - MessagePort* port = IS_OPENER_PORT_ID(dest_port_id) ? - channel->opener.get() : channel->receiver.get(); - - port->DispatchOnMessage(message); -} - -bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask( - BrowserContext* context, - const Extension* extension, - std::unique_ptr* params, - const PendingMessagesQueue& pending_messages) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - if (!BackgroundInfo::HasLazyBackgroundPage(extension)) - return false; - - // If the extension uses spanning incognito mode, make sure we're always - // using the original profile since that is what the extension process - // will use. - if (!IncognitoInfo::IsSplitMode(extension)) - context = ExtensionsBrowserClient::Get()->GetOriginalContext(context); - - if (!lazy_background_task_queue_->ShouldEnqueueTask(context, extension)) - return false; - - int channel_id = GET_CHANNEL_ID((*params)->receiver_port_id); - pending_lazy_background_page_channels_[channel_id] = - PendingLazyBackgroundPageChannel(context, extension->id()); - int source_id = (*params)->source_process_id; - lazy_background_task_queue_->AddPendingTask( - context, extension->id(), - base::Bind(&MessageService::PendingLazyBackgroundPageOpenChannel, - weak_factory_.GetWeakPtr(), base::Passed(params), source_id)); - - for (const PendingMessage& message : pending_messages) { - EnqueuePendingMessageForLazyBackgroundLoad(message.first, channel_id, - message.second); - } - return true; -} - -void MessageService::OnOpenChannelAllowed( - std::unique_ptr params, - bool allowed) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - int channel_id = GET_CHANNEL_ID(params->receiver_port_id); - - PendingChannelMap::iterator pending_for_incognito = - pending_incognito_channels_.find(channel_id); - if (pending_for_incognito == pending_incognito_channels_.end()) { - NOTREACHED(); - return; - } - PendingMessagesQueue pending_messages; - pending_messages.swap(pending_for_incognito->second); - pending_incognito_channels_.erase(pending_for_incognito); - - // Re-lookup the source process since it may no longer be valid. - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(params->source_process_id, - params->source_routing_id); - if (!source) { - return; - } - - if (!allowed) { - DispatchOnDisconnect(source, params->receiver_port_id, - kReceivingEndDoesntExistError); - return; - } - - BrowserContext* context = source->GetProcess()->GetBrowserContext(); - - // Note: we use the source's profile here. If the source is an incognito - // process, we will use the incognito EPM to find the right extension process, - // which depends on whether the extension uses spanning or split mode. - if (content::RenderProcessHost* extension_process = - GetExtensionProcess(context, params->target_extension_id)) { - params->receiver.reset( - new ExtensionMessagePort( - weak_factory_.GetWeakPtr(), params->receiver_port_id, - params->target_extension_id, extension_process)); - } else { - params->receiver.reset(); - } - - // If the target requests the TLS channel id, begin the lookup for it. - // The target might also be a lazy background page, checked next, but the - // loading of lazy background pages continues asynchronously, so enqueue - // messages awaiting TLS channel ID first. - if (params->include_tls_channel_id) { - // Transfer pending messages to the next pending channel list. - pending_tls_channel_id_channels_[channel_id].swap(pending_messages); - // Capture this reference before params is invalidated by base::Passed(). - const GURL& source_url = params->source_url; - property_provider_.GetChannelID(context, source_url, - base::Bind(&MessageService::GotChannelID, weak_factory_.GetWeakPtr(), - base::Passed(¶ms))); - return; - } - - ExtensionRegistry* registry = ExtensionRegistry::Get(context); - const Extension* target_extension = - registry->enabled_extensions().GetByID(params->target_extension_id); - if (!target_extension) { - DispatchOnDisconnect(source, params->receiver_port_id, - kReceivingEndDoesntExistError); - return; - } - - // The target might be a lazy background page. In that case, we have to check - // if it is loaded and ready, and if not, queue up the task and load the - // page. - if (!MaybeAddPendingLazyBackgroundPageOpenChannelTask( - context, target_extension, ¶ms, pending_messages)) { - OpenChannelImpl(context, std::move(params), target_extension, - false /* did_enqueue */); - DispatchPendingMessages(pending_messages, channel_id); - } -} - -void MessageService::GotChannelID(std::unique_ptr params, - const std::string& tls_channel_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - params->tls_channel_id.assign(tls_channel_id); - int channel_id = GET_CHANNEL_ID(params->receiver_port_id); - - PendingChannelMap::iterator pending_for_tls_channel_id = - pending_tls_channel_id_channels_.find(channel_id); - if (pending_for_tls_channel_id == pending_tls_channel_id_channels_.end()) { - NOTREACHED(); - return; - } - PendingMessagesQueue pending_messages; - pending_messages.swap(pending_for_tls_channel_id->second); - pending_tls_channel_id_channels_.erase(pending_for_tls_channel_id); - - // Re-lookup the source process since it may no longer be valid. - content::RenderFrameHost* source = - content::RenderFrameHost::FromID(params->source_process_id, - params->source_routing_id); - if (!source) { - return; - } - - BrowserContext* context = source->GetProcess()->GetBrowserContext(); - ExtensionRegistry* registry = ExtensionRegistry::Get(context); - const Extension* target_extension = - registry->enabled_extensions().GetByID(params->target_extension_id); - if (!target_extension) { - DispatchOnDisconnect(source, params->receiver_port_id, - kReceivingEndDoesntExistError); - return; - } - - if (!MaybeAddPendingLazyBackgroundPageOpenChannelTask( - context, target_extension, ¶ms, pending_messages)) { - OpenChannelImpl(context, std::move(params), target_extension, - false /* did_enqueue */); - DispatchPendingMessages(pending_messages, channel_id); - } -} - -void MessageService::PendingLazyBackgroundPageOpenChannel( - std::unique_ptr params, - int source_process_id, - ExtensionHost* host) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - if (!host) - return; // TODO(mpcomplete): notify source of disconnect? - - params->receiver.reset( - new ExtensionMessagePort( - weak_factory_.GetWeakPtr(), params->receiver_port_id, - params->target_extension_id, host->render_process_host())); - OpenChannelImpl(host->browser_context(), std::move(params), host->extension(), - true /* did_enqueue */); -} - -void MessageService::DispatchOnDisconnect(content::RenderFrameHost* source, - int port_id, - const std::string& error_message) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - ExtensionMessagePort port(weak_factory_.GetWeakPtr(), - GET_OPPOSITE_PORT_ID(port_id), "", source, false); - if (!port.IsValidPort()) - return; - port.DispatchOnDisconnect(error_message); -} - -void MessageService::DispatchPendingMessages(const PendingMessagesQueue& queue, - int channel_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - MessageChannelMap::iterator channel_iter = channels_.find(channel_id); - if (channel_iter != channels_.end()) { - for (const PendingMessage& message : queue) { - DispatchMessage(message.first, channel_iter->second, message.second); - } - } -} - -} // namespace extensions diff --git a/atom/browser/extensions/api/messaging/message_service.h b/atom/browser/extensions/api/messaging/message_service.h deleted file mode 100644 index 9b90db414f..0000000000 --- a/atom/browser/extensions/api/messaging/message_service.h +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 ATOM_BROWSER_EXTENSIONS_API_MESSAGING_MESSAGE_SERVICE_H_ -#define ATOM_BROWSER_EXTENSIONS_API_MESSAGING_MESSAGE_SERVICE_H_ - -#include -#include -#include -#include -#include -#include "atom/browser/extensions/api/messaging/message_property_provider.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/values.h" -#include "extensions/browser/api/messaging/native_message_host.h" -#include "extensions/browser/browser_context_keyed_api_factory.h" -#include "extensions/common/api/messaging/message.h" - -class GURL; -class Profile; - -namespace content { -class BrowserContext; -} - -namespace extensions { -class Extension; -class ExtensionHost; -class LazyBackgroundTaskQueue; - -// This class manages message and event passing between renderer processes. -// It maintains a list of processes that are listening to events and a set of -// open channels. -// -// Messaging works this way: -// - An extension-owned script context (like a background page or a content -// script) adds an event listener to the "onConnect" event. -// - Another context calls "runtime.connect()" to open a channel to the -// extension process, or an extension context calls "tabs.connect(tabId)" to -// open a channel to the content scripts for the given tab. The EMS notifies -// the target process/tab, which then calls the onConnect event in every -// context owned by the connecting extension in that process/tab. -// - Once the channel is established, either side can call postMessage to send -// a message to the opposite side of the channel, which may have multiple -// listeners. -// -// Terminology: -// channel: connection between two ports -// port: One sender or receiver tied to one or more RenderFrameHost instances. -class MessageService : public BrowserContextKeyedAPI { - public: - // A messaging channel. Note that the opening port can be the same as the - // receiver, if an extension background page wants to talk to its tab (for - // example). - struct MessageChannel; - - // One side of the communication handled by extensions::MessageService. - class MessagePort { - public: - virtual ~MessagePort() {} - - // Called right before a channel is created for this MessagePort and |port|. - // This allows us to ensure that the ports have no RenderFrameHost instances - // in common. - virtual void RemoveCommonFrames(const MessagePort& port); - - // Check whether the given RenderFrameHost is associated with this port. - virtual bool HasFrame(content::RenderFrameHost* rfh) const; - - // Called right before a port is connected to a channel. If false, the port - // is not used and the channel is closed. - virtual bool IsValidPort() = 0; - - // Notify the port that the channel has been opened. - virtual void DispatchOnConnect(const std::string& channel_name, - std::unique_ptr - source_tab, - int source_frame_id, - int guest_process_id, - int guest_render_frame_routing_id, - const std::string& source_extension_id, - const std::string& target_extension_id, - const GURL& source_url, - const std::string& tls_channel_id) {} - - // Notify the port that the channel has been closed. If |error_message| is - // non-empty, it indicates an error occurred while opening the connection. - virtual void DispatchOnDisconnect(const std::string& error_message) {} - - // Dispatch a message to this end of the communication. - virtual void DispatchOnMessage(const Message& message) = 0; - - // Mark the port as opened by the specific frame. - virtual void OpenPort(int process_id, int routing_id) {} - - // Close the port for the given frame. - virtual void ClosePort(int process_id, int routing_id) {} - - // MessagePorts that target extensions will need to adjust their keepalive - // counts for their lazy background page. - virtual void IncrementLazyKeepaliveCount() {} - virtual void DecrementLazyKeepaliveCount() {} - - protected: - MessagePort() {} - - private: - DISALLOW_COPY_AND_ASSIGN(MessagePort); - }; - - enum PolicyPermission { - DISALLOW, // The host is not allowed. - ALLOW_SYSTEM_ONLY, // Allowed only when installed on system level. - ALLOW_ALL, // Allowed when installed on system or user level. - }; - - static PolicyPermission IsNativeMessagingHostAllowed( - const PrefService* pref_service, - const std::string& native_host_name); - - // Allocates a pair of port ids. - // NOTE: this can be called from any thread. - static void AllocatePortIdPair(int* port1, int* port2); - - explicit MessageService(content::BrowserContext* context); - ~MessageService() override; - - // BrowserContextKeyedAPI implementation. - static BrowserContextKeyedAPIFactory* GetFactoryInstance(); - - // Convenience method to get the MessageService for a browser context. - static MessageService* Get(content::BrowserContext* context); - - // Given an extension's ID, opens a channel between the given renderer "port" - // and every listening context owned by that extension. |channel_name| is - // an optional identifier for use by extension developers. - void OpenChannelToExtension( - int source_process_id, int source_routing_id, int receiver_port_id, - const std::string& source_extension_id, - const std::string& target_extension_id, - const GURL& source_url, - const std::string& channel_name, - bool include_tls_channel_id); - - // Same as above, but opens a channel to the tab with the given ID. Messages - // are restricted to that tab, so if there are multiple tabs in that process, - // only the targeted tab will receive messages. - void OpenChannelToTab(int source_process_id, - int source_routing_id, - int receiver_port_id, - int tab_id, - int frame_id, - const std::string& extension_id, - const std::string& channel_name); - - void OpenChannelToNativeApp( - int source_process_id, - int source_routing_id, - int receiver_port_id, - const std::string& native_app_name); - - // Mark the given port as opened by the frame identified by - // (process_id, routing_id). - void OpenPort(int port_id, int process_id, int routing_id); - - // Closes the given port in the given frame. If this was the last frame or if - // |force_close| is true, then the other side is closed as well. - void ClosePort(int port_id, int process_id, int routing_id, bool force_close); - - // Closes the message channel associated with the given port, and notifies - // the other side. - void CloseChannel(int port_id, const std::string& error_message); - - // Enqueues a message on a pending channel, or sends a message to the given - // port if the channel isn't pending. - void PostMessage(int port_id, const Message& message); - - private: - friend class MockMessageService; - friend class BrowserContextKeyedAPIFactory; - struct OpenChannelParams; - - // A map of channel ID to its channel object. - typedef std::map MessageChannelMap; - - typedef std::pair PendingMessage; - typedef std::vector PendingMessagesQueue; - // A set of channel IDs waiting to complete opening, and any pending messages - // queued to be sent on those channels. - typedef std::map PendingChannelMap; - - // A map of channel ID to information about the extension that is waiting - // for that channel to open. Used for lazy background pages. - typedef std::string ExtensionID; - typedef std::pair - PendingLazyBackgroundPageChannel; - typedef std::map - PendingLazyBackgroundPageChannelMap; - - // Common implementation for opening a channel configured by |params|. - // - // |target_extension| will be non-null if |params->target_extension_id| is - // non-empty, that is, if the target is an extension, it must exist. - // - // |did_enqueue| will be true if the channel opening was delayed while - // waiting for an event page to start, false otherwise. - void OpenChannelImpl(content::BrowserContext* browser_context, - std::unique_ptr params, - const Extension* target_extension, - bool did_enqueue); - - void ClosePortImpl(int port_id, - int process_id, - int routing_id, - bool force_close, - const std::string& error_message); - - void CloseChannelImpl(MessageChannelMap::iterator channel_iter, - int port_id, - const std::string& error_message, - bool notify_other_port); - - // Have MessageService take ownership of |channel|, and remove any pending - // channels with the same id. - void AddChannel(MessageChannel* channel, int receiver_port_id); - - // If the channel is being opened from an incognito tab the user must allow - // the connection. - void OnOpenChannelAllowed(std::unique_ptr params, - bool allowed); - void GotChannelID(std::unique_ptr params, - const std::string& tls_channel_id); - - // Enqueues a message on a pending channel. - void EnqueuePendingMessage(int port_id, int channel_id, - const Message& message); - - // Enqueues a message on a channel pending on a lazy background page load. - void EnqueuePendingMessageForLazyBackgroundLoad(int port_id, - int channel_id, - const Message& message); - - // Immediately sends a message to the given port. - void DispatchMessage(int port_id, MessageChannel* channel, - const Message& message); - - // Potentially registers a pending task with the LazyBackgroundTaskQueue - // to open a channel. Returns true if a task was queued. - // Takes ownership of |params| if true is returned. - bool MaybeAddPendingLazyBackgroundPageOpenChannelTask( - content::BrowserContext* context, - const Extension* extension, - std::unique_ptr* params, - const PendingMessagesQueue& pending_messages); - - // Callbacks for LazyBackgroundTaskQueue tasks. The queue passes in an - // ExtensionHost to its task callbacks, though some of our callbacks don't - // use that argument. - void PendingLazyBackgroundPageOpenChannel( - std::unique_ptr params, - int source_process_id, - extensions::ExtensionHost* host); - void PendingLazyBackgroundPageClosePort(int port_id, - int process_id, - int routing_id, - bool force_close, - const std::string& error_message, - extensions::ExtensionHost* host) { - if (host) - ClosePortImpl(port_id, process_id, routing_id, force_close, - error_message); - } - void PendingLazyBackgroundPagePostMessage(int port_id, - const Message& message, - extensions::ExtensionHost* host) { - if (host) - PostMessage(port_id, message); - } - - // Immediate dispatches a disconnect to |source| for |port_id|. Sets source's - // runtime.lastMessage to |error_message|, if any. - void DispatchOnDisconnect(content::RenderFrameHost* source, - int port_id, - const std::string& error_message); - - void DispatchPendingMessages(const PendingMessagesQueue& queue, - int channel_id); - - // BrowserContextKeyedAPI implementation. - static const char* service_name() { - return "MessageService"; - } - static const bool kServiceRedirectedInIncognito = true; - static const bool kServiceIsCreatedWithBrowserContext = false; - static const bool kServiceIsNULLWhileTesting = true; - - MessageChannelMap channels_; - // A set of channel IDs waiting for TLS channel IDs to complete opening, and - // any pending messages queued to be sent on those channels. This and the - // following two maps form a pipeline where messages are queued before the - // channel they are addressed to is ready. - PendingChannelMap pending_tls_channel_id_channels_; - // A set of channel IDs waiting for user permission to cross the border - // between an incognito page and an app or extension, and any pending messages - // queued to be sent on those channels. - PendingChannelMap pending_incognito_channels_; - PendingLazyBackgroundPageChannelMap pending_lazy_background_page_channels_; - MessagePropertyProvider property_provider_; - - // Weak pointer. Guaranteed to outlive this class. - LazyBackgroundTaskQueue* lazy_background_task_queue_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(MessageService); -}; - -} // namespace extensions - -#endif // ATOM_BROWSER_EXTENSIONS_API_MESSAGING_MESSAGE_SERVICE_H_ diff --git a/atom/browser/extensions/atom_browser_client_extensions_part.cc b/atom/browser/extensions/atom_browser_client_extensions_part.cc index 83dddc9e07..10f374ba96 100644 --- a/atom/browser/extensions/atom_browser_client_extensions_part.cc +++ b/atom/browser/extensions/atom_browser_client_extensions_part.cc @@ -8,8 +8,8 @@ #include "atom/browser/api/atom_api_extension.h" #include "atom/common/api/api_messages.h" #include "base/command_line.h" -#include "brave/browser/brave_browser_context.h" #include "chrome/browser/renderer_host/chrome_extension_message_filter.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_registry_simple.h" @@ -20,40 +20,34 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" +#include "content/public/common/content_switches.h" #include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/extension_message_filter.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_service_worker_message_filter.h" #include "extensions/browser/extension_system.h" +#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h" #include "extensions/browser/info_map.h" #include "extensions/browser/io_thread_extension_message_filter.h" #include "extensions/common/constants.h" #include "extensions/common/extension_l10n_util.h" +#include "extensions/common/extensions_client.h" #include "extensions/common/manifest_handlers/web_accessible_resources_info.h" #include "extensions/common/switches.h" -using brave::BraveBrowserContext; using content::BrowserContext; using content::BrowserThread; using content::BrowserURLHandler; using content::SiteInstance; +namespace extensions { + namespace { static std::map render_process_hosts_; -// Cached version of the locale so we can return the locale on the I/O -// thread. -base::LazyInstance io_thread_application_locale; - -void SetApplicationLocaleOnIOThread(const std::string& locale) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - io_thread_application_locale.Get() = locale; -} - } // namespace -namespace extensions { - AtomBrowserClientExtensionsPart::AtomBrowserClientExtensionsPart() { } @@ -162,29 +156,11 @@ bool AtomBrowserClientExtensionsPart::ShouldAllowOpenURL( } std::string AtomBrowserClientExtensionsPart::GetApplicationLocale() { - if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { - return io_thread_application_locale.Get(); - } else { - return extension_l10n_util::CurrentLocaleOrDefault(); - } + return extension_l10n_util::CurrentLocaleOrDefault(); } // static void AtomBrowserClientExtensionsPart::SetApplicationLocale(std::string locale) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - // This object is guaranteed to outlive all threads so we don't have to - // worry about the lack of refcounting and can just post as Unretained. - // - // The common case is that this function is called early in Chrome startup - // before any threads are created (it will also be called later if the user - // changes the pref). In this case, there will be no threads created and - // posting will fail. When there are no threads, we can just set the string - // without worrying about threadsafety. - if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&SetApplicationLocaleOnIOThread, locale))) { - io_thread_application_locale.Get() = locale; - } extension_l10n_util::SetProcessLocale(locale); } @@ -205,11 +181,16 @@ void AtomBrowserClientExtensionsPart::RenderProcessWillLaunch( content::RenderProcessHost* host) { int id = host->GetID(); auto context = - BraveBrowserContext::FromBrowserContext(host->GetBrowserContext()); + Profile::FromBrowserContext(host->GetBrowserContext()); host->AddFilter(new ChromeExtensionMessageFilter(id, context)); host->AddFilter(new ExtensionMessageFilter(id, context)); host->AddFilter(new IOThreadExtensionMessageFilter(id, context)); + host->AddFilter(new ExtensionsGuestViewMessageFilter(id, context)); + if (extensions::ExtensionsClient::Get() + ->ExtensionAPIEnabledInExtensionServiceWorkers()) { + host->AddFilter(new ExtensionServiceWorkerMessageFilter(id, context)); + } extension_web_request_api_helpers::SendExtensionWebRequestStatusToHost(host); auto user_prefs_registrar = context->user_prefs_change_registrar(); @@ -222,6 +203,31 @@ void AtomBrowserClientExtensionsPart::RenderProcessWillLaunch( UpdateContentSettingsForHost(host->GetID()); } +// static +GURL AtomBrowserClientExtensionsPart::GetEffectiveURL( + BrowserContext* context, const GURL& url) { + // If the input |url| is part of an installed app, the effective URL is an + // extension URL with the ID of that extension as the host. This has the + // effect of grouping apps together in a common SiteInstance. + ExtensionRegistry* registry = ExtensionRegistry::Get(context); + if (!registry) + return url; + + const Extension* extension = + registry->enabled_extensions().GetHostedAppByURL(url); + if (!extension) + return url; + + // Bookmark apps do not use the hosted app process model, and should be + // treated as normal URLs. + if (extension->from_bookmark()) + return url; + + // If the URL is part of an extension's web extent, convert it to an + // extension URL. + return extension->GetResourceURL(url.path()); +} + void AtomBrowserClientExtensionsPart::UpdateContentSettingsForHost( int render_process_id) { auto host = content::RenderProcessHost::FromID(render_process_id); @@ -321,6 +327,13 @@ void AtomBrowserClientExtensionsPart:: DCHECK(context); if (ProcessMap::Get(context)->Contains(process->GetID())) { command_line->AppendSwitch(switches::kExtensionProcess); +#if defined(ENABLE_WEBRTC) + command_line->AppendSwitch(::switches::kEnableWebRtcHWH264Encoding); +#endif + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableMojoSerialService)) { + command_line->AppendSwitch(switches::kEnableMojoSerialService); + } } } diff --git a/atom/browser/extensions/atom_browser_client_extensions_part.h b/atom/browser/extensions/atom_browser_client_extensions_part.h index 17d7e8746f..a626f5248f 100644 --- a/atom/browser/extensions/atom_browser_client_extensions_part.h +++ b/atom/browser/extensions/atom_browser_client_extensions_part.h @@ -71,6 +71,8 @@ class AtomBrowserClientExtensionsPart { content::RenderProcessHost* process, content::BrowserContext* browser_context); std::string GetApplicationLocale(); + static GURL GetEffectiveURL( + content::BrowserContext*, const GURL& url); private: void UpdateContentSettings(); diff --git a/atom/browser/extensions/atom_extension_system.cc b/atom/browser/extensions/atom_extension_system.cc index 6a8098be41..d128029505 100644 --- a/atom/browser/extensions/atom_extension_system.cc +++ b/atom/browser/extensions/atom_extension_system.cc @@ -17,6 +17,7 @@ #include "base/path_service.h" #include "base/version.h" #include "components/component_updater/component_updater_paths.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" @@ -46,7 +47,7 @@ namespace extensions { // AtomExtensionSystem::Shared // -AtomExtensionSystem::Shared::Shared(brave::BraveBrowserContext* browser_context) +AtomExtensionSystem::Shared::Shared(Profile* browser_context) : registry_(ExtensionRegistry::Get(browser_context)), browser_context_(browser_context), extension_prefs_(ExtensionPrefs::Get(browser_context_)) { @@ -359,6 +360,24 @@ const Extension* AtomExtensionSystem::Shared::AddExtension( return extension; } +void AtomExtensionSystem::Shared::RegisterContentSettings( + HostContentSettingsMap* host_content_settings_map) { + TRACE_EVENT0("browser,startup", "ExtensionService::RegisterContentSettings"); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // host_content_settings_map->RegisterProvider( + // HostContentSettingsMap::INTERNAL_EXTENSION_PROVIDER, + // std::unique_ptr( + // new content_settings::InternalExtensionProvider(browser_context_))); + + // host_content_settings_map->RegisterProvider( + // HostContentSettingsMap::CUSTOM_EXTENSION_PROVIDER, + // std::unique_ptr( + // new content_settings::CustomExtensionProvider( + // extensions::ContentSettingsService::Get(browser_context_) + // ->content_settings_store(), + // browser_context_->GetOriginalProfile() != browser_context_))); +}; + void AtomExtensionSystem::Shared::NotifyExtensionLoaded( const Extension* extension) { AtomExtensionSystemFactory::GetInstance()-> @@ -472,12 +491,12 @@ void AtomExtensionSystem::Shared::Observe(int type, // AtomExtensionSystem // AtomExtensionSystem::AtomExtensionSystem( - brave::BraveBrowserContext* browser_context) + Profile* browser_context) : browser_context_(browser_context) { shared_ = AtomExtensionSystemSharedFactory::GetForBrowserContext(browser_context_); - if (browser_context_->original_context() == browser_context_) { + if (browser_context_->GetOriginalProfile() == browser_context_) { shared_->InitPrefs(); } } @@ -489,7 +508,7 @@ void AtomExtensionSystem::Shutdown() { } void AtomExtensionSystem::InitForRegularProfile(bool extensions_enabled) { - DCHECK(browser_context_->original_context() == browser_context_); + DCHECK(browser_context_->GetOriginalProfile() == browser_context_); if (shared_user_script_master() || extension_service()) return; // Already initialized. diff --git a/atom/browser/extensions/atom_extension_system.h b/atom/browser/extensions/atom_extension_system.h index 83de7d1dba..736160e040 100644 --- a/atom/browser/extensions/atom_extension_system.h +++ b/atom/browser/extensions/atom_extension_system.h @@ -13,6 +13,8 @@ #include "extensions/browser/extension_system.h" #include "extensions/common/one_shot_event.h" +class HostContentSettingsMap; + class ExtensionService { public: virtual bool IsExtensionEnabled(const std::string& extension_id) const = 0; @@ -30,6 +32,10 @@ class ExtensionService { virtual const extensions::Extension* GetExtensionById( const std::string& id, bool include_disabled) const = 0; + + virtual void RegisterContentSettings( + HostContentSettingsMap* host_content_settings_map) = 0; + virtual bool is_ready() = 0; }; @@ -42,10 +48,9 @@ class ExtensionPrefs; class ExtensionRegistry; class ValueStoreFactory; - class AtomExtensionSystem : public ExtensionSystem { public: - explicit AtomExtensionSystem(brave::BraveBrowserContext* browser_context); + explicit AtomExtensionSystem(Profile* browser_context); ~AtomExtensionSystem() override; // KeyedService implementation. @@ -86,7 +91,7 @@ class AtomExtensionSystem : public ExtensionSystem { public content::NotificationObserver, public base::SupportsWeakPtr { public: - explicit Shared(brave::BraveBrowserContext* browser_context); + explicit Shared(Profile* browser_context); ~Shared() override; void InitPrefs(); @@ -118,6 +123,8 @@ class AtomExtensionSystem : public ExtensionSystem { void EnableExtension(const std::string& extension_id) override; void DisableExtension(const std::string& extension_id, int disable_reasons) override; + void RegisterContentSettings( + HostContentSettingsMap* host_content_settings_map) override; // Removes the extension with the given id from the list of // terminated extensions if it is there. @@ -149,7 +156,7 @@ class AtomExtensionSystem : public ExtensionSystem { ExtensionRegistry* registry_; // Not owned. - brave::BraveBrowserContext* browser_context_; + Profile* browser_context_; extensions::ExtensionPrefs* extension_prefs_; @@ -177,7 +184,7 @@ class AtomExtensionSystem : public ExtensionSystem { OneShotEvent ready_; }; - brave::BraveBrowserContext* browser_context_; + Profile* browser_context_; Shared* shared_; diff --git a/atom/browser/extensions/atom_extension_system_factory.cc b/atom/browser/extensions/atom_extension_system_factory.cc index 98ae7b1c5e..18cb2e1109 100644 --- a/atom/browser/extensions/atom_extension_system_factory.cc +++ b/atom/browser/extensions/atom_extension_system_factory.cc @@ -52,21 +52,21 @@ AtomExtensionSystemSharedFactory::~AtomExtensionSystemSharedFactory() { KeyedService* AtomExtensionSystemSharedFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - return new AtomExtensionSystem::Shared( - static_cast(context)); + return new AtomExtensionSystem::Shared(static_cast(context)); } -content::BrowserContext* - AtomExtensionSystemSharedFactory::GetBrowserContextToUse( - content::BrowserContext* context) const { +content::BrowserContext* AtomExtensionSystemSharedFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { // Redirected in incognito. return ExtensionsBrowserClient::Get()->GetOriginalContext(context); } -// ExtensionSystemFactory +// AtomExtensionSystemFactory + +// static ExtensionSystem* AtomExtensionSystemFactory::GetForBrowserContext( content::BrowserContext* context) { - return static_cast( + return static_cast( GetInstance()->GetServiceForBrowserContext(context, true)); } @@ -79,7 +79,7 @@ AtomExtensionSystemFactory::AtomExtensionSystemFactory() : ExtensionSystemProvider("ExtensionSystem", BrowserContextDependencyManager::GetInstance()) { DCHECK(ExtensionsBrowserClient::Get()) - << "ExtensionSystemFactory must be initialized after BrowserProcess"; + << "AtomExtensionSystemFactory must be initialized after BrowserProcess"; DependsOn(AtomExtensionSystemSharedFactory::GetInstance()); } @@ -88,13 +88,12 @@ AtomExtensionSystemFactory::~AtomExtensionSystemFactory() { KeyedService* AtomExtensionSystemFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - return new AtomExtensionSystem( - static_cast(context)); + return new AtomExtensionSystem(static_cast(context)); } content::BrowserContext* AtomExtensionSystemFactory::GetBrowserContextToUse( content::BrowserContext* context) const { - // Use a separate instance for incognito. + // Separate instance in incognito. return context; } diff --git a/atom/browser/extensions/atom_extensions_browser_client.cc b/atom/browser/extensions/atom_extensions_browser_client.cc index 8e38452b27..5633e6b86d 100644 --- a/atom/browser/extensions/atom_extensions_browser_client.cc +++ b/atom/browser/extensions/atom_extensions_browser_client.cc @@ -62,6 +62,11 @@ #include "extensions/common/file_util.h" #include "net/url_request/url_request_simple_job.h" + +// #include "chrome/browser/extensions/api/generated_api_registration.h" +#include "electron/brave/common/extensions/api/generated_api_registration.h" +#include "extensions/browser/api/generated_api_registration.h" + namespace { bool IsWhitelistedForIncognito(const extensions::Extension* extension) { @@ -428,103 +433,109 @@ AtomExtensionsBrowserClient::GetExtensionSystemFactory() { void AtomExtensionsBrowserClient::RegisterExtensionFunctions( ExtensionFunctionRegistry* registry) const { - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction< - ManagementGetPermissionWarningsByManifestFunction>(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); - registry->RegisterFunction(); + api::GeneratedFunctionRegistry::RegisterAll(registry); + + api::BraveGeneratedFunctionRegistry::RegisterAll(registry); + // Generated APIs from Chrome. + // api::ChromeGeneratedFunctionRegistry::RegisterAll(registry); + + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction< + // ManagementGetPermissionWarningsByManifestFunction>(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); + // registry->RegisterFunction(); } std::unique_ptr diff --git a/atom/browser/extensions/atom_extensions_network_delegate.cc b/atom/browser/extensions/atom_extensions_network_delegate.cc index 528e86aa93..9765719271 100644 --- a/atom/browser/extensions/atom_extensions_network_delegate.cc +++ b/atom/browser/extensions/atom_extensions_network_delegate.cc @@ -5,6 +5,7 @@ #include "atom/browser/extensions/atom_extensions_network_delegate.h" #include "base/stl_util.h" +#include "chrome/browser/profiles/profile.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/resource_request_info.h" #include "extensions/browser/api/web_request/web_request_api.h" @@ -20,7 +21,7 @@ bool g_accept_all_cookies = true; } AtomExtensionsNetworkDelegate::AtomExtensionsNetworkDelegate( - content::BrowserContext* browser_context) { + Profile* browser_context) { browser_context_ = browser_context; } diff --git a/atom/browser/extensions/atom_extensions_network_delegate.h b/atom/browser/extensions/atom_extensions_network_delegate.h index 603cd57845..bc0f31febc 100644 --- a/atom/browser/extensions/atom_extensions_network_delegate.h +++ b/atom/browser/extensions/atom_extensions_network_delegate.h @@ -10,9 +10,7 @@ #include "atom/browser/extensions/atom_extension_system_factory.h" #include "atom/browser/net/atom_network_delegate.h" -namespace content { -class BrowserContext; -} +class Profile; namespace extensions { @@ -21,7 +19,7 @@ class InfoMap; class AtomExtensionsNetworkDelegate : public atom::AtomNetworkDelegate { public: explicit AtomExtensionsNetworkDelegate( - content::BrowserContext* browser_context); + Profile* browser_context); ~AtomExtensionsNetworkDelegate() override; void set_extension_info_map(extensions::InfoMap* extension_info_map); diff --git a/atom/browser/extensions/atom_process_manager_delegate.cc b/atom/browser/extensions/atom_process_manager_delegate.cc index 5feba56ced..3f323b6058 100644 --- a/atom/browser/extensions/atom_process_manager_delegate.cc +++ b/atom/browser/extensions/atom_process_manager_delegate.cc @@ -8,17 +8,15 @@ #include "atom/browser/browser.h" #include "base/command_line.h" #include "base/logging.h" -#include "brave/browser/brave_browser_context.h" #include "build/build_config.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/profiles/profile.h" #include "content/public/browser/notification_service.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/process_manager_factory.h" #include "extensions/common/one_shot_event.h" -using brave::BraveBrowserContext; - namespace extensions { AtomProcessManagerDelegate::AtomProcessManagerDelegate() { @@ -49,15 +47,15 @@ void AtomProcessManagerDelegate::Observe( const content::NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_PROFILE_CREATED: { - BraveBrowserContext* browser_context = - content::Source(source).ptr(); - OnProfileCreated(browser_context); + Profile* profile = + content::Source(source).ptr(); + OnProfileCreated(profile); break; } case chrome::NOTIFICATION_PROFILE_DESTROYED: { - BraveBrowserContext* browser_context = - content::Source(source).ptr(); - OnProfileDestroyed(browser_context); + Profile* profile = + content::Source(source).ptr(); + OnProfileDestroyed(profile); break; } default: @@ -66,25 +64,25 @@ void AtomProcessManagerDelegate::Observe( } void AtomProcessManagerDelegate::OnProfileCreated( - BraveBrowserContext* browser_context) { - // Incognito browser_contexts are handled by their original browser_context. - if (browser_context->IsOffTheRecord() || browser_context->HasParentContext()) + Profile* profile) { + // Profiles are handled by their original profile. + if (profile->GetOriginalProfile() != profile) return; - // The browser_context can be created before the extension system is ready. - if (!ExtensionSystem::Get(browser_context)->ready().is_signaled()) + // The profile can be created before the extension system is ready. + if (!ExtensionSystem::Get(profile)->ready().is_signaled()) return; - // The browser_context might have been initialized asynchronously (in + // The profile might have been initialized asynchronously (in // parallel with extension system startup). Now that initialization is // complete the ProcessManager can load deferred background pages. - ProcessManager::Get(browser_context)->MaybeCreateStartupBackgroundHosts(); + ProcessManager::Get(profile)->MaybeCreateStartupBackgroundHosts(); } void AtomProcessManagerDelegate::OnProfileDestroyed( - BraveBrowserContext* browser_context) { + Profile* profile) { ProcessManager* manager = - ProcessManagerFactory::GetForBrowserContextIfExists(browser_context); + ProcessManagerFactory::GetForBrowserContextIfExists(profile); if (manager) { manager->CloseBackgroundHosts(); } diff --git a/atom/browser/extensions/atom_process_manager_delegate.h b/atom/browser/extensions/atom_process_manager_delegate.h index c937840695..095e4feb9c 100644 --- a/atom/browser/extensions/atom_process_manager_delegate.h +++ b/atom/browser/extensions/atom_process_manager_delegate.h @@ -15,9 +15,7 @@ namespace atom { class Browser; } -namespace brave { -class BraveBrowserContext; -} +class Profile; namespace extensions { @@ -42,8 +40,8 @@ class AtomProcessManagerDelegate : public ProcessManagerDelegate, private: // Notification handlers. void OnBrowserWindowReady(atom::Browser* browser); - void OnProfileCreated(brave::BraveBrowserContext* profile); - void OnProfileDestroyed(brave::BraveBrowserContext* profile); + void OnProfileCreated(Profile* profile); + void OnProfileDestroyed(Profile* profile); content::NotificationRegistrar registrar_; diff --git a/atom/browser/extensions/shared_user_script_master.cc b/atom/browser/extensions/shared_user_script_master.cc index 29dd24033e..4278a77528 100644 --- a/atom/browser/extensions/shared_user_script_master.cc +++ b/atom/browser/extensions/shared_user_script_master.cc @@ -34,26 +34,30 @@ void SharedUserScriptMaster::OnExtensionUnloaded( content::BrowserContext* browser_context, const Extension* extension, UnloadedExtensionInfo::Reason reason) { - loader_.RemoveScripts(GetScriptsMetadata(extension)); + const UserScriptList& script_list = + ContentScriptsInfo::GetContentScripts(extension); + std::set scripts_to_remove; + for (const std::unique_ptr& script : script_list) + scripts_to_remove.insert(UserScriptIDPair(script->id(), script->host_id())); + loader_.RemoveScripts(scripts_to_remove); } -const std::set SharedUserScriptMaster::GetScriptsMetadata( +std::unique_ptr SharedUserScriptMaster::GetScriptsMetadata( const Extension* extension) { bool incognito_enabled = AtomExtensionsBrowserClient::IsIncognitoEnabled( extension->id(), browser_context_); const UserScriptList& script_list = ContentScriptsInfo::GetContentScripts(extension); - std::set script_set; - for (UserScriptList::const_iterator it = script_list.begin(); - it != script_list.end(); - ++it) { - UserScript script = *it; - script.set_incognito_enabled(incognito_enabled); - script_set.insert(script); + std::unique_ptr script_vector(new UserScriptList()); + script_vector->reserve(script_list.size()); + for (const std::unique_ptr& script : script_list) { + std::unique_ptr script_copy = + UserScript::CopyMetadataFrom(*script); + script_copy->set_incognito_enabled(incognito_enabled); + script_vector->push_back(std::move(script_copy)); } - - return script_set; + return script_vector; } } // namespace extensions diff --git a/atom/browser/extensions/shared_user_script_master.h b/atom/browser/extensions/shared_user_script_master.h index 195ded690f..1c05c43104 100644 --- a/atom/browser/extensions/shared_user_script_master.h +++ b/atom/browser/extensions/shared_user_script_master.h @@ -42,7 +42,7 @@ class SharedUserScriptMaster : public ExtensionRegistryObserver { // Gets an extension's scripts' metadata; i.e., gets a list of UserScript // objects that contains script info, but not the contents of the scripts. - const std::set GetScriptsMetadata(const Extension* extension); + std::unique_ptr GetScriptsMetadata(const Extension* extension); // Script loader that handles loading contents of scripts into shared memory // and notifying renderers of scripts in shared memory. diff --git a/atom/browser/extensions/tab_helper.cc b/atom/browser/extensions/tab_helper.cc index 162bdda14f..c0363e4c04 100644 --- a/atom/browser/extensions/tab_helper.cc +++ b/atom/browser/extensions/tab_helper.cc @@ -191,7 +191,7 @@ bool TabHelper::ExecuteScriptInTab(mate::Arguments* args) { } ExecuteScript(extension_id, std::move(copy), result, callback, file_url, - true, file.empty() ? code_string : file); + true, base::MakeUnique(file.empty() ? code_string : file)); return true; } @@ -202,7 +202,7 @@ void TabHelper::ExecuteScript( extensions::ScriptExecutor::ExecuteScriptCallback callback, const GURL& file_url, bool success, - const std::string& code_string) { + std::unique_ptr code_string) { extensions::ScriptExecutor* executor = script_executor(); bool all_frames = false; @@ -233,7 +233,7 @@ void TabHelper::ExecuteScript( executor->ExecuteScript( HostID(HostID::EXTENSIONS, extension_id), extensions::ScriptExecutor::JAVASCRIPT, - code_string, + *code_string, frame_scope, frame_id, match_about_blank ? extensions::ScriptExecutor::MATCH_ABOUT_BLANK @@ -249,13 +249,22 @@ void TabHelper::ExecuteScript( } // static -content::WebContents* TabHelper::GetTabById(int32_t tab_id, - content::BrowserContext* browser_context) { +content::WebContents* TabHelper::GetTabById(int32_t tab_id) { content::RenderViewHost* rvh = content::RenderViewHost::FromID(render_view_map_[tab_id].first, render_view_map_[tab_id].second); if (rvh) { - auto contents = content::WebContents::FromRenderViewHost(rvh); + return content::WebContents::FromRenderViewHost(rvh); + } else { + return NULL; + } +} + +// static +content::WebContents* TabHelper::GetTabById(int32_t tab_id, + content::BrowserContext* browser_context) { + auto contents = GetTabById(tab_id); + if (contents) { if (extensions::ExtensionsBrowserClient::Get()->IsSameContext( browser_context, contents->GetBrowserContext())) { diff --git a/atom/browser/extensions/tab_helper.h b/atom/browser/extensions/tab_helper.h index 11ba25d683..0e9b377cfe 100644 --- a/atom/browser/extensions/tab_helper.h +++ b/atom/browser/extensions/tab_helper.h @@ -79,6 +79,7 @@ class TabHelper : public content::WebContentsObserver, static content::WebContents* GetTabById(int tab_id, content::BrowserContext* browser_context); + static content::WebContents* GetTabById(int32_t tab_id); static int32_t IdForWindowContainingTab( const content::WebContents* tab); @@ -97,7 +98,7 @@ class TabHelper : public content::WebContentsObserver, extensions::ScriptExecutor::ExecuteScriptCallback callback, const GURL& file_url, bool success, - const std::string& code_string); + std::unique_ptr code_string); // content::WebContentsObserver overrides. void RenderViewCreated(content::RenderViewHost* render_view_host) override; diff --git a/atom/browser/importer/profile_writer.cc b/atom/browser/importer/profile_writer.cc index b9f00bc5dc..84bdfa38f5 100644 --- a/atom/browser/importer/profile_writer.cc +++ b/atom/browser/importer/profile_writer.cc @@ -474,34 +474,6 @@ void ProfileWriter::AddAutofillFormDataEntries( } } -void ProfileWriter::AddCookies( - const std::vector& cookies) { - if (importer_) { - base::ListValue imported_cookies; - for (const ImportedCookieEntry& cookie_entry : cookies) { - base::DictionaryValue* cookie = new base::DictionaryValue(); - base::string16 url; - if (cookie_entry.httponly) { - url.append(base::UTF8ToUTF16("http://")); - url.append(cookie_entry.host); - } else { - url.append(base::UTF8ToUTF16("https://")); - url.append(cookie_entry.host); - } - cookie->SetString("url", url); - cookie->SetString("domain", cookie_entry.domain); - cookie->SetString("name", cookie_entry.name); - cookie->SetString("value", cookie_entry.value); - cookie->SetString("path", cookie_entry.path); - cookie->SetInteger("expiry_date", cookie_entry.expiry_date.ToDoubleT()); - cookie->SetBoolean("secure", cookie_entry.secure); - cookie->SetBoolean("httponly", cookie_entry.httponly); - imported_cookies.Append(cookie); - } - importer_->Emit("add-cookies", imported_cookies); - } -} - void ProfileWriter::Initialize(atom::api::Importer* importer) { importer_ = importer; } diff --git a/atom/browser/lib/bluetooth_chooser.cc b/atom/browser/lib/bluetooth_chooser.cc index 2ed21bd333..6f8c7c37ca 100644 --- a/atom/browser/lib/bluetooth_chooser.cc +++ b/atom/browser/lib/bluetooth_chooser.cc @@ -89,11 +89,32 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) { } } -void BluetoothChooser::AddDevice(const std::string& device_id, - const base::string16& device_name) { - DeviceInfo info = {device_id, device_name}; +void BluetoothChooser::AddOrUpdateDevice(const std::string& device_id, + bool should_update_name, + const base::string16& device_name, + bool is_gatt_connected, + bool is_paired, + int signal_strength_level) { + + for (auto it = device_list_.begin(); it != device_list_.end(); ++it) { + if (it->device_id == device_id) { + if (should_update_name) { + base::string16 previous_device_name = it->device_name; + it->device_name = device_name; + } + + // When Bluetooth device scanning stops, the |signal_strength_level| + // is -1, and in this case, should still use the previously stored + // signal strength level value. + if (signal_strength_level != -1) + it->signal_strength_level = signal_strength_level; + return; + } + } + + DeviceInfo info = {device_id, device_name, signal_strength_level}; device_list_.push_back(info); -} +}; void BluetoothChooser::RemoveDevice(const std::string& device_id) { for (auto it = device_list_.begin(); it != device_list_.end(); ++it) { diff --git a/atom/browser/lib/bluetooth_chooser.h b/atom/browser/lib/bluetooth_chooser.h index 615dfcb8c6..f9566e980c 100644 --- a/atom/browser/lib/bluetooth_chooser.h +++ b/atom/browser/lib/bluetooth_chooser.h @@ -18,6 +18,7 @@ class BluetoothChooser : public content::BluetoothChooser { struct DeviceInfo { std::string device_id; base::string16 device_name; + int signal_strength_level; }; explicit BluetoothChooser(api::WebContents* contents, @@ -27,8 +28,13 @@ class BluetoothChooser : public content::BluetoothChooser { // content::BluetoothChooser: void SetAdapterPresence(AdapterPresence presence) override; void ShowDiscoveryState(DiscoveryState state) override; - void AddDevice(const std::string& device_id, - const base::string16& device_name) override; + void AddOrUpdateDevice(const std::string& device_id, + bool should_update_name, + const base::string16& device_name, + bool is_gatt_connected, + bool is_paired, + int signal_strength_level) override; + void RemoveDevice(const std::string& device_id) override; private: diff --git a/atom/browser/mac/atom_application.mm b/atom/browser/mac/atom_application.mm index 159852a59a..3ae27d8b6d 100644 --- a/atom/browser/mac/atom_application.mm +++ b/atom/browser/mac/atom_application.mm @@ -5,7 +5,9 @@ #import "atom/browser/mac/atom_application.h" #include "atom/browser/browser.h" -#include "base/auto_reset.h" +// #include "base/auto_reset.h" +#include "base/mac/call_with_eh_frame.h" +#import "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" #include "content/public/browser/browser_accessibility_state.h" @@ -20,8 +22,10 @@ - (BOOL)isHandlingSendEvent { } - (void)sendEvent:(NSEvent*)event { - base::AutoReset scoper(&handlingSendEvent_, YES); - [super sendEvent:event]; + base::mac::CallWithEHFrame(^{ + base::mac::ScopedSendingEvent sendingEventScoper; + [super sendEvent:event]; + }); } - (void)setHandlingSendEvent:(BOOL)handlingSendEvent { diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 7280533237..b84e00c160 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -12,6 +12,7 @@ #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/browser.h" #include "atom/browser/unresponsive_suppressor.h" +#include "atom/browser/web_contents_preferences.h" #include "atom/browser/window_list.h" #include "atom/common/api/api_messages.h" #include "atom/common/native_mate_converters/file_path_converter.h" @@ -56,7 +57,6 @@ NativeWindow::NativeWindow( NativeWindow* parent) : content::WebContentsObserver(inspectable_web_contents->GetWebContents()), has_frame_(true), - transparent_(false), enable_larger_than_screen_(false), is_closed_(false), sheet_offset_x_(0.0), @@ -67,7 +67,6 @@ NativeWindow::NativeWindow( inspectable_web_contents_(inspectable_web_contents), weak_factory_(this) { options.Get(options::kFrame, &has_frame_); - options.Get(options::kTransparent, &transparent_); options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_); if (parent) @@ -86,11 +85,6 @@ NativeWindow::NativeWindow( prefs->use_bitmaps = params.use_bitmaps; prefs->subpixel_rendering = params.subpixel_rendering; #endif - - // Tell the content module to initialize renderer widget with transparent - // mode. - ui::GpuSwitchingManager::SetTransparent(transparent_); - WindowList::AddWindow(this); } @@ -188,7 +182,7 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) { std::string color; if (options.Get(options::kBackgroundColor, &color)) { SetBackgroundColor(color); - } else if (!transparent()) { + } else { // For normal window, use white as default background. SetBackgroundColor("#FFFF"); } @@ -557,18 +551,6 @@ std::unique_ptr NativeWindow::DraggableRegionsToSkRegion( return sk_region; } -void NativeWindow::RenderViewCreated( - content::RenderViewHost* render_view_host) { - if (!transparent_) - return; - - content::RenderWidgetHostImpl* impl = content::RenderWidgetHostImpl::FromID( - render_view_host->GetProcess()->GetID(), - render_view_host->GetRoutingID()); - if (impl) - impl->SetBackgroundOpaque(false); -} - void NativeWindow::BeforeUnloadDialogCancelled() { WindowList::WindowCloseCancelled(this); @@ -587,7 +569,7 @@ void NativeWindow::DidFirstVisuallyNonEmptyPaint() { view->SetSize(GetContentSize()); // Emit the ReadyToShow event in next tick in case of pending drawing work. - base::MessageLoop::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&NativeWindow::NotifyReadyToShow, GetWeakPtr())); } @@ -618,7 +600,7 @@ void NativeWindow::ScheduleUnresponsiveEvent(int ms) { window_unresposive_closure_.Reset( base::Bind(&NativeWindow::NotifyWindowUnresponsive, weak_factory_.GetWeakPtr())); - base::MessageLoop::current()->PostDelayedTask( + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, window_unresposive_closure_.callback(), base::TimeDelta::FromMilliseconds(ms)); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 734584f8c7..393a8d8310 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -21,6 +21,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" #include "extensions/browser/app_window/size_constraints.h" +#include "ui/base/ui_base_types.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" @@ -77,14 +78,15 @@ class NativeWindow : public base::SupportsUserData, virtual bool IsEnabled() = 0; virtual void Maximize() = 0; virtual void Unmaximize() = 0; - virtual bool IsMaximized() = 0; + virtual bool IsMaximized() const = 0; virtual void Minimize() = 0; virtual void Restore() = 0; - virtual bool IsMinimized() = 0; + virtual bool IsMinimized() const = 0; virtual void SetFullScreen(bool fullscreen) = 0; virtual bool IsFullscreen() const = 0; + virtual void SetBounds(const gfx::Rect& bounds) = 0; virtual void SetBounds(const gfx::Rect& bounds, bool animate = false) = 0; - virtual gfx::Rect GetBounds() = 0; + virtual gfx::Rect GetBounds() const = 0; virtual void SetSize(const gfx::Size& size, bool animate = false); virtual gfx::Size GetSize(); virtual void SetPosition(const gfx::Point& position, bool animate = false); @@ -119,7 +121,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetClosable(bool closable) = 0; virtual bool IsClosable() = 0; virtual void SetAlwaysOnTop(bool top) = 0; - virtual bool IsAlwaysOnTop() = 0; + virtual bool IsAlwaysOnTop() const = 0; virtual void Center() = 0; virtual void SetTitle(const std::string& title) = 0; virtual std::string GetTitle() = 0; @@ -139,6 +141,8 @@ class NativeWindow : public base::SupportsUserData, virtual void SetFocusable(bool focusable); virtual void SetMenu(AtomMenuModel* menu); virtual void SetParentWindow(NativeWindow* parent); + virtual gfx::Rect GetRestoredBounds() const = 0; + virtual ui::WindowShowState GetRestoredState() const = 0; virtual gfx::NativeWindow GetNativeWindow() = 0; virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0; @@ -233,7 +237,6 @@ class NativeWindow : public base::SupportsUserData, bool has_frame() const { return has_frame_; } void set_has_frame(bool has_frame) { has_frame_ = has_frame; } - bool transparent() const { return transparent_; } SkRegion* draggable_region() const { return draggable_region_.get(); } bool enable_larger_than_screen() const { return enable_larger_than_screen_; } @@ -259,7 +262,6 @@ class NativeWindow : public base::SupportsUserData, const std::vector& regions); // content::WebContentsObserver: - void RenderViewCreated(content::RenderViewHost* render_view_host) override; void BeforeUnloadDialogCancelled() override; void DidFirstVisuallyNonEmptyPaint() override; bool OnMessageReceived(const IPC::Message& message) override; @@ -277,9 +279,6 @@ class NativeWindow : public base::SupportsUserData, // Whether window has standard frame. bool has_frame_; - // Whether window is transparent. - bool transparent_; - // For custom drag, the whole window is non-draggable and the draggable region // has to been explicitly provided. std::unique_ptr draggable_region_; // used in custom drag. diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 10b3516597..926183eff6 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -40,14 +40,16 @@ class NativeWindowMac : public NativeWindow, bool IsEnabled() override; void Maximize() override; void Unmaximize() override; - bool IsMaximized() override; + bool IsActive() const; + bool IsMaximized() const override; void Minimize() override; void Restore() override; - bool IsMinimized() override; + bool IsMinimized() const override; void SetFullScreen(bool fullscreen) override; bool IsFullscreen() const override; + void SetBounds(const gfx::Rect& bounds) override; void SetBounds(const gfx::Rect& bounds, bool animate = false) override; - gfx::Rect GetBounds() override; + gfx::Rect GetBounds() const override; void SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; @@ -65,7 +67,7 @@ class NativeWindowMac : public NativeWindow, void SetClosable(bool closable) override; bool IsClosable() override; void SetAlwaysOnTop(bool top) override; - bool IsAlwaysOnTop() override; + bool IsAlwaysOnTop() const override; void Center() override; void SetTitle(const std::string& title) override; std::string GetTitle() override; @@ -84,6 +86,8 @@ class NativeWindowMac : public NativeWindow, void SetContentProtection(bool enable) override; void SetParentWindow(NativeWindow* parent) override; gfx::NativeWindow GetNativeWindow() override; + gfx::Rect GetRestoredBounds() const override; + ui::WindowShowState GetRestoredState() const override; gfx::AcceleratedWidget GetAcceleratedWidget() override; void SetProgressBar(double progress, const ProgressState state) override; void SetOverlayIcon(const gfx::Image& overlay, @@ -160,6 +164,10 @@ class NativeWindowMac : public NativeWindow, // The "titleBarStyle" option. TitleBarStyle title_bar_style_; + bool is_maximized_; + bool is_fullscreen_; + NSRect restored_bounds_; + DISALLOW_COPY_AND_ASSIGN(NativeWindowMac); }; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 87353da7f8..ff37bb5ce5 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -206,7 +206,7 @@ - (void)windowDidEnterFullScreen:(NSNotification*)notification { // titlebar is expected to be empty, but after entering fullscreen mode we // have to set one, because title bar is visible here. NSWindow* window = shell_->GetNativeWindow(); - if ((shell_->transparent() || !shell_->has_frame()) && + if (!shell_->has_frame() && base::mac::IsOSYosemiteOrLater() && // FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under // fullscreen mode. @@ -232,7 +232,7 @@ - (void)windowDidEnterFullScreen:(NSNotification*)notification { - (void)windowWillExitFullScreen:(NSNotification*)notification { // Restore the titlebar visibility. NSWindow* window = shell_->GetNativeWindow(); - if ((shell_->transparent() || !shell_->has_frame()) && + if (!shell_->has_frame() && base::mac::IsOSYosemiteOrLater() && shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) { [window setTitleVisibility:NSWindowTitleHidden]; @@ -578,7 +578,7 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, // The window without titlebar is treated the same with frameless window. set_has_frame(false); } - if (!useStandardWindow || transparent() || !has_frame()) { + if (!useStandardWindow || !has_frame()) { styleMask |= NSTexturedBackgroundWindowMask; } if (resizable) { @@ -601,11 +601,6 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, SetParentWindow(parent); } - if (transparent()) { - // Setting the background color to clear will also hide the shadow. - [window_ setBackgroundColor:[NSColor clearColor]]; - } - if (windowType == "desktop") { [window_ setLevel:kCGDesktopWindowLevel - 1]; [window_ setDisableKeyOrMainWindow:YES]; @@ -619,7 +614,7 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, if (options.Get(options::kFocusable, &focusable) && !focusable) [window_ setDisableKeyOrMainWindow:YES]; - if (transparent() || !has_frame()) { + if (!has_frame()) { if (base::mac::IsOSYosemiteOrLater()) { // Don't show title bar. [window_ setTitleVisibility:NSWindowTitleHidden]; @@ -789,7 +784,11 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, [window_ zoom:nil]; } -bool NativeWindowMac::IsMaximized() { +bool NativeWindowMac::IsActive() const { + return [window_ isKeyWindow]; +} + +bool NativeWindowMac::IsMaximized() const { if (([window_ styleMask] & NSResizableWindowMask) != 0) { return [window_ isZoomed]; } else { @@ -810,7 +809,7 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, [window_ deminiaturize:nil]; } -bool NativeWindowMac::IsMinimized() { +bool NativeWindowMac::IsMinimized() const { return [window_ isMiniaturized]; } @@ -825,6 +824,10 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, return [window_ styleMask] & NSFullScreenWindowMask; } +void NativeWindowMac::SetBounds(const gfx::Rect& bounds) { + SetBounds(bounds, false); +} + void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) { // Do nothing if in fullscreen mode. if (IsFullscreen()) @@ -846,7 +849,7 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, [window_ setFrame:cocoa_bounds display:YES animate:animate]; } -gfx::Rect NativeWindowMac::GetBounds() { +gfx::Rect NativeWindowMac::GetBounds() const { NSRect frame = [window_ frame]; gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; @@ -948,7 +951,7 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, [window_ setLevel:(top ? NSFloatingWindowLevel : NSNormalWindowLevel)]; } -bool NativeWindowMac::IsAlwaysOnTop() { +bool NativeWindowMac::IsAlwaysOnTop() const { return [window_ level] == NSFloatingWindowLevel; } @@ -959,7 +962,7 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, void NativeWindowMac::SetTitle(const std::string& title) { // For macOS <= 10.9, the setTitleVisibility API is not available, we have // to avoid calling setTitle for frameless window. - if (!base::mac::IsOSYosemiteOrLater() && (transparent() || !has_frame())) + if (!base::mac::IsOSYosemiteOrLater() && !has_frame()) return; [window_ setTitle:base::SysUTF8ToNSString(title)]; @@ -1069,6 +1072,23 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, return window_; } +gfx::Rect NativeWindowMac::GetRestoredBounds() const { + // Flip coordinates based on the primary screen. + NSScreen* screen = [[NSScreen screens] firstObject]; + NSRect frame = restored_bounds_; + gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); + bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); + return bounds; +} + +ui::WindowShowState NativeWindowMac::GetRestoredState() const { + if (IsMaximized()) + return ui::SHOW_STATE_MAXIMIZED; + if (IsFullscreen()) + return ui::SHOW_STATE_FULLSCREEN; + return ui::SHOW_STATE_NORMAL; +} + gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() { return inspectable_web_contents()->GetView()->GetNativeView(); } @@ -1198,17 +1218,16 @@ static bool FromV8(v8::Isolate* isolate, v8::Handle val, [view.superview addSubview:view positioned:NSWindowAbove relativeTo:nil]; } +// inspectable_web_contents()->GetView()->GetNativeView() void NativeWindowMac::InstallView() { // Make sure the bottom corner is rounded: http://crbug.com/396264. - // But do not enable it on OS X 10.9 for transparent window, otherwise a - // semi-transparent frame would show. - if (!(transparent() && base::mac::IsOSMavericks())) - [[window_ contentView] setWantsLayer:YES]; + [[window_ contentView] setWantsLayer:YES]; NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); if (has_frame()) { - [view setFrame:[[window_ contentView] bounds]]; - [[window_ contentView] addSubview:view]; + NSView* frameView = [window_ contentView]; + [view setFrame:[frameView bounds]]; + [frameView addSubview:view]; } else { // In OSX 10.10, adding subviews to the root view for the NSView hierarchy // produces warnings. To eliminate the warnings, we resize the contentView diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index d0d45fc535..b1e7f896cb 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -60,14 +60,14 @@ class NativeWindowViews : public NativeWindow, bool IsEnabled() override; void Maximize() override; void Unmaximize() override; - bool IsMaximized() override; + bool IsMaximized() const override; void Minimize() override; void Restore() override; - bool IsMinimized() override; + bool IsMinimized() const override; void SetFullScreen(bool fullscreen) override; bool IsFullscreen() const override; void SetBounds(const gfx::Rect& bounds, bool animate) override; - gfx::Rect GetBounds() override; + gfx::Rect GetBounds() const override; gfx::Rect GetContentBounds() override; gfx::Size GetContentSize() override; void SetContentSizeConstraints( @@ -85,7 +85,7 @@ class NativeWindowViews : public NativeWindow, void SetClosable(bool closable) override; bool IsClosable() override; void SetAlwaysOnTop(bool top) override; - bool IsAlwaysOnTop() override; + bool IsAlwaysOnTop() const override; void Center() override; void SetTitle(const std::string& title) override; std::string GetTitle() override; diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index 3caa3d0582..8d51668b74 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -257,7 +257,7 @@ int AtomNetworkDelegate::OnBeforeURLRequest( net::URLRequest* request, const net::CompletionCallback& callback, GURL* new_url) { - if (!ContainsKey(response_listeners_, kOnBeforeRequest)) + if (!base::ContainsKey(response_listeners_, kOnBeforeRequest)) return brightray::NetworkDelegate::OnBeforeURLRequest( request, callback, new_url); @@ -278,7 +278,7 @@ int AtomNetworkDelegate::OnBeforeStartTransaction( headers->SetHeader( DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId, client_id); - if (!ContainsKey(response_listeners_, kOnBeforeSendHeaders)) + if (!base::ContainsKey(response_listeners_, kOnBeforeSendHeaders)) return brightray::NetworkDelegate::OnBeforeStartTransaction( request, callback, headers); @@ -289,7 +289,7 @@ int AtomNetworkDelegate::OnBeforeStartTransaction( void AtomNetworkDelegate::OnStartTransaction( net::URLRequest* request, const net::HttpRequestHeaders& headers) { - if (!ContainsKey(simple_listeners_, kOnSendHeaders)) { + if (!base::ContainsKey(simple_listeners_, kOnSendHeaders)) { brightray::NetworkDelegate::OnStartTransaction(request, headers); return; } @@ -303,7 +303,7 @@ int AtomNetworkDelegate::OnHeadersReceived( const net::HttpResponseHeaders* original, scoped_refptr* override, GURL* allowed) { - if (!ContainsKey(response_listeners_, kOnHeadersReceived)) + if (!base::ContainsKey(response_listeners_, kOnHeadersReceived)) return brightray::NetworkDelegate::OnHeadersReceived( request, callback, original, override, allowed); @@ -314,7 +314,7 @@ int AtomNetworkDelegate::OnHeadersReceived( void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, const GURL& new_location) { - if (!ContainsKey(simple_listeners_, kOnBeforeRedirect)) { + if (!base::ContainsKey(simple_listeners_, kOnBeforeRedirect)) { brightray::NetworkDelegate::OnBeforeRedirect(request, new_location); return; } @@ -325,7 +325,7 @@ void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, } void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { - if (!ContainsKey(simple_listeners_, kOnResponseStarted)) { + if (!base::ContainsKey(simple_listeners_, kOnResponseStarted)) { brightray::NetworkDelegate::OnResponseStarted(request); return; } @@ -354,7 +354,7 @@ void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { return; } - if (!ContainsKey(simple_listeners_, kOnCompleted)) { + if (!base::ContainsKey(simple_listeners_, kOnCompleted)) { brightray::NetworkDelegate::OnCompleted(request, started); return; } @@ -369,7 +369,7 @@ void AtomNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { void AtomNetworkDelegate::OnErrorOccurred( net::URLRequest* request, bool started) { - if (!ContainsKey(simple_listeners_, kOnErrorOccurred)) { + if (!base::ContainsKey(simple_listeners_, kOnErrorOccurred)) { brightray::NetworkDelegate::OnCompleted(request, started); return; } @@ -424,7 +424,7 @@ template void AtomNetworkDelegate::OnListenerResultInIO( uint64_t id, T out, std::unique_ptr response) { // The request has been destroyed. - if (!ContainsKey(callbacks_, id)) + if (!base::ContainsKey(callbacks_, id)) return; ReadFromResponseObject(*response.get(), out); diff --git a/atom/browser/net/atom_url_request_job_factory.cc b/atom/browser/net/atom_url_request_job_factory.cc index 7e2eb95149..92c4eb40a9 100644 --- a/atom/browser/net/atom_url_request_job_factory.cc +++ b/atom/browser/net/atom_url_request_job_factory.cc @@ -36,7 +36,7 @@ bool AtomURLRequestJobFactory::SetProtocolHandler( return true; } - if (ContainsKey(protocol_handler_map_, scheme)) + if (base::ContainsKey(protocol_handler_map_, scheme)) return false; protocol_handler_map_[scheme] = protocol_handler.release(); return true; @@ -45,8 +45,8 @@ bool AtomURLRequestJobFactory::SetProtocolHandler( bool AtomURLRequestJobFactory::InterceptProtocol( const std::string& scheme, std::unique_ptr protocol_handler) { - if (!ContainsKey(protocol_handler_map_, scheme) || - ContainsKey(original_protocols_, scheme)) + if (!base::ContainsKey(protocol_handler_map_, scheme) || + base::ContainsKey(original_protocols_, scheme)) return false; ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme]; protocol_handler_map_[scheme] = protocol_handler.release(); @@ -74,11 +74,11 @@ ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler( bool AtomURLRequestJobFactory::HasProtocolHandler( const std::string& scheme) const { - return ContainsKey(protocol_handler_map_, scheme); + return base::ContainsKey(protocol_handler_map_, scheme); } void AtomURLRequestJobFactory::Clear() { - STLDeleteValues(&protocol_handler_map_); + base::STLDeleteValues(&protocol_handler_map_); } net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler( diff --git a/atom/browser/node_debugger.cc b/atom/browser/node_debugger.cc index 55025dd69f..eea401d0fe 100644 --- a/atom/browser/node_debugger.cc +++ b/atom/browser/node_debugger.cc @@ -11,6 +11,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/common/chrome_version.h" #include "content/public/browser/browser_thread.h" #include "net/test/embedded_test_server/tcp_listen_socket.h" @@ -51,7 +52,7 @@ NodeDebugger::NodeDebugger(v8::Isolate* isolate) base::StringToInt(port_str, &port); isolate_->SetData(kIsolateSlot, this); - v8::Debug::SetMessageHandler(DebugMessageHandler); + v8::Debug::SetMessageHandler(isolate_, DebugMessageHandler); uv_async_init(uv_default_loop(), &weak_up_ui_handle_, ProcessMessageInUI); @@ -64,7 +65,7 @@ NodeDebugger::NodeDebugger(v8::Isolate* isolate) } // Start the server in new IO thread. - thread_.message_loop()->PostTask( + thread_.message_loop()->task_runner()->PostTask( FROM_HERE, base::Bind(&NodeDebugger::StartServer, weak_factory_.GetWeakPtr(), port)); @@ -121,12 +122,13 @@ void NodeDebugger::SendConnectMessage() { "Protocol-Version: 1\r\n" "Embedding-Host: %s\r\n" "%s: 0\r\n", - v8::V8::GetVersion(), ATOM_PRODUCT_NAME, kContentLength), true); + v8::V8::GetVersion(), PRODUCT_SHORTNAME_STRING, kContentLength), true); } // static void NodeDebugger::ProcessMessageInUI(uv_async_t* handle) { - v8::Debug::ProcessDebugMessages(); + // TODO(bridiver) how do we get the isolate?? + // v8::Debug::ProcessDebugMessages(); } // static @@ -136,7 +138,7 @@ void NodeDebugger::DebugMessageHandler(const v8::Debug::Message& message) { if (self) { std::string message8(*v8::String::Utf8Value(message.GetJSON())); - self->thread_.message_loop()->PostTask( + self->thread_.message_loop()->task_runner()->PostTask( FROM_HERE, base::Bind(&NodeDebugger::SendMessage, self->weak_factory_.GetWeakPtr(), message8)); diff --git a/atom/browser/osr/osr_output_device.cc b/atom/browser/osr/osr_output_device.cc deleted file mode 100644 index dd80613633..0000000000 --- a/atom/browser/osr/osr_output_device.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/osr/osr_output_device.h" - -#include "third_party/skia/include/core/SkDevice.h" -#include "ui/gfx/skia_util.h" - -namespace atom { - -OffScreenOutputDevice::OffScreenOutputDevice(bool transparent, - const OnPaintCallback& callback) - : transparent_(transparent), - callback_(callback), - active_(false) { - DCHECK(!callback_.is_null()); -} - -OffScreenOutputDevice::~OffScreenOutputDevice() { -} - -void OffScreenOutputDevice::Resize( - const gfx::Size& pixel_size, float scale_factor) { - scale_factor_ = scale_factor; - - if (viewport_pixel_size_ == pixel_size) return; - viewport_pixel_size_ = pixel_size; - - canvas_.reset(); - bitmap_.reset(new SkBitmap); - bitmap_->allocN32Pixels(viewport_pixel_size_.width(), - viewport_pixel_size_.height(), - !transparent_); - if (bitmap_->drawsNothing()) { - NOTREACHED(); - bitmap_.reset(); - return; - } - - if (transparent_) - bitmap_->eraseARGB(0, 0, 0, 0); - - canvas_.reset(new SkCanvas(*bitmap_)); -} - -SkCanvas* OffScreenOutputDevice::BeginPaint(const gfx::Rect& damage_rect) { - DCHECK(canvas_.get()); - DCHECK(bitmap_.get()); - - damage_rect_ = damage_rect; - - return canvas_.get(); -} - -void OffScreenOutputDevice::EndPaint() { - DCHECK(canvas_.get()); - DCHECK(bitmap_.get()); - - if (!bitmap_.get()) return; - - cc::SoftwareOutputDevice::EndPaint(); - - if (active_) - OnPaint(damage_rect_); -} - -void OffScreenOutputDevice::SetActive(bool active) { - if (active == active_) - return; - active_ = active; - - if (active_) - OnPaint(gfx::Rect(viewport_pixel_size_)); -} - -void OffScreenOutputDevice::OnPaint(const gfx::Rect& damage_rect) { - gfx::Rect rect = damage_rect; - - rect.Intersect(gfx::Rect(viewport_pixel_size_)); - if (rect.IsEmpty()) - return; - - SkAutoLockPixels bitmap_pixels_lock(*bitmap_); - callback_.Run(rect, *bitmap_); -} - -} // namespace atom diff --git a/atom/browser/osr/osr_output_device.h b/atom/browser/osr/osr_output_device.h deleted file mode 100644 index 5710bc41b8..0000000000 --- a/atom/browser/osr/osr_output_device.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_OSR_OSR_OUTPUT_DEVICE_H_ -#define ATOM_BROWSER_OSR_OSR_OUTPUT_DEVICE_H_ - -#include "base/callback.h" -#include "cc/output/software_output_device.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkCanvas.h" - -namespace atom { - -typedef base::Callback OnPaintCallback; - -class OffScreenOutputDevice : public cc::SoftwareOutputDevice { - public: - OffScreenOutputDevice(bool transparent, const OnPaintCallback& callback); - ~OffScreenOutputDevice(); - - // cc::SoftwareOutputDevice: - void Resize(const gfx::Size& pixel_size, float scale_factor) override; - SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override; - void EndPaint() override; - - void SetActive(bool active); - void OnPaint(const gfx::Rect& damage_rect); - - private: - const bool transparent_; - OnPaintCallback callback_; - - bool active_; - - std::unique_ptr canvas_; - std::unique_ptr bitmap_; - - DISALLOW_COPY_AND_ASSIGN(OffScreenOutputDevice); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_OSR_OSR_OUTPUT_DEVICE_H_ diff --git a/atom/browser/osr/osr_render_widget_host_view.cc b/atom/browser/osr/osr_render_widget_host_view.cc deleted file mode 100644 index 606aba3ddd..0000000000 --- a/atom/browser/osr/osr_render_widget_host_view.cc +++ /dev/null @@ -1,913 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/osr/osr_render_widget_host_view.h" - -#include - -#include "base/callback_helpers.h" -#include "base/location.h" -#include "base/memory/ptr_util.h" -#include "base/single_thread_task_runner.h" -#include "base/time/time.h" -#include "cc/output/copy_output_request.h" -#include "cc/scheduler/delay_based_time_source.h" -#include "components/display_compositor/gl_helper.h" -#include "content/browser/renderer_host/render_widget_host_delegate.h" -#include "content/browser/renderer_host/render_widget_host_impl.h" -#include "content/common/view_messages.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/context_factory.h" -#include "content/public/browser/render_widget_host_view_frame_subscriber.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_type.h" -#include "ui/events/latency_info.h" -#include "ui/gfx/geometry/dip_util.h" -#include "ui/gfx/native_widget_types.h" - -namespace atom { - -namespace { - -const float kDefaultScaleFactor = 1.0; -const int kFrameRetryLimit = 2; - -} // namespace - -class AtomCopyFrameGenerator { - public: - AtomCopyFrameGenerator(int frame_rate_threshold_ms, - OffScreenRenderWidgetHostView* view) - : frame_rate_threshold_ms_(frame_rate_threshold_ms), - view_(view), - frame_pending_(false), - frame_in_progress_(false), - frame_retry_count_(0), - weak_ptr_factory_(this) { - last_time_ = base::Time::Now(); - } - - void GenerateCopyFrame( - bool force_frame, - const gfx::Rect& damage_rect) { - if (force_frame && !frame_pending_) - frame_pending_ = true; - - if (!frame_pending_) - return; - - if (!damage_rect.IsEmpty()) - pending_damage_rect_.Union(damage_rect); - - if (frame_in_progress_) - return; - - frame_in_progress_ = true; - - const int64_t frame_rate_delta = - (base::TimeTicks::Now() - frame_start_time_).InMilliseconds(); - if (frame_rate_delta < frame_rate_threshold_ms_) { - content::BrowserThread::PostDelayedTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(&AtomCopyFrameGenerator::InternalGenerateCopyFrame, - weak_ptr_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds( - frame_rate_threshold_ms_ - frame_rate_delta)); - return; - } - - InternalGenerateCopyFrame(); - } - - bool frame_pending() const { return frame_pending_; } - - void set_frame_rate_threshold_ms(int frame_rate_threshold_ms) { - frame_rate_threshold_ms_ = frame_rate_threshold_ms; - } - - private: - void InternalGenerateCopyFrame() { - frame_pending_ = false; - frame_start_time_ = base::TimeTicks::Now(); - - if (!view_->render_widget_host()) - return; - - const gfx::Rect damage_rect = pending_damage_rect_; - pending_damage_rect_.SetRect(0, 0, 0, 0); - - std::unique_ptr request = - cc::CopyOutputRequest::CreateRequest(base::Bind( - &AtomCopyFrameGenerator::CopyFromCompositingSurfaceHasResult, - weak_ptr_factory_.GetWeakPtr(), - damage_rect)); - - request->set_area(gfx::Rect(view_->GetPhysicalBackingSize())); - view_->GetRootLayer()->RequestCopyOfOutput(std::move(request)); - } - - void CopyFromCompositingSurfaceHasResult( - const gfx::Rect& damage_rect, - std::unique_ptr result) { - if (result->IsEmpty() || result->size().IsEmpty() || - !view_->render_widget_host()) { - OnCopyFrameCaptureFailure(damage_rect); - return; - } - - if (result->HasTexture()) { - PrepareTextureCopyOutputResult(damage_rect, std::move(result)); - return; - } - - DCHECK(result->HasBitmap()); - PrepareBitmapCopyOutputResult(damage_rect, std::move(result)); - } - - void PrepareTextureCopyOutputResult( - const gfx::Rect& damage_rect, - std::unique_ptr result) { - DCHECK(result->HasTexture()); - base::ScopedClosureRunner scoped_callback_runner( - base::Bind(&AtomCopyFrameGenerator::OnCopyFrameCaptureFailure, - weak_ptr_factory_.GetWeakPtr(), - damage_rect)); - - const gfx::Size& result_size = result->size(); - SkIRect bitmap_size; - if (bitmap_) - bitmap_->getBounds(&bitmap_size); - - if (!bitmap_ || - bitmap_size.width() != result_size.width() || - bitmap_size.height() != result_size.height()) { - bitmap_.reset(new SkBitmap); - bitmap_->allocN32Pixels(result_size.width(), - result_size.height(), - true); - if (bitmap_->drawsNothing()) - return; - } - - content::ImageTransportFactory* factory = - content::ImageTransportFactory::GetInstance(); - display_compositor::GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return; - - std::unique_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*bitmap_)); - uint8_t* pixels = static_cast(bitmap_->getPixels()); - - cc::TextureMailbox texture_mailbox; - std::unique_ptr release_callback; - result->TakeTexture(&texture_mailbox, &release_callback); - DCHECK(texture_mailbox.IsTexture()); - if (!texture_mailbox.IsTexture()) - return; - - ignore_result(scoped_callback_runner.Release()); - - gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox.mailbox(), - texture_mailbox.sync_token(), - result_size, - gfx::Rect(result_size), - result_size, - pixels, - kN32_SkColorType, - base::Bind( - &AtomCopyFrameGenerator::CopyFromCompositingSurfaceFinishedProxy, - weak_ptr_factory_.GetWeakPtr(), - base::Passed(&release_callback), - damage_rect, - base::Passed(&bitmap_), - base::Passed(&bitmap_pixels_lock)), - display_compositor::GLHelper::SCALER_QUALITY_FAST); - } - - static void CopyFromCompositingSurfaceFinishedProxy( - base::WeakPtr generator, - std::unique_ptr release_callback, - const gfx::Rect& damage_rect, - std::unique_ptr bitmap, - std::unique_ptr bitmap_pixels_lock, - bool result) { - gpu::SyncToken sync_token; - if (result) { - display_compositor::GLHelper* gl_helper = - content::ImageTransportFactory::GetInstance()->GetGLHelper(); - if (gl_helper) - gl_helper->GenerateSyncToken(&sync_token); - } - const bool lost_resource = !sync_token.HasData(); - release_callback->Run(sync_token, lost_resource); - - if (generator) { - generator->CopyFromCompositingSurfaceFinished( - damage_rect, std::move(bitmap), std::move(bitmap_pixels_lock), - result); - } else { - bitmap_pixels_lock.reset(); - bitmap.reset(); - } - } - - void CopyFromCompositingSurfaceFinished( - const gfx::Rect& damage_rect, - std::unique_ptr bitmap, - std::unique_ptr bitmap_pixels_lock, - bool result) { - DCHECK(!bitmap_); - bitmap_ = std::move(bitmap); - - if (result) { - OnCopyFrameCaptureSuccess(damage_rect, *bitmap_, - std::move(bitmap_pixels_lock)); - } else { - bitmap_pixels_lock.reset(); - OnCopyFrameCaptureFailure(damage_rect); - } - } - - void PrepareBitmapCopyOutputResult( - const gfx::Rect& damage_rect, - std::unique_ptr result) { - DCHECK(result->HasBitmap()); - std::unique_ptr source = result->TakeBitmap(); - DCHECK(source); - if (source) { - std::unique_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*source)); - OnCopyFrameCaptureSuccess(damage_rect, *source, - std::move(bitmap_pixels_lock)); - } else { - OnCopyFrameCaptureFailure(damage_rect); - } - } - - void OnCopyFrameCaptureFailure( - const gfx::Rect& damage_rect) { - pending_damage_rect_.Union(damage_rect); - - const bool force_frame = (++frame_retry_count_ <= kFrameRetryLimit); - OnCopyFrameCaptureCompletion(force_frame); - } - - void OnCopyFrameCaptureSuccess( - const gfx::Rect& damage_rect, - const SkBitmap& bitmap, - std::unique_ptr bitmap_pixels_lock) { - view_->OnPaint(damage_rect, bitmap); - - if (frame_retry_count_ > 0) - frame_retry_count_ = 0; - - OnCopyFrameCaptureCompletion(false); - } - - void OnCopyFrameCaptureCompletion(bool force_frame) { - frame_in_progress_ = false; - - if (frame_pending_) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&AtomCopyFrameGenerator::GenerateCopyFrame, - weak_ptr_factory_.GetWeakPtr(), - force_frame, - gfx::Rect())); - } - } - - int frame_rate_threshold_ms_; - OffScreenRenderWidgetHostView* view_; - - base::Time last_time_; - - base::TimeTicks frame_start_time_; - bool frame_pending_; - bool frame_in_progress_; - int frame_retry_count_; - std::unique_ptr bitmap_; - gfx::Rect pending_damage_rect_; - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(AtomCopyFrameGenerator); -}; - -class AtomBeginFrameTimer : public cc::DelayBasedTimeSourceClient { - public: - AtomBeginFrameTimer(int frame_rate_threshold_ms, - const base::Closure& callback) - : callback_(callback) { - time_source_.reset(new cc::DelayBasedTimeSource( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::UI).get())); - time_source_->SetClient(this); - } - - void SetActive(bool active) { - time_source_->SetActive(active); - } - - bool IsActive() const { - return time_source_->Active(); - } - - void SetFrameRateThresholdMs(int frame_rate_threshold_ms) { - time_source_->SetTimebaseAndInterval( - base::TimeTicks::Now(), - base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms)); - } - - private: - void OnTimerTick() override { - callback_.Run(); - } - - const base::Closure callback_; - std::unique_ptr time_source_; - - DISALLOW_COPY_AND_ASSIGN(AtomBeginFrameTimer); -}; - -OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( - bool transparent, - const OnPaintCallback& callback, - content::RenderWidgetHost* host, - NativeWindow* native_window) - : render_widget_host_(content::RenderWidgetHostImpl::From(host)), - native_window_(native_window), - software_output_device_(nullptr), - transparent_(transparent), - callback_(callback), - frame_rate_(60), - frame_rate_threshold_ms_(0), - last_time_(base::Time::Now()), - scale_factor_(kDefaultScaleFactor), - is_showing_(!render_widget_host_->is_hidden()), - size_(native_window->GetSize()), - painting_(true), -#if !defined(OS_MACOSX) - delegated_frame_host_(new content::DelegatedFrameHost(this)), -#endif - weak_ptr_factory_(this) { - DCHECK(render_widget_host_); - render_widget_host_->SetView(this); - -#if !defined(OS_MACOSX) - root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); -#endif - -#if defined(OS_MACOSX) - CreatePlatformWidget(); -#else - compositor_.reset( - new ui::Compositor(content::GetContextFactory(), - base::ThreadTaskRunnerHandle::Get())); - compositor_->SetAcceleratedWidget(native_window_->GetAcceleratedWidget()); - compositor_->SetDelegate(this); - compositor_->SetRootLayer(root_layer_.get()); -#endif - - ResizeRootLayer(); -} - -OffScreenRenderWidgetHostView::~OffScreenRenderWidgetHostView() { -#if defined(OS_MACOSX) - if (is_showing_) - browser_compositor_->SetRenderWidgetHostIsHidden(true); -#else - // Marking the DelegatedFrameHost as removed from the window hierarchy is - // necessary to remove all connections to its old ui::Compositor. - if (is_showing_) - delegated_frame_host_->WasHidden(); - delegated_frame_host_->ResetCompositor(); -#endif - -#if defined(OS_MACOSX) - DestroyPlatformWidget(); -#endif -} - -void OffScreenRenderWidgetHostView::OnBeginFrameTimerTick() { - const base::TimeTicks frame_time = base::TimeTicks::Now(); - const base::TimeDelta vsync_period = - base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_); - SendBeginFrame(frame_time, vsync_period); -} - -void OffScreenRenderWidgetHostView::SendBeginFrame( - base::TimeTicks frame_time, base::TimeDelta vsync_period) { - base::TimeTicks display_time = frame_time + vsync_period; - - base::TimeDelta estimated_browser_composite_time = - base::TimeDelta::FromMicroseconds( - (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60)); - - base::TimeTicks deadline = display_time - estimated_browser_composite_time; - - render_widget_host_->Send(new ViewMsg_BeginFrame( - render_widget_host_->GetRoutingID(), - cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, - vsync_period, cc::BeginFrameArgs::NORMAL))); -} - -bool OffScreenRenderWidgetHostView::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(OffScreenRenderWidgetHostView, message) - IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrames, - OnSetNeedsBeginFrames) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - if (!handled) - return content::RenderWidgetHostViewBase::OnMessageReceived(message); - return handled; -} - -void OffScreenRenderWidgetHostView::InitAsChild(gfx::NativeView) { -} - -content::RenderWidgetHost* OffScreenRenderWidgetHostView::GetRenderWidgetHost() - const { - return render_widget_host_; -} - -void OffScreenRenderWidgetHostView::SetSize(const gfx::Size& size) { - size_ = size; - - const gfx::Size& size_in_pixels = - gfx::ConvertSizeToPixel(scale_factor_, size); - - GetRootLayer()->SetBounds(gfx::Rect(size)); - GetCompositor()->SetScaleAndSize(scale_factor_, size_in_pixels); -} - -void OffScreenRenderWidgetHostView::SetBounds(const gfx::Rect& new_bounds) { -} - -gfx::Vector2dF OffScreenRenderWidgetHostView::GetLastScrollOffset() const { - return last_scroll_offset_; -} - -gfx::NativeView OffScreenRenderWidgetHostView::GetNativeView() const { - return gfx::NativeView(); -} - -gfx::NativeViewAccessible -OffScreenRenderWidgetHostView::GetNativeViewAccessible() { - return gfx::NativeViewAccessible(); -} - -ui::TextInputClient* OffScreenRenderWidgetHostView::GetTextInputClient() { - return nullptr; -} - -void OffScreenRenderWidgetHostView::Focus() { -} - -bool OffScreenRenderWidgetHostView::HasFocus() const { - return false; -} - -bool OffScreenRenderWidgetHostView::IsSurfaceAvailableForCopy() const { - return GetDelegatedFrameHost()->CanCopyToBitmap(); -} - -void OffScreenRenderWidgetHostView::Show() { - if (is_showing_) - return; - - is_showing_ = true; - -#if defined(OS_MACOSX) - browser_compositor_->SetRenderWidgetHostIsHidden(false); -#else - delegated_frame_host_->SetCompositor(compositor_.get()); - delegated_frame_host_->WasShown(ui::LatencyInfo()); -#endif - - if (render_widget_host_) - render_widget_host_->WasShown(ui::LatencyInfo()); -} - -void OffScreenRenderWidgetHostView::Hide() { - if (!is_showing_) - return; - - if (render_widget_host_) - render_widget_host_->WasHidden(); - -#if defined(OS_MACOSX) - browser_compositor_->SetRenderWidgetHostIsHidden(true); -#else - GetDelegatedFrameHost()->WasHidden(); - GetDelegatedFrameHost()->ResetCompositor(); -#endif - - is_showing_ = false; -} - -bool OffScreenRenderWidgetHostView::IsShowing() { - return is_showing_; -} - -gfx::Rect OffScreenRenderWidgetHostView::GetViewBounds() const { - return gfx::Rect(size_); -} - -void OffScreenRenderWidgetHostView::SetBackgroundColor(SkColor color) { - if (transparent_) - color = SkColorSetARGB(SK_AlphaTRANSPARENT, 0, 0, 0); - - content::RenderWidgetHostViewBase::SetBackgroundColor(color); - - const bool opaque = !transparent_ && GetBackgroundOpaque(); - if (render_widget_host_) - render_widget_host_->SetBackgroundOpaque(opaque); -} - -gfx::Size OffScreenRenderWidgetHostView::GetVisibleViewportSize() const { - return size_; -} - -void OffScreenRenderWidgetHostView::SetInsets(const gfx::Insets& insets) { -} - -bool OffScreenRenderWidgetHostView::LockMouse() { - return false; -} - -void OffScreenRenderWidgetHostView::UnlockMouse() { -} - -void OffScreenRenderWidgetHostView::OnSwapCompositorFrame( - uint32_t output_surface_id, - cc::CompositorFrame frame) { - TRACE_EVENT0("electron", - "OffScreenRenderWidgetHostView::OnSwapCompositorFrame"); - - if (frame.metadata.root_scroll_offset != last_scroll_offset_) { - last_scroll_offset_ = frame.metadata.root_scroll_offset; - } - - if (frame.delegated_frame_data) { - if (software_output_device_) { - if (!begin_frame_timer_.get()) { - software_output_device_->SetActive(painting_); - } - - // The compositor will draw directly to the SoftwareOutputDevice which - // then calls OnPaint. -#if defined(OS_MACOSX) - browser_compositor_->SwapCompositorFrame(output_surface_id, - std::move(frame)); -#else - delegated_frame_host_->SwapDelegatedFrame(output_surface_id, - std::move(frame)); -#endif - } else { - if (!copy_frame_generator_.get()) { - copy_frame_generator_.reset( - new AtomCopyFrameGenerator(frame_rate_threshold_ms_, this)); - } - - // Determine the damage rectangle for the current frame. This is the same - // calculation that SwapDelegatedFrame uses. - cc::RenderPass* root_pass = - frame.delegated_frame_data->render_pass_list.back().get(); - gfx::Size frame_size = root_pass->output_rect.size(); - gfx::Rect damage_rect = - gfx::ToEnclosingRect(gfx::RectF(root_pass->damage_rect)); - damage_rect.Intersect(gfx::Rect(frame_size)); - -#if defined(OS_MACOSX) - browser_compositor_->SwapCompositorFrame(output_surface_id, - std::move(frame)); -#else - delegated_frame_host_->SwapDelegatedFrame(output_surface_id, - std::move(frame)); -#endif - - // Request a copy of the last compositor frame which will eventually call - // OnPaint asynchronously. - copy_frame_generator_->GenerateCopyFrame(true, damage_rect); - } - } -} - -void OffScreenRenderWidgetHostView::ClearCompositorFrame() { - GetDelegatedFrameHost()->ClearDelegatedFrame(); -} - -void OffScreenRenderWidgetHostView::InitAsPopup( - content::RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { -} - -void OffScreenRenderWidgetHostView::InitAsFullscreen( - content::RenderWidgetHostView *) { -} - -void OffScreenRenderWidgetHostView::UpdateCursor(const content::WebCursor &) { -} - -void OffScreenRenderWidgetHostView::SetIsLoading(bool loading) { -} - -void OffScreenRenderWidgetHostView::TextInputStateChanged( - const content::TextInputState& params) { -} - -void OffScreenRenderWidgetHostView::ImeCancelComposition() { -} - -void OffScreenRenderWidgetHostView::RenderProcessGone(base::TerminationStatus, - int) { - Destroy(); -} - -void OffScreenRenderWidgetHostView::Destroy() { - delete this; -} - -void OffScreenRenderWidgetHostView::SetTooltipText(const base::string16 &) { -} - -void OffScreenRenderWidgetHostView::SelectionBoundsChanged( - const ViewHostMsg_SelectionBounds_Params &) { -} - -void OffScreenRenderWidgetHostView::CopyFromCompositingSurface( - const gfx::Rect& src_subrect, - const gfx::Size& dst_size, - const content::ReadbackRequestCallback& callback, - const SkColorType preferred_color_type) { - GetDelegatedFrameHost()->CopyFromCompositingSurface( - src_subrect, dst_size, callback, preferred_color_type); -} - -void OffScreenRenderWidgetHostView::CopyFromCompositingSurfaceToVideoFrame( - const gfx::Rect& src_subrect, - const scoped_refptr& target, - const base::Callback& callback) { - GetDelegatedFrameHost()->CopyFromCompositingSurfaceToVideoFrame( - src_subrect, target, callback); -} - -bool OffScreenRenderWidgetHostView::CanCopyToVideoFrame() const { - return GetDelegatedFrameHost()->CanCopyToVideoFrame(); -} - -void OffScreenRenderWidgetHostView::BeginFrameSubscription( - std::unique_ptr subscriber) { - GetDelegatedFrameHost()->BeginFrameSubscription(std::move(subscriber)); -} - -void OffScreenRenderWidgetHostView::EndFrameSubscription() { - GetDelegatedFrameHost()->EndFrameSubscription(); -} - -bool OffScreenRenderWidgetHostView::HasAcceleratedSurface(const gfx::Size &) { - return false; -} - -void OffScreenRenderWidgetHostView::GetScreenInfo( - blink::WebScreenInfo* results) { - results->rect = gfx::Rect(size_); - results->availableRect = gfx::Rect(size_); - results->depth = 24; - results->depthPerComponent = 8; - results->deviceScaleFactor = scale_factor_; - results->orientationAngle = 0; - results->orientationType = blink::WebScreenOrientationLandscapePrimary; -} - -bool OffScreenRenderWidgetHostView::GetScreenColorProfile( - blink::WebVector*) { - return false; -} - -gfx::Rect OffScreenRenderWidgetHostView::GetBoundsInRootWindow() { - return gfx::Rect(size_); -} - -void OffScreenRenderWidgetHostView::LockCompositingSurface() { -} - -void OffScreenRenderWidgetHostView::UnlockCompositingSurface() { -} - -void OffScreenRenderWidgetHostView::ImeCompositionRangeChanged( - const gfx::Range &, const std::vector&) { -} - -gfx::Size OffScreenRenderWidgetHostView::GetPhysicalBackingSize() const { - return size_; -} - -gfx::Size OffScreenRenderWidgetHostView::GetRequestedRendererSize() const { - return size_; -} - -int OffScreenRenderWidgetHostView:: - DelegatedFrameHostGetGpuMemoryBufferClientId() - const { - return render_widget_host_->GetProcess()->GetID(); -} - -ui::Layer* OffScreenRenderWidgetHostView::DelegatedFrameHostGetLayer() const { - return const_cast(root_layer_.get()); -} - -bool OffScreenRenderWidgetHostView::DelegatedFrameHostIsVisible() const { - return !render_widget_host_->is_hidden(); -} - -SkColor OffScreenRenderWidgetHostView::DelegatedFrameHostGetGutterColor( - SkColor color) const { - return color; -} - -gfx::Size OffScreenRenderWidgetHostView::DelegatedFrameHostDesiredSizeInDIP() - const { - return size_; -} - -bool OffScreenRenderWidgetHostView::DelegatedFrameCanCreateResizeLock() const { - return false; -} - -std::unique_ptr - OffScreenRenderWidgetHostView::DelegatedFrameHostCreateResizeLock( - bool defer_compositor_lock) { - return nullptr; -} - -void OffScreenRenderWidgetHostView::DelegatedFrameHostResizeLockWasReleased() { - return render_widget_host_->WasResized(); -} - -void OffScreenRenderWidgetHostView::DelegatedFrameHostSendCompositorSwapAck( - int output_surface_id, const cc::CompositorFrameAck& ack) { - render_widget_host_->Send(new ViewMsg_SwapCompositorFrameAck( - render_widget_host_->GetRoutingID(), - output_surface_id, ack)); -} - -void OffScreenRenderWidgetHostView:: - DelegatedFrameHostSendReclaimCompositorResources( - int output_surface_id, const cc::CompositorFrameAck& ack) { - render_widget_host_->Send(new ViewMsg_ReclaimCompositorResources( - render_widget_host_->GetRoutingID(), - output_surface_id, ack)); -} - -void OffScreenRenderWidgetHostView:: - DelegatedFrameHostOnLostCompositorResources() { - render_widget_host_->ScheduleComposite(); -} - -void OffScreenRenderWidgetHostView::DelegatedFrameHostUpdateVSyncParameters( - const base::TimeTicks& timebase, const base::TimeDelta& interval) { - render_widget_host_->UpdateVSyncParameters(timebase, interval); -} - -void OffScreenRenderWidgetHostView::SetBeginFrameSource( - cc::BeginFrameSource* source) { -} - -std::unique_ptr - OffScreenRenderWidgetHostView::CreateSoftwareOutputDevice( - ui::Compositor* compositor) { - DCHECK_EQ(GetCompositor(), compositor); - DCHECK(!copy_frame_generator_); - DCHECK(!software_output_device_); - - software_output_device_ = new OffScreenOutputDevice( - transparent_, - base::Bind(&OffScreenRenderWidgetHostView::OnPaint, - weak_ptr_factory_.GetWeakPtr())); - return base::WrapUnique(software_output_device_); -} - -bool OffScreenRenderWidgetHostView::InstallTransparency() { - if (transparent_) { - SetBackgroundColor(SkColor()); -#if defined(OS_MACOSX) - browser_compositor_->SetHasTransparentBackground(true); -#else - compositor_->SetHostHasTransparentBackground(true); -#endif - return true; - } - return false; -} - -bool OffScreenRenderWidgetHostView::IsAutoResizeEnabled() const { - return false; -} - -void OffScreenRenderWidgetHostView::OnSetNeedsBeginFrames(bool enabled) { - SetupFrameRate(false); - - begin_frame_timer_->SetActive(enabled); - - if (software_output_device_) { - software_output_device_->SetActive(enabled && painting_); - } -} - -void OffScreenRenderWidgetHostView::OnPaint( - const gfx::Rect& damage_rect, const SkBitmap& bitmap) { - TRACE_EVENT0("electron", "OffScreenRenderWidgetHostView::OnPaint"); - callback_.Run(damage_rect, bitmap); -} - -void OffScreenRenderWidgetHostView::SetPainting(bool painting) { - painting_ = painting; - - if (software_output_device_) { - software_output_device_->SetActive(painting_); - } -} - -bool OffScreenRenderWidgetHostView::IsPainting() const { - return painting_; -} - -void OffScreenRenderWidgetHostView::SetFrameRate(int frame_rate) { - if (frame_rate <= 0) - frame_rate = 1; - if (frame_rate > 60) - frame_rate = 60; - - frame_rate_ = frame_rate; - - SetupFrameRate(true); -} - -int OffScreenRenderWidgetHostView::GetFrameRate() const { - return frame_rate_; -} - -#if !defined(OS_MACOSX) -ui::Compositor* OffScreenRenderWidgetHostView::GetCompositor() const { - return compositor_.get(); -} - -ui::Layer* OffScreenRenderWidgetHostView::GetRootLayer() const { - return root_layer_.get(); -} - -content::DelegatedFrameHost* -OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const { - return delegated_frame_host_.get(); -} -#endif - -void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) { - if (!force && frame_rate_threshold_ms_ != 0) - return; - - frame_rate_threshold_ms_ = 1000 / frame_rate_; - - GetCompositor()->vsync_manager()->SetAuthoritativeVSyncInterval( - base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_)); - - if (copy_frame_generator_.get()) { - copy_frame_generator_->set_frame_rate_threshold_ms( - frame_rate_threshold_ms_); - } - - if (begin_frame_timer_.get()) { - begin_frame_timer_->SetFrameRateThresholdMs(frame_rate_threshold_ms_); - } else { - begin_frame_timer_.reset(new AtomBeginFrameTimer( - frame_rate_threshold_ms_, - base::Bind(&OffScreenRenderWidgetHostView::OnBeginFrameTimerTick, - weak_ptr_factory_.GetWeakPtr()))); - } -} - -void OffScreenRenderWidgetHostView::ResizeRootLayer() { - SetupFrameRate(false); - - const float orgScaleFactor = scale_factor_; - const bool scaleFactorDidChange = (orgScaleFactor != scale_factor_); - - gfx::Size size = GetViewBounds().size(); - - if (!scaleFactorDidChange && size == GetRootLayer()->bounds().size()) - return; - - const gfx::Size& size_in_pixels = - gfx::ConvertSizeToPixel(scale_factor_, size); - - GetRootLayer()->SetBounds(gfx::Rect(size)); - GetCompositor()->SetScaleAndSize(scale_factor_, size_in_pixels); -} - -} // namespace atom diff --git a/atom/browser/osr/osr_render_widget_host_view.h b/atom/browser/osr/osr_render_widget_host_view.h deleted file mode 100644 index c6641ec06a..0000000000 --- a/atom/browser/osr/osr_render_widget_host_view.h +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_ -#define ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_ - -#include -#include - -#if defined(OS_WIN) -#include -#endif - -#include "atom/browser/native_window.h" -#include "atom/browser/osr/osr_output_device.h" -#include "base/process/kill.h" -#include "base/threading/thread.h" -#include "base/time/time.h" -#include "cc/output/compositor_frame.h" -#include "cc/scheduler/begin_frame_source.h" -#include "content/browser/renderer_host/delegated_frame_host.h" -#include "content/browser/renderer_host/render_widget_host_impl.h" -#include "content/browser/renderer_host/render_widget_host_view_base.h" -#include "content/browser/renderer_host/resize_lock.h" -#include "third_party/WebKit/public/platform/WebVector.h" -#include "ui/base/ime/text_input_client.h" -#include "ui/compositor/compositor.h" -#include "ui/compositor/layer_delegate.h" -#include "ui/compositor/layer_owner.h" -#include "ui/gfx/geometry/point.h" - -#if defined(OS_WIN) -#include "ui/gfx/win/window_impl.h" -#endif - -#if defined(OS_MACOSX) -#include "content/browser/renderer_host/browser_compositor_view_mac.h" -#endif - -#if defined(OS_MACOSX) -#ifdef __OBJC__ -@class CALayer; -@class NSWindow; -#else -class CALayer; -class NSWindow; -#endif -#endif - -namespace atom { - -class AtomCopyFrameGenerator; -class AtomBeginFrameTimer; - -#if defined(OS_MACOSX) -class MacHelper; -#endif - -class OffScreenRenderWidgetHostView - : public content::RenderWidgetHostViewBase, - public ui::CompositorDelegate, - public content::DelegatedFrameHostClient { - public: - OffScreenRenderWidgetHostView(bool transparent, - const OnPaintCallback& callback, - content::RenderWidgetHost* render_widget_host, - NativeWindow* native_window); - ~OffScreenRenderWidgetHostView() override; - - // content::RenderWidgetHostView: - bool OnMessageReceived(const IPC::Message&) override; - void InitAsChild(gfx::NativeView) override; - content::RenderWidgetHost* GetRenderWidgetHost(void) const override; - void SetSize(const gfx::Size &) override; - void SetBounds(const gfx::Rect &) override; - gfx::Vector2dF GetLastScrollOffset(void) const override; - gfx::NativeView GetNativeView(void) const override; - gfx::NativeViewAccessible GetNativeViewAccessible(void) override; - ui::TextInputClient* GetTextInputClient() override; - void Focus(void) override; - bool HasFocus(void) const override; - bool IsSurfaceAvailableForCopy(void) const override; - void Show(void) override; - void Hide(void) override; - bool IsShowing(void) override; - gfx::Rect GetViewBounds(void) const override; - gfx::Size GetVisibleViewportSize() const override; - void SetInsets(const gfx::Insets&) override; - void SetBackgroundColor(SkColor color) override; - bool LockMouse(void) override; - void UnlockMouse(void) override; -#if defined(OS_MACOSX) - ui::AcceleratedWidgetMac* GetAcceleratedWidgetMac() const override; - void SetActive(bool active) override; - void ShowDefinitionForSelection() override; - bool SupportsSpeech() const override; - void SpeakSelection() override; - bool IsSpeaking() const override; - void StopSpeaking() override; -#endif // defined(OS_MACOSX) - - // content::RenderWidgetHostViewBase: - void OnSwapCompositorFrame(uint32_t, cc::CompositorFrame) - override; - void ClearCompositorFrame(void) override; - void InitAsPopup(content::RenderWidgetHostView *rwhv, const gfx::Rect& rect) - override; - void InitAsFullscreen(content::RenderWidgetHostView *) override; - void UpdateCursor(const content::WebCursor &) override; - void SetIsLoading(bool is_loading) override; - void TextInputStateChanged(const content::TextInputState& params) override; - void ImeCancelComposition(void) override; - void RenderProcessGone(base::TerminationStatus, int) override; - void Destroy(void) override; - void SetTooltipText(const base::string16 &) override; -#if defined(OS_MACOSX) - void SelectionChanged(const base::string16& text, - size_t offset, - const gfx::Range& range) override; -#endif - void SelectionBoundsChanged(const ViewHostMsg_SelectionBounds_Params &) - override; - void CopyFromCompositingSurface(const gfx::Rect &, - const gfx::Size &, - const content::ReadbackRequestCallback &, - const SkColorType) override; - void CopyFromCompositingSurfaceToVideoFrame( - const gfx::Rect &, - const scoped_refptr &, - const base::Callback &) override; - bool CanCopyToVideoFrame(void) const override; - void BeginFrameSubscription( - std::unique_ptr) override; - void EndFrameSubscription() override; - bool HasAcceleratedSurface(const gfx::Size &) override; - void GetScreenInfo(blink::WebScreenInfo *) override; - bool GetScreenColorProfile(blink::WebVector*); - gfx::Rect GetBoundsInRootWindow(void) override; - void LockCompositingSurface(void) override; - void UnlockCompositingSurface(void) override; - void ImeCompositionRangeChanged( - const gfx::Range &, const std::vector&) override; - gfx::Size GetPhysicalBackingSize() const override; - gfx::Size GetRequestedRendererSize() const override; - - // content::DelegatedFrameHostClient: - int DelegatedFrameHostGetGpuMemoryBufferClientId(void) const; - ui::Layer *DelegatedFrameHostGetLayer(void) const override; - bool DelegatedFrameHostIsVisible(void) const override; - SkColor DelegatedFrameHostGetGutterColor(SkColor) const override; - gfx::Size DelegatedFrameHostDesiredSizeInDIP(void) const override; - bool DelegatedFrameCanCreateResizeLock(void) const override; - std::unique_ptr DelegatedFrameHostCreateResizeLock( - bool defer_compositor_lock) override; - void DelegatedFrameHostResizeLockWasReleased(void) override; - void DelegatedFrameHostSendCompositorSwapAck( - int, const cc::CompositorFrameAck &) override; - void DelegatedFrameHostSendReclaimCompositorResources( - int, const cc::CompositorFrameAck &) override; - void DelegatedFrameHostOnLostCompositorResources(void) override; - void DelegatedFrameHostUpdateVSyncParameters( - const base::TimeTicks &, const base::TimeDelta &) override; - void SetBeginFrameSource(cc::BeginFrameSource* source) override; - - // ui::CompositorDelegate: - std::unique_ptr CreateSoftwareOutputDevice( - ui::Compositor* compositor) override; - - bool InstallTransparency(); - bool IsAutoResizeEnabled() const; - void OnSetNeedsBeginFrames(bool enabled); - - void OnBeginFrameTimerTick(); - void SendBeginFrame(base::TimeTicks frame_time, - base::TimeDelta vsync_period); - -#if defined(OS_MACOSX) - void CreatePlatformWidget(); - void DestroyPlatformWidget(); -#endif - - void OnPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap); - - void SetPainting(bool painting); - bool IsPainting() const; - - void SetFrameRate(int frame_rate); - int GetFrameRate() const; - - ui::Compositor* GetCompositor() const; - ui::Layer* GetRootLayer() const; - content::DelegatedFrameHost* GetDelegatedFrameHost() const; - - content::RenderWidgetHostImpl* render_widget_host() const - { return render_widget_host_; } - NativeWindow* window() const { return native_window_; } - - private: - void SetupFrameRate(bool force); - void ResizeRootLayer(); - - // Weak ptrs. - content::RenderWidgetHostImpl* render_widget_host_; - NativeWindow* native_window_; - OffScreenOutputDevice* software_output_device_; - - const bool transparent_; - OnPaintCallback callback_; - - int frame_rate_; - int frame_rate_threshold_ms_; - - base::Time last_time_; - - float scale_factor_; - bool is_showing_; - gfx::Vector2dF last_scroll_offset_; - gfx::Size size_; - bool painting_; - - std::unique_ptr root_layer_; - std::unique_ptr compositor_; - std::unique_ptr delegated_frame_host_; - - std::unique_ptr copy_frame_generator_; - std::unique_ptr begin_frame_timer_; - -#if defined(OS_MACOSX) - CALayer* background_layer_; - std::unique_ptr browser_compositor_; - - // Can not be managed by smart pointer because its header can not be included - // in the file that has the destructor. - MacHelper* mac_helper_; - - // Selected text on the renderer. - std::string selected_text_; -#endif - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(OffScreenRenderWidgetHostView); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_ diff --git a/atom/browser/osr/osr_render_widget_host_view_mac.mm b/atom/browser/osr/osr_render_widget_host_view_mac.mm deleted file mode 100644 index 505243b8a9..0000000000 --- a/atom/browser/osr/osr_render_widget_host_view_mac.mm +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/osr/osr_render_widget_host_view.h" - -#import - -#include "base/strings/utf_string_conversions.h" -#include "content/common/view_messages.h" -#include "ui/accelerated_widget_mac/accelerated_widget_mac.h" - -namespace atom { - -class MacHelper : - public content::BrowserCompositorMacClient, - public ui::AcceleratedWidgetMacNSView { - public: - explicit MacHelper(OffScreenRenderWidgetHostView* view) : view_(view) {} - virtual ~MacHelper() {} - - // content::BrowserCompositorMacClient: - NSView* BrowserCompositorMacGetNSView() const override { - // Intentionally return nil so that - // BrowserCompositorMac::DelegatedFrameHostDesiredSizeInDIP uses the layer - // size instead of the NSView size. - return nil; - } - - SkColor BrowserCompositorMacGetGutterColor(SkColor color) const override { - // When making an element on the page fullscreen the element's background - // may not match the page's, so use black as the gutter color to avoid - // flashes of brighter colors during the transition. - if (view_->render_widget_host()->delegate() && - view_->render_widget_host()->delegate()->IsFullscreenForCurrentTab()) { - return SK_ColorBLACK; - } - return color; - } - - void BrowserCompositorMacSendCompositorSwapAck( - int output_surface_id, - const cc::CompositorFrameAck& ack) override { - view_->render_widget_host()->Send(new ViewMsg_SwapCompositorFrameAck( - view_->render_widget_host()->GetRoutingID(), output_surface_id, ack)); - } - - void BrowserCompositorMacSendReclaimCompositorResources( - int output_surface_id, - const cc::CompositorFrameAck& ack) override { - view_->render_widget_host()->Send(new ViewMsg_ReclaimCompositorResources( - view_->render_widget_host()->GetRoutingID(), output_surface_id, ack)); - } - - void BrowserCompositorMacOnLostCompositorResources() override { - view_->render_widget_host()->ScheduleComposite(); - } - - void BrowserCompositorMacUpdateVSyncParameters( - const base::TimeTicks& timebase, - const base::TimeDelta& interval) override { - view_->render_widget_host()->UpdateVSyncParameters(timebase, interval); - } - - void BrowserCompositorMacSendBeginFrame( - const cc::BeginFrameArgs& args) override { - view_->render_widget_host()->Send( - new ViewMsg_BeginFrame(view_->render_widget_host()->GetRoutingID(), - args)); - } - // ui::AcceleratedWidgetMacNSView: - NSView* AcceleratedWidgetGetNSView() const override { - return [view_->window()->GetNativeWindow() contentView]; - } - - void AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const override { - *timebase = base::TimeTicks(); - *interval = base::TimeDelta(); - } - - void AcceleratedWidgetSwapCompleted() override { - } - - private: - OffScreenRenderWidgetHostView* view_; - - DISALLOW_COPY_AND_ASSIGN(MacHelper); -}; - -ui::AcceleratedWidgetMac* -OffScreenRenderWidgetHostView::GetAcceleratedWidgetMac() const { - if (browser_compositor_) - return browser_compositor_->GetAcceleratedWidgetMac(); - return nullptr; -} - -void OffScreenRenderWidgetHostView::SetActive(bool active) { -} - -void OffScreenRenderWidgetHostView::ShowDefinitionForSelection() { -} - -bool OffScreenRenderWidgetHostView::SupportsSpeech() const { - return false; -} - -void OffScreenRenderWidgetHostView::SpeakSelection() { -} - -bool OffScreenRenderWidgetHostView::IsSpeaking() const { - return false; -} - -void OffScreenRenderWidgetHostView::StopSpeaking() { -} - -void OffScreenRenderWidgetHostView::SelectionChanged( - const base::string16& text, - size_t offset, - const gfx::Range& range) { - if (range.is_empty() || text.empty()) { - selected_text_.clear(); - } else { - size_t pos = range.GetMin() - offset; - size_t n = range.length(); - - DCHECK(pos + n <= text.length()) << "The text can not fully cover range."; - if (pos >= text.length()) { - DCHECK(false) << "The text can not cover range."; - return; - } - selected_text_ = base::UTF16ToUTF8(text.substr(pos, n)); - } - - RenderWidgetHostViewBase::SelectionChanged(text, offset, range); -} - -void OffScreenRenderWidgetHostView::CreatePlatformWidget() { - mac_helper_ = new MacHelper(this); - browser_compositor_.reset(new content::BrowserCompositorMac( - mac_helper_, mac_helper_, render_widget_host_->is_hidden(), true)); -} - -void OffScreenRenderWidgetHostView::DestroyPlatformWidget() { - browser_compositor_.reset(); - delete mac_helper_; -} - -ui::Compositor* OffScreenRenderWidgetHostView::GetCompositor() const { - return browser_compositor_->GetCompositor(); -} - -ui::Layer* OffScreenRenderWidgetHostView::GetRootLayer() const { - return browser_compositor_->GetRootLayer(); -} - -content::DelegatedFrameHost* -OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const { - return browser_compositor_->GetDelegatedFrameHost(); -} - -} // namespace diff --git a/atom/browser/osr/osr_web_contents_view.cc b/atom/browser/osr/osr_web_contents_view.cc deleted file mode 100644 index 35aa7dcc3a..0000000000 --- a/atom/browser/osr/osr_web_contents_view.cc +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/osr/osr_web_contents_view.h" - -namespace atom { - -OffScreenWebContentsView::OffScreenWebContentsView( - bool transparent, const OnPaintCallback& callback) - : transparent_(transparent), - callback_(callback), - web_contents_(nullptr) { -#if defined(OS_MACOSX) - PlatformCreate(); -#endif -} - -OffScreenWebContentsView::~OffScreenWebContentsView() { -#if defined(OS_MACOSX) - PlatformDestroy(); -#endif -} - -void OffScreenWebContentsView::SetWebContents( - content::WebContents* web_contents) { - web_contents_ = web_contents; -} - -#if !defined(OS_MACOSX) -gfx::NativeView OffScreenWebContentsView::GetNativeView() const { - return gfx::NativeView(); -} - -gfx::NativeView OffScreenWebContentsView::GetContentNativeView() const { - return gfx::NativeView(); -} - -gfx::NativeWindow OffScreenWebContentsView::GetTopLevelNativeWindow() const { - return gfx::NativeWindow(); -} -#endif - -void OffScreenWebContentsView::GetContainerBounds(gfx::Rect* out) const { - *out = GetViewBounds(); -} - -void OffScreenWebContentsView::SizeContents(const gfx::Size& size) { -} - -void OffScreenWebContentsView::Focus() { -} - -void OffScreenWebContentsView::SetInitialFocus() { -} - -void OffScreenWebContentsView::StoreFocus() { -} - -void OffScreenWebContentsView::RestoreFocus() { -} - -content::DropData* OffScreenWebContentsView::GetDropData() const { - return nullptr; -} - -gfx::Rect OffScreenWebContentsView::GetViewBounds() const { - return view_ ? view_->GetViewBounds() : gfx::Rect(); -} - -void OffScreenWebContentsView::CreateView(const gfx::Size& initial_size, - gfx::NativeView context) { -} - -content::RenderWidgetHostViewBase* - OffScreenWebContentsView::CreateViewForWidget( - content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) { - auto relay = NativeWindowRelay::FromWebContents(web_contents_); - view_ = new OffScreenRenderWidgetHostView( - transparent_, callback_, render_widget_host, relay->window.get()); - return view_; -} - -content::RenderWidgetHostViewBase* - OffScreenWebContentsView::CreateViewForPopupWidget( - content::RenderWidgetHost* render_widget_host) { - auto relay = NativeWindowRelay::FromWebContents(web_contents_); - view_ = new OffScreenRenderWidgetHostView( - transparent_, callback_, render_widget_host, relay->window.get()); - return view_; -} - -void OffScreenWebContentsView::SetPageTitle(const base::string16& title) { -} - -void OffScreenWebContentsView::RenderViewCreated( - content::RenderViewHost* host) { - if (view_) - view_->InstallTransparency(); -} - -void OffScreenWebContentsView::RenderViewSwappedIn( - content::RenderViewHost* host) { -} - -void OffScreenWebContentsView::SetOverscrollControllerEnabled(bool enabled) { -} - -#if defined(OS_MACOSX) -void OffScreenWebContentsView::SetAllowOtherViews(bool allow) { -} - -bool OffScreenWebContentsView::GetAllowOtherViews() const { - return false; -} - -bool OffScreenWebContentsView::IsEventTracking() const { - return false; -} - -void OffScreenWebContentsView::CloseTabAfterEventTracking() { -} -#endif // defined(OS_MACOSX) - -void OffScreenWebContentsView::StartDragging( - const content::DropData& drop_data, - blink::WebDragOperationsMask allowed_ops, - const gfx::ImageSkia& image, - const gfx::Vector2d& image_offset, - const content::DragEventSourceInfo& event_info) { - if (web_contents_) - web_contents_->SystemDragEnded(); -} - -void OffScreenWebContentsView::UpdateDragCursor( - blink::WebDragOperation operation) { -} - -} // namespace atom diff --git a/atom/browser/osr/osr_web_contents_view.h b/atom/browser/osr/osr_web_contents_view.h deleted file mode 100644 index d47f71b1e9..0000000000 --- a/atom/browser/osr/osr_web_contents_view.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_OSR_OSR_WEB_CONTENTS_VIEW_H_ -#define ATOM_BROWSER_OSR_OSR_WEB_CONTENTS_VIEW_H_ - -#include "atom/browser/osr/osr_render_widget_host_view.h" -#include "content/browser/renderer_host/render_view_host_delegate_view.h" -#include "content/browser/web_contents/web_contents_view.h" -#include "content/public/browser/web_contents.h" - -#if defined(OS_MACOSX) -#ifdef __OBJC__ -@class OffScreenView; -#else -class OffScreenView; -#endif -#endif - -namespace atom { - -class OffScreenWebContentsView : public content::WebContentsView, - public content::RenderViewHostDelegateView { - public: - OffScreenWebContentsView(bool transparent, const OnPaintCallback& callback); - ~OffScreenWebContentsView(); - - void SetWebContents(content::WebContents*); - - // content::WebContentsView: - gfx::NativeView GetNativeView() const override; - gfx::NativeView GetContentNativeView() const override; - gfx::NativeWindow GetTopLevelNativeWindow() const override; - void GetContainerBounds(gfx::Rect* out) const override; - void SizeContents(const gfx::Size& size) override; - void Focus() override; - void SetInitialFocus() override; - void StoreFocus() override; - void RestoreFocus() override; - content::DropData* GetDropData() const override; - gfx::Rect GetViewBounds() const override; - void CreateView( - const gfx::Size& initial_size, gfx::NativeView context) override; - content::RenderWidgetHostViewBase* CreateViewForWidget( - content::RenderWidgetHost* render_widget_host, - bool is_guest_view_hack) override; - content::RenderWidgetHostViewBase* CreateViewForPopupWidget( - content::RenderWidgetHost* render_widget_host) override; - void SetPageTitle(const base::string16& title) override; - void RenderViewCreated(content::RenderViewHost* host) override; - void RenderViewSwappedIn(content::RenderViewHost* host) override; - void SetOverscrollControllerEnabled(bool enabled) override; - -#if defined(OS_MACOSX) - void SetAllowOtherViews(bool allow) override; - bool GetAllowOtherViews() const override; - bool IsEventTracking() const override; - void CloseTabAfterEventTracking() override; -#endif - - // content::RenderViewHostDelegateView - void StartDragging( - const content::DropData& drop_data, - blink::WebDragOperationsMask allowed_ops, - const gfx::ImageSkia& image, - const gfx::Vector2d& image_offset, - const content::DragEventSourceInfo& event_info) override; - void UpdateDragCursor(blink::WebDragOperation operation) override; - - private: -#if defined(OS_MACOSX) - void PlatformCreate(); - void PlatformDestroy(); -#endif - - const bool transparent_; - OnPaintCallback callback_; - - // Weak refs. - OffScreenRenderWidgetHostView* view_; - content::WebContents* web_contents_; - -#if defined(OS_MACOSX) - OffScreenView* offScreenView_; -#endif -}; - -} // namespace atom - -#endif // ATOM_BROWSER_OSR_OSR_WEB_CONTENTS_VIEW_H_ diff --git a/atom/browser/osr/osr_web_contents_view_mac.mm b/atom/browser/osr/osr_web_contents_view_mac.mm deleted file mode 100644 index 66838ab590..0000000000 --- a/atom/browser/osr/osr_web_contents_view_mac.mm +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/osr/osr_web_contents_view.h" - -#import - -@interface OffScreenView : NSView -@end - -@implementation OffScreenView - -- (void)drawRect:(NSRect)dirtyRect { - NSString* str = @"No content under offscreen mode"; - NSMutableParagraphStyle* paragraphStyle = - [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; - [paragraphStyle setAlignment:NSCenterTextAlignment]; - NSDictionary* attributes = [NSDictionary - dictionaryWithObject:paragraphStyle - forKey:NSParagraphStyleAttributeName]; - NSAttributedString* text = - [[[NSAttributedString alloc] initWithString:str - attributes:attributes] autorelease]; - NSRect frame = NSMakeRect(0, (self.frame.size.height - text.size.height) / 2, - self.frame.size.width, text.size.height); - [str drawInRect:frame withAttributes:attributes]; -} - -@end - -namespace atom { - -gfx::NativeView OffScreenWebContentsView::GetNativeView() const { - return offScreenView_; -} - -gfx::NativeView OffScreenWebContentsView::GetContentNativeView() const { - return offScreenView_; -} - -gfx::NativeWindow OffScreenWebContentsView::GetTopLevelNativeWindow() const { - return [offScreenView_ window]; -} - -void OffScreenWebContentsView::PlatformCreate() { - offScreenView_ = [[OffScreenView alloc] init]; -} - -void OffScreenWebContentsView::PlatformDestroy() { - [offScreenView_ release]; -} - -} // namespace atom diff --git a/atom/browser/resources/mac/electron.icns b/atom/browser/resources/mac/electron.icns deleted file mode 100644 index 3a24a4d29f30949c709cff169ab1518f687fde6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154411 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Ν&>kaqY+YfUQD*e$# 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 diff --git a/atom/browser/ui/accelerator_util.cc b/atom/browser/ui/accelerator_util.cc index eb89bf0c35..d51e9350d0 100644 --- a/atom/browser/ui/accelerator_util.cc +++ b/atom/browser/ui/accelerator_util.cc @@ -88,7 +88,7 @@ void GenerateAcceleratorTable(AcceleratorTable* table, bool TriggerAcceleratorTableCommand(AcceleratorTable* table, const ui::Accelerator& accelerator) { - if (ContainsKey(*table, accelerator)) { + if (base::ContainsKey(*table, accelerator)) { const accelerator_util::MenuItem& item = (*table)[accelerator]; item.model->ActivatedAt(item.position); return true; diff --git a/atom/browser/ui/atom_menu_model.h b/atom/browser/ui/atom_menu_model.h index 887f6123bc..e7d6fc92c0 100644 --- a/atom/browser/ui/atom_menu_model.h +++ b/atom/browser/ui/atom_menu_model.h @@ -26,7 +26,7 @@ class AtomMenuModel : public ui::SimpleMenuModel { private: // ui::SimpleMenuModel::Delegate: bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) { + ui::Accelerator* accelerator) const { return GetAcceleratorForCommandIdWithParams( command_id, false, accelerator); } diff --git a/atom/browser/web_contents_permission_helper.cc b/atom/browser/web_contents_permission_helper.cc index 452f43c907..bc48e1034e 100644 --- a/atom/browser/web_contents_permission_helper.cc +++ b/atom/browser/web_contents_permission_helper.cc @@ -21,6 +21,7 @@ void MediaAccessAllowed( const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback, bool allowed) { + // TODO(bridiver) - IOThread >> brightray::MediaStreamDevicesController controller(request, callback); if (allowed) controller.TakeAction(); @@ -65,7 +66,7 @@ void WebContentsPermissionHelper::RequestPermission( origin = security_origin; } permission_manager->RequestPermission( - permission, rfh, origin, + permission, rfh, origin, user_gesture, base::Bind(&OnPermissionResponse, callback)); } diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 7e0542a0d8..d0efb84133 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -9,7 +9,6 @@ #include #include "atom/browser/native_window.h" -#include "atom/browser/web_view_manager.h" #include "atom/common/native_mate_converters/value_converter.h" #include "base/strings/string_number_conversions.h" #include "cc/base/switches.h" @@ -99,28 +98,6 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( if (web_preferences.GetBoolean(options::kExperimentalCanvasFeatures, &b) && b) command_line->AppendSwitch(::switches::kEnableExperimentalCanvasFeatures); - // Check if we have node integration specified. - bool node_integration = true; - web_preferences.GetBoolean(options::kNodeIntegration, &node_integration); - command_line->AppendSwitchASCII(switches::kNodeIntegration, - node_integration ? "true" : "false"); - - // The preload script. - base::FilePath::StringType preload; - if (web_preferences.GetString(options::kPreloadScript, &preload)) { - if (base::FilePath(preload).IsAbsolute()) - command_line->AppendSwitchNative(switches::kPreloadScript, preload); - else - LOG(ERROR) << "preload script must have absolute path."; - } else if (web_preferences.GetString(options::kPreloadURL, &preload)) { - // Translate to file path if there is "preload-url" option. - base::FilePath preload_path; - if (net::FileURLToFilePath(GURL(preload), &preload_path)) - command_line->AppendSwitchPath(switches::kPreloadScript, preload_path); - else - LOG(ERROR) << "preload url must be file:// protocol."; - } - // --background-color. std::string color; if (web_preferences.GetString(options::kBackgroundColor, &color)) @@ -133,26 +110,6 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( command_line->AppendSwitchASCII(switches::kZoomFactor, base::DoubleToString(zoom_factor)); - // --guest-instance-id, which is used to identify guest WebContents. - int guest_instance_id = 0; - if (web_preferences.GetInteger(options::kGuestInstanceID, &guest_instance_id)) - command_line->AppendSwitchASCII(switches::kGuestInstanceID, - base::IntToString(guest_instance_id)); - - // Pass the opener's window id. - int opener_id; - if (web_preferences.GetInteger(options::kOpenerID, &opener_id)) - command_line->AppendSwitchASCII(switches::kOpenerID, - base::IntToString(opener_id)); - -#if defined(OS_MACOSX) - // Enable scroll bounce. - bool scroll_bounce; - if (web_preferences.GetBoolean(options::kScrollBounce, &scroll_bounce) && - scroll_bounce) - command_line->AppendSwitch(switches::kScrollBounce); -#endif - // Custom command line switches. const base::ListValue* args; if (web_preferences.GetList("commandLineSwitches", &args)) { @@ -175,31 +132,6 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( &disable_blink_features)) command_line->AppendSwitchASCII(::switches::kDisableBlinkFeatures, disable_blink_features); - - // The initial visibility state. - NativeWindow* window = NativeWindow::FromWebContents(web_contents); - - // Use embedder window for webviews - if (guest_instance_id && !window) { - auto manager = WebViewManager::GetWebViewManager(web_contents); - if (manager) { - auto embedder = manager->GetEmbedder(guest_instance_id); - if (embedder) - window = NativeWindow::FromWebContents(embedder); - } - } - - if (window) { - bool visible = window->IsVisible() && !window->IsMinimized(); - if (!visible) // Default state is visible. - command_line->AppendSwitch("hidden-page"); - } - - // Use frame scheduling for offscreen renderers. - // TODO(zcbenz): Remove this after Chrome 54, on which it becomes default. - bool offscreen; - if (web_preferences.GetBoolean("offscreen", &offscreen) && offscreen) - command_line->AppendSwitch(cc::switches::kEnableBeginFrameScheduling); } // static diff --git a/atom/browser/web_contents_preferences.h b/atom/browser/web_contents_preferences.h index fc6756990c..a4b1c4007b 100644 --- a/atom/browser/web_contents_preferences.h +++ b/atom/browser/web_contents_preferences.h @@ -7,17 +7,12 @@ #include -#include "atom/browser/api/atom_api_extension.h" #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/values.h" #include "content/public/browser/web_contents_user_data.h" #include "content/public/common/content_switches.h" -#if defined(ENABLE_EXTENSIONS) -#include "extensions/common/switches.h" -#endif - namespace base { class CommandLine; } @@ -44,25 +39,6 @@ class WebContentsPreferences static void AppendExtraCommandLineSwitches( content::WebContents* web_contents, base::CommandLine* command_line); - static bool run_node(const base::CommandLine* cmd_line) { - // don't run node if -#if defined(ENABLE_EXTENSIONS) - if (cmd_line-> - HasSwitch(::extensions::switches::kExtensionProcess)) - return false; -#endif - return !( - // node integration is disabled - cmd_line->GetSwitchValueASCII(switches::kNodeIntegration) != "true" && - // and there is no preload script - !cmd_line->HasSwitch(switches::kPreloadScript) && - !cmd_line->HasSwitch(switches::kPreloadURL)); - } - - static bool run_node() { - return run_node(base::CommandLine::ForCurrentProcess()); - } - // Modify the WebPreferences according to |web_contents|'s preferences. static void OverrideWebkitPrefs( content::WebContents* web_contents, content::WebPreferences* prefs); diff --git a/atom/browser/web_view_guest_delegate.cc b/atom/browser/web_view_guest_delegate.cc deleted file mode 100644 index 69eb2a379b..0000000000 --- a/atom/browser/web_view_guest_delegate.cc +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/api/atom_api_session.h" -#include "atom/browser/api/atom_api_web_contents.h" -#include "atom/browser/api/event.h" -#include "atom/browser/atom_browser_context.h" -#include "atom/browser/native_window.h" -#include "atom/browser/web_contents_preferences.h" -#include "atom/browser/web_view_guest_delegate.h" -#include "atom/common/native_mate_converters/gurl_converter.h" -#include "atom/common/node_includes.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/guest_host.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_widget_host.h" -#include "content/public/browser/render_widget_host_view.h" -#include "native_mate/dictionary.h" - -namespace atom { - -namespace { - -const int kDefaultWidth = 300; -const int kDefaultHeight = 300; - -} // namespace - -WebViewGuestDelegate::WebViewGuestDelegate() - : guest_host_(nullptr), - auto_size_enabled_(false), - is_full_page_plugin_(false), - api_web_contents_(nullptr), - guest_proxy_routing_id_(-1) { -} - -WebViewGuestDelegate::~WebViewGuestDelegate() { -} - -void WebViewGuestDelegate::Initialize(api::WebContents* api_web_contents) { - api_web_contents_ = api_web_contents; - Observe(api_web_contents->GetWebContents()); -} - -void WebViewGuestDelegate::Destroy() { - // Give the content module an opportunity to perform some cleanup. - guest_host_->WillDestroy(); - guest_host_ = nullptr; -} - -content::WebContents* WebViewGuestDelegate::CreateNewGuestWindow( - const content::WebContents::CreateParams& params) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - auto isolate = api_web_contents_->isolate(); - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - // window options will come from features that needs to be passed through - mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate); - options.Set("isGuest", true); - - int guest_instance_id = GetNextInstanceId(); - options.Set(options::kGuestInstanceID, guest_instance_id); - - // TODO(bridiver) should we create a new site instance - // from web_contents_impl.cc - // scoped_refptr site_instance = - // params.opener_suppressed && !is_guest - // ? SiteInstance::CreateForURL(GetBrowserContext(), - // params.target_url) - // : source_site_instance; - - if (params.site_instance) { - auto session = atom::api::Session::CreateFrom(isolate, - static_cast( - params.site_instance->GetBrowserContext())); - options.Set("session", session); - } - - // get the underlying contents::WebContents object - mate::Handle new_api_web_contents = - api::WebContents::CreateWithParams(isolate, options, params); - content::WebContents* web_contents = new_api_web_contents->GetWebContents(); - RegisterGuest(new_api_web_contents, guest_instance_id); - - return web_contents; -} - -void WebViewGuestDelegate::RegisterGuest(mate::Handle guest, - int guest_instance_id) { - auto isolate = api_web_contents_->isolate(); - v8::HandleScope handle_scope(isolate); - - auto add_guest_event = - v8::Local::Cast(mate::Event::Create(isolate).ToV8()); - node::Environment* env = node::Environment::GetCurrent(isolate); - mate::EmitEvent(isolate, - env->process_object(), - "ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST", - add_guest_event, - guest, - guest_instance_id); -} - -int WebViewGuestDelegate::GetNextInstanceId() { - auto isolate = api_web_contents_->isolate(); - v8::HandleScope handle_scope(isolate); - - // get the next guest id and assign it to options and webPreferences - auto next_instance_id_event = v8::Local::Cast( - mate::Event::Create(isolate).ToV8()); - node::Environment* env = node::Environment::GetCurrent(isolate); - mate::EmitEvent(isolate, - env->process_object(), - "ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID", - next_instance_id_event); - return next_instance_id_event->Get( - mate::StringToV8(isolate, "returnValue"))->NumberValue(); -} - -void WebViewGuestDelegate::SetSize(const SetSizeParams& params) { - bool enable_auto_size = - params.enable_auto_size ? *params.enable_auto_size : auto_size_enabled_; - gfx::Size min_size = params.min_size ? *params.min_size : min_auto_size_; - gfx::Size max_size = params.max_size ? *params.max_size : max_auto_size_; - - if (params.normal_size) - normal_size_ = *params.normal_size; - - min_auto_size_ = min_size; - min_auto_size_.SetToMin(max_size); - max_auto_size_ = max_size; - max_auto_size_.SetToMax(min_size); - - enable_auto_size &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty(); - - auto rvh = web_contents()->GetRenderViewHost(); - if (enable_auto_size) { - // Autosize is being enabled. - rvh->EnableAutoResize(min_auto_size_, max_auto_size_); - normal_size_.SetSize(0, 0); - } else { - // Autosize is being disabled. - // Use default width/height if missing from partially defined normal size. - if (normal_size_.width() && !normal_size_.height()) - normal_size_.set_height(GetDefaultSize().height()); - if (!normal_size_.width() && normal_size_.height()) - normal_size_.set_width(GetDefaultSize().width()); - - gfx::Size new_size; - if (!normal_size_.IsEmpty()) { - new_size = normal_size_; - } else if (!guest_size_.IsEmpty()) { - new_size = guest_size_; - } else { - new_size = GetDefaultSize(); - } - - if (auto_size_enabled_) { - // Autosize was previously enabled. - rvh->DisableAutoResize(new_size); - GuestSizeChangedDueToAutoSize(guest_size_, new_size); - } else { - // Autosize was already disabled. - guest_host_->SizeContents(new_size); - } - - guest_size_ = new_size; - } - - auto_size_enabled_ = enable_auto_size; -} - -bool WebViewGuestDelegate::IsAttached() { - return guest_proxy_routing_id_ != -1; -} - -void WebViewGuestDelegate::DidAttach(int guest_proxy_routing_id) { - guest_proxy_routing_id_ = guest_proxy_routing_id; - api_web_contents_->Emit("did-attach", guest_proxy_routing_id); - api_web_contents_->ResumeLoadingCreatedWebContents(); -} - -content::WebContents* WebViewGuestDelegate::GetOwnerWebContents() const { - return embedder_web_contents_; -} - -void WebViewGuestDelegate::GuestSizeChanged(const gfx::Size& new_size) { - if (!auto_size_enabled_) - return; - GuestSizeChangedDueToAutoSize(guest_size_, new_size); - guest_size_ = new_size; -} - -void WebViewGuestDelegate::SetGuestHost(content::GuestHost* guest_host) { - guest_host_ = guest_host; -} - -void WebViewGuestDelegate::DidDetach() { - guest_proxy_routing_id_ = -1; - api_web_contents_->Emit("did-detach"); -} - -void WebViewGuestDelegate::WillAttach( - content::WebContents* embedder_web_contents, - int element_instance_id, - bool is_full_page_plugin, - const base::Closure& completion_callback) { - embedder_web_contents_ = embedder_web_contents; - is_full_page_plugin_ = is_full_page_plugin; - // update the owner window - auto relay = NativeWindowRelay::FromWebContents(embedder_web_contents_); - if (relay) - api_web_contents_->SetOwnerWindow(relay->window.get()); - completion_callback.Run(); -} - -void WebViewGuestDelegate::GuestSizeChangedDueToAutoSize( - const gfx::Size& old_size, const gfx::Size& new_size) { - api_web_contents_->Emit("size-changed", - old_size.width(), old_size.height(), - new_size.width(), new_size.height()); -} - -gfx::Size WebViewGuestDelegate::GetDefaultSize() const { - if (is_full_page_plugin_) { - // Full page plugins default to the size of the owner's viewport. - return embedder_web_contents_->GetRenderWidgetHostView() - ->GetVisibleViewportSize(); - } else { - return gfx::Size(kDefaultWidth, kDefaultHeight); - } -} - -} // namespace atom diff --git a/atom/browser/web_view_guest_delegate.h b/atom/browser/web_view_guest_delegate.h deleted file mode 100644 index 280e2ca159..0000000000 --- a/atom/browser/web_view_guest_delegate.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_WEB_VIEW_GUEST_DELEGATE_H_ -#define ATOM_BROWSER_WEB_VIEW_GUEST_DELEGATE_H_ - -#include "content/public/browser/web_contents.h" -#include "content/public/browser/browser_plugin_guest_delegate.h" -#include "content/public/browser/web_contents_observer.h" - -namespace atom { - -namespace api { -class WebContents; -} - -// A struct of parameters for SetSize(). The parameters are all declared as -// scoped pointers since they are all optional. Null pointers indicate that the -// parameter has not been provided, and the last used value should be used. Note -// that when |enable_auto_size| is true, providing |normal_size| is not -// meaningful. This is because the normal size of the guestview is overridden -// whenever autosizing occurs. -struct SetSizeParams { - SetSizeParams() {} - ~SetSizeParams() {} - - std::unique_ptr enable_auto_size; - std::unique_ptr min_size; - std::unique_ptr max_size; - std::unique_ptr normal_size; -}; - -class WebViewGuestDelegate : public content::BrowserPluginGuestDelegate, - public content::WebContentsObserver { - public: - WebViewGuestDelegate(); - ~WebViewGuestDelegate() override; - - content::WebContents* CreateNewGuestWindow( - const content::WebContents::CreateParams& create_params) override; - - void Initialize(api::WebContents* api_web_contents); - - // Called when the WebContents is going to be destroyed. - void Destroy(); - - // Used to toggle autosize mode for this GuestView, and set both the automatic - // and normal sizes. - void SetSize(const SetSizeParams& params); - - // Returns the routing ID of the guest proxy in the owner's renderer process. - // This value is only valid after attachment or first navigation. - int proxy_routing_id() const { return guest_proxy_routing_id_; } - - bool IsAttached(); - - void RegisterGuest(mate::Handle guest, - int guest_instance_id); - int GetNextInstanceId(); - protected: - // content::BrowserPluginGuestDelegate: - void DidAttach(int guest_proxy_routing_id) final; - void DidDetach() final; - content::WebContents* GetOwnerWebContents() const final; - void GuestSizeChanged(const gfx::Size& new_size) final; - void SetGuestHost(content::GuestHost* guest_host) final; - void WillAttach(content::WebContents* embedder_web_contents, - int element_instance_id, - bool is_full_page_plugin, - const base::Closure& completion_callback) final; - - - - private: - // This method is invoked when the contents auto-resized to give the container - // an opportunity to match it if it wishes. - // - // This gives the derived class an opportunity to inform its container element - // or perform other actions. - void GuestSizeChangedDueToAutoSize(const gfx::Size& old_size, - const gfx::Size& new_size); - - // Returns the default size of the guestview. - gfx::Size GetDefaultSize() const; - - // The WebContents that attaches this guest view. - content::WebContents* embedder_web_contents_; - - // The size of the container element. - gfx::Size element_size_; - - // The size of the guest content. Note: In autosize mode, the container - // element may not match the size of the guest. - gfx::Size guest_size_; - - // A pointer to the guest_host. - content::GuestHost* guest_host_; - - // Indicates whether autosize mode is enabled or not. - bool auto_size_enabled_; - - // The maximum size constraints of the container element in autosize mode. - gfx::Size max_auto_size_; - - // The minimum size constraints of the container element in autosize mode. - gfx::Size min_auto_size_; - - // The size that will be used when autosize mode is disabled. - gfx::Size normal_size_; - - // Whether the guest view is inside a plugin document. - bool is_full_page_plugin_; - - api::WebContents* api_web_contents_; - - int guest_proxy_routing_id_; - - DISALLOW_COPY_AND_ASSIGN(WebViewGuestDelegate); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_WEB_VIEW_GUEST_DELEGATE_H_ diff --git a/atom/browser/web_view_manager.cc b/atom/browser/web_view_manager.cc deleted file mode 100644 index 815e60a166..0000000000 --- a/atom/browser/web_view_manager.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/web_view_manager.h" - -#include "atom/browser/atom_browser_context.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/web_contents.h" - -namespace atom { - -WebViewManager::WebViewManager() { -} - -WebViewManager::~WebViewManager() { -} - -void WebViewManager::AddGuest(int guest_instance_id, - int element_instance_id, - content::WebContents* embedder, - content::WebContents* web_contents) { - web_contents_embedder_map_[guest_instance_id] = { web_contents, embedder }; - - // Map the element in embedder to guest. - int owner_process_id = embedder->GetRenderProcessHost()->GetID(); - ElementInstanceKey key(owner_process_id, element_instance_id); - element_instance_id_to_guest_map_[key] = guest_instance_id; -} - -void WebViewManager::RemoveGuest(int guest_instance_id) { - if (!ContainsKey(web_contents_embedder_map_, guest_instance_id)) - return; - - web_contents_embedder_map_.erase(guest_instance_id); - - // Remove the record of element in embedder too. - for (const auto& element : element_instance_id_to_guest_map_) - if (element.second == guest_instance_id) { - element_instance_id_to_guest_map_.erase(element.first); - break; - } -} - -content::WebContents* WebViewManager::GetEmbedder(int guest_instance_id) { - if (ContainsKey(web_contents_embedder_map_, guest_instance_id)) - return web_contents_embedder_map_[guest_instance_id].embedder; - else - return nullptr; -} - -content::WebContents* WebViewManager::GetGuestByInstanceID( - int owner_process_id, - int element_instance_id) { - ElementInstanceKey key(owner_process_id, element_instance_id); - if (!ContainsKey(element_instance_id_to_guest_map_, key)) - return nullptr; - - int guest_instance_id = element_instance_id_to_guest_map_[key]; - if (ContainsKey(web_contents_embedder_map_, guest_instance_id)) - return web_contents_embedder_map_[guest_instance_id].web_contents; - else - return nullptr; -} - -bool WebViewManager::ForEachGuest(content::WebContents* embedder_web_contents, - const GuestCallback& callback) { - for (auto& item : web_contents_embedder_map_) - if (item.second.embedder == embedder_web_contents && - callback.Run(item.second.web_contents)) - return true; - return false; -} - -// static -WebViewManager* WebViewManager::GetWebViewManager( - content::WebContents* web_contents) { - auto context = web_contents->GetBrowserContext(); - if (context) { - auto manager = context->GetGuestManager(); - return static_cast(manager); - } else { - return nullptr; - } -} - -} // namespace atom diff --git a/atom/browser/web_view_manager.h b/atom/browser/web_view_manager.h deleted file mode 100644 index eb2ba8ad42..0000000000 --- a/atom/browser/web_view_manager.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_WEB_VIEW_MANAGER_H_ -#define ATOM_BROWSER_WEB_VIEW_MANAGER_H_ - -#include - -#include "content/public/browser/browser_plugin_guest_manager.h" - -namespace atom { - -class WebViewManager : public content::BrowserPluginGuestManager { - public: - WebViewManager(); - ~WebViewManager() override; - - void AddGuest(int guest_instance_id, - int element_instance_id, - content::WebContents* embedder, - content::WebContents* web_contents); - void RemoveGuest(int guest_instance_id); - content::WebContents* GetEmbedder(int guest_instance_id); - - static WebViewManager* GetWebViewManager(content::WebContents* web_contents); - - protected: - // content::BrowserPluginGuestManager: - content::WebContents* GetGuestByInstanceID(int owner_process_id, - int element_instance_id) override; - bool ForEachGuest(content::WebContents* embedder, - const GuestCallback& callback) override; - - private: - struct WebContentsWithEmbedder { - content::WebContents* web_contents; - content::WebContents* embedder; - }; - // guest_instance_id => (web_contents, embedder) - std::map web_contents_embedder_map_; - - struct ElementInstanceKey { - int embedder_process_id; - int element_instance_id; - - ElementInstanceKey(int embedder_process_id, int element_instance_id) - : embedder_process_id(embedder_process_id), - element_instance_id(element_instance_id) {} - - bool operator<(const ElementInstanceKey& other) const { - if (embedder_process_id != other.embedder_process_id) - return embedder_process_id < other.embedder_process_id; - return element_instance_id < other.element_instance_id; - } - - bool operator==(const ElementInstanceKey& other) const { - return (embedder_process_id == other.embedder_process_id) && - (element_instance_id == other.element_instance_id); - } - }; - // (embedder_process_id, element_instance_id) => guest_instance_id - std::map element_instance_id_to_guest_map_; - - DISALLOW_COPY_AND_ASSIGN(WebViewManager); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_WEB_VIEW_MANAGER_H_ diff --git a/atom/common/BUILD.gn b/atom/common/BUILD.gn new file mode 100644 index 0000000000..ae581dda9f --- /dev/null +++ b/atom/common/BUILD.gn @@ -0,0 +1,208 @@ +import("//build/buildflag_header.gni") +import("//build/config/chrome_build.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/features.gni") + +source_set("node") { + public_configs = [ + "//electron/build:electron_config", + ] + + sources = [ + "api/atom_api_asar.cc", + "api/atom_api_clipboard.cc", + # "api/atom_api_crash_reporter.cc", + "api/atom_api_key_weak_map.h", + "api/atom_api_native_image.cc", + "api/atom_api_native_image.h", + "api/atom_api_shell.cc", + "api/atom_api_v8_util.cc", + "api/atom_bindings.cc", + "api/atom_bindings.h", + "api/event_emitter_caller.cc", + "api/event_emitter_caller.h", + "api/locker.cc", + "api/locker.h", + "native_mate_converters/blink_converter.cc", + "native_mate_converters/blink_converter.h", + "native_mate_converters/callback.cc", + "native_mate_converters/callback.h", + "native_mate_converters/content_converter.cc", + "native_mate_converters/content_converter.h", + "native_mate_converters/file_path_converter.h", + "native_mate_converters/gfx_converter.cc", + "native_mate_converters/gfx_converter.h", + "native_mate_converters/gurl_converter.h", + "native_mate_converters/image_converter.cc", + "native_mate_converters/image_converter.h", + "native_mate_converters/net_converter.cc", + "native_mate_converters/net_converter.h", + "native_mate_converters/string16_converter.h", + "native_mate_converters/ui_base_types_converter.h", + "native_mate_converters/v8_value_converter.cc", + "native_mate_converters/v8_value_converter.h", + "native_mate_converters/value_converter.cc", + "native_mate_converters/value_converter.h", + "node_bindings.cc", + "node_bindings.h", + "node_includes.h", + ] + + sources += [ + # move the converter to accelerator.h + "native_mate_converters/accelerator_converter.cc", + "native_mate_converters/accelerator_converter.h", + ] + + public_deps = [ + "//electron/build/node", + "//electron:native_mate", + ] + + deps = [ + "//third_party/WebKit/public:blink", + ] +} + +source_set("common") { + public_configs = [ + "//electron/build:electron_config", + ] + + sources = [ + "api/api_messages.h", + "api/object_life_monitor.cc", + "api/object_life_monitor.h", + "api/remote_callback_freer.cc", + "api/remote_callback_freer.h", + "api/remote_object_freer.cc", + "api/remote_object_freer.h", + "asar/archive.cc", + "asar/archive.h", + "asar/asar_util.cc", + "asar/asar_util.h", + "asar/scoped_temporary_file.cc", + "asar/scoped_temporary_file.h", + "atom_command_line.cc", + "atom_command_line.h", + "atom_constants.cc", + "atom_constants.h", + "color_util.cc", + "color_util.h", + "common_message_generator.cc", + "common_message_generator.h", + # "crash_reporter/crash_reporter.cc", + # "crash_reporter/crash_reporter.h", + "draggable_region.cc", + "draggable_region.h", + "google_api_key.h", + "importer/chrome_importer_utils.cc", + "importer/chrome_importer_utils.h", + "importer/imported_cookie_entry.cc", + "importer/imported_cookie_entry.h", + "key_weak_map.h", + "keyboard_util.cc", + "keyboard_util.h", + "mouse_util.cc", + "mouse_util.h", + "options_switches.cc", + "options_switches.h", + "pepper_flash_util.cc", + "pepper_flash_util.h", + "platform_util.h", + ] + + public_deps = [ + ":node", + "//chrome/common", + "//components/autofill/content/common", + "//components/autofill/core/common", + ] + + deps = [ + "//base", + "//base:base_static", + "//base:i18n", + "//components/autofill/content/common", + "//components/autofill/core/common", + ] + + if (is_mac) { + sources += [ + "api/atom_api_native_image_mac.mm", + # "crash_reporter/crash_reporter_mac.h", + # "crash_reporter/crash_reporter_mac.mm", + "importer/chrome_importer_utils_mac.mm", + "node_bindings_mac.cc", + "node_bindings_mac.h", + "platform_util_mac.mm", + ] + + if (is_component_build) { + libs = [ + "Carbon.framework", + "CoreFoundation.framework", + "CoreGraphics.framework", + "CoreText.framework", + "Foundation.framework", + ] + } + } + + if (is_linux) { + sources += [ + # "crash_reporter/crash_reporter_linux.cc", + # "crash_reporter/crash_reporter_linux.h", + # "crash_reporter/linux/crash_dump_handler.cc", + # "crash_reporter/linux/crash_dump_handler.h", + "importer/chrome_importer_utils_linux.cc", + "linux/application_info.cc", + "node_bindings_linux.cc", + "node_bindings_linux.h", + "platform_util_linux.cc", + ] + } + + if (is_win) { + sources += [ + # "crash_reporter/crash_reporter_win.cc", + # "crash_reporter/crash_reporter_win.h", + # "crash_reporter/win/crash_service.cc", + # "crash_reporter/win/crash_service.h", + # "crash_reporter/win/crash_service_main.cc", + # "crash_reporter/win/crash_service_main.h", + "importer/chrome_importer_utils_win.cc", + "node_bindings_win.cc", + "node_bindings_win.h", + "platform_util_win.cc", + ] + } + + if (enable_basic_printing || enable_print_preview) { + public_deps += [ + "//components/printing/common", + "//printing", + ] + } + + if (enable_extensions) { + sources += [ + "extensions/atom_extensions_client.cc", + "extensions/atom_extensions_client.h", + ] + deps += [ + "//chrome/common/extensions/api", + "//chrome/common/extensions/api:api_registration", + "//chrome/common/extensions/api:extensions_features", + "//device/usb", + "//electron/brave/common/extensions/api", + "//electron/brave/common/extensions/api:api_registration", + "//electron/brave/common/extensions/api:extensions_features", + "//extensions:extensions_resources", + "//extensions/common", + "//extensions/common/api", + "//extensions/strings", + "//media/cast:net", + ] + } +} diff --git a/atom/common/api/atom_bindings.cc b/atom/common/api/atom_bindings.cc index cf80d64441..96676562a9 100644 --- a/atom/common/api/atom_bindings.cc +++ b/atom/common/api/atom_bindings.cc @@ -9,11 +9,11 @@ #include #include "atom/common/atom_version.h" -#include "atom/common/chrome_version.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/node_includes.h" #include "base/logging.h" #include "base/process/process_metrics.h" +#include "chrome/common/chrome_version.h" #include "native_mate/dictionary.h" namespace atom { @@ -116,7 +116,7 @@ void AtomBindings::BindTo(v8::Isolate* isolate, mate::Dictionary versions; if (dict.Get("versions", &versions)) { - versions.Set(ATOM_PROJECT_NAME, ATOM_VERSION_STRING); + versions.Set(PRODUCT_FULLNAME_STRING, ATOM_VERSION_STRING); versions.Set("atom-shell", ATOM_VERSION_STRING); // For compatibility. versions.Set("chrome", CHROME_VERSION_STRING); } diff --git a/atom/common/api/resources/_api_features.json b/atom/common/api/resources/_api_features.json deleted file mode 100644 index 78a0c14342..0000000000 --- a/atom/common/api/resources/_api_features.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "ipc": { - "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"], - "contexts": [ - "content_script", - "blessed_extension", - "unblessed_extension" - ] - }, - "webFrame": { - "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"], - "contexts": [ - "content_script", - "blessed_extension" - ] - }, - "app": { - "blacklist": [ - "2FC374607C2DF285634B67C64A2E356C607091C3", // Quickoffice - "3727DD3E564B6055387425027AD74C58784ACC15", // Quickoffice internal - "12E618C3C6E97495AAECF2AC12DEB082353241C6", // QO component extension - "06BE211D5F014BAB34BC22D9DDA09C63A81D828E", // Official xkb extension - "F94EE6AB36D6C6588670B2B01EB65212D9C64E33", // Open source xkb extension - "B9EF10DDFEA11EF77873CC5009809E5037FC4C7A" // Google input tools - ], - "channel": "stable", - "extension_types": ["hosted_app", "extension", "legacy_packaged_app"], - "contexts": [ - "blessed_extension", - "unblessed_extension", - "content_script", - "web_page", - "blessed_web_page" - ], - // Any webpage can use the app API. - "matches": [""] - }, - "tabs": { - "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"], - "contexts": ["blessed_extension"] - }, - "browserAction": { - "channel": "stable", - "contexts": ["blessed_extension"] - }, - "contextMenus": { - "channel": "stable", - "contexts": ["blessed_extension"] - }, - "privacy": { - "channel": "stable", - "contexts": ["blessed_extension"] - }, - "windows": { - "channel": "stable", - "contexts": ["blessed_extension"] - }, - "extension": { - "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"], - "contexts": ["blessed_extension"] - }, - "extension.getURL": { - "contexts": ["blessed_extension", "unblessed_extension", "content_script"] - }, - "extension.getViews": { - "channel": "stable", - "contexts": ["blessed_extension"], - "extension_types": ["extension", "legacy_packaged_app"] - }, - "extension.inIncognitoContext": { - "contexts": ["blessed_extension", "unblessed_extension", "content_script"] - }, - "extension.lastError": { - "contexts": ["blessed_extension", "unblessed_extension", "content_script"] - }, - "extension.onRequest": { - "contexts": ["blessed_extension", "unblessed_extension", "content_script"] - }, - "extension.sendRequest": { - "contexts": ["blessed_extension", "unblessed_extension", "content_script"] - }, - "contentSettings": { - "dependencies": ["permission:contentSettings"], - "contexts": ["blessed_extension"] - }, - "contentSettings.get": { - "dependencies": ["permission:contentSettings"], - "contexts": ["blessed_extension", "content_script"] - } -} diff --git a/atom/common/api/resources/_permission_features.json b/atom/common/api/resources/_permission_features.json deleted file mode 100644 index 7c3cce0f9d..0000000000 --- a/atom/common/api/resources/_permission_features.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "contentSettings": { - "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"] - }, - "nativeMessaging": { - "channel": "stable", - "extension_types": [ - "extension", "legacy_packaged_app", "platform_app" - ] - }, - "management": [ - { - "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"] - } - ] -} diff --git a/atom/common/api/resources/app_bindings.js b/atom/common/api/resources/app_bindings.js index 4484dc7dfb..9cee491d53 100644 --- a/atom/common/api/resources/app_bindings.js +++ b/atom/common/api/resources/app_bindings.js @@ -1,9 +1 @@ -var app = { - // this is just a stub to make some `isChrome` checks pass -} - -var binding = { - app -} - -exports.binding = binding; +exports.binding = {} diff --git a/atom/common/api/resources/event_emitter.js b/atom/common/api/resources/event_emitter.js index 375d77c280..763d402db1 100644 --- a/atom/common/api/resources/event_emitter.js +++ b/atom/common/api/resources/event_emitter.js @@ -18,7 +18,7 @@ EventEmitter2.prototype.once = function (event, fn) { return this; }; -EventEmitter2.prototype.off = function off(event, fn) { +EventEmitter2.prototype.off = function (event, fn) { this._callbacks = this._callbacks || {}; var callbacks = this._callbacks['$' + event]; @@ -56,4 +56,10 @@ EventEmitter2.prototype.hasListeners = function(event){ return !!this.listeners(event).length; }; +EventEmitter2.prototype.removeAllListeners = function(event){ + delete this._callbacks['$' + event] +}; + +EventEmitter2.prototype.removeListener = EventEmitter2.prototype.off + exports.EventEmitter = EventEmitter2; diff --git a/atom/common/api/resources/ipc.json b/atom/common/api/resources/ipc.json deleted file mode 100644 index c2dcabf889..0000000000 --- a/atom/common/api/resources/ipc.json +++ /dev/null @@ -1,72 +0,0 @@ -[ - { - "namespace": "ipc", - "description": "none", - "functions": [ - { - "name": "send", - "nocompile": true, - "type": "function", - "description": "none", - "allowAmbiguousOptionalArguments": true, - "parameters": [ - { - "type": "string", - "name": "message" - } - ], - "returns": { - "type": "object" - } - }, - { - "name": "sendSync", - "nocompile": true, - "type": "function", - "description": "none", - "allowAmbiguousOptionalArguments": true, - "parameters": [ - { - "type": "string", - "name": "message" - } - ], - "returns": { - "type": "object" - } - }, - { - "name": "sendToHost", - "nocompile": true, - "type": "function", - "description": "none", - "allowAmbiguousOptionalArguments": true, - "parameters": [ - { - "type": "string", - "name": "message" - } - ], - "returns": { - "type": "object" - } - }, - { - "name": "on", - "nocompile": true, - "type": "function", - "description": "none", - "allowAmbiguousOptionalArguments": true, - "parameters": [ - { - "type": "string", - "name": "message" - } - ], - "returns": { - "type": "object" - } - } - ] - } -] diff --git a/atom/common/api/resources/ipc_bindings.js b/atom/common/api/resources/ipc_bindings.js index 6ff274da85..cec845f7cb 100644 --- a/atom/common/api/resources/ipc_bindings.js +++ b/atom/common/api/resources/ipc_bindings.js @@ -1,4 +1,4 @@ -var ipc = require('ipc_utils') +const ipc = require('ipc_utils') exports.didCreateDocumentElement = function() { // don't try to run if there is no window object @@ -31,5 +31,3 @@ exports.didCreateDocumentElement = function() { return window.chrome.tabs.create({url: url}) } } - -exports.binding = ipc diff --git a/atom/common/api/resources/ipc_utils.js b/atom/common/api/resources/ipc_utils.js index 966a21f65f..da67ef7ce6 100644 --- a/atom/common/api/resources/ipc_utils.js +++ b/atom/common/api/resources/ipc_utils.js @@ -26,15 +26,30 @@ if (!ipcRenderer) { } ipcRenderer.emit = function () { - arguments[1].sender = ipcRenderer + arguments[1] && (arguments[1].sender = ipcRenderer) return EventEmitter.prototype.emit.apply(ipcRenderer, arguments) } atom.v8.setHiddenValue('ipc', ipcRenderer) } -exports.on = ipcRenderer.on.bind(ipcRenderer); -exports.once = ipcRenderer.once.bind(ipcRenderer); -exports.send = ipcRenderer.send.bind(ipcRenderer); -exports.sendSync = ipcRenderer.sendSync.bind(ipcRenderer); -exports.sendToHost = ipcRenderer.sendToHost.bind(ipcRenderer); -exports.emit = ipcRenderer.emit.bind(ipcRenderer); +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +} + +exports.$set('guid', guid) +exports.$set('removeListener', ipcRenderer.removeListener.bind(ipcRenderer)) +exports.$set('off', ipcRenderer.off.bind(ipcRenderer)) +exports.$set('removeAllListeners', ipcRenderer.removeAllListeners.bind(ipcRenderer)) +exports.$set('on', ipcRenderer.on.bind(ipcRenderer)) +exports.$set('once', ipcRenderer.once.bind(ipcRenderer)) +exports.$set('send', ipcRenderer.send.bind(ipcRenderer)) +exports.$set('sendSync', ipcRenderer.sendSync.bind(ipcRenderer)) +exports.$set('sendToHost', ipcRenderer.sendToHost.bind(ipcRenderer)) +exports.$set('emit', ipcRenderer.emit.bind(ipcRenderer)) + diff --git a/atom/common/api/resources/tab_view_internal_bindings.js b/atom/common/api/resources/tab_view_internal_bindings.js new file mode 100644 index 0000000000..9f009950cb --- /dev/null +++ b/atom/common/api/resources/tab_view_internal_bindings.js @@ -0,0 +1,3 @@ +exports.$set( + 'TabViewInternal', + require('binding').Binding.create('tabViewInternal').generate()); diff --git a/atom/common/api/resources/web_frame.json b/atom/common/api/resources/web_frame.json deleted file mode 100644 index dcaeb06da1..0000000000 --- a/atom/common/api/resources/web_frame.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "namespace": "webFrame", - "description": "none", - "functions": [ - { - "name": "setGlobal", - "nocompile": true, - "type": "function", - }, - { - "name": "setSpellCheckProvider", - "nocompile": true, - "type": "function" - }, - { - "name": "executeJavaScript", - "nocompile": true, - "type": "function" - } - ] - } -] diff --git a/atom/common/api/resources/web_frame_bindings.js b/atom/common/api/resources/web_frame_bindings.js index 38545beb5a..f6f47788a9 100644 --- a/atom/common/api/resources/web_frame_bindings.js +++ b/atom/common/api/resources/web_frame_bindings.js @@ -1,13 +1,22 @@ -var webFrame = requireNative('webFrame'); +var binding = require('binding').Binding.create('webFrame') -function setGlobal(path, value) { - return webFrame.setGlobal(path.split('.'), value) -} +var webFrameNatives = requireNative('webFrame') -var binding = { - setSpellCheckProvider: webFrame.setSpellCheckProvider, - executeJavaScript: webFrame.executeJavaScript, - setGlobal -} +binding.registerCustomHook(function(bindingsAPI, extensionId) { + var apiFunctions = bindingsAPI.apiFunctions; + var webFrame = bindingsAPI.compiledApi; -exports.binding = binding + apiFunctions.setHandleRequest('setGlobal', function(path, value) { + return webFrameNatives.setGlobal(path.split('.'), value) + }) + + apiFunctions.setHandleRequest('executeJavaScript', function(code) { + return webFrameNatives.executeJavaScript(code) + }) + + apiFunctions.setHandleRequest('setSpellCheckProvider', function(lang, autoCorrectEnabled, spellCheckProvider) { + return webFrameNatives.setSpellCheckProvider(lang, autoCorrectEnabled, spellCheckProvider) + }) +}) + +exports.$set('binding', binding.generate()) diff --git a/atom/common/api/resources/web_view_api_bindings.js b/atom/common/api/resources/web_view_api_bindings.js new file mode 100644 index 0000000000..ae641b5f3f --- /dev/null +++ b/atom/common/api/resources/web_view_api_bindings.js @@ -0,0 +1,210 @@ +// Copyright 2014 The Brave 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 module implements the public-facing API functions for the tag. + +const WebViewInternal = require('webViewInternal').WebViewInternal; +const TabViewInternal = require('tabViewInternal').TabViewInternal; +const WebViewImpl = require('webView').WebViewImpl; +const remote = require('remote') +const GuestViewInternal = require('guest-view-internal').GuestViewInternal + +const asyncMethods = [ + 'loadURL', + 'stop', + 'reload', + 'reloadIgnoringCache', + 'clearHistory', + 'goBack', + 'goForward', + 'goToIndex', + 'goToOffset', + 'setUserAgent', + 'openDevTools', + 'closeDevTools', + 'inspectElement', + 'setAudioMuted', + 'undo', + 'redo', + 'cut', + 'copy', + 'paste', + 'pasteAndMatchStyle', + 'delete', + 'selectAll', + 'unselect', + 'replace', + 'replaceMisspelling', + 'findInPage', + 'stopFindInPage', + 'downloadURL', + 'inspectServiceWorker', + 'print', + 'printToPDF', + 'showDefinitionForSelection', + 'capturePage', + 'setActive', + 'setWebRTCIPHandlingPolicy', + 'executeScriptInTab', + 'setZoomLevel', + 'zoomIn', + 'zoomOut', + 'zoomReset', + 'enablePreferredSizeMode', + 'close', + 'focus', + 'send', +] + +const syncMethods = [ + 'getURL', + 'canGoBack', + 'canGoForward', + 'getWebRTCIPHandlingPolicy', + 'getWebContents', + 'isDevToolsOpened', +] + +var WEB_VIEW_API_METHODS = [ + 'setGuestInstanceId', + // Returns Chrome's internal process ID for the guest web page's current + // process. + 'getProcessId', + + // Returns the user agent string used by the webview for guest page requests. + 'getUserAgent', + + // Gets the current zoom factor. + 'getZoom', + + // Gets the current zoom mode of the webview. + 'getZoomMode', + + // Indicates whether or not the webview's user agent string has been + // overridden. + 'isUserAgentOverridden', + + // Override the user agent string used by the webview for guest page requests. + 'setUserAgentOverride', + + // Changes the zoom factor of the page. + 'setZoom', +].concat(asyncMethods).concat(syncMethods) + +asyncMethods.forEach((method) => { + WebViewImpl.prototype[method] = function () { + if (!this.guest.getId()) + return + + this.getTabID(this.guest.getId(), (tabID) => { + remote.callAsyncWebContentsFunction(tabID, method, arguments) + }) + } +}) + +syncMethods.forEach((method) => { + WebViewImpl.prototype[method] = function () { + if (!this.guest.getId()) + return + + let webContents = this.getWebContents() + if (!webContents) + throw "webContents is not available" + return webContents[method].apply(this, arguments) + } +}) + +// GuestViewContainer.prototype.weakWrapper = function(func) { +// var viewInstanceId = this.viewInstanceId; +// return function() { +// var view = GuestViewInternalNatives.GetViewFromID(viewInstanceId); +// if (view) { +// return $Function.apply(func, view, $Array.slice(arguments)); +// } +// }; +// }; + +// ----------------------------------------------------------------------------- +// Custom API method implementations. +WebViewImpl.prototype.getTabID = function (instanceId, cb) { + if (!this.tabID) { + TabViewInternal.getTabID(instanceId, (tabID) => { + this.tabID = tabID + cb(tabID) + }) + } else { + cb(this.tabID) + } +} + +const attachWindow = WebViewImpl.prototype.attachWindow$ +WebViewImpl.prototype.attachWindow$ = function(opt_guestInstanceId) { + let attached = attachWindow.bind(this)(opt_guestInstanceId) + // preload the webcontents and tabID + const guestInstanceId = opt_guestInstanceId || this.guest.getId() + this.getTabID(guestInstanceId, (tabID) => { + this.tabID = tabID + GuestViewInternal.registerEvents(this, tabID) + }) + this.getWebContents() + return attached +} + +WebViewImpl.prototype.setGuestInstanceId = function (guestInstanceId) { + return this.attachWindow$(guestInstanceId) +} + +WebViewImpl.prototype.getWebContents = function (cb) { + if (!this.webContents) { + WebViewInternal.getWebContents(this.guest.getId(), (webContents) => { + this.webContents_ = webContents + cb && cb(this.webContents_) + }) + } else { + cb(this.webContents_) + } + return this.webContents_ +} + +WebViewImpl.prototype.getProcessId = function() { + return this.processId; +}; + +WebViewImpl.prototype.getUserAgent = function() { + return this.userAgentOverride || navigator.userAgent; +}; + +WebViewImpl.prototype.isUserAgentOverridden = function() { + return !!this.userAgentOverride && + this.userAgentOverride != navigator.userAgent; +}; + +WebViewImpl.prototype.setUserAgentOverride = function(userAgentOverride) { + this.userAgentOverride = userAgentOverride; + if (!this.guest.getId()) { + // If we are not attached yet, then we will pick up the user agent on + // attachment. + return false; + } + WebViewInternal.overrideUserAgent(this.guest.getId(), userAgentOverride); + return true; +}; + +WebViewImpl.prototype.setZoom = function(zoomFactor, callback) { + if (!this.guest.getId()) { + this.cachedZoomFactor = zoomFactor; + return false; + } + this.cachedZoomFactor = 1; + WebViewInternal.setZoom(this.guest.getId(), zoomFactor, callback); + return true; +}; + +// ----------------------------------------------------------------------------- + +WebViewImpl.getApiMethods = function() { + return WEB_VIEW_API_METHODS; +}; + +// WebViewImpl.setupElement(WebViewImpl.prototype) diff --git a/atom/common/api/resources/web_view_internal_bindings.js b/atom/common/api/resources/web_view_internal_bindings.js new file mode 100644 index 0000000000..13e0425ed9 --- /dev/null +++ b/atom/common/api/resources/web_view_internal_bindings.js @@ -0,0 +1,42 @@ +const TabViewInternal = require('tabViewInternal').TabViewInternal; +const remote = require('remote') + +const WebViewInternal = { + getWebContents: function (instanceId, cb) { + if (!instanceId) { + return + } + + TabViewInternal.getTabID(instanceId, (tabID) => { + if (tabID && tabID !== -1) { + cb(remote.getWebContents(tabID)) + } else { + // TODO(bridiver) - lastError + console.warn('Could not find tab for guestInstanceId ' + instanceId) + cb() + } + }) + }, + + navigate: function (instanceId, src) { + const webContents = WebViewInternal.getWebContents(instanceId, (webContents) => { + if (webContents) { + webContents.loadURL(src) + } + }) + }, + + setAllowScaling: function (instanceId, val) { + // ignore + }, + + setAllowTransparency: function (instanceId, val) { + // ignore + }, + + setName: function (instanceId, val) { + // ignore + } +} + +exports.$set('WebViewInternal', WebViewInternal); diff --git a/atom/common/asar/archive.cc b/atom/common/asar/archive.cc index a3a02996d4..af3319060d 100644 --- a/atom/common/asar/archive.cc +++ b/atom/common/asar/archive.cc @@ -20,6 +20,8 @@ #include "atom/node/osfhandle.h" #endif +#include "base/threading/thread_restrictions.h" + namespace asar { namespace { @@ -129,6 +131,7 @@ Archive::Archive(const base::FilePath& path) } Archive::~Archive() { + base::ThreadRestrictions::SetIOAllowed(true); // ugh electron #if defined(OS_WIN) if (fd_ != -1) { node::close(fd_); diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h deleted file mode 100644 index 9fbacf4e73..0000000000 --- a/atom/common/chrome_version.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -// This file is generated by script/bootstrap.py, you should never modify it -// by hand. - -#ifndef ATOM_COMMON_CHROME_VERSION_H_ -#define ATOM_COMMON_CHROME_VERSION_H_ - -#define CHROME_VERSION_STRING "53.0.2785.143" -#define CHROME_VERSION "v" CHROME_VERSION_STRING - -#endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/atom/common/common_message_generator.cc b/atom/common/common_message_generator.cc index 854fc8778e..75ee93162a 100644 --- a/atom/common/common_message_generator.cc +++ b/atom/common/common_message_generator.cc @@ -14,6 +14,12 @@ #include "ipc/struct_destructor_macros.h" #include "atom/common/common_message_generator.h" +// Generate param traits size methods. +#include "ipc/param_traits_size_macros.h" +namespace IPC { +#include "atom/common/common_message_generator.h" +} // namespace IPC + // Generate param traits write methods. #include "ipc/param_traits_write_macros.h" namespace IPC { @@ -31,4 +37,3 @@ namespace IPC { namespace IPC { #include "atom/common/common_message_generator.h" } // namespace IPC - diff --git a/atom/common/common_message_generator.h b/atom/common/common_message_generator.h index 6420695686..57c5377d10 100644 --- a/atom/common/common_message_generator.h +++ b/atom/common/common_message_generator.h @@ -3,9 +3,15 @@ // found in the LICENSE file. // Multiply-included file, no traditional include guard. - #include "atom/common/api/api_messages.h" -#include "chrome/common/print_messages.h" -#include "chrome/common/tts_messages.h" -#include "chrome/common/widevine_cdm_messages.h" -#include "chrome/common/chrome_utility_messages.h" +// #if defined(ENABLE_PLUGINS) +// #include "ppapi/proxy/ppapi_messages.h" +// #endif + +// #include "chrome/common/tts_messages.h" +// #include "chrome/common/render_messages.h" +// #include "chrome/common/chrome_utility_messages.h" +// #include "content/public/common/common_param_traits.h" +// #include "content/public/common/common_param_traits_macros.h" +// #include "chrome/common/common_param_traits_macros.h" +// #include "components/printing/common/print_messages.h" diff --git a/atom/common/crash_reporter/crash_reporter_mac.mm b/atom/common/crash_reporter/crash_reporter_mac.mm index ee2ce03604..d70cfa1f54 100644 --- a/atom/common/crash_reporter/crash_reporter_mac.mm +++ b/atom/common/crash_reporter/crash_reporter_mac.mm @@ -67,7 +67,7 @@ simple_string_dictionary_.reset(new crashpad::SimpleStringDictionary()); crashpad_info->set_simple_annotations(simple_string_dictionary_.get()); - SetCrashKeyValue("prod", ATOM_PRODUCT_NAME); + SetCrashKeyValue("prod", CHROMIUM_SHORT_NAME); SetCrashKeyValue("process_type", is_browser_ ? "browser" : "renderer"); SetCrashKeyValue("ver", version); diff --git a/atom/common/crash_reporter/win/crash_service_main.cc b/atom/common/crash_reporter/win/crash_service_main.cc index c6325f090a..9cf005ff01 100644 --- a/atom/common/crash_reporter/win/crash_service_main.cc +++ b/atom/common/crash_reporter/win/crash_service_main.cc @@ -81,7 +81,7 @@ int Main(const wchar_t* cmd) { NULL); cmd_line.AppendSwitch("no-window"); cmd_line.AppendSwitchASCII("max-reports", "128"); - cmd_line.AppendSwitchASCII("reporter", ATOM_PROJECT_NAME "-crash-service"); + cmd_line.AppendSwitchASCII("reporter", CHROMIUM_SHORT_NAME "-crash-service"); cmd_line.AppendSwitchNative("pipe-name", pipe_name); breakpad::CrashService crash_service; diff --git a/atom/common/extensions/atom_extensions_client.cc b/atom/common/extensions/atom_extensions_client.cc index 6f53c431dc..c888b59907 100644 --- a/atom/common/extensions/atom_extensions_client.cc +++ b/atom/common/extensions/atom_extensions_client.cc @@ -5,17 +5,23 @@ #include "atom/common/extensions/atom_extensions_client.h" #include -#include "atom/grit/atom_resources.h" // NOLINT: This file is generated +#include "brave/grit/brave_resources.h" // NOLINT: This file is generated +#include "brave/common/extensions/api/generated_schemas.h" // NOLINT: This file is generated +#include "chrome/common/chrome_version.h" #include "chrome/common/extensions/api/generated_schemas.h" // NOLINT: This file is generated #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" #include "chrome/grit/common_resources.h" // NOLINT: This file is generated -#include "chrome/grit/extensions_api_resources.h" // NOLINT: This file is generated +#include "brave/common/extensions/api/api_features.h" +#include "brave/common/extensions/api/behavior_features.h" +#include "chrome/common/extensions/api/extension_action/action_info.h" +#include "chrome/common/extensions/api/generated_schemas.h" +#include "brave/common/extensions/api/manifest_features.h" +#include "brave/common/extensions/api/permission_features.h" #include "extensions/common/api/generated_schemas.h" // NOLINT: This file is generated #include "extensions/common/common_manifest_handlers.h" #include "extensions/common/extension_api.h" #include "extensions/common/extension_urls.h" #include "extensions/common/features/api_feature.h" -#include "extensions/common/features/base_feature_provider.h" #include "extensions/common/features/behavior_feature.h" #include "extensions/common/features/json_feature_provider_source.h" #include "extensions/common/features/manifest_feature.h" @@ -99,26 +105,20 @@ AtomExtensionsClient::GetPermissionMessageProvider() const { } const std::string AtomExtensionsClient::GetProductName() { - return ATOM_PRODUCT_NAME; + return PRODUCT_SHORTNAME_STRING; } std::unique_ptr AtomExtensionsClient::CreateFeatureProvider( const std::string& name) const { std::unique_ptr provider; - std::unique_ptr source( - CreateFeatureProviderSource(name)); if (name == "api") { - provider.reset(new BaseFeatureProvider(source->dictionary(), - CreateFeature)); + provider.reset(new BraveAPIFeatureProvider()); } else if (name == "manifest") { - provider.reset(new BaseFeatureProvider(source->dictionary(), - CreateFeature)); + provider.reset(new BraveManifestFeatureProvider()); } else if (name == "permission") { - provider.reset(new BaseFeatureProvider(source->dictionary(), - CreateFeature)); + provider.reset(new BravePermissionFeatureProvider()); } else if (name == "behavior") { - provider.reset(new BaseFeatureProvider(source->dictionary(), - CreateFeature)); + provider.reset(new BraveBehaviorFeatureProvider()); } else { NOTREACHED(); } @@ -126,27 +126,31 @@ std::unique_ptr AtomExtensionsClient::CreateFeatureProvider( } std::unique_ptr -AtomExtensionsClient::CreateFeatureProviderSource( - const std::string& name) const { +AtomExtensionsClient::CreateAPIFeatureSource() const { std::unique_ptr source( - new JSONFeatureProviderSource(name)); - if (name == "api") { - source->LoadJSON(IDR_EXTENSION_API_FEATURES); - source->LoadJSON(IDR_ATOM_API_FEATURES); - } else if (name == "manifest") { - source->LoadJSON(IDR_EXTENSION_MANIFEST_FEATURES); - } else if (name == "permission") { - source->LoadJSON(IDR_EXTENSION_PERMISSION_FEATURES); - source->LoadJSON(IDR_ATOM_PERMISSION_FEATURES); - } else if (name == "behavior") { - source->LoadJSON(IDR_EXTENSION_BEHAVIOR_FEATURES); - } else { - NOTREACHED(); - source.reset(); - } + new JSONFeatureProviderSource("api")); + source->LoadJSON(IDR_EXTENSION_API_FEATURES); + source->LoadJSON(IDR_BRAVE_API_FEATURES); return source; } +std::set AtomExtensionsClient::GetBrowserImagePaths( + const Extension* extension) { + std::set image_paths = + ExtensionsClient::GetBrowserImagePaths(extension); + + const ActionInfo* page_action = ActionInfo::GetPageActionInfo(extension); + if (page_action && !page_action->default_icon.empty()) + page_action->default_icon.GetPaths(&image_paths); + + const ActionInfo* browser_action = + ActionInfo::GetBrowserActionInfo(extension); + if (browser_action && !browser_action->default_icon.empty()) + browser_action->default_icon.GetPaths(&image_paths); + + return image_paths; +} + void AtomExtensionsClient::FilterHostPermissions( const URLPatternSet& hosts, URLPatternSet* new_hosts, @@ -177,12 +181,16 @@ bool AtomExtensionsClient::IsScriptableURL( bool AtomExtensionsClient::IsAPISchemaGenerated( const std::string& name) const { - return api::ChromeGeneratedSchemas::IsGenerated(name) || + return api::BraveGeneratedSchemas::IsGenerated(name) || + api::ChromeGeneratedSchemas::IsGenerated(name) || api::GeneratedSchemas::IsGenerated(name); } base::StringPiece AtomExtensionsClient::GetAPISchema( const std::string& name) const { + if (api::BraveGeneratedSchemas::IsGenerated(name)) + return api::BraveGeneratedSchemas::Get(name); + // Test from most common to least common. if (api::ChromeGeneratedSchemas::IsGenerated(name)) return api::ChromeGeneratedSchemas::Get(name); @@ -190,13 +198,6 @@ base::StringPiece AtomExtensionsClient::GetAPISchema( return api::GeneratedSchemas::Get(name); } -void AtomExtensionsClient::RegisterAPISchemaResources( - ExtensionAPI* api) const { - api->RegisterSchemaResource("ipc", IDR_ATOM_EXTENSION_API_JSON_IPC); - api->RegisterSchemaResource("webFrame", - IDR_ATOM_EXTENSION_API_JSON_WEB_FRAME); -} - bool AtomExtensionsClient::ShouldSuppressFatalErrors() const { return true; } diff --git a/atom/common/extensions/atom_extensions_client.h b/atom/common/extensions/atom_extensions_client.h index 79798e7082..d7525e55b2 100644 --- a/atom/common/extensions/atom_extensions_client.h +++ b/atom/common/extensions/atom_extensions_client.h @@ -7,9 +7,9 @@ #include -#include "atom/common/extensions/permissions/chrome_api_permissions.h" #include "base/compiler_specific.h" #include "base/macros.h" +#include "chrome/common/extensions/permissions/chrome_api_permissions.h" #include "extensions/common/extensions_client.h" #include "extensions/common/permissions/extensions_api_permissions.h" @@ -27,8 +27,8 @@ class AtomExtensionsClient : public ExtensionsClient { const std::string GetProductName() override; std::unique_ptr CreateFeatureProvider( const std::string& name) const override; - std::unique_ptr CreateFeatureProviderSource( - const std::string& name) const override; + std::unique_ptr CreateAPIFeatureSource() + const override; void FilterHostPermissions(const URLPatternSet& hosts, URLPatternSet* new_hosts, PermissionIDSet* permissions) const override; @@ -40,12 +40,13 @@ class AtomExtensionsClient : public ExtensionsClient { bool IsScriptableURL(const GURL& url, std::string* error) const override; bool IsAPISchemaGenerated(const std::string& name) const override; base::StringPiece GetAPISchema(const std::string& name) const override; - void RegisterAPISchemaResources(ExtensionAPI* api) const override; bool ShouldSuppressFatalErrors() const override; void RecordDidSuppressFatalError() override; std::string GetWebstoreBaseURL() const override; std::string GetWebstoreUpdateURL() const override; bool IsBlacklistUpdateURL(const GURL& url) const override; + std::set GetBrowserImagePaths( + const Extension* extension); // Get the LazyInstance for AtomExtensionsClient. static AtomExtensionsClient* GetInstance(); diff --git a/atom/common/extensions/permissions/chrome_api_permissions.cc b/atom/common/extensions/permissions/chrome_api_permissions.cc deleted file mode 100644 index 0c7b0ad59b..0000000000 --- a/atom/common/extensions/permissions/chrome_api_permissions.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. -#include "atom/common/extensions/permissions/chrome_api_permissions.h" - -#include - -#include "base/macros.h" -#include "extensions/common/permissions/api_permission.h" -#include "extensions/common/permissions/api_permission_set.h" -#include "extensions/common/permissions/permissions_info.h" -#include "extensions/common/permissions/settings_override_permission.h" -#include "extensions/strings/grit/extensions_strings.h" - -namespace extensions { - -namespace { - -const char kWindowsPermission[] = "windows"; - -} // namespace - -std::vector ChromeAPIPermissions::GetAllPermissions() - const { - APIPermissionInfo::InitInfo permissions_to_register[] = { - // Register permissions for all extension types. - {APIPermission::kBackground, "background"}, - {APIPermission::kActiveTab, "activeTab"}, - {APIPermission::kContextMenus, "contextMenus"}, - {APIPermission::kCookie, "cookies"}, - {APIPermission::kManagement, "management"}, - {APIPermission::kNativeMessaging, "nativeMessaging"}, - {APIPermission::kPrivacy, "privacy"}, - {APIPermission::kTab, "tabs"}, - {APIPermission::kContentSettings, "contentSettings"}, - - // Full url access permissions. - {APIPermission::kDebugger, "debugger", - APIPermissionInfo::kFlagImpliesFullURLAccess | - APIPermissionInfo::kFlagCannotBeOptional}, - {APIPermission::kDevtools, "devtools", - APIPermissionInfo::kFlagImpliesFullURLAccess | - APIPermissionInfo::kFlagCannotBeOptional | - APIPermissionInfo::kFlagInternal}, - {APIPermission::kPageCapture, "pageCapture", - APIPermissionInfo::kFlagImpliesFullURLAccess}, - }; - std::vector permissions; - - for (size_t i = 0; i < arraysize(permissions_to_register); ++i) - permissions.push_back(new APIPermissionInfo(permissions_to_register[i])); - return permissions; -} - -std::vector -ChromeAPIPermissions::GetAllAliases() const { - std::vector aliases; - aliases.push_back(PermissionsProvider::AliasInfo("tabs", kWindowsPermission)); - return aliases; -} - -} // namespace extensions diff --git a/atom/common/extensions/permissions/chrome_api_permissions.h b/atom/common/extensions/permissions/chrome_api_permissions.h deleted file mode 100644 index 454d4f0d68..0000000000 --- a/atom/common/extensions/permissions/chrome_api_permissions.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_COMMON_EXTENSIONS_PERMISSIONS_CHROME_API_PERMISSIONS_H_ -#define ATOM_COMMON_EXTENSIONS_PERMISSIONS_CHROME_API_PERMISSIONS_H_ - -#include - -#include "base/compiler_specific.h" -#include "extensions/common/permissions/permissions_provider.h" - -namespace extensions { - -// this class has to be extensions::ChromeAPIPermissions because -// InitInfo is a private member of APIPermissionInfo and only -// ExtensionAPIPermission and ChromeAPIPermission are listed -// as friend classes -class ChromeAPIPermissions : public PermissionsProvider { - public: - std::vector GetAllPermissions() const override; - std::vector GetAllAliases() const override; -}; - -} // namespace extensions - -#endif // ATOM_COMMON_EXTENSIONS_PERMISSIONS_CHROME_API_PERMISSIONS_H_ diff --git a/atom/common/javascript_bindings.cc b/atom/common/javascript_bindings.cc index 3f9e1eef9c..82afd0fbf1 100644 --- a/atom/common/javascript_bindings.cc +++ b/atom/common/javascript_bindings.cc @@ -5,8 +5,11 @@ #include "atom/common/javascript_bindings.h" #include -#include "atom/browser/web_contents_preferences.h" #include "atom/common/api/api_messages.h" +#include "atom/common/api/remote_callback_freer.h" +#include "atom/common/api/remote_object_freer.h" +#include "atom/common/api/atom_api_key_weak_map.h" +#include "atom/common/native_mate_converters/content_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/native_mate_converters/value_converter.h" #include "content/public/renderer/render_frame.h" @@ -72,6 +75,33 @@ v8::Local JavascriptBindings::GetHiddenValue(v8::Isolate* isolate, return v8::Local(); } +v8::Local JavascriptBindings::GetHiddenValueOnObject( + v8::Isolate* isolate, + v8::Local object, + v8::Local key) { + v8::Local context = isolate->GetCurrentContext(); + v8::Local privateKey = v8::Private::ForApi(isolate, key); + v8::Local value; + v8::Maybe result = object->HasPrivate(context, privateKey); + if (!(result.IsJust() && result.FromJust())) + return v8::Local(); + if (object->GetPrivate(context, privateKey).ToLocal(&value)) + return value; + return v8::Local(); +} + +void JavascriptBindings::SetHiddenValueOnObject(v8::Isolate* isolate, + v8::Local object, + v8::Local key, + v8::Local value) { + if (value.IsEmpty()) + return; + v8::Local context = isolate->GetCurrentContext(); + v8::Local privateKey = v8::Private::ForApi(isolate, key); + object->SetPrivate(context, privateKey, value); +} + + void JavascriptBindings::SetHiddenValue(v8::Isolate* isolate, v8::Local key, v8::Local value) { @@ -91,6 +121,22 @@ void JavascriptBindings::SetHiddenValue(v8::Isolate* isolate, main_context->Global()->SetPrivate(main_context, privateKey, value); } +void JavascriptBindings::DeleteHiddenValue(v8::Isolate* isolate, + v8::Local object, + v8::Local key) { + if (!is_valid() || !render_view()) + return; + + v8::Local main_context = + render_view()->GetWebView()->mainFrame()->mainWorldScriptContext(); + + v8::Local privateKey = v8::Private::ForApi(isolate, key); + // Actually deleting the value would make force the object into + // dictionary mode which is unnecessarily slow. Instead, we replace + // the hidden value with "undefined". + object->SetPrivate(main_context, privateKey, v8::Undefined(isolate)); +} + void JavascriptBindings::IPCSend(mate::Arguments* args, const base::string16& channel, const base::ListValue& arguments) { @@ -143,13 +189,27 @@ void JavascriptBindings::GetBinding( base::Unretained(this))); v8.SetMethod("setHiddenValue", base::Bind(&JavascriptBindings::SetHiddenValue, base::Unretained(this))); + v8.SetMethod("deleteHiddenValue", + base::Bind(&JavascriptBindings::DeleteHiddenValue, + base::Unretained(this))); + v8.SetMethod("getHiddenValueOnObject", + base::Bind(&JavascriptBindings::GetHiddenValueOnObject, + base::Unretained(this))); + v8.SetMethod("setHiddenValueOnObject", + base::Bind(&JavascriptBindings::SetHiddenValueOnObject, + base::Unretained(this))); + + + v8.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo); + v8.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo); + v8.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap::Create); binding.Set("v8", v8.GetHandle()); args.GetReturnValue().Set(binding.GetHandle()); } bool JavascriptBindings::OnMessageReceived(const IPC::Message& message) { - if (!is_valid() || WebContentsPreferences::run_node()) + if (!is_valid()) return false; // only handle ipc messages in the main frame script context diff --git a/atom/common/javascript_bindings.h b/atom/common/javascript_bindings.h index 496c3f1995..a5011f6a00 100644 --- a/atom/common/javascript_bindings.h +++ b/atom/common/javascript_bindings.h @@ -38,6 +38,22 @@ class JavascriptBindings : public content::RenderViewObserver, v8::Local key, v8::Local value); + void DeleteHiddenValue(v8::Isolate* isolate, + v8::Local object, + v8::Local key); + + v8::Local GetHiddenValueOnObject( + v8::Isolate* isolate, + v8::Local object, + v8::Local key); + void SetHiddenValueOnObject(v8::Isolate* isolate, + v8::Local object, + v8::Local key, + v8::Local value); + + + + void OnDestruct() override; bool OnMessageReceived(const IPC::Message& message) override; void OnBrowserMessage(bool all_frames, diff --git a/atom/common/native_mate_converters/blink_converter.cc b/atom/common/native_mate_converters/blink_converter.cc index 9adb03d4cd..7a7fb985d5 100644 --- a/atom/common/native_mate_converters/blink_converter.cc +++ b/atom/common/native_mate_converters/blink_converter.cc @@ -88,11 +88,11 @@ struct Converter { blink::WebMouseEvent::Button* out) { std::string button = base::ToLowerASCII(V8ToString(val)); if (button == "left") - *out = blink::WebMouseEvent::Button::ButtonLeft; + *out = blink::WebMouseEvent::Button::Left; else if (button == "middle") - *out = blink::WebMouseEvent::Button::ButtonMiddle; + *out = blink::WebMouseEvent::Button::Middle; else if (button == "right") - *out = blink::WebMouseEvent::Button::ButtonRight; + *out = blink::WebMouseEvent::Button::Right; else return false; return true; @@ -141,6 +141,16 @@ int GetWebInputEventType(v8::Isolate* isolate, v8::Local val) { return type; } +bool IsSystemKeyEvent(const blink::WebKeyboardEvent& event) { +#if defined(OS_MACOSX) + return event.modifiers & blink::WebInputEvent::MetaKey && + event.windowsKeyCode != ui::VKEY_B && + event.windowsKeyCode != ui::VKEY_I; +#else + return !!(event.modifiers & blink::WebInputEvent::AltKey); +#endif +} + bool Converter::FromV8( v8::Isolate* isolate, v8::Local val, blink::WebInputEvent* out) { @@ -165,6 +175,9 @@ bool Converter::FromV8( if (!ConvertFromV8(isolate, val, static_cast(out))) return false; + if (out->modifiers != 0) + out->isSystemKey = IsSystemKeyEvent(*out); + std::string str; bool shifted = false; if (dict.Get("keyCode", &str)) @@ -174,7 +187,6 @@ bool Converter::FromV8( if (shifted) out->modifiers |= blink::WebInputEvent::ShiftKey; - out->setKeyIdentifierFromWindowsKeyCode(); if ((out->type == blink::WebInputEvent::Char || out->type == blink::WebInputEvent::RawKeyDown)) { // Make sure to not read beyond the buffer in case some bad code doesn't @@ -214,7 +226,7 @@ bool Converter::FromV8( if (!dict.Get("x", &out->x) || !dict.Get("y", &out->y)) return false; if (!dict.Get("button", &out->button)) - out->button = blink::WebMouseEvent::Button::ButtonLeft; + out->button = blink::WebMouseEvent::Button::Left; dict.Get("globalX", &out->globalX); dict.Get("globalY", &out->globalY); dict.Get("movementX", &out->movementX); diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index bf5a8a2e40..12e4497b99 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -23,6 +23,7 @@ #include "atom/common/node_includes.h" +#include "atom/common/asar/asar_util.h" using content::BrowserThread; // Force all builtin modules to be referenced so they can actually run their @@ -32,14 +33,16 @@ using content::BrowserThread; void (*fp_register_ ## name)(void) = _register_ ## name // Electron's builtin modules. REFERENCE_MODULE(atom_browser_app); -REFERENCE_MODULE(atom_browser_auto_updater); +// REFERENCE_MODULE(atom_browser_auto_updater); REFERENCE_MODULE(atom_browser_component_updater); REFERENCE_MODULE(atom_browser_content_tracing); REFERENCE_MODULE(atom_browser_dialog); -REFERENCE_MODULE(atom_browser_debugger); +// REFERENCE_MODULE(atom_browser_debugger); +#if defined(ENABLE_WEBRTC) REFERENCE_MODULE(atom_browser_desktop_capturer); +#endif REFERENCE_MODULE(atom_browser_download_item); -REFERENCE_MODULE(atom_browser_importer); +// REFERENCE_MODULE(atom_browser_importer); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_power_save_blocker); @@ -50,18 +53,15 @@ REFERENCE_MODULE(atom_browser_session); REFERENCE_MODULE(atom_browser_system_preferences); REFERENCE_MODULE(atom_browser_tray); REFERENCE_MODULE(atom_browser_web_contents); -REFERENCE_MODULE(atom_browser_web_view_manager); REFERENCE_MODULE(atom_browser_window); REFERENCE_MODULE(atom_browser_extension); REFERENCE_MODULE(atom_common_asar); REFERENCE_MODULE(atom_common_clipboard); -REFERENCE_MODULE(atom_common_crash_reporter); +// REFERENCE_MODULE(atom_common_crash_reporter); REFERENCE_MODULE(atom_common_native_image); REFERENCE_MODULE(atom_common_screen); REFERENCE_MODULE(atom_common_shell); REFERENCE_MODULE(atom_common_v8_util); -REFERENCE_MODULE(atom_renderer_ipc); -REFERENCE_MODULE(atom_renderer_web_frame); #undef REFERENCE_MODULE // The "v8::Function::kLineOffsetNotFound" is exported in node.dll, but the @@ -172,8 +172,8 @@ node::Environment* NodeBindings::CreateEnvironment( std::unique_ptr c_argv = StringVectorToArgArray(args); node::Environment* env = node::CreateEnvironment( - context->GetIsolate(), uv_default_loop(), context, - args.size(), c_argv.get(), 0, nullptr); + node::CreateIsolateData(context->GetIsolate(), uv_default_loop()), + context, args.size(), c_argv.get(), 0, nullptr); // Node uses the deprecated SetAutorunMicrotasks(false) mode, we should switch // to use the scoped policy to match blink's behavior. @@ -191,7 +191,6 @@ node::Environment* NodeBindings::CreateEnvironment( base::FilePath helper_exec_path; PathService::Get(content::CHILD_PROCESS_EXE, &helper_exec_path); process.Set("helperExecPath", helper_exec_path); - // Set process._debugWaitConnect if --debug-brk was specified to stop // the debugger on the first line if (is_browser_ && @@ -255,7 +254,7 @@ void NodeBindings::UvRunOnce() { void NodeBindings::WakeupMainThread() { DCHECK(message_loop_); - message_loop_->PostTask(FROM_HERE, base::Bind(&NodeBindings::UvRunOnce, + message_loop_->task_runner()->PostTask(FROM_HERE, base::Bind(&NodeBindings::UvRunOnce, weak_factory_.GetWeakPtr())); } diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 6b514599f0..a3a2035454 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -57,9 +57,6 @@ const char kEnableLargerThanScreen[] = "enableLargerThanScreen"; // Forces to use dark theme on Linux. const char kDarkTheme[] = "darkTheme"; -// Whether the window should be transparent. -const char kTransparent[] = "transparent"; - // Window type hint. const char kType[] = "type"; @@ -84,15 +81,6 @@ const char kWebPreferences[] = "webPreferences"; // The factor of which page should be zoomed. const char kZoomFactor[] = "zoomFactor"; -// Script that will be loaded by guest WebContents before other scripts. -const char kPreloadScript[] = "preload"; - -// Like --preload, but the passed argument is an URL. -const char kPreloadURL[] = "preloadURL"; - -// Enable the node integration. -const char kNodeIntegration[] = "nodeIntegration"; - // Instancd ID of guest WebContents. const char kGuestInstanceID[] = "guestInstanceId"; @@ -100,12 +88,6 @@ const char kGuestInstanceID[] = "guestInstanceId"; const char kExperimentalFeatures[] = "experimentalFeatures"; const char kExperimentalCanvasFeatures[] = "experimentalCanvasFeatures"; -// Opener window's ID. -const char kOpenerID[] = "openerId"; - -// Enable the rubber banding effect. -const char kScrollBounce[] = "scrollBounce"; - // Enable blink features. // TODO(kevinsawicki) Rename to enableBlinkFeatures in 2.0 const char kBlinkFeatures[] = "blinkFeatures"; @@ -148,19 +130,12 @@ const char kAppUserModelId[] = "app-user-model-id"; // The command line switch versions of the options. const char kBackgroundColor[] = "background-color"; const char kZoomFactor[] = "zoom-factor"; -const char kPreloadScript[] = "preload"; -const char kPreloadURL[] = "preload-url"; -const char kNodeIntegration[] = "node-integration"; -const char kGuestInstanceID[] = "guest-instance-id"; -const char kOpenerID[] = "opener-id"; -const char kScrollBounce[] = "scroll-bounce"; // Widevine options // Path to Widevine CDM binaries. const char kWidevineCdmPath[] = "widevine-cdm-path"; // Widevine CDM version. const char kWidevineCdmVersion[] = "widevine-cdm-version"; - } // namespace switches } // namespace atom diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 54c6387728..22c8c6d385 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -38,7 +38,6 @@ extern const char kTitleBarStyle[]; extern const char kAutoHideMenuBar[]; extern const char kEnableLargerThanScreen[]; extern const char kDarkTheme[]; -extern const char kTransparent[]; extern const char kType[]; extern const char kDisableAutoHideCursor[]; extern const char kStandardWindow[]; @@ -49,14 +48,9 @@ extern const char kWebPreferences[]; // WebPreferences. extern const char kZoomFactor[]; -extern const char kPreloadScript[]; -extern const char kPreloadURL[]; -extern const char kNodeIntegration[]; extern const char kGuestInstanceID[]; extern const char kExperimentalFeatures[]; extern const char kExperimentalCanvasFeatures[]; -extern const char kOpenerID[]; -extern const char kScrollBounce[]; extern const char kBlinkFeatures[]; extern const char kDisableBlinkFeatures[]; @@ -79,16 +73,10 @@ extern const char kAppUserModelId[]; extern const char kBackgroundColor[]; extern const char kZoomFactor[]; -extern const char kPreloadScript[]; -extern const char kPreloadURL[]; -extern const char kNodeIntegration[]; extern const char kGuestInstanceID[]; -extern const char kOpenerID[]; -extern const char kScrollBounce[]; extern const char kWidevineCdmPath[]; extern const char kWidevineCdmVersion[]; - } // namespace switches } // namespace atom diff --git a/atom/common/resources/mac/Info.plist b/atom/common/resources/mac/Info.plist deleted file mode 100644 index 7b56a46470..0000000000 --- a/atom/common/resources/mac/Info.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - CFBundleIdentifier - ${ATOM_BUNDLE_ID} - CFBundleName - ${PRODUCT_NAME} - CFBundleExecutable - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - NSSupportsAutomaticGraphicsSwitching - - - diff --git a/atom/common/resources/mac/MainMenu.xib b/atom/common/resources/mac/MainMenu.xib deleted file mode 100644 index fb4656cac6..0000000000 --- a/atom/common/resources/mac/MainMenu.xib +++ /dev/null @@ -1,180 +0,0 @@ - - - - 101000 - 14D136 - 7531 - 1347.57 - 758.70 - - com.apple.InterfaceBuilder.CocoaPlugin - 7531 - - - NSCustomObject - NSMenu - NSMenuItem - - - com.apple.InterfaceBuilder.CocoaPlugin - - - PluginDependencyRecalculationVersion - - - - - AtomApplication - - - FirstResponder - - - NSApplication - - - NSFontManager - - - Main Menu - - - - Electron - - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - - Electron - - - - Quit - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - _NSMainMenu - - - - - - - terminate: - - - - 807 - - - - - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 371 - - - - - 29 - - - - - - - - 56 - - - - - - - - 57 - - - - - - - - 136 - - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - - 807 - - 0 - IBCocoaFramework - NO - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - - - YES - 3 - - {12, 12} - {10, 2} - - YES - - diff --git a/atom/renderer/api/atom_api_renderer_ipc.cc b/atom/renderer/api/atom_api_renderer_ipc.cc deleted file mode 100644 index 49f917292b..0000000000 --- a/atom/renderer/api/atom_api_renderer_ipc.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/common/api/api_messages.h" -#include "atom/common/native_mate_converters/string16_converter.h" -#include "atom/common/native_mate_converters/value_converter.h" -#include "atom/common/node_includes.h" -#include "content/public/renderer/render_view.h" -#include "native_mate/dictionary.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/WebKit/public/web/WebView.h" - -using content::RenderView; -using blink::WebLocalFrame; -using blink::WebView; - -namespace { - -RenderView* GetCurrentRenderView() { - WebLocalFrame* frame = WebLocalFrame::frameForCurrentContext(); - if (!frame) - return nullptr; - - WebView* view = frame->view(); - if (!view) - return nullptr; // can happen during closing. - - return RenderView::FromWebView(view); -} - -void Send(mate::Arguments* args, - const base::string16& channel, - const base::ListValue& arguments) { - RenderView* render_view = GetCurrentRenderView(); - if (render_view == nullptr) - return; - - bool success = render_view->Send(new AtomViewHostMsg_Message( - render_view->GetRoutingID(), channel, arguments)); - - if (!success) - args->ThrowError("Unable to send AtomViewHostMsg_Message"); -} - -base::string16 SendSync(mate::Arguments* args, - const base::string16& channel, - const base::ListValue& arguments) { - base::string16 json; - - RenderView* render_view = GetCurrentRenderView(); - if (render_view == nullptr) - return json; - - IPC::SyncMessage* message = new AtomViewHostMsg_Message_Sync( - render_view->GetRoutingID(), channel, arguments, &json); - bool success = render_view->Send(message); - - if (!success) - args->ThrowError("Unable to send AtomViewHostMsg_Message_Sync"); - - return json; -} - -void Initialize(v8::Local exports, v8::Local unused, - v8::Local context, void* priv) { - mate::Dictionary dict(context->GetIsolate(), exports); - dict.SetMethod("send", &Send); - dict.SetMethod("sendSync", &SendSync); -} - -} // namespace - -NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_renderer_ipc, Initialize) diff --git a/atom/renderer/api/atom_api_spell_check_client.cc b/atom/renderer/api/atom_api_spell_check_client.cc index f840455710..1c2cded943 100644 --- a/atom/renderer/api/atom_api_spell_check_client.cc +++ b/atom/renderer/api/atom_api_spell_check_client.cc @@ -64,19 +64,6 @@ void SpellCheckClient::spellCheck( } } -void SpellCheckClient::checkTextOfParagraph( - const blink::WebString& text, - blink::WebTextCheckingTypeMask mask, - blink::WebVector* results) { - if (!results) - return; - - if (!(mask & blink::WebTextCheckingTypeSpelling)) - return; - - NOTREACHED() << "checkTextOfParagraph should never be called"; -} - void SpellCheckClient::requestCheckingOfText( const blink::WebString& textToCheck, const blink::WebVector& markersInText, diff --git a/atom/renderer/api/atom_api_spell_check_client.h b/atom/renderer/api/atom_api_spell_check_client.h index af72756e2e..15c9b0c749 100644 --- a/atom/renderer/api/atom_api_spell_check_client.h +++ b/atom/renderer/api/atom_api_spell_check_client.h @@ -9,7 +9,7 @@ #include #include "base/callback.h" -#include "chrome/renderer/spellchecker/spellcheck_worditerator.h" +#include "components/spellcheck/renderer/spellcheck_worditerator.h" #include "native_mate/scoped_persistent.h" #include "third_party/WebKit/public/web/WebSpellCheckClient.h" @@ -32,10 +32,6 @@ class SpellCheckClient : public blink::WebSpellCheckClient { int& misspelledOffset, int& misspelledLength, blink::WebVector* optionalSuggestions) override; - void checkTextOfParagraph( - const blink::WebString&, - blink::WebTextCheckingTypeMask mask, - blink::WebVector* results) override; void requestCheckingOfText( const blink::WebString& textToCheck, const blink::WebVector& markersInText, @@ -45,6 +41,7 @@ class SpellCheckClient : public blink::WebSpellCheckClient { bool isShowingSpellingUI() override; void updateSpellingUIWithMisspelledWord( const blink::WebString& word) override; + void cancelAllPendingRequests() { } // Check the spelling of text. void SpellCheckText(const base::string16& text, diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc deleted file mode 100644 index ac8e4df2b3..0000000000 --- a/atom/renderer/api/atom_api_web_frame.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/renderer/api/atom_api_web_frame.h" - -#include "atom/common/api/event_emitter_caller.h" -#include "atom/common/native_mate_converters/blink_converter.h" -#include "atom/common/native_mate_converters/callback.h" -#include "atom/common/native_mate_converters/gfx_converter.h" -#include "atom/common/native_mate_converters/string16_converter.h" -#include "atom/renderer/api/atom_api_spell_check_client.h" -#include "base/memory/memory_pressure_listener.h" -#include "content/public/renderer/render_frame.h" -#include "content/renderer/browser_plugin/browser_plugin.h" -#include "content/renderer/browser_plugin/browser_plugin_manager.h" -#include "native_mate/dictionary.h" -#include "native_mate/object_template_builder.h" -#include "third_party/WebKit/public/web/WebCache.h" -#include "third_party/WebKit/public/web/WebDocument.h" -#include "third_party/WebKit/public/web/WebKit.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/WebKit/public/web/WebRemoteFrame.h" -#include "third_party/WebKit/public/web/WebScriptExecutionCallback.h" -#include "third_party/WebKit/public/web/WebScriptSource.h" -#include "third_party/WebKit/public/web/WebSecurityPolicy.h" -#include "third_party/WebKit/public/web/WebView.h" - -#include "atom/common/node_includes.h" - -namespace atom { - -namespace api { - -namespace { - -class ScriptExecutionCallback : public blink::WebScriptExecutionCallback { - public: - using CompletionCallback = - base::Callback& result)>; - - explicit ScriptExecutionCallback(const CompletionCallback& callback) - : callback_(callback) {} - ~ScriptExecutionCallback() override {} - - void completed( - const blink::WebVector>& result) override { - if (!callback_.is_null() && !result.isEmpty() && !result[0].IsEmpty()) - // Right now only single results per frame is supported. - callback_.Run(result[0]); - delete this; - } - - private: - CompletionCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(ScriptExecutionCallback); -}; - -} // namespace - -WebFrame::WebFrame(v8::Isolate* isolate) - : web_frame_(blink::WebLocalFrame::frameForCurrentContext()) { - Init(isolate); -} - -WebFrame::~WebFrame() { -} - -void WebFrame::SetName(const std::string& name) { - web_frame_->setName(blink::WebString::fromUTF8(name)); -} - -double WebFrame::SetZoomLevel(double level) { - double ret = web_frame_->view()->setZoomLevel(level); - return ret; -} - -double WebFrame::GetZoomLevel() const { - return web_frame_->view()->zoomLevel(); -} - -double WebFrame::SetZoomFactor(double factor) { - return blink::WebView::zoomLevelToZoomFactor(SetZoomLevel( - blink::WebView::zoomFactorToZoomLevel(factor))); -} - -double WebFrame::GetZoomFactor() const { - return blink::WebView::zoomLevelToZoomFactor(GetZoomLevel()); -} - -void WebFrame::SetZoomLevelLimits(double min_level, double max_level) { - web_frame_->view()->zoomLimitsChanged(min_level, max_level); -} - -float WebFrame::GetPageScaleFactor() { - return web_frame_->view()->pageScaleFactor(); -} - -void WebFrame::SetPageScaleFactor(float factor) { - web_frame_->view()->setPageScaleFactor(factor); -} - -void WebFrame::SetPageScaleLimits(float min_scale, float max_scale) { - web_frame_->view()->setDefaultPageScaleLimits(min_scale, max_scale); - web_frame_->view()->setIgnoreViewportTagScaleLimits(true); -} - -float WebFrame::GetTextZoomFactor() { - return web_frame_->view()->textZoomFactor(); -} - -void WebFrame::SetTextZoomFactor(float factor) { - web_frame_->view()->setTextZoomFactor(factor); -} - -v8::Local WebFrame::GetContentWindow(int content_window_id) { - content::RenderView* view = - content::RenderView::FromRoutingID(content_window_id); - blink::WebFrame* frame = view->GetWebView()->mainFrame(); - - v8::Local window; - if (frame->isWebLocalFrame()) { - window = frame->mainWorldScriptContext()->Global(); - } else { - window = - frame->toWebRemoteFrame()->deprecatedMainWorldScriptContext()->Global(); - } - return window; -} - -v8::Local WebFrame::RegisterEmbedderCustomElement( - const base::string16& name, v8::Local options) { - blink::WebExceptionCode c = 0; - return web_frame_->document().registerEmbedderCustomElement(name, options, c); -} - -void WebFrame::RegisterElementResizeCallback( - int element_instance_id, - const GuestViewContainer::ResizeCallback& callback) { - auto guest_view_container = GuestViewContainer::FromID(element_instance_id); - if (guest_view_container) - guest_view_container->RegisterElementResizeCallback(callback); -} - -void WebFrame::AttachGuest(int id) { - // This is a workaround for a strange issue on windows with background tabs - // libchromiumcontent doesn't appear to be making the check for - // params.disposition == NEW_BACKGROUND_TAB in WebContentsImpl - // This results in the BrowserPluginGuest trying to access the native - // window before it's actually ready. - // - // It's also possible that the guest is being treated as - // visible because the "embedder", which is the same for all tabs - // in the window, is always visible. - // - // This hack works around the issue by always - // marking it as hidden while attaching - content::BrowserPluginManager::Get()->GetBrowserPlugin(id)-> - updateVisibility(false); - content::RenderFrame::FromWebFrame(web_frame_)->AttachGuest(id); - content::BrowserPluginManager::Get()->GetBrowserPlugin(id)-> - updateVisibility(true); -} - -void WebFrame::SetSpellCheckProvider(mate::Arguments* args, - const std::string& language, - bool auto_spell_correct_turned_on, - v8::Local provider) { - if (!provider->Has(mate::StringToV8(args->isolate(), "spellCheck"))) { - args->ThrowError("\"spellCheck\" has to be defined"); - return; - } - - spell_check_client_.reset(new SpellCheckClient( - language, auto_spell_correct_turned_on, args->isolate(), provider)); - web_frame_->view()->setSpellCheckClient(spell_check_client_.get()); -} - -void WebFrame::RegisterURLSchemeAsSecure(const std::string& scheme) { - // Register scheme to secure list (https, wss, data). - blink::WebSecurityPolicy::registerURLSchemeAsSecure( - blink::WebString::fromUTF8(scheme)); -} - -void WebFrame::RegisterURLSchemeAsBypassingCSP(const std::string& scheme) { - // Register scheme to bypass pages's Content Security Policy. - blink::WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy( - blink::WebString::fromUTF8(scheme)); -} - -void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme) { - // Register scheme to privileged list (https, wss, data, chrome-extension) - blink::WebString privileged_scheme(blink::WebString::fromUTF8(scheme)); - blink::WebSecurityPolicy::registerURLSchemeAsSecure(privileged_scheme); - blink::WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy( - privileged_scheme); - blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers( - privileged_scheme); - blink::WebSecurityPolicy::registerURLSchemeAsSupportingFetchAPI( - privileged_scheme); - blink::WebSecurityPolicy::registerURLSchemeAsCORSEnabled(privileged_scheme); -} - -void WebFrame::InsertText(const std::string& text) { - web_frame_->insertText(blink::WebString::fromUTF8(text)); -} - -void WebFrame::ExecuteJavaScript(const base::string16& code, - mate::Arguments* args) { - v8::Isolate* isolate = blink::mainThreadIsolate(); - v8::Isolate::Scope isolate_scope(isolate); - v8::Local context = web_frame_->mainWorldScriptContext(); - v8::Context::Scope context_scope(context); - - bool has_user_gesture = false; - args->GetNext(&has_user_gesture); - ScriptExecutionCallback::CompletionCallback completion_callback; - args->GetNext(&completion_callback); - std::unique_ptr callback( - new ScriptExecutionCallback(completion_callback)); - web_frame_->requestExecuteScriptAndReturnValue( - blink::WebScriptSource(code), - has_user_gesture, - callback.release()); -} - -// static -mate::Handle WebFrame::Create(v8::Isolate* isolate) { - return mate::CreateHandle(isolate, new WebFrame(isolate)); -} - -blink::WebCache::ResourceTypeStats WebFrame::GetResourceUsage( - v8::Isolate* isolate) { - blink::WebCache::ResourceTypeStats stats; - blink::WebCache::getResourceTypeStats(&stats); - return stats; -} - -void WebFrame::ClearCache(v8::Isolate* isolate) { - isolate->IdleNotificationDeadline(0.5); - blink::WebCache::clear(); - base::MemoryPressureListener::NotifyMemoryPressure( - base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); -} - -// static -void WebFrame::BuildPrototype( - v8::Isolate* isolate, v8::Local prototype) { - prototype->SetClassName(mate::StringToV8(isolate, "WebFrame")); - mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) - .SetMethod("setName", &WebFrame::SetName) - .SetMethod("setZoomLevel", &WebFrame::SetZoomLevel) - .SetMethod("getZoomLevel", &WebFrame::GetZoomLevel) - .SetMethod("setZoomFactor", &WebFrame::SetZoomFactor) - .SetMethod("getZoomFactor", &WebFrame::GetZoomFactor) - .SetMethod("setZoomLevelLimits", &WebFrame::SetZoomLevelLimits) - .SetMethod("getPageScaleFactor", &WebFrame::GetPageScaleFactor) - .SetMethod("setPageScaleFactor", &WebFrame::SetPageScaleFactor) - .SetMethod("setPageScaleLimits", &WebFrame::SetPageScaleLimits) - .SetMethod("getTextZoomFactor", &WebFrame::GetTextZoomFactor) - .SetMethod("setTextZoomFactor", &WebFrame::SetTextZoomFactor) - .SetMethod("registerEmbedderCustomElement", - &WebFrame::RegisterEmbedderCustomElement) - .SetMethod("registerElementResizeCallback", - &WebFrame::RegisterElementResizeCallback) - .SetMethod("attachGuest", &WebFrame::AttachGuest) - .SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider) - .SetMethod("registerURLSchemeAsSecure", - &WebFrame::RegisterURLSchemeAsSecure) - .SetMethod("registerURLSchemeAsBypassingCSP", - &WebFrame::RegisterURLSchemeAsBypassingCSP) - .SetMethod("registerURLSchemeAsPrivileged", - &WebFrame::RegisterURLSchemeAsPrivileged) - .SetMethod("insertText", &WebFrame::InsertText) - .SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript) - .SetMethod("getResourceUsage", &WebFrame::GetResourceUsage) - .SetMethod("clearCache", &WebFrame::ClearCache) - .SetMethod("getContentWindow", &WebFrame::GetContentWindow); -} - -} // namespace api - -} // namespace atom - -namespace { - -using atom::api::WebFrame; - -void Initialize(v8::Local exports, v8::Local unused, - v8::Local context, void* priv) { - v8::Isolate* isolate = context->GetIsolate(); - mate::Dictionary dict(isolate, exports); - dict.Set("webFrame", WebFrame::Create(isolate)); - dict.Set("WebFrame", WebFrame::GetConstructor(isolate)->GetFunction()); -} - -} // namespace - -NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_renderer_web_frame, Initialize) diff --git a/atom/renderer/api/atom_api_web_frame.h b/atom/renderer/api/atom_api_web_frame.h deleted file mode 100644 index 7ea754cd07..0000000000 --- a/atom/renderer/api/atom_api_web_frame.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_ -#define ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_ - -#include -#include -#include - -#include "atom/renderer/guest_view_container.h" -#include "native_mate/handle.h" -#include "native_mate/wrappable.h" -#include "third_party/WebKit/public/web/WebCache.h" - -namespace blink { -class WebLocalFrame; -} - -namespace mate { -class Arguments; -} - -namespace atom { - -namespace api { - -class SpellCheckClient; - -class WebFrame : public mate::Wrappable { - public: - static mate::Handle Create(v8::Isolate* isolate); - - static void BuildPrototype(v8::Isolate* isolate, - v8::Local prototype); - - private: - explicit WebFrame(v8::Isolate* isolate); - ~WebFrame() override; - - void SetName(const std::string& name); - - double SetZoomLevel(double level); - double GetZoomLevel() const; - double SetZoomFactor(double factor); - double GetZoomFactor() const; - - void SetZoomLevelLimits(double min_level, double max_level); - - float GetPageScaleFactor(); - void SetPageScaleFactor(float factor); - void SetPageScaleLimits(float min_scale, float max_scale); - - float GetTextZoomFactor(); - void SetTextZoomFactor(float factor); - - v8::Local GetContentWindow(int guest_instance_id); - v8::Local RegisterEmbedderCustomElement( - const base::string16& name, v8::Local options); - void RegisterElementResizeCallback( - int element_instance_id, - const GuestViewContainer::ResizeCallback& callback); - void AttachGuest(int element_instance_id); - - // Set the provider that will be used by SpellCheckClient for spell check. - void SetSpellCheckProvider(mate::Arguments* args, - const std::string& language, - bool auto_spell_correct_turned_on, - v8::Local provider); - - void RegisterURLSchemeAsSecure(const std::string& scheme); - void RegisterURLSchemeAsBypassingCSP(const std::string& scheme); - void RegisterURLSchemeAsPrivileged(const std::string& scheme); - - // Editing. - void InsertText(const std::string& text); - - // Excecuting scripts. - void ExecuteJavaScript(const base::string16& code, mate::Arguments* args); - - // Resource related methods - blink::WebCache::ResourceTypeStats GetResourceUsage(v8::Isolate* isolate); - void ClearCache(v8::Isolate* isolate); - - std::unique_ptr spell_check_client_; - - blink::WebLocalFrame* web_frame_; - - DISALLOW_COPY_AND_ASSIGN(WebFrame); -}; - -} // namespace api - -} // namespace atom - -#endif // ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_ diff --git a/atom/renderer/atom_render_view_observer.cc b/atom/renderer/atom_render_view_observer.cc index 029d2e30d9..773e0abe2a 100644 --- a/atom/renderer/atom_render_view_observer.cc +++ b/atom/renderer/atom_render_view_observer.cc @@ -19,6 +19,7 @@ #include "atom/renderer/atom_renderer_client.h" #include "base/command_line.h" #include "base/strings/string_number_conversions.h" +#include "components/web_cache/renderer/web_cache_impl.h" #include "content/public/renderer/render_view.h" #include "ipc/ipc_message_macros.h" #include "native_mate/dictionary.h" @@ -36,58 +37,6 @@ namespace atom { namespace { -bool GetIPCObject(v8::Isolate* isolate, - v8::Local context, - v8::Local* ipc) { - v8::Local key = mate::StringToV8(isolate, "ipc"); - v8::Local privateKey = v8::Private::ForApi(isolate, key); - v8::Local global_object = context->Global(); - v8::Local value; - if (!global_object->GetPrivate(context, privateKey).ToLocal(&value)) - return false; - if (value.IsEmpty() || !value->IsObject()) - return false; - *ipc = value->ToObject(); - return true; -} - -std::vector> ListValueToVector( - v8::Isolate* isolate, - const base::ListValue& list) { - v8::Local array = mate::ConvertToV8(isolate, list); - std::vector> result; - mate::ConvertFromV8(isolate, array, &result); - return result; -} - -void EmitIPCEvent(blink::WebFrame* frame, - const base::string16& channel, - const base::ListValue& args) { - if (!frame || frame->isWebRemoteFrame()) - return; - - v8::Isolate* isolate = blink::mainThreadIsolate(); - v8::HandleScope handle_scope(isolate); - - v8::Local context = frame->mainWorldScriptContext(); - v8::Context::Scope context_scope(context); - - // Only emit IPC event for context with node integration. - node::Environment* env = node::Environment::GetCurrent(context); - if (!env) - return; - - v8::Local ipc; - if (GetIPCObject(isolate, context, &ipc)) { - auto args_vector = ListValueToVector(isolate, args); - // Insert the Event object, event.sender is ipc. - mate::Dictionary event = mate::Dictionary::CreateEmpty(isolate); - event.Set("sender", ipc); - args_vector.insert(args_vector.begin(), event.GetHandle()); - mate::EmitEvent(isolate, ipc, channel, args_vector); - } -} - base::StringPiece NetResourceProvider(int key) { if (key == IDR_DIR_HEADER_HTML) { base::StringPiece html_data = @@ -102,8 +51,10 @@ base::StringPiece NetResourceProvider(int key) { AtomRenderViewObserver::AtomRenderViewObserver( content::RenderView* render_view, - AtomRendererClient* renderer_client) + AtomRendererClient* renderer_client, + web_cache::WebCacheImpl* web_cache_impl) : content::RenderViewObserver(render_view), + web_cache_impl_(web_cache_impl), document_created_(false) { // Initialise resource for directory listing. net::NetModule::SetResourceProvider(NetResourceProvider); @@ -115,17 +66,13 @@ AtomRenderViewObserver::~AtomRenderViewObserver() { void AtomRenderViewObserver::DidCreateDocumentElement( blink::WebLocalFrame* frame) { document_created_ = true; +} - // Read --zoom-factor from command line. - std::string zoom_factor_str = base::CommandLine::ForCurrentProcess()-> - GetSwitchValueASCII(switches::kZoomFactor); - if (zoom_factor_str.empty()) - return; - double zoom_factor; - if (!base::StringToDouble(zoom_factor_str, &zoom_factor)) - return; - double zoom_level = blink::WebView::zoomFactorToZoomLevel(zoom_factor); - frame->view()->setZoomLevel(zoom_level); +void AtomRenderViewObserver::Navigate(const GURL& url) { + // Execute cache clear operations that were postponed until a navigation + // event (including tab reload). + if (web_cache_impl_) + web_cache_impl_->ExecutePendingClearCache(); } void AtomRenderViewObserver::DraggableRegionsChanged(blink::WebFrame* frame) { @@ -142,45 +89,9 @@ void AtomRenderViewObserver::DraggableRegionsChanged(blink::WebFrame* frame) { Send(new AtomViewHostMsg_UpdateDraggableRegions(routing_id(), regions)); } -bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) { - // only handle messages for node renderers and non-extension processes - if (!WebContentsPreferences::run_node()) - return false; - - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(AtomRenderViewObserver, message) - IPC_MESSAGE_HANDLER(AtomViewMsg_Message, OnBrowserMessage) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - return handled; -} - void AtomRenderViewObserver::OnDestruct() { delete this; } -void AtomRenderViewObserver::OnBrowserMessage(bool send_to_all, - const base::string16& channel, - const base::ListValue& args) { - if (!document_created_) - return; - - if (!render_view()->GetWebView()) - return; - - blink::WebFrame* frame = render_view()->GetWebView()->mainFrame(); - if (!frame || frame->isWebRemoteFrame()) - return; - - EmitIPCEvent(frame, channel, args); - - // Also send the message to all sub-frames. - if (send_to_all) { - for (blink::WebFrame* child = frame->firstChild(); child; - child = child->nextSibling()) - EmitIPCEvent(child, channel, args); - } -} } // namespace atom diff --git a/atom/renderer/atom_render_view_observer.h b/atom/renderer/atom_render_view_observer.h index 76b05cd19e..bf258897b5 100644 --- a/atom/renderer/atom_render_view_observer.h +++ b/atom/renderer/atom_render_view_observer.h @@ -12,6 +12,10 @@ namespace base { class ListValue; } +namespace web_cache { +class WebCacheImpl; +} + namespace atom { class AtomRendererClient; @@ -19,7 +23,8 @@ class AtomRendererClient; class AtomRenderViewObserver : public content::RenderViewObserver { public: explicit AtomRenderViewObserver(content::RenderView* render_view, - AtomRendererClient* renderer_client); + AtomRendererClient* renderer_client, + web_cache::WebCacheImpl* web_cache_impl); protected: virtual ~AtomRenderViewObserver(); @@ -28,14 +33,11 @@ class AtomRenderViewObserver : public content::RenderViewObserver { // content::RenderViewObserver implementation. void DidCreateDocumentElement(blink::WebLocalFrame* frame) override; void DraggableRegionsChanged(blink::WebFrame* frame) override; - bool OnMessageReceived(const IPC::Message& message) override; void OnDestruct() override; - - void OnBrowserMessage(bool send_to_all, - const base::string16& channel, - const base::ListValue& args); + void Navigate(const GURL& url) override; // Whether the document object has been created. + web_cache::WebCacheImpl* web_cache_impl_; // not owned bool document_created_; DISALLOW_COPY_AND_ASSIGN(AtomRenderViewObserver); diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 4e16e3c845..2c4ad2f32c 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -8,38 +8,24 @@ #include #include "atom/common/api/api_messages.h" -#include "atom/common/api/atom_bindings.h" -#include "atom/common/api/event_emitter_caller.h" #include "atom/common/color_util.h" -#include "atom/common/native_mate_converters/value_converter.h" -#include "atom/common/node_bindings.h" #include "atom/common/options_switches.h" #include "atom/renderer/atom_render_view_observer.h" -#include "atom/renderer/guest_view_container.h" -#include "atom/renderer/node_array_buffer_bridge.h" -#include "atom/renderer/preferences_manager.h" #include "base/command_line.h" #include "chrome/renderer/media/chrome_key_systems.h" #include "chrome/renderer/pepper/pepper_helper.h" -#include "chrome/renderer/printing/print_web_view_helper.h" #include "chrome/renderer/tts_dispatcher.h" +#include "components/web_cache/renderer/web_cache_impl.h" #include "content/public/common/content_constants.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container.h" #include "ipc/ipc_message_macros.h" -#include "native_mate/dictionary.h" #include "third_party/WebKit/public/web/WebCustomElement.h" -#include "third_party/WebKit/public/web/WebDocument.h" -#include "third_party/WebKit/public/web/WebFrameWidget.h" -#include "third_party/WebKit/public/web/WebKit.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebPluginParams.h" -#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" -#include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" -#include "third_party/WebKit/public/web/WebView.h" #if defined(OS_MACOSX) #include "base/mac/mac_util.h" @@ -50,83 +36,12 @@ #include #endif -#include "atom/common/node_includes.h" - namespace atom { -namespace { - -// Helper class to forward the messages to the client. -class AtomRenderFrameObserver : public content::RenderFrameObserver { - public: - AtomRenderFrameObserver(content::RenderFrame* frame, - AtomRendererClient* renderer_client) - : content::RenderFrameObserver(frame), - render_frame_(frame), - world_id_(-1), - renderer_client_(renderer_client) {} - - // content::RenderFrameObserver: - void DidClearWindowObject() override { - renderer_client_->DidClearWindowObject(render_frame_); - } - - void DidCreateScriptContext(v8::Handle context, - int extension_group, - int world_id) override { - if (world_id_ != -1 && world_id_ != world_id) - return; - world_id_ = world_id; - renderer_client_->DidCreateScriptContext(context, render_frame_); - } - void WillReleaseScriptContext(v8::Local context, - int world_id) override { - if (world_id_ != world_id) - return; - renderer_client_->WillReleaseScriptContext(context, render_frame_); - } - - void OnDestruct() override { - delete this; - } - - private: - content::RenderFrame* render_frame_; - int world_id_; - AtomRendererClient* renderer_client_; - - DISALLOW_COPY_AND_ASSIGN(AtomRenderFrameObserver); -}; - -v8::Local GetRenderProcessPreferences( - const PreferencesManager* preferences_manager, v8::Isolate* isolate) { - if (preferences_manager->preferences()) - return mate::ConvertToV8(isolate, *preferences_manager->preferences()); - else - return v8::Null(isolate); -} - -void AddRenderBindings(v8::Isolate* isolate, - v8::Local process, - const PreferencesManager* preferences_manager) { - mate::Dictionary dict(isolate, process); - dict.SetMethod( - "getRenderProcessPreferences", - base::Bind(GetRenderProcessPreferences, preferences_manager)); -} - -bool IsDevToolsExtension(content::RenderFrame* render_frame) { - return static_cast(render_frame->GetWebFrame()->document().url()) - .SchemeIs("chrome-extension"); -} - -} // namespace - -AtomRendererClient::AtomRendererClient() - : node_bindings_(NodeBindings::Create(false)), - atom_bindings_(new AtomBindings) { +AtomRendererClient::AtomRendererClient() { // Parse --standard-schemes=scheme1,scheme2 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + std::string custom_schemes = command_line->GetSwitchValueASCII( switches::kStandardSchemes); if (!custom_schemes.empty()) { @@ -141,12 +56,7 @@ AtomRendererClient::~AtomRendererClient() { } void AtomRendererClient::RenderThreadStarted() { - blink::WebCustomElement::addEmbedderCustomElementName("webview"); - blink::WebCustomElement::addEmbedderCustomElementName("browserplugin"); - - OverrideNodeArrayBuffer(); - - preferences_manager_.reset(new PreferencesManager); + web_cache_impl_.reset(new web_cache::WebCacheImpl()); #if defined(OS_WIN) // Set ApplicationUserModelID in renderer process. @@ -157,75 +67,18 @@ void AtomRendererClient::RenderThreadStarted() { SetCurrentProcessExplicitAppUserModelID(app_id.c_str()); } #endif - -#if defined(OS_MACOSX) - // Disable rubber banding by default. - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch(switches::kScrollBounce)) { - base::ScopedCFTypeRef key( - base::SysUTF8ToCFStringRef("NSScrollViewRubberbanding")); - base::ScopedCFTypeRef value( - base::SysUTF8ToCFStringRef("false")); - CFPreferencesSetAppValue(key, value, kCFPreferencesCurrentApplication); - CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); - } -#endif } void AtomRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { new PepperHelper(render_frame); - new AtomRenderFrameObserver(render_frame, this); - // Allow file scheme to handle service worker by default. // FIXME(zcbenz): Can this be moved elsewhere? blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers("file"); } void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) { - new printing::PrintWebViewHelper(render_view); - new AtomRenderViewObserver(render_view, this); - - blink::WebFrameWidget* web_frame_widget = render_view->GetWebFrameWidget(); - if (!web_frame_widget) - return; - - base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); - if (cmd->HasSwitch(switches::kGuestInstanceID)) { // webview. - web_frame_widget->setBaseBackgroundColor(SK_ColorTRANSPARENT); - } else { // normal window. - // If backgroundColor is specified then use it. - std::string name = cmd->GetSwitchValueASCII(switches::kBackgroundColor); - // Otherwise use white background. - SkColor color = name.empty() ? SK_ColorWHITE : ParseHexColor(name); - web_frame_widget->setBaseBackgroundColor(color); - } -} - -void AtomRendererClient::DidClearWindowObject( - content::RenderFrame* render_frame) { - // Make sure every page will get a script context created. - render_frame->GetWebFrame()->executeScript(blink::WebScriptSource("void 0")); -} - -void AtomRendererClient::RunScriptsAtDocumentStart( - content::RenderFrame* render_frame) { - // Inform the document start pharse. - node::Environment* env = node_bindings_->uv_env(); - if (env) { - v8::HandleScope handle_scope(env->isolate()); - mate::EmitEvent(env->isolate(), env->process_object(), "document-start"); - } -} - -void AtomRendererClient::RunScriptsAtDocumentEnd( - content::RenderFrame* render_frame) { - // Inform the document end pharse. - node::Environment* env = node_bindings_->uv_env(); - if (env) { - v8::HandleScope handle_scope(env->isolate()); - mate::EmitEvent(env->isolate(), env->process_object(), "document-end"); - } + new AtomRenderViewObserver(render_view, this, web_cache_impl_.get()); } blink::WebSpeechSynthesizer* AtomRendererClient::OverrideSpeechSynthesizer( @@ -247,74 +100,12 @@ bool AtomRendererClient::OverrideCreatePlugin( return true; } -void AtomRendererClient::DidCreateScriptContext( - v8::Handle context, content::RenderFrame* render_frame) { - // Only allow node integration for the main frame, unless it is a devtools - // extension page. - if (!render_frame->IsMainFrame() && !IsDevToolsExtension(render_frame)) - return; - - // Whether the node binding has been initialized. - bool first_time = node_bindings_->uv_env() == nullptr; - - // Prepare the node bindings. - if (first_time) { - node_bindings_->Initialize(); - node_bindings_->PrepareMessageLoop(); - } - - // Setup node environment for each window. - node::Environment* env = node_bindings_->CreateEnvironment(context); - - // Add atom-shell extended APIs. - atom_bindings_->BindTo(env->isolate(), env->process_object()); - AddRenderBindings(env->isolate(), env->process_object(), - preferences_manager_.get()); - - // Load everything. - node_bindings_->LoadEnvironment(env); - - if (first_time) { - // Make uv loop being wrapped by window context. - node_bindings_->set_uv_env(env); - - // Give the node loop a run to make sure everything is ready. - node_bindings_->RunMessageLoop(); - } -} - -void AtomRendererClient::WillReleaseScriptContext( - v8::Handle context, content::RenderFrame* render_frame) { - // Only allow node integration for the main frame, unless it is a devtools - // extension page. - if (!render_frame->IsMainFrame() && !IsDevToolsExtension(render_frame)) - return; - - node::Environment* env = node::Environment::GetCurrent(context); - if (env) - mate::EmitEvent(env->isolate(), env->process_object(), "exit"); -} - -bool AtomRendererClient::ShouldFork(blink::WebLocalFrame* frame, - const GURL& url, - const std::string& http_method, - bool is_initial_navigation, - bool is_server_redirect, - bool* send_referrer) { - // Handle all the navigations and reloads in browser. - // FIXME We only support GET here because http method will be ignored when - // the OpenURLFromTab is triggered, which means form posting would not work, - // we should solve this by patching Chromium in future. - *send_referrer = true; - return http_method == "GET"; -} - content::BrowserPluginDelegate* AtomRendererClient::CreateBrowserPluginDelegate( content::RenderFrame* render_frame, const std::string& mime_type, const GURL& original_url) { if (mime_type == content::kBrowserPluginMimeType) { - return new GuestViewContainer(render_frame); + return new extensions::ExtensionsGuestViewContainer(render_frame); } else { return nullptr; } diff --git a/atom/renderer/atom_renderer_client.h b/atom/renderer/atom_renderer_client.h index d5c70777a8..e316f6ca40 100644 --- a/atom/renderer/atom_renderer_client.h +++ b/atom/renderer/atom_renderer_client.h @@ -14,11 +14,11 @@ namespace brave { class BraveContentRendererClient; } -namespace atom { +namespace web_cache { +class WebCacheImpl; +} -class AtomBindings; -class PreferencesManager; -class NodeBindings; +namespace atom { class AtomRendererClient : public content::ContentRendererClient { public: @@ -43,20 +43,12 @@ class AtomRendererClient : public content::ContentRendererClient { void RenderThreadStarted() override; void RenderFrameCreated(content::RenderFrame*) override; void RenderViewCreated(content::RenderView*) override; - void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override; - void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer( blink::WebSpeechSynthesizerClient* client) override; bool OverrideCreatePlugin(content::RenderFrame* render_frame, blink::WebLocalFrame* frame, const blink::WebPluginParams& params, blink::WebPlugin** plugin) override; - bool ShouldFork(blink::WebLocalFrame* frame, - const GURL& url, - const std::string& http_method, - bool is_initial_navigation, - bool is_server_redirect, - bool* send_referrer) override; content::BrowserPluginDelegate* CreateBrowserPluginDelegate( content::RenderFrame* render_frame, const std::string& mime_type, @@ -65,9 +57,7 @@ class AtomRendererClient : public content::ContentRendererClient { std::vector>* key_systems) override; - std::unique_ptr node_bindings_; - std::unique_ptr atom_bindings_; - std::unique_ptr preferences_manager_; + std::unique_ptr web_cache_impl_; friend class brave::BraveContentRendererClient; diff --git a/atom/renderer/extensions/atom_extensions_dispatcher_delegate.cc b/atom/renderer/extensions/atom_extensions_dispatcher_delegate.cc index acbe3be818..2da0639eaa 100644 --- a/atom/renderer/extensions/atom_extensions_dispatcher_delegate.cc +++ b/atom/renderer/extensions/atom_extensions_dispatcher_delegate.cc @@ -8,15 +8,68 @@ #include #include "atom/common/javascript_bindings.h" #include "atom/grit/atom_resources.h" // NOLINT: This file is generated +#include "atom/grit/electron_api_resources.h" // NOLINT: This file is generated +#include "brave/grit/brave_resources.h" // NOLINT: This file is generated #include "brave/renderer/extensions/content_settings_bindings.h" #include "brave/renderer/extensions/web_frame_bindings.h" #include "chrome/grit/renderer_resources.h" // NOLINT: This file is generated #include "chrome/renderer/extensions/tabs_custom_bindings.h" +#include "content/public/common/bindings_policy.h" #include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_view.h" +#include "extensions/common/extension.h" +#include "extensions/renderer/css_native_handler.h" +#include "extensions/renderer/i18n_custom_bindings.h" +#include "extensions/renderer/lazy_background_page_native_handler.h" #include "extensions/renderer/resource_bundle_source_map.h" +#include "extensions/renderer/v8_helpers.h" +#include "gin/converter.h" namespace extensions { +namespace { + +#if defined(OS_MACOSX) +const char* kPlatform = "darwin"; +#elif defined(OS_LINUX) +const char* kPlatform = "linux"; +#elif defined(OS_WIN) +const char* kPlatform = "win32"; +#else +const char* kPlatform = "unknown"; +#endif + +v8::Local GetOrCreateProcess(ScriptContext* context) { + v8::Local process_string( + v8::String::NewFromUtf8(context->isolate(), "process")); + v8::Local global(context->v8_context()->Global()); + v8::Local process(global->Get(process_string)); + if (process->IsUndefined()) { + process = v8::Object::New(context->isolate()); + gin::SetProperty(context->isolate(), process.As(), + v8_helpers::ToV8StringUnsafe(context->isolate(), "type"), + v8_helpers::ToV8StringUnsafe(context->isolate(), "renderer")); + gin::SetProperty(context->isolate(), process.As(), + v8_helpers::ToV8StringUnsafe(context->isolate(), "platform"), + v8_helpers::ToV8StringUnsafe(context->isolate(), kPlatform)), + // TODO(bridiver) - add a function to return env vars + // std::unique_ptr env(base::Environment::Create()); + // gin::SetProperty(context->isolate(), process.As(), + // v8_helpers::ToV8StringUnsafe(context->isolate(), "env"), + // v8_helpers::ToV8StringUnsafe(context->isolate(), "TODO")); + global->Set(process_string, process); + } + + return process; +} + +// Returns |value| cast to an object if possible, else an empty handle. +v8::Local AsObjectOrEmpty(v8::Local value) { + return value->IsObject() ? value.As() : v8::Local(); +} + +} + AtomExtensionsDispatcherDelegate::AtomExtensionsDispatcherDelegate() { } @@ -49,17 +102,44 @@ void AtomExtensionsDispatcherDelegate::RegisterNativeHandlers( "tabs", std::unique_ptr( new extensions::TabsCustomBindings(context))); + + // The following are native handlers that are defined in //extensions, but + // are only used for APIs defined in Chrome. + // See chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc + module_system->RegisterNativeHandler( + "i18n", std::unique_ptr( + new extensions::I18NCustomBindings(context))); + module_system->RegisterNativeHandler( + "lazy_background_page", + std::unique_ptr( + new extensions::LazyBackgroundPageNativeHandler(context))); + module_system->RegisterNativeHandler( + "css_natives", std::unique_ptr( + new extensions::CssNativeHandler(context))); + } void AtomExtensionsDispatcherDelegate::PopulateSourceMap( extensions::ResourceBundleSourceMap* source_map) { + // TODO(bridiver) - add a permission for these + // so only component extensions can use + // blessed extension?? source_map->RegisterSource("event_emitter", IDR_ATOM_EVENT_EMITTER_JS); + source_map->RegisterSource("ipcRenderer", IDR_BRAVE_IPC_RENDERER_JS); source_map->RegisterSource("ipc_utils", IDR_ATOM_IPC_INTERNAL_JS); - source_map->RegisterSource("ipc", IDR_ATOM_IPC_BINDINGS_JS); - // TODO(bridiver) - add a permission for this - // so only component extensions can use + source_map->RegisterSource("windowDialogs", IDR_ATOM_IPC_WINDOW_DIALOGS_JS); source_map->RegisterSource("webFrame", IDR_ATOM_WEB_FRAME_BINDINGS_JS); + source_map->RegisterSource("remote", + IDR_ELECTRON_REMOTE_BINDINGS_JS); + source_map->RegisterSource("buffer", + IDR_ELECTRON_BUFFER_BINDINGS_JS); + source_map->RegisterSource("is-promise", + IDR_ELECTRON_IS_PROMISE_BINDINGS_JS); + source_map->RegisterSource("callbacks-registry", + IDR_ELECTRON_CALLBACKS_REGISTRY_BINDINGS_JS); + source_map->RegisterSource("guest-view-internal", + IDR_ELECTRON_GUEST_VIEW_INTERNAL_BINDINGS_JS); source_map->RegisterSource("browserAction", IDR_ATOM_BROWSER_ACTION_BINDINGS_JS); source_map->RegisterSource("privacy", IDR_ATOM_PRIVACY_BINDINGS_JS); @@ -71,20 +151,68 @@ void AtomExtensionsDispatcherDelegate::PopulateSourceMap( IDR_ATOM_CONTENT_SETTINGS_BINDINGS_JS); source_map->RegisterSource("windows", IDR_ATOM_WINDOWS_BINDINGS_JS); - source_map->RegisterSource("app", - IDR_ATOM_APP_BINDINGS_JS); + // source_map->RegisterSource("chromeWebViewInternal", + // IDR_CHROME_WEB_VIEW_INTERNAL_CUSTOM_BINDINGS_JS); + // source_map->RegisterSource("chromeWebView", IDR_CHROME_WEB_VIEW_JS); + // source_map->RegisterSource("app", + // IDR_ATOM_APP_BINDINGS_JS); + source_map->RegisterSource("ChromeSetting", IDR_CHROME_SETTING_JS); + source_map->RegisterSource("ContentSetting", IDR_CONTENT_SETTING_JS); + source_map->RegisterSource("ChromeDirectSetting", + IDR_CHROME_DIRECT_SETTING_JS); + source_map->RegisterSource("webViewInternal", + IDR_ATOM_WEB_VIEW_INTERNAL_BINDINGS_JS); + source_map->RegisterSource("tabViewInternal", + IDR_ATOM_TAB_VIEW_INTERNAL_BINDINGS_JS); + source_map->RegisterSource("webViewApiMethods", + IDR_ATOM_WEB_VIEW_API_BINDINGS_JS); + + } + + // // Unpacked extensions start off with file access since they are a developer + // // feature. + // static inline bool ShouldAlwaysAllowFileAccess(Location location) { + // return IsUnpackedLocation(location); + // } + + void AtomExtensionsDispatcherDelegate::RequireAdditionalModules( extensions::ScriptContext* context, bool is_within_platform_app) { + extensions::ModuleSystem* module_system = context->module_system(); + extensions::Feature::Context context_type = context->context_type(); + + if (context_type == extensions::Feature::WEBUI_CONTEXT) { + module_system->Require("webView"); + module_system->Require("tabViewInternal"); + module_system->Require("webViewInternal"); + module_system->Require("webViewApiMethods"); + module_system->Require("webViewAttributes"); + // module_system->Require("web-view"); + // module_system->Require("web-view-attributes"); + } + + // Note: setting up the WebView class here, not the chrome.webview API. + // The API will be automatically set up when first used. + if (context->GetAvailability("webViewInternal").is_available()) { + LOG(ERROR) << "web view internal is available"; + // module_system->Require("chromeWebView"); + } else { + LOG(ERROR) << "web view internal is not available"; + } + + if (context_type == extensions::Feature::WEBUI_CONTEXT || + (context->extension() && context->extension()->is_extension() && + Manifest::IsComponentLocation(context->extension()->location()))) { + v8::Local process = + AsObjectOrEmpty(GetOrCreateProcess(context)); + } } void AtomExtensionsDispatcherDelegate::OnActiveExtensionsUpdated( const std::set& extension_ids) { } -void AtomExtensionsDispatcherDelegate::SetChannel(int channel) { -} - } // namespace extensions diff --git a/atom/renderer/extensions/atom_extensions_dispatcher_delegate.h b/atom/renderer/extensions/atom_extensions_dispatcher_delegate.h index 713370d761..79984e1721 100644 --- a/atom/renderer/extensions/atom_extensions_dispatcher_delegate.h +++ b/atom/renderer/extensions/atom_extensions_dispatcher_delegate.h @@ -35,7 +35,6 @@ class AtomExtensionsDispatcherDelegate bool is_within_platform_app) override; void OnActiveExtensionsUpdated( const std::set& extensions_ids) override; - void SetChannel(int channel) override; DISALLOW_COPY_AND_ASSIGN(AtomExtensionsDispatcherDelegate); }; diff --git a/atom/renderer/extensions/atom_extensions_renderer_client.cc b/atom/renderer/extensions/atom_extensions_renderer_client.cc index 6bcdae74d9..5ec8740064 100644 --- a/atom/renderer/extensions/atom_extensions_renderer_client.cc +++ b/atom/renderer/extensions/atom_extensions_renderer_client.cc @@ -9,6 +9,8 @@ #include "base/command_line.h" #include "base/strings/stringprintf.h" #include "chrome/common/chrome_isolated_world_ids.h" +#include "chrome/common/extensions/extension_process_policy.h" +#include "chrome/common/url_constants.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "extensions/common/manifest_handlers/background_info.h" @@ -20,6 +22,10 @@ #include "extensions/renderer/extension_helper.h" #include "extensions/renderer/extensions_render_frame_observer.h" #include "extensions/renderer/extensions_renderer_client.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h" +#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h" +#include "extensions/renderer/module_system.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" @@ -28,6 +34,11 @@ namespace { +bool IsStandaloneExtensionProcess() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + extensions::switches::kExtensionProcess); +} + void DidCreateDocumentElement(content::RenderFrame* render_frame) { v8::Isolate* isolate = blink::mainThreadIsolate(); v8::HandleScope handle_scope(isolate); @@ -39,9 +50,12 @@ void DidCreateDocumentElement(content::RenderFrame* render_frame) { auto script_context = extensions::ScriptContextSet::GetContextByV8Context(context); - if (script_context) + if (script_context) { + extensions::ModuleSystem::NativesEnabledScope + natives_enabled(script_context->module_system()); script_context->module_system() - ->CallModuleMethod("ipc", "didCreateDocumentElement"); + ->CallModuleMethod("windowDialogs", "didCreateDocumentElement"); + } // reschedule the callback because a new render frame // is not always created when navigating @@ -100,8 +114,11 @@ void AtomExtensionsRendererClient::RenderThreadStarted() { new extensions::Dispatcher(extension_dispatcher_delegate_.get())); permissions_policy_delegate_.reset( new extensions::RendererPermissionsPolicyDelegate()); + guest_view_container_dispatcher_.reset( + new extensions::ExtensionsGuestViewContainerDispatcher()); thread->AddObserver(extension_dispatcher_.get()); + thread->AddObserver(guest_view_container_dispatcher_.get()); } void AtomExtensionsRendererClient::RenderFrameCreated( @@ -201,7 +218,7 @@ bool AtomExtensionsRendererClient::WillSendRequest( frame->addMessageToConsole( blink::WebConsoleMessage(blink::WebConsoleMessage::LevelError, blink::WebString::fromUTF8(message))); - *new_url = GURL("chrome-extension://invalid/"); + *new_url = GURL(chrome::kExtensionInvalidRequestURL); return true; } } @@ -209,6 +226,34 @@ bool AtomExtensionsRendererClient::WillSendRequest( return false; } +// static +bool AtomExtensionsRendererClient::ShouldFork(blink::WebLocalFrame* frame, + const GURL& url, + bool is_initial_navigation, + bool is_server_redirect, + bool* send_referrer) { + const extensions::RendererExtensionRegistry* extension_registry = + extensions::RendererExtensionRegistry::Get(); + + // Determine if the new URL is an extension (excluding bookmark apps). + const Extension* new_url_extension = extensions::GetNonBookmarkAppExtension( + *extension_registry->GetMainThreadExtensionSet(), url); + bool is_extension_url = !!new_url_extension; + + // If this is a reload, check whether it has the wrong process type. We + // should send it to the browser if it's an extension URL (e.g., hosted app) + // in a normal process, or if it's a process for an extension that has been + // uninstalled. Without --site-per-process mode, we never fork processes for + // subframes, so this check only makes sense for top-level frames. + // TODO(alexmos,nasko): Figure out how this check should work when reloading + // subframes in --site-per-process mode. + if (!frame->parent() && frame->document().url() == url) { + if (is_extension_url != IsStandaloneExtensionProcess()) + return true; + } + return false; +} + void AtomExtensionsRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { extension_dispatcher_->RunScriptsAtDocumentStart(render_frame); diff --git a/atom/renderer/extensions/atom_extensions_renderer_client.h b/atom/renderer/extensions/atom_extensions_renderer_client.h index 22ccbeac5f..eedaaaab51 100644 --- a/atom/renderer/extensions/atom_extensions_renderer_client.h +++ b/atom/renderer/extensions/atom_extensions_renderer_client.h @@ -16,6 +16,7 @@ class GURL; namespace blink { struct WebPluginParams; class WebFrame; +class WebLocalFrame; } namespace content { @@ -26,6 +27,7 @@ class RenderView; namespace extensions { class Dispatcher; +class ExtensionsGuestViewContainerDispatcher; class RendererPermissionsPolicyDelegate; class AtomExtensionsDispatcherDelegate; @@ -52,6 +54,11 @@ class AtomExtensionsRendererClient : public ExtensionsRendererClient { void RunScriptsAtDocumentStart(content::RenderFrame* render_frame); void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame); + static bool ShouldFork(blink::WebLocalFrame* frame, + const GURL& url, + bool is_initial_navigation, + bool is_server_redirect, + bool* send_referrer); extensions::Dispatcher* extension_dispatcher() { return extension_dispatcher_.get(); } @@ -62,7 +69,8 @@ class AtomExtensionsRendererClient : public ExtensionsRendererClient { std::unique_ptr extension_dispatcher_; std::unique_ptr permissions_policy_delegate_; - + std::unique_ptr + guest_view_container_dispatcher_; DISALLOW_COPY_AND_ASSIGN(AtomExtensionsRendererClient); }; diff --git a/atom/renderer/guest_view_container.cc b/atom/renderer/guest_view_container.cc deleted file mode 100644 index 5a22e3f121..0000000000 --- a/atom/renderer/guest_view_container.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/renderer/guest_view_container.h" - -#include - -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "base/message_loop/message_loop.h" -#include "ui/gfx/geometry/size.h" - -namespace atom { - -namespace { - -using GuestViewContainerMap = std::map; -static base::LazyInstance g_guest_view_container_map = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -GuestViewContainer::GuestViewContainer(content::RenderFrame* render_frame) - : weak_ptr_factory_(this) { -} - -GuestViewContainer::~GuestViewContainer() { - if (element_instance_id_ > 0) - g_guest_view_container_map.Get().erase(element_instance_id_); -} - -// static -GuestViewContainer* GuestViewContainer::FromID(int element_instance_id) { - GuestViewContainerMap* guest_view_containers = - g_guest_view_container_map.Pointer(); - auto it = guest_view_containers->find(element_instance_id); - return it == guest_view_containers->end() ? nullptr : it->second; -} - -void GuestViewContainer::RegisterElementResizeCallback( - const ResizeCallback& callback) { - element_resize_callback_ = callback; -} - -void GuestViewContainer::SetElementInstanceID(int element_instance_id) { - element_instance_id_ = element_instance_id; - g_guest_view_container_map.Get().insert( - std::make_pair(element_instance_id, this)); -} - -void GuestViewContainer::DidResizeElement(const gfx::Size& new_size) { - if (element_resize_callback_.is_null()) - return; - - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(element_resize_callback_, new_size)); -} - -base::WeakPtr GuestViewContainer::GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - -} // namespace atom diff --git a/atom/renderer/guest_view_container.h b/atom/renderer/guest_view_container.h deleted file mode 100644 index eefec7bf74..0000000000 --- a/atom/renderer/guest_view_container.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_RENDERER_GUEST_VIEW_CONTAINER_H_ -#define ATOM_RENDERER_GUEST_VIEW_CONTAINER_H_ - -#include "base/callback.h" -#include "content/public/renderer/browser_plugin_delegate.h" - -namespace gfx { -class Size; -} - -namespace atom { - -class GuestViewContainer : public content::BrowserPluginDelegate { - public: - typedef base::Callback ResizeCallback; - - explicit GuestViewContainer(content::RenderFrame* render_frame); - ~GuestViewContainer() override; - - static GuestViewContainer* FromID(int element_instance_id); - - void RegisterElementResizeCallback(const ResizeCallback& callback); - - // content::BrowserPluginDelegate: - void SetElementInstanceID(int element_instance_id) final; - void DidResizeElement(const gfx::Size& new_size) final; - base::WeakPtr GetWeakPtr() final; - - private: - int element_instance_id_; - - ResizeCallback element_resize_callback_; - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(GuestViewContainer); -}; - -} // namespace atom - -#endif // ATOM_RENDERER_GUEST_VIEW_CONTAINER_H_ diff --git a/atom/renderer/node_array_buffer_bridge.cc b/atom/renderer/node_array_buffer_bridge.cc deleted file mode 100644 index 14e84d6398..0000000000 --- a/atom/renderer/node_array_buffer_bridge.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/renderer/node_array_buffer_bridge.h" - -#include "atom/common/node_includes.h" -#include "base/macros.h" -#include "native_mate/converter.h" -#include "third_party/WebKit/public/web/WebArrayBuffer.h" -#include "third_party/WebKit/public/web/WebArrayBufferConverter.h" - -namespace atom { - -namespace { - -// global.Uint8Array; -v8::Local GetUint8ArrayConstructor( - v8::Isolate* isolate, v8::Local context) { - v8::Local constructor = context->Global()->Get( - mate::StringToV8(isolate, "Uint8Array")); - return v8::Local::Cast(constructor); -} - -// new ArrayBuffer(size); -v8::Local BlinkArrayBufferNew( - v8::Isolate* isolate, size_t size) { - blink::WebArrayBuffer buffer = blink::WebArrayBuffer::create(size, 1); - return v8::Local::Cast( - blink::WebArrayBufferConverter::toV8Value( - &buffer, isolate->GetCurrentContext()->Global(), isolate)); -} - -// new ArrayBuffer(data, size); -v8::Local BlinkArrayBufferNewWith( - v8::Isolate* isolate, void* data, size_t size) { - blink::WebArrayBuffer buffer = blink::WebArrayBuffer::createExternal( - data, size); - return v8::Local::Cast( - blink::WebArrayBufferConverter::toV8Value( - &buffer, isolate->GetCurrentContext()->Global(), isolate)); -} - -// new Uint8Array(array_buffer, offset, size); -v8::Local BlinkUint8ArrayNew( - v8::Local ab, size_t offset, size_t size) { - // Use the DOM's Uint8Array constructor to create Uint8Array. - v8::Local context = ab->CreationContext(); - v8::Isolate* isolate = context->GetIsolate(); - v8::Local constructor = - GetUint8ArrayConstructor(isolate, context); - v8::Local args[] = { - ab, mate::ConvertToV8(isolate, offset), mate::ConvertToV8(isolate, size) - }; - return v8::Local::Cast(constructor->NewInstance( - context, node::arraysize(args), args).ToLocalChecked()); -} - -} // namespace - -void OverrideNodeArrayBuffer() { - node::Buffer::SetArrayBufferCreator( - BlinkArrayBufferNew, BlinkArrayBufferNewWith, BlinkUint8ArrayNew); -} - -} // namespace atom diff --git a/atom/renderer/node_array_buffer_bridge.h b/atom/renderer/node_array_buffer_bridge.h deleted file mode 100644 index 61d1806993..0000000000 --- a/atom/renderer/node_array_buffer_bridge.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_RENDERER_NODE_ARRAY_BUFFER_BRIDGE_H_ -#define ATOM_RENDERER_NODE_ARRAY_BUFFER_BRIDGE_H_ - -namespace atom { - -// Override Node's ArrayBuffer with DOM's ArrayBuffer. -void OverrideNodeArrayBuffer(); - -} // namespace atom - -#endif // ATOM_RENDERER_NODE_ARRAY_BUFFER_BRIDGE_H_ diff --git a/atom/renderer/preferences_manager.cc b/atom/renderer/preferences_manager.cc deleted file mode 100644 index a9ed710a9d..0000000000 --- a/atom/renderer/preferences_manager.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/renderer/preferences_manager.h" - -#include "atom/common/api/api_messages.h" -#include "content/public/renderer/render_thread.h" - -namespace atom { - -PreferencesManager::PreferencesManager() { - content::RenderThread::Get()->AddObserver(this); -} - -PreferencesManager::~PreferencesManager() { -} - -bool PreferencesManager::OnControlMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PreferencesManager, message) - IPC_MESSAGE_HANDLER(AtomMsg_UpdatePreferences, OnUpdatePreferences) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void PreferencesManager::OnUpdatePreferences( - const base::ListValue& preferences) { - preferences_ = preferences.CreateDeepCopy(); -} - -} // namespace atom diff --git a/atom/renderer/preferences_manager.h b/atom/renderer/preferences_manager.h deleted file mode 100644 index c531fe879a..0000000000 --- a/atom/renderer/preferences_manager.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2016 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_RENDERER_PREFERENCES_MANAGER_H_ -#define ATOM_RENDERER_PREFERENCES_MANAGER_H_ - -#include - -#include "base/values.h" -#include "content/public/renderer/render_thread_observer.h" - -namespace atom { - -class PreferencesManager : public content::RenderThreadObserver { - public: - PreferencesManager(); - ~PreferencesManager() override; - - const base::ListValue* preferences() const { return preferences_.get(); } - - private: - // content::RenderThreadObserver: - bool OnControlMessageReceived(const IPC::Message& message) override; - - void OnUpdatePreferences(const base::ListValue& preferences); - - std::unique_ptr preferences_; - - DISALLOW_COPY_AND_ASSIGN(PreferencesManager); -}; - -} // namespace atom - -#endif // ATOM_RENDERER_PREFERENCES_MANAGER_H_ diff --git a/atom/utility/atom_content_utility_client.cc b/atom/utility/atom_content_utility_client.cc index bf8c91c9b4..73441803c0 100644 --- a/atom/utility/atom_content_utility_client.cc +++ b/atom/utility/atom_content_utility_client.cc @@ -9,13 +9,17 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "chrome/common/chrome_utility_messages.h" +#include "chrome/common/resource_usage_reporter.mojom.h" #include "chrome/utility/profile_import_handler.h" #include "chrome/utility/utility_message_handler.h" #include "content/public/common/content_switches.h" #include "content/public/utility/utility_thread.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_message_macros.h" - +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "net/proxy/mojo_proxy_resolver_factory_impl.h" +#include "net/proxy/proxy_resolver_v8.h" +#include "services/shell/public/cpp/interface_registry.h" #if defined(OS_WIN) #include "chrome/utility/printing_handler_win.h" @@ -23,17 +27,50 @@ #if defined(ENABLE_EXTENSIONS) #include "atom/common/extensions/atom_extensions_client.h" +#include "extensions/utility/utility_handler.h" #endif +namespace atom { + namespace { -bool Send(IPC::Message* message) { - return content::UtilityThread::Get()->Send(message); +void CreateProxyResolverFactory( + mojo::InterfaceRequest request) { + // MojoProxyResolverFactoryImpl is strongly bound to the Mojo message pipe it + // is connected to. When that message pipe is closed, either explicitly on the + // other end (in the browser process), or by a connection error, this object + // will be destroyed. + new net::MojoProxyResolverFactoryImpl(std::move(request)); } -} // namespace +class ResourceUsageReporterImpl : public mojom::ResourceUsageReporter { + public: + explicit ResourceUsageReporterImpl( + mojo::InterfaceRequest req) + : binding_(this, std::move(req)) {} + ~ResourceUsageReporterImpl() override {} + + private: + void GetUsageData(const GetUsageDataCallback& callback) override { + mojom::ResourceUsageDataPtr data = mojom::ResourceUsageData::New(); + size_t total_heap_size = net::ProxyResolverV8::GetTotalHeapSize(); + if (total_heap_size) { + data->reports_v8_stats = true; + data->v8_bytes_allocated = total_heap_size; + data->v8_bytes_used = net::ProxyResolverV8::GetUsedHeapSize(); + } + callback.Run(std::move(data)); + } -namespace atom { + mojo::StrongBinding binding_; +}; + +void CreateResourceUsageReporter( + mojo::InterfaceRequest request) { + new ResourceUsageReporterImpl(std::move(request)); +} + +} // namespace int64_t AtomContentUtilityClient::max_ipc_message_size_ = IPC::Channel::kMaximumMessageSize; @@ -44,9 +81,10 @@ AtomContentUtilityClient::AtomContentUtilityClient() #if defined(OS_WIN) handlers_.push_back(new printing::PrintingHandlerWin()); #endif -#if defined(ENABLE_EXTENSIONS) - extensions::ExtensionsClient::Set( - extensions::AtomExtensionsClient::GetInstance()); + +#if defined(ENABLE_PRINT_PREVIEW) || \ + (defined(ENABLE_BASIC_PRINTING) && defined(OS_WIN)) + handlers_.push_back(new printing::PrintingHandler()); #endif } @@ -54,29 +92,60 @@ AtomContentUtilityClient::~AtomContentUtilityClient() { } void AtomContentUtilityClient::UtilityThreadStarted() { +#if defined(ENABLE_EXTENSIONS) + extensions::UtilityHandler::UtilityThreadStarted(); +#endif } bool AtomContentUtilityClient::OnMessageReceived( const IPC::Message& message) { - if (filter_messages_ && !ContainsKey(message_id_whitelist_, message.type())) + if (filter_messages_ && + !base::ContainsKey(message_id_whitelist_, message.type())) return false; bool handled = true; IPC_BEGIN_MESSAGE_MAP(AtomContentUtilityClient, message) - IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() - for (auto it = handlers_.begin(); !handled && it != handlers_.end(); ++it) { - handled = (*it)->OnMessageReceived(message); + if (handled) + return true; + + for (auto* handler : handlers_) { + // At least one of the utility process handlers adds a new handler to + // |handlers_| when it handles a message. This causes any iterator over + // |handlers_| to become invalid. Therefore, it is necessary to break the + // loop at this point instead of evaluating it as a loop condition (if the + // for loop was using iterators explicitly, as originally done). + if (handler->OnMessageReceived(message)) + return true; } - return handled; + return false; } -void AtomContentUtilityClient::OnStartupPing() { - Send(new ChromeUtilityHostMsg_ProcessStarted); - // Don't release the process, we assume further messages are on the way. +void AtomContentUtilityClient::ExposeInterfacesToBrowser( + shell::InterfaceRegistry* registry) { + // When the utility process is running with elevated privileges, we need to + // filter messages so that only a whitelist of IPCs can run. In Mojo, there's + // no way of filtering individual messages. Instead, we can avoid adding + // non-whitelisted Mojo services to the shell::InterfaceRegistry. + // TODO(amistry): Use a whitelist once the whistlisted IPCs have been + // converted to Mojo. + if (filter_messages_) + return; + + registry->AddInterface( + base::Bind(CreateProxyResolverFactory)); + registry->AddInterface(base::Bind(CreateResourceUsageReporter)); +} + +// static +void AtomContentUtilityClient::PreSandboxStartup() { +#if defined(ENABLE_EXTENSIONS) + extensions::ExtensionsClient::Set( + extensions::AtomExtensionsClient::GetInstance()); +#endif } } // namespace atom diff --git a/atom/utility/atom_content_utility_client.h b/atom/utility/atom_content_utility_client.h index ba0fef471a..fec31b23a7 100644 --- a/atom/utility/atom_content_utility_client.h +++ b/atom/utility/atom_content_utility_client.h @@ -19,6 +19,10 @@ class FilePath; struct FileDescriptor; } +namespace shell { +class InterfaceRegistry; +} + class UtilityMessageHandler; namespace atom { @@ -30,11 +34,9 @@ class AtomContentUtilityClient : public content::ContentUtilityClient { void UtilityThreadStarted() override; bool OnMessageReceived(const IPC::Message& message) override; + void ExposeInterfacesToBrowser(shell::InterfaceRegistry* registry) override; - static void set_max_ipc_message_size_for_test(int64_t max_message_size) { - max_ipc_message_size_ = max_message_size; - } - + static void PreSandboxStartup(); private: void OnStartupPing(); diff --git a/atom/utility/importer/chrome_importer.cc b/atom/utility/importer/chrome_importer.cc index e84bfd1b6c..3ed5dbb971 100644 --- a/atom/utility/importer/chrome_importer.cc +++ b/atom/utility/importer/chrome_importer.cc @@ -208,45 +208,6 @@ void ChromeImporter::LoadFaviconData( } } -void ChromeImporter::ImportCookies() { - base::FilePath cookies_path = - source_path_.Append( - base::FilePath::StringType(FILE_PATH_LITERAL("Cookies"))); - if (!base::PathExists(cookies_path)) - return; - - sql::Connection db; - if (!db.Open(cookies_path)) - return; - - const char query[] = - "SELECT host_key, name, value, path, expires_utc, secure, httponly, " - "encrypted_value FROM cookies WHERE length(encrypted_value) = 0"; - - sql::Statement s(db.GetUniqueStatement(query)); - - std::vector cookies; - while (s.Step() && !cancelled()) { - ImportedCookieEntry cookie; - base::string16 host(base::UTF8ToUTF16("*")); - host.append(s.ColumnString16(0)); - cookie.domain = s.ColumnString16(0); - cookie.name = s.ColumnString16(1); - cookie.value = s.ColumnString16(2); - cookie.host = host; - cookie.path = s.ColumnString16(3); - cookie.expiry_date = - base::Time::FromDoubleT(chromeTimeToDouble((s.ColumnInt64(4)))); - cookie.secure = s.ColumnBool(5); - cookie.httponly = s.ColumnBool(6); - - cookies.push_back(cookie); - } - - if (!cookies.empty() && !cancelled()) - bridge_->SetCookies(cookies); -} - void ChromeImporter::RecursiveReadBookmarksFolder( const base::DictionaryValue* folder, const std::vector& parent_path, diff --git a/autofill.gypi b/autofill.gypi deleted file mode 100644 index 19775e20e4..0000000000 --- a/autofill.gypi +++ /dev/null @@ -1,65 +0,0 @@ -{ - 'variables': { - 'autofill_sources': [ - 'atom/browser/api/atom_api_autofill.cc', - 'atom/browser/api/atom_api_autofill.h', - 'atom/browser/autofill/atom_autofill_client.cc', - 'atom/browser/autofill/atom_autofill_client.h', - 'atom/browser/autofill/autofill_observer.h', - 'atom/browser/autofill/personal_data_manager_factory.cc', - 'atom/browser/autofill/personal_data_manager_factory.h', - ], - }, - 'conditions': [ - ['OS=="mac" or OS=="linux"', { - 'variables': { - 'autofill_libraries': [ - '<(libchromiumcontent_dir)/libaddressinput_util.a', - '<(libchromiumcontent_dir)/libautofill_content_browser.a', - '<(libchromiumcontent_dir)/libautofill_content_common.a', - '<(libchromiumcontent_dir)/libautofill_content_mojo_bindings.a', - '<(libchromiumcontent_dir)/libautofill_content_renderer.a', - '<(libchromiumcontent_dir)/libautofill_core_browser.a', - '<(libchromiumcontent_dir)/libautofill_core_common.a', - '<(libchromiumcontent_dir)/libautofill_server_proto.a', - '<(libchromiumcontent_dir)/libdata_use_measurement_core.a', - '<(libchromiumcontent_dir)/libgoogle_apis.a', - '<(libchromiumcontent_dir)/libphonenumber.a', - '<(libchromiumcontent_dir)/libphonenumber_without_metadata.a', - '<(libchromiumcontent_dir)/libsignin_core_browser.a', - '<(libchromiumcontent_dir)/libsignin_core_common.a', - '<(libchromiumcontent_dir)/libos_crypt.a', - '<(libchromiumcontent_dir)/libversion_info.a', - ] - } - }], - ['OS=="win"', { - 'variables': { - 'autofill_libraries': [ - '<(libchromiumcontent_dir)/libaddressinput_util.lib', - '<(libchromiumcontent_dir)/autofill_content_browser.lib', - '<(libchromiumcontent_dir)/autofill_content_common.lib', - '<(libchromiumcontent_dir)/autofill_content_mojo_bindings.lib', - '<(libchromiumcontent_dir)/autofill_content_renderer.lib', - '<(libchromiumcontent_dir)/autofill_core_browser.lib', - '<(libchromiumcontent_dir)/autofill_core_common.lib', - '<(libchromiumcontent_dir)/autofill_server_proto.lib', - '<(libchromiumcontent_dir)/data_use_measurement_core.lib', - '<(libchromiumcontent_dir)/google_apis.lib', - '<(libchromiumcontent_dir)/libphonenumber.lib', - '<(libchromiumcontent_dir)/libphonenumber_without_metadata.lib', - '<(libchromiumcontent_dir)/signin_core_browser.lib', - '<(libchromiumcontent_dir)/signin_core_common.lib', - '<(libchromiumcontent_dir)/os_crypt.lib', - '-lcrypt32.lib', - '<(libchromiumcontent_dir)/version_info.lib', - ], - }, - }], - ], - 'target_defaults': { - 'defines': [ - 'GOOGLE_PROTOBUF_NO_RTTI' - ], - }, -} diff --git a/brave/brave_resources.grd b/brave/brave_resources.grd new file mode 100644 index 0000000000..260321d3b0 --- /dev/null +++ b/brave/brave_resources.grd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/brave/brave_strings.grd b/brave/brave_strings.grd new file mode 100644 index 0000000000..f6ebdb5795 --- /dev/null +++ b/brave/brave_strings.grd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + Brave + + + Brave + + + + diff --git a/brave/browser/BUILD.gn b/brave/browser/BUILD.gn new file mode 100644 index 0000000000..24ba32dca4 --- /dev/null +++ b/brave/browser/BUILD.gn @@ -0,0 +1,119 @@ +import("//build/buildflag_header.gni") +import("//build/config/chrome_build.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/features.gni") + +source_set("browser") { + configs += [ + "//electron/build:electron_config", + ] + + include_dirs = [ + # make sure chromium_src comes before the chrome src root + "//electron/chromium_src", + ] + + sources = [ + # "api" + "guest_view/tab_view/tab_view_guest.h", + "guest_view/tab_view/tab_view_guest.cc", + "guest_view/brave_guest_view_manager_delegate.h", + "guest_view/brave_guest_view_manager_delegate.cc", + "notifications/platform_notification_service_impl.h", + "notifications/platform_notification_service_impl.cc", + "brave_browser_context.h", + "brave_browser_context.cc", + "brave_content_browser_client.h", + "brave_content_browser_client.cc", + "brave_permission_manager.h", + "brave_permission_manager.cc", + "renderer_preferences_helper.h", + "renderer_preferences_helper.cc", + ] + + public_deps = [ + "//components/autofill/core/browser", + ] + + deps = [ + ":component_updater_api", + "//electron/atom/browser", + "//electron/chromium_src:browser", + "//electron:common", + "//chrome/common", + "//components/autofill/content/browser:risk_proto", + "//components/autofill/content/browser", + "//components/autofill/core/browser", + "//components/devtools_discovery", + "//components/devtools_http_handler", + "//components/resources", + "//components/subresource_filter/content/browser", + "//components/subresource_filter/core/browser", + "//components/syncable_prefs", + "//device/usb/mojo", + "//device/usb/public/interfaces", + "//gpu/config", + "//mojo/common", + "//mojo/edk/system", + "//mojo/public/cpp/bindings", + "//mojo/public/js", + "//third_party/WebKit/public:image_resources", + "//third_party/WebKit/public:resources", + ] + + if (enable_basic_printing || enable_print_preview) { + deps += [ + "//components/printing/browser", + "//printing", + ] + } + + if (enable_extensions) { + public_deps += [ + "extensions", + ] + + deps += [ + "//chrome/common/extensions/api", + "//chrome/common/extensions/api:api_registration", + "//chrome/common/extensions/api:extensions_features", + "//extensions/browser", + ] + } + + if (is_posix && !is_mac) { + sources += [ + # "//chrome/app/chrome_crash_reporter_client.cc", + # "//chrome/app/chrome_crash_reporter_client.h", + ] + deps += [ + "//components/crash/content/app", + "//components/crash/content/browser", + "//components/version_info:generate_version_info", + ] + } +} + +source_set("component_updater_api") { + configs += [ + "//electron/build:electron_config", + ] + + include_dirs = [ + # make sure chromium_src comes before the chrome src root + "//electron/chromium_src", + ] + + sources = [ + "api/brave_api_component_updater.cc", + "api/brave_api_component_updater.h", + ] + + deps = [ + "//v8:v8" + ] + + public_deps = [ + "component_updater", + ] +} diff --git a/brave/browser/api/brave_api_component_updater.cc b/brave/browser/api/brave_api_component_updater.cc index f5bd99816d..000ba58f43 100644 --- a/brave/browser/api/brave_api_component_updater.cc +++ b/brave/browser/api/brave_api_component_updater.cc @@ -13,10 +13,14 @@ #include "brave/browser/component_updater/widevine_cdm_component_installer.h" #include "native_mate/dictionary.h" + +void Noop(int error) {} + void ComponentsUI::OnDemandUpdate( component_updater::ComponentUpdateService* cus, const std::string& component_id) { - cus->GetOnDemandUpdater().OnDemandUpdate(component_id); + cus->GetOnDemandUpdater().OnDemandUpdate(component_id, + base::Bind(Noop)); } bool @@ -31,7 +35,7 @@ ComponentsUI::GetComponentDetails(const std::string& component_id, component_updater::ComponentUpdateService* ComponentsUI::GetCUSForID( const std::string& component_id) const { if (component_id == kWidevineId) { - return g_browser_process->google_component_updater(); + return g_browser_process->component_updater(); } return g_browser_process->brave_component_updater(); } @@ -101,7 +105,7 @@ void ComponentUpdater::RegisterComponent(const std::string& component_id) { static bool registeredObserver = false; if (!registeredObserver) { g_browser_process->brave_component_updater()->AddObserver(this); - g_browser_process->google_component_updater()->AddObserver(this); + g_browser_process->component_updater()->AddObserver(this); registeredObserver = true; } base::Closure registered_callback = @@ -127,16 +131,16 @@ void ComponentUpdater::RegisterComponent(const std::string& component_id) { kPocketPublicKeyStr, registered_callback, ready_callback); } else if (component_id == kWidevineId) { brave::RegisterWidevineCdmComponent( - g_browser_process->google_component_updater(), + g_browser_process->component_updater(), registered_callback, ready_callback); } } std::vector ComponentUpdater::GetComponentIDs() { std::vector components = - g_browser_process->google_component_updater()->GetComponentIDs(); + g_browser_process->component_updater()->GetComponentIDs(); std::vector brave_components = - g_browser_process->google_component_updater()->GetComponentIDs(); + g_browser_process->component_updater()->GetComponentIDs(); components.insert(components.end(), brave_components.begin(), brave_components.end()); return components; diff --git a/brave/browser/brave_browser_context.cc b/brave/browser/brave_browser_context.cc index 4664ac2c84..1966a89029 100644 --- a/brave/browser/brave_browser_context.cc +++ b/brave/browser/brave_browser_context.cc @@ -6,15 +6,24 @@ #include "atom/browser/net/atom_url_request_job_factory.h" #include "base/path_service.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" #include "brave/browser/brave_permission_manager.h" #include "brightray/browser/brightray_paths.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/common/pref_names.h" +#include "common/application_info.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/component_updater/component_updater_paths.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/guest_view/browser/guest_view_manager.h" +#include "components/guest_view/browser/guest_view_manager_delegate.h" #include "components/prefs/json_pref_store.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_filter.h" @@ -29,13 +38,15 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/dom_storage_context.h" #include "content/public/browser/storage_partition.h" +#include "net/base/escape.h" #include "net/cookies/cookie_store.h" #include "net/url_request/url_request_context.h" #if defined(ENABLE_EXTENSIONS) #include "atom/browser/extensions/atom_browser_client_extensions_part.h" -#include "atom/browser/extensions/atom_extension_system_factory.h" #include "atom/browser/extensions/atom_extensions_network_delegate.h" +#include "atom/browser/extensions/atom_extension_system_factory.h" +#include "extensions/browser/api/extensions_api_client.h" #include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/extension_pref_store.h" #include "extensions/browser/extension_pref_value_map_factory.h" @@ -68,25 +79,24 @@ void NotifyOTRProfileDestroyedOnIOThread(void* original_profile, namespace brave { -void DatabaseErrorCallback(sql::InitStatus status) { - // TODO(bridiver) - we should send a notification of failure +void DatabaseErrorCallback(sql::InitStatus init_status, + const std::string& diagnostics) { LOG(WARNING) << "initializing autocomplete database failed"; } BraveBrowserContext::BraveBrowserContext(const std::string& partition, - bool in_memory, - const base::DictionaryValue& options) - : atom::AtomBrowserContext(partition, in_memory, options), + bool in_memory, + const base::DictionaryValue& options, + scoped_refptr task_runner) + : Profile(partition, in_memory, options), pref_registry_(new user_prefs::PrefRegistrySyncable), -#if defined(ENABLE_EXTENSIONS) - network_delegate_(new extensions::AtomExtensionsNetworkDelegate(this)), -#else - network_delegate_(new AtomNetworkDelegate), -#endif - sent_destroyed_notification_(false), has_parent_(false), original_context_(nullptr), - partition_(partition) { + partition_(partition), + ready_( + new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED)) + { std::string parent_partition; if (options.GetString("parent_partition", &parent_partition)) { has_parent_ = true; @@ -99,8 +109,7 @@ BraveBrowserContext::BraveBrowserContext(const std::string& partition, atom::AtomBrowserContext::From(partition, false).get()); original_context_->otr_context_ = this; } - InitPrefs(); - + CreateProfilePrefs(task_runner); if (original_context_) { TrackZoomLevelsFromParent(); } @@ -115,18 +124,6 @@ BraveBrowserContext::BraveBrowserContext(const std::string& partition, #endif } -void BraveBrowserContext::MaybeSendDestroyedNotification() { - if (!sent_destroyed_notification_) { - sent_destroyed_notification_ = true; - - NotifyWillBeDestroyed(this); - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PROFILE_DESTROYED, - content::Source(this), - content::NotificationService::NoDetails()); - } -} - BraveBrowserContext::~BraveBrowserContext() { MaybeSendDestroyedNotification(); @@ -161,15 +158,6 @@ BraveBrowserContext::~BraveBrowserContext() { } } - if (!IsOffTheRecord()) { - // temporary fix for https://github.com/brave/browser-laptop/issues/2335 - // TODO(brivdiver) - it seems like something is holding onto a reference to - // url_request_context_getter or the url_request_context and is preventing - // it from being destroyed - url_request_context_getter()->GetURLRequestContext()->cookie_store()-> - FlushStore(base::Closure()); - } - BrowserContextDependencyManager::GetInstance()-> DestroyBrowserContextServices(this); @@ -181,6 +169,8 @@ BraveBrowserContext::~BraveBrowserContext() { base::Unretained(original_context_.get()), base::Unretained(this))); #endif } + + ShutdownStoragePartitions(); } // static @@ -189,6 +179,22 @@ BraveBrowserContext* BraveBrowserContext::FromBrowserContext( return static_cast(browser_context); } +Profile* BraveBrowserContext::GetOffTheRecordProfile() { + return otr_context(); +} + +bool BraveBrowserContext::HasOffTheRecordProfile() { + return !!otr_context(); +} + +Profile* BraveBrowserContext::GetOriginalProfile() { + return original_context(); +} + +bool BraveBrowserContext::IsSameProfile(Profile* profile) { + return GetOriginalProfile() == profile->GetOriginalProfile(); +} + bool BraveBrowserContext::HasParentContext() { return has_parent_; } @@ -215,6 +221,14 @@ BraveBrowserContext* BraveBrowserContext::otr_context() { return nullptr; } +content::BrowserPluginGuestManager* BraveBrowserContext::GetGuestManager() { +#if defined(ENABLE_EXTENSIONS) + return guest_view::GuestViewManager::FromBrowserContext(this); +#else + return NULL; +#endif +} + void BraveBrowserContext::TrackZoomLevelsFromParent() { // Here we only want to use zoom levels stored in the main-context's default // storage partition. We're not interested in zoom levels in special @@ -279,8 +293,20 @@ content::PermissionManager* BraveBrowserContext::GetPermissionManager() { return permission_manager_.get(); } +atom::AtomNetworkDelegate* BraveBrowserContext::network_delegate() { + auto getter = GetRequestContext(); + DCHECK(getter); + return static_cast( + getter->GetURLRequestContext()->network_delegate()); +} + +net::URLRequestContextGetter* BraveBrowserContext::GetRequestContext() { + return GetDefaultStoragePartition(this)->GetURLRequestContext(); +} + net::NetworkDelegate* BraveBrowserContext::CreateNetworkDelegate() { - return network_delegate_; + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return new extensions::AtomExtensionsNetworkDelegate(this); } std::unique_ptr @@ -298,18 +324,13 @@ BraveBrowserContext::CreateURLRequestJobFactory( extensions::CreateExtensionProtocolHandler(IsOffTheRecord(), extension_info_map)); #endif - - std::unique_ptr - protocol_handler_interceptor = - ProtocolHandlerRegistryFactory::GetForBrowserContext(this) - ->CreateJobInterceptorFactory(); - - protocol_handler_interceptor->Chain(std::move(job_factory)); - return std::move(protocol_handler_interceptor); + protocol_handler_interceptor_->Chain(std::move(job_factory)); + return std::move(protocol_handler_interceptor_); } -void BraveBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { - AtomBrowserContext::RegisterPrefs(pref_registry); +void BraveBrowserContext::CreateProfilePrefs( + scoped_refptr task_runner) { + InitPrefs(task_runner); #if defined(ENABLE_EXTENSIONS) PrefStore* extension_prefs = new ExtensionPrefStore( ExtensionPrefValueMapFactory::GetForBrowserContext(original_context()), @@ -340,9 +361,14 @@ void BraveBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { pref_registry_->RegisterDictionaryPref("app_state"); pref_registry_->RegisterDictionaryPref(prefs::kPartitionDefaultZoomLevel); pref_registry_->RegisterDictionaryPref(prefs::kPartitionPerHostZoomLevels); +#if defined(ENABLE_PRINTING) + pref_registry_->RegisterBooleanPref(prefs::kPrintingEnabled, true); +#endif + pref_registry_->RegisterBooleanPref(prefs::kPrintPreviewDisabled, false); // TODO(bridiver) - is this necessary or is it covered by // BrowserContextDependencyManager ProtocolHandlerRegistry::RegisterProfilePrefs(pref_registry_.get()); + HostContentSettingsMap::RegisterProfilePrefs(pref_registry_.get()); autofill::AutofillManager::RegisterProfilePrefs(pref_registry_.get()); #if defined(ENABLE_EXTENSIONS) extensions::AtomBrowserClientExtensionsPart::RegisterProfilePrefs( @@ -355,9 +381,6 @@ void BraveBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { // create profile prefs base::FilePath filepath = GetPath().Append( FILE_PATH_LITERAL("UserPrefs")); - scoped_refptr task_runner = - JsonPrefStore::GetTaskRunnerForFile( - filepath, BrowserThread::GetBlockingPool()); scoped_refptr pref_store = new JsonPrefStore(filepath, task_runner, std::unique_ptr()); @@ -368,14 +391,14 @@ void BraveBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { factory.set_user_prefs(pref_store); user_prefs_ = factory.CreateSyncable(pref_registry_.get()); user_prefs::UserPrefs::Set(this, user_prefs_.get()); + if (async) { + user_prefs_->AddPrefInitObserver(base::Bind( + &BraveBrowserContext::OnPrefsLoaded, base::Unretained(this))); + return; + } } - if (async) { - user_prefs_->AddPrefInitObserver(base::Bind( - &BraveBrowserContext::OnPrefsLoaded, base::Unretained(this))); - } else { - OnPrefsLoaded(true); - } + OnPrefsLoaded(true); } void BraveBrowserContext::OnPrefsLoaded(bool success) { @@ -387,9 +410,6 @@ void BraveBrowserContext::OnPrefsLoaded(bool success) { if (!IsOffTheRecord() && !HasParentContext()) { #if defined(ENABLE_EXTENSIONS) extensions::ExtensionSystem::Get(this)->InitForRegularProfile(true); - static_cast(network_delegate_)-> - set_extension_info_map( - extensions::ExtensionSystem::Get(this)->info_map()); #endif content::BrowserContext::GetDefaultStoragePartition(this)-> GetDOMStorageContext()->SetSaveSessionStorageOnDisk(); @@ -399,24 +419,41 @@ void BraveBrowserContext::OnPrefsLoaded(bool success) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); web_database_ = new WebDatabaseService(webDataPath, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); + BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), + BrowserThread::GetTaskRunnerForThread(BrowserThread::DB)); web_database_->AddTable(base::WrapUnique(new autofill::AutofillTable)); web_database_->LoadDatabase(); autofill_data_ = new autofill::AutofillWebDataService( web_database_, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB), + BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), + BrowserThread::GetTaskRunnerForThread(BrowserThread::DB), base::Bind(&DatabaseErrorCallback)); autofill_data_->Init(); } + protocol_handler_interceptor_ = + ProtocolHandlerRegistryFactory::GetForBrowserContext(this) + ->CreateJobInterceptorFactory(); + user_prefs_registrar_->Init(user_prefs_.get()); + +#if defined(ENABLE_EXTENSIONS) + guest_view::GuestViewManager* guest_view_manager = + guest_view::GuestViewManager::FromBrowserContext(this); + if (!guest_view_manager) { + guest_view::GuestViewManager::CreateWithDelegate( + this, + extensions::ExtensionsAPIClient::Get()-> + CreateGuestViewManagerDelegate(this)); + } +#endif + content::NotificationService::current()->Notify( chrome::NOTIFICATION_PROFILE_CREATED, content::Source(this), content::NotificationService::NoDetails()); + ready_->Signal(); } content::ResourceContext* BraveBrowserContext::GetResourceContext() { @@ -445,6 +482,40 @@ base::FilePath BraveBrowserContext::GetPath() const { namespace atom { +void CreateDirectoryAndSignal(const base::FilePath& path, + base::WaitableEvent* done_creating) { + if (!base::PathExists(path)) { + DVLOG(1) << "Creating directory " << path.value(); + base::CreateDirectory(path); + } + done_creating->Signal(); +} + +// Task that blocks the FILE thread until CreateDirectoryAndSignal() finishes on +// blocking I/O pool. +void BlockFileThreadOnDirectoryCreate(base::WaitableEvent* done_creating) { + done_creating->Wait(); +} + +// Initiates creation of profile directory on |sequenced_task_runner| and +// ensures that FILE thread is blocked until that operation finishes. If +// |create_readme| is true, the profile README will be created in the profile +// directory. +void CreateProfileDirectory(base::SequencedTaskRunner* sequenced_task_runner, + const base::FilePath& path) { + base::WaitableEvent* done_creating = + new base::WaitableEvent(base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + sequenced_task_runner->PostTask( + FROM_HERE, base::Bind(&CreateDirectoryAndSignal, path, done_creating)); + // Block the FILE thread until directory is created on I/O pool to make sure + // that we don't attempt any operation until that part completes. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&BlockFileThreadOnDirectoryCreate, + base::Owned(done_creating))); +} + // TODO(bridiver) find a better way to do this // static scoped_refptr AtomBrowserContext::From( @@ -454,7 +525,31 @@ scoped_refptr AtomBrowserContext::From( if (browser_context) return static_cast(browser_context.get()); - return new brave::BraveBrowserContext(partition, in_memory, options); + // TODO(bridiver) - pass the path to initialize the browser context + // TODO(bridiver) - create these with the profile manager + base::FilePath path; + PathService::Get(brightray::DIR_USER_DATA, &path); + if (!in_memory && !partition.empty()) + path = path.Append(FILE_PATH_LITERAL("Partitions")) + .Append(base::FilePath::FromUTF8Unsafe( + net::EscapePath(base::ToLowerASCII(partition)))); + + // Get sequenced task runner for making sure that file operations of + // this profile (defined by |path|) are executed in expected order + // (what was previously assured by the FILE thread). + scoped_refptr sequenced_task_runner = + JsonPrefStore::GetTaskRunnerForFile(path, + BrowserThread::GetBlockingPool()); + + CreateProfileDirectory(sequenced_task_runner.get(), path); + + auto profile = new brave::BraveBrowserContext(partition, in_memory, options, + sequenced_task_runner); + + if (profile == profile->GetOriginalProfile()) + g_browser_process->profile_manager()->AddProfile(profile); + + return profile; } } // namespace atom diff --git a/brave/browser/brave_browser_context.h b/brave/browser/brave_browser_context.h index 3a1dff6575..a753a32068 100644 --- a/brave/browser/brave_browser_context.h +++ b/brave/browser/brave_browser_context.h @@ -9,6 +9,8 @@ #include #include "atom/browser/atom_browser_context.h" #include "content/public/browser/host_zoom_map.h" +#include "chrome/browser/custom_handlers/protocol_handler_registry.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/prefs/overlay_user_pref_store.h" @@ -28,10 +30,12 @@ namespace brave { class BravePermissionManager; -class BraveBrowserContext : public atom::AtomBrowserContext { +class BraveBrowserContext : public Profile { public: BraveBrowserContext(const std::string& partition, - bool in_memory, const base::DictionaryValue& options); + bool in_memory, + const base::DictionaryValue& options, + scoped_refptr task_runner); ~BraveBrowserContext() override; std::unique_ptr CreateZoomLevelDelegate( @@ -45,44 +49,53 @@ class BraveBrowserContext : public atom::AtomBrowserContext { std::unique_ptr CreateURLRequestJobFactory( content::ProtocolHandlerMap* protocol_handlers) override; - // brightray::BrowserContext: - void RegisterPrefs(PrefRegistrySimple* pref_registry) override; + void CreateProfilePrefs(scoped_refptr task_runner); - ChromeZoomLevelPrefs* GetZoomLevelPrefs(); + ChromeZoomLevelPrefs* GetZoomLevelPrefs() override; bool HasParentContext(); // content::BrowserContext: - net::NetworkDelegate* CreateNetworkDelegate() override; content::PermissionManager* GetPermissionManager() override; content::ResourceContext* GetResourceContext() override; + net::NetworkDelegate* CreateNetworkDelegate() override; + content::BrowserPluginGuestManager* GetGuestManager() override; // atom::AtomBrowserContext - atom::AtomNetworkDelegate* network_delegate() const override { - return network_delegate_; } + atom::AtomNetworkDelegate* network_delegate() override; + + // Profile + net::URLRequestContextGetter* GetRequestContext() override; BraveBrowserContext* original_context(); BraveBrowserContext* otr_context(); - user_prefs::PrefRegistrySyncable* pref_registry() const { + Profile* GetOffTheRecordProfile() override; + bool HasOffTheRecordProfile() override; + Profile* GetOriginalProfile() override; + bool IsSameProfile(Profile* profile) override; + + user_prefs::PrefRegistrySyncable* pref_registry() const override { return pref_registry_.get(); } syncable_prefs::PrefServiceSyncable* user_prefs() const { return user_prefs_.get(); } - syncable_prefs::PrefServiceSyncable* GetPrefs() const { - return user_prefs_.get(); } + syncable_prefs::PrefServiceSyncable* GetPrefs() { return user_prefs_.get(); } + + // const PrefService* GetPrefs() const override; - PrefChangeRegistrar* user_prefs_change_registrar() const { + PrefChangeRegistrar* user_prefs_change_registrar() const override { return user_prefs_registrar_.get(); } const std::string& partition() const { return partition_; } + base::WaitableEvent* ready() { return ready_.get(); } - void AddOverlayPref(const std::string name) { + void AddOverlayPref(const std::string name) override { overlay_pref_names_.push_back(name.c_str()); } scoped_refptr - GetAutofillWebdataService(); + GetAutofillWebdataService() override; base::FilePath GetPath() const override; @@ -92,7 +105,6 @@ class BraveBrowserContext : public atom::AtomBrowserContext { void OnParentZoomLevelChanged( const content::HostZoomMap::ZoomLevelChange& change); void UpdateDefaultZoomLevel(); - void MaybeSendDestroyedNotification(); scoped_refptr pref_registry_; std::unique_ptr user_prefs_; @@ -105,16 +117,16 @@ class BraveBrowserContext : public atom::AtomBrowserContext { std::unique_ptr permission_manager_; - atom::AtomNetworkDelegate* network_delegate_; - - bool sent_destroyed_notification_; bool has_parent_; scoped_refptr original_context_; BraveBrowserContext* otr_context_; const std::string partition_; + std::unique_ptr ready_; scoped_refptr autofill_data_; scoped_refptr web_database_; + std::unique_ptr + protocol_handler_interceptor_; DISALLOW_COPY_AND_ASSIGN(BraveBrowserContext); }; diff --git a/brave/browser/brave_content_browser_client.cc b/brave/browser/brave_content_browser_client.cc index 83231a1ae1..8dd10cf6c6 100644 --- a/brave/browser/brave_content_browser_client.cc +++ b/brave/browser/brave_content_browser_client.cc @@ -6,31 +6,59 @@ #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" +#include "base/base_switches.h" +#include "base/lazy_instance.h" +#include "base/path_service.h" #include "brave/browser/notifications/platform_notification_service_impl.h" +#include "brightray/browser/brightray_paths.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/chrome_render_message_filter.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/render_messages.h" +#include "content/public/browser/storage_partition.h" +#include "components/autofill/content/browser/content_autofill_driver_factory.h" +#include "components/autofill/core/common/autofill_switches.h" +#include "components/content_settings/core/browser/content_settings_utils.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/common/content_settings.h" +#include "components/content_settings/core/common/content_settings_types.h" +#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h" +#include "components/subresource_filter/content/browser/subresource_filter_navigation_throttle.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_process_host.h" +#include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" +#include "extensions/common/constants.h" +#include "services/shell/public/cpp/interface_registry.h" #include "ui/base/l10n/l10n_util.h" #if defined(ENABLE_EXTENSIONS) #include "atom/browser/extensions/atom_browser_client_extensions_part.h" -// fix for undefined symbol error from io_thread_extension_message_filter.h -#include "components/web_modal/web_contents_modal_dialog_manager.h" -namespace web_modal { -SingleWebContentsDialogManager* -WebContentsModalDialogManager::CreateNativeWebModalManager( - gfx::NativeWindow dialog, - SingleWebContentsDialogManagerDelegate* native_delegate) { - // TODO(bridiver): Investigate if we need to implement this. - NOTREACHED(); - return NULL; -} -} // namespace web_modal +#include "extensions/browser/extension_navigation_throttle.h" +#include "extensions/browser/extension_util.h" +#include "extensions/common/switches.h" #endif +using content::BrowserThread; namespace brave { +namespace { + +// Cached version of the locale so we can return the locale on the I/O +// thread. +base::LazyInstance io_thread_application_locale; + +void SetApplicationLocaleOnIOThread(const std::string& locale) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + io_thread_application_locale.Get() = locale; +} + +} // namespace + BraveContentBrowserClient::BraveContentBrowserClient() { #if defined(ENABLE_EXTENSIONS) extensions_part_.reset(new extensions::AtomBrowserClientExtensionsPart); @@ -40,12 +68,149 @@ BraveContentBrowserClient::BraveContentBrowserClient() { BraveContentBrowserClient::~BraveContentBrowserClient() { } +std::string BraveContentBrowserClient::GetStoragePartitionIdForSite( + content::BrowserContext* browser_context, + const GURL& site) { + std::string partition_id; + +#if defined(ENABLE_EXTENSIONS) + if (site.SchemeIs(extensions::kExtensionScheme) && + extensions::util::SiteHasIsolatedStorage(site, browser_context)) + partition_id = site.spec(); +#endif + + DCHECK(IsValidStoragePartitionId(browser_context, partition_id)); + return partition_id; +} + +bool BraveContentBrowserClient::IsValidStoragePartitionId( + content::BrowserContext* browser_context, + const std::string& partition_id) { + // The default ID is empty and is always valid. + if (partition_id.empty()) + return true; + + return GURL(partition_id).is_valid(); +} + +void BraveContentBrowserClient::GetStoragePartitionConfigForSite( + content::BrowserContext* browser_context, + const GURL& site, + bool can_be_default, + std::string* partition_domain, + std::string* partition_name, + bool* in_memory) { + // Default to the browser-wide storage partition and override based on |site| + // below. + partition_domain->clear(); + partition_name->clear(); + *in_memory = false; + +#if defined(ENABLE_EXTENSIONS) + if (site.SchemeIs(extensions::kExtensionScheme)) { + // If |can_be_default| is false, the caller is stating that the |site| + // should be parsed as if it had isolated storage. In particular it is + // important to NOT check ExtensionService for the is_storage_isolated() + // attribute because this code path is run during Extension uninstall + // to do cleanup after the Extension has already been unloaded from the + // ExtensionService. + bool is_isolated = !can_be_default; + if (can_be_default) { + if (extensions::util::SiteHasIsolatedStorage(site, browser_context)) + is_isolated = true; + } + + if (is_isolated) { + CHECK(site.has_host()); + // For extensions with isolated storage, the the host of the |site| is + // the |partition_domain|. The |in_memory| and |partition_name| are only + // used in guest schemes so they are cleared here. + *partition_domain = site.host(); + *in_memory = false; + partition_name->clear(); + } + } +#endif + + // Assert that if |can_be_default| is false, the code above must have found a + // non-default partition. If this fails, the caller has a serious logic + // error about which StoragePartition they expect to be in and it is not + // safe to continue. + CHECK(can_be_default || !partition_domain->empty()); +} + +// TODO(bridiver) - investigate this +// content::WebContentsViewDelegate* +// ChromeContentBrowserClient::GetWebContentsViewDelegate( +// content::WebContents* web_contents) { +// return chrome::CreateWebContentsViewDelegate(web_contents); +// } + +void BraveContentBrowserClient::RegisterRenderFrameMojoInterfaces( + shell::InterfaceRegistry* registry, + content::RenderFrameHost* render_frame_host) { + registry->AddInterface( + base::Bind(&autofill::ContentAutofillDriverFactory::BindAutofillDriver, + render_frame_host)); +} + void BraveContentBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { + int id = host->GetID(); + Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); + // initialize request context + host->GetStoragePartition()->GetURLRequestContext(); + atom::AtomBrowserClient::RenderProcessWillLaunch(host); + // host->AddFilter(new ChromeRenderMessageFilter( + // id, profile, host->GetStoragePartition()->GetServiceWorkerContext())); +#if defined(ENABLE_WEBRTC) + // WebRtcLoggingHandlerHost* webrtc_logging_handler_host = + // new WebRtcLoggingHandlerHost(id, profile, + // g_browser_process->webrtc_log_uploader()); + // host->AddFilter(webrtc_logging_handler_host); + // host->SetUserData(WebRtcLoggingHandlerHost::kWebRtcLoggingHandlerHostKey, + // new base::UserDataAdapter( + // webrtc_logging_handler_host)); + + // AudioDebugRecordingsHandler* audio_debug_recordings_handler = + // new AudioDebugRecordingsHandler(profile); + // host->SetUserData( + // AudioDebugRecordingsHandler::kAudioDebugRecordingsHandlerKey, + // new base::UserDataAdapter( + // audio_debug_recordings_handler)); + + // WebRtcEventLogHandler* webrtc_event_log_handler = + // new WebRtcEventLogHandler(profile); + // host->SetUserData(WebRtcEventLogHandler::kWebRtcEventLogHandlerKey, + // new base::UserDataAdapter( + // webrtc_event_log_handler)); +#endif + host->Send(new ChromeViewMsg_SetIsIncognitoProcess( + profile->IsOffTheRecord())); + #if defined(ENABLE_EXTENSIONS) extensions_part_->RenderProcessWillLaunch(host); #endif + + RendererContentSettingRules rules; + GetRendererContentSettingRules( + HostContentSettingsMapFactory::GetForProfile(profile), &rules); + host->Send(new ChromeViewMsg_SetContentSettingRules(rules)); +} + +GURL BraveContentBrowserClient::GetEffectiveURL( + content::BrowserContext* browser_context, const GURL& url) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (!profile) + return url; + +#if defined(ENABLE_EXTENSIONS) + return extensions::AtomBrowserClientExtensionsPart::GetEffectiveURL( + profile, url); +#else + return url; +#endif } void BraveContentBrowserClient::BrowserURLHandlerCreated( @@ -63,42 +228,116 @@ std::string BraveContentBrowserClient::GetAcceptLangs( } std::string BraveContentBrowserClient::GetApplicationLocale() { - std::string locale; -#if defined(ENABLE_EXTENSIONS) - locale = extensions_part_->GetApplicationLocale(); -#endif - return locale.empty() ? l10n_util::GetApplicationLocale(locale) : locale; + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { + return io_thread_application_locale.Get(); + } else { + return extensions_part_->GetApplicationLocale(); + } } void BraveContentBrowserClient::SetApplicationLocale(std::string locale) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); #if defined(ENABLE_EXTENSIONS) extensions::AtomBrowserClientExtensionsPart::SetApplicationLocale(locale); #endif + // This object is guaranteed to outlive all threads so we don't have to + // worry about the lack of refcounting and can just post as Unretained. + // + // The common case is that this function is called early in Chrome startup + // before any threads are created (it will also be called later if the user + // changes the pref). In this case, there will be no threads created and + // posting will fail. When there are no threads, we can just set the string + // without worrying about threadsafety. + if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&SetApplicationLocaleOnIOThread, locale))) { + io_thread_application_locale.Get() = locale; + } } void BraveContentBrowserClient::AppendExtraCommandLineSwitches( base::CommandLine* command_line, - int process_id) { - std::string process_type = command_line->GetSwitchValueASCII("type"); - if (process_type != "renderer") - return; - + int child_process_id) { atom::AtomBrowserClient::AppendExtraCommandLineSwitches( - command_line, process_id); + command_line, child_process_id); -#if defined(ENABLE_EXTENSIONS) - content::RenderProcessHost* process = - content::RenderProcessHost::FromID(process_id); - extensions_part_->AppendExtraRendererCommandLineSwitches( - command_line, process, process->GetBrowserContext()); +#if defined(OS_POSIX) && !defined(OS_MACOSX) + if (breakpad::IsCrashReporterEnabled()) { + command_line->AppendSwitch(switches::kEnableCrashReporter); + } #endif -#if !defined(OS_LINUX) - if (atom::WebContentsPreferences::run_node(command_line)) { - // Disable renderer sandbox for most of node's functions. - command_line->AppendSwitch(::switches::kNoSandbox); - } + std::string process_type = + command_line->GetSwitchValueASCII(switches::kProcessType); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + + static const char* const kCommonSwitchNames[] = { + switches::kUserAgent, + switches::kUserDataDir, // Make logs go to the right file. + }; + command_line->CopySwitchesFrom(browser_command_line, kCommonSwitchNames, + arraysize(kCommonSwitchNames)); + + if (process_type == switches::kRendererProcess) { + content::RenderProcessHost* process = + content::RenderProcessHost::FromID(child_process_id); + + if (process) { + #if defined(ENABLE_EXTENSIONS) + extensions_part_->AppendExtraRendererCommandLineSwitches( + command_line, process, process->GetBrowserContext()); + #endif + } + + static const char* const kSwitchNames[] = { + autofill::switches::kDisablePasswordGeneration, + autofill::switches::kEnablePasswordGeneration, + autofill::switches::kEnableSingleClickAutofill, + autofill::switches::kEnableSuggestionsWithSubstringMatch, + autofill::switches::kIgnoreAutocompleteOffForAutofill, + autofill::switches::kLocalHeuristicsOnlyForPasswordGeneration, +#if defined(ENABLE_EXTENSIONS) + extensions::switches::kAllowHTTPBackgroundPage, + extensions::switches::kAllowLegacyExtensionManifests, + extensions::switches::kEnableAppWindowControls, + extensions::switches::kEnableEmbeddedExtensionOptions, + extensions::switches::kEnableExperimentalExtensionApis, + extensions::switches::kExtensionsOnChromeURLs, + extensions::switches::kIsolateExtensions, + extensions::switches::kWhitelistedExtensionID, #endif + }; + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames, + arraysize(kSwitchNames)); + } else if (process_type == switches::kUtilityProcess) { +#if defined(ENABLE_EXTENSIONS) + static const char* const kSwitchNames[] = { + extensions::switches::kAllowHTTPBackgroundPage, + extensions::switches::kEnableExperimentalExtensionApis, + extensions::switches::kExtensionsOnChromeURLs, + extensions::switches::kWhitelistedExtensionID, + }; + + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames, + arraysize(kSwitchNames)); +#endif + } else if (process_type == switches::kZygoteProcess) { + static const char* const kSwitchNames[] = { + // Load (in-process) Pepper plugins in-process in the zygote pre-sandbox. + switches::kDisableBundledPpapiFlash, + switches::kPpapiFlashPath, + switches::kPpapiFlashVersion, + }; + + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames, + arraysize(kSwitchNames)); + } else if (process_type == switches::kGpuProcess) { + // If --ignore-gpu-blacklist is passed in, don't send in crash reports + // because GPU is expected to be unreliable. + if (browser_command_line.HasSwitch(switches::kIgnoreGpuBlacklist) && + !command_line->HasSwitch(switches::kDisableBreakpad)) + command_line->AppendSwitch(switches::kDisableBreakpad); + } } bool BraveContentBrowserClient::CanCreateWindow( @@ -106,9 +345,9 @@ bool BraveContentBrowserClient::CanCreateWindow( const GURL& opener_top_level_frame_url, const GURL& source_origin, WindowContainerType container_type, - const std::string& frame_name, const GURL& target_url, const content::Referrer& referrer, + const std::string& frame_name, WindowOpenDisposition disposition, const blink::WebWindowFeatures& features, bool user_gesture, @@ -122,16 +361,7 @@ bool BraveContentBrowserClient::CanCreateWindow( *no_javascript_access = false; - if (container_type == WINDOW_CONTAINER_TYPE_BACKGROUND) { - return true; - } - - // this will override allowpopups we need some way to integerate - // it so we can turn popup blocking off if desired - if (!user_gesture) { - return false; - } - + // always return true because we'll block popups later return true; } @@ -187,16 +417,26 @@ bool BraveContentBrowserClient::ShouldUseProcessPerSite( void BraveContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem( std::vector* additional_allowed_schemes) { + AtomBrowserClient::GetAdditionalAllowedSchemesForFileSystem(additional_allowed_schemes); + ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem( + additional_allowed_schemes); + + additional_allowed_schemes->push_back(content::kChromeDevToolsScheme); + additional_allowed_schemes->push_back(content::kChromeUIScheme); + #if defined(ENABLE_EXTENSIONS) extensions_part_-> GetAdditionalAllowedSchemesForFileSystem(additional_allowed_schemes); #endif } +void BraveContentBrowserClient::GetAdditionalWebUISchemes( + std::vector* additional_schemes) { +} + bool BraveContentBrowserClient::ShouldAllowOpenURL( content::SiteInstance* site_instance, const GURL& url) { GURL from_url = site_instance->GetSiteURL(); - #if defined(ENABLE_EXTENSIONS) bool result; if (extensions::AtomBrowserClientExtensionsPart::ShouldAllowOpenURL( @@ -207,7 +447,39 @@ bool BraveContentBrowserClient::ShouldAllowOpenURL( return true; } -// TODO(bridiver) can OverrideSiteInstanceForNavigation be replaced with this? +base::FilePath BraveContentBrowserClient::GetShaderDiskCacheDirectory() { + base::FilePath user_data_dir; + PathService::Get(brightray::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + return user_data_dir.Append(FILE_PATH_LITERAL("ShaderCache")); +} + +gpu::GpuChannelEstablishFactory* +BraveContentBrowserClient::GetGpuChannelEstablishFactory() { +#if defined(USE_AURA) + if (views::WindowManagerConnection::Exists()) + return views::WindowManagerConnection::Get()->gpu_service(); +#endif + return nullptr; +} + +void BraveContentBrowserClient::RegisterInProcessMojoApplications( + StaticMojoApplicationMap* apps) { +#if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) + content::MojoApplicationInfo app_info; + app_info.application_factory = base::Bind(&media::CreateMojoMediaApplication); + apps->insert(std::make_pair("mojo:media", app_info)); +#endif +} + +void BraveContentBrowserClient::RegisterOutOfProcessMojoApplications( + OutOfProcessMojoApplicationMap* apps) { +#if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS) + apps->insert(std::make_pair("mojo:media", + base::ASCIIToUTF16("Media App"))); +#endif +} + bool BraveContentBrowserClient::ShouldSwapBrowsingInstancesForNavigation( content::SiteInstance* site_instance, const GURL& current_url, @@ -221,5 +493,31 @@ bool BraveContentBrowserClient::ShouldSwapBrowsingInstancesForNavigation( #endif } +ScopedVector +BraveContentBrowserClient::CreateThrottlesForNavigation( + content::NavigationHandle* handle) { + ScopedVector throttles; +#if defined(ENABLE_EXTENSIONS) + if (!handle->IsInMainFrame()) + throttles.push_back(new extensions::ExtensionNavigationThrottle(handle)); +#endif + + // TODO(bridiver) + // subresource_filter::ContentSubresourceFilterDriverFactory* + // subresource_filter_driver_factory = + // subresource_filter::ContentSubresourceFilterDriverFactory:: + // FromWebContents(handle->GetWebContents()); + // if (subresource_filter_driver_factory && handle->IsInMainFrame() && + // handle->GetURL().SchemeIsHTTPOrHTTPS()) { + // // TODO(melandory): Activation logic should be moved to the + // // WebContentsObserver, once ReadyToCommitNavigation is available on + // // pre-PlzNavigate world (tracking bug: https://crbug.com/621856). + // throttles.push_back( + // subresource_filter::SubresourceFilterNavigationThrottle::Create( + // handle)); + // } + + return throttles; +} } // namespace brave diff --git a/brave/browser/brave_content_browser_client.h b/brave/browser/brave_content_browser_client.h index 983bfd2360..87d28df7b6 100644 --- a/brave/browser/brave_content_browser_client.h +++ b/brave/browser/brave_content_browser_client.h @@ -12,6 +12,7 @@ namespace content { class PlatformNotificationService; +class NavigationHandle; } #if defined(ENABLE_EXTENSIONS) @@ -33,6 +34,9 @@ class BraveContentBrowserClient : public atom::AtomBrowserClient { protected: // content::ContentBrowserClient: + void RegisterRenderFrameMojoInterfaces( + shell::InterfaceRegistry* registry, + content::RenderFrameHost* render_frame_host) override; void RenderProcessWillLaunch(content::RenderProcessHost* host) override; void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override; @@ -44,9 +48,9 @@ class BraveContentBrowserClient : public atom::AtomBrowserClient { const GURL& opener_top_level_frame_url, const GURL& source_origin, WindowContainerType container_type, - const std::string& frame_name, const GURL& target_url, const content::Referrer& referrer, + const std::string& frame_name, WindowOpenDisposition disposition, const blink::WebWindowFeatures& features, bool user_gesture, @@ -60,18 +64,48 @@ class BraveContentBrowserClient : public atom::AtomBrowserClient { const GURL& effective_url) override; void GetAdditionalAllowedSchemesForFileSystem( std::vector* additional_allowed_schemes) override; + void GetAdditionalWebUISchemes( + std::vector* additional_schemes) override; bool ShouldAllowOpenURL(content::SiteInstance* site_instance, const GURL& url) override; void BrowserURLHandlerCreated( content::BrowserURLHandler* handler) override; void SiteInstanceGotProcess( - content::SiteInstance* site_instance) override; + content::SiteInstance* site_instance) override; void SiteInstanceDeleting( - content::SiteInstance* site_instance) override; + content::SiteInstance* site_instance) override; bool ShouldSwapBrowsingInstancesForNavigation( content::SiteInstance* site_instance, const GURL& current_url, const GURL& new_url) override; + std::string GetStoragePartitionIdForSite( + content::BrowserContext* browser_context, + const GURL& site) override; + void GetStoragePartitionConfigForSite( + content::BrowserContext* browser_context, + const GURL& site, + bool can_be_default, + std::string* partition_domain, + std::string* partition_name, + bool* in_memory) override; + GURL GetEffectiveURL( + content::BrowserContext* browser_context, const GURL& url) override; + base::FilePath GetShaderDiskCacheDirectory() override; + gpu::GpuChannelEstablishFactory* GetGpuChannelEstablishFactory() override; + +void RegisterInProcessMojoApplications(StaticMojoApplicationMap* apps) override; +void RegisterOutOfProcessMojoApplications( + OutOfProcessMojoApplicationMap* apps) override; + + + ScopedVector CreateThrottlesForNavigation( + content::NavigationHandle* handle) override; + + protected: + bool IsValidStoragePartitionId( + content::BrowserContext* browser_context, + const std::string& partition_id); + private: #if defined(ENABLE_EXTENSIONS) diff --git a/brave/browser/brave_permission_manager.cc b/brave/browser/brave_permission_manager.cc index 139f4fb699..bb244441c2 100644 --- a/brave/browser/brave_permission_manager.cc +++ b/brave/browser/brave_permission_manager.cc @@ -61,7 +61,8 @@ int BravePermissionManager::RequestPermission( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - const ResponseCallback& response_callback) { + bool user_gesture, + const base::Callback& response_callback) { int render_frame_id = MSG_ROUTING_NONE; int render_process_id = MSG_ROUTING_NONE; GURL url; @@ -105,7 +106,7 @@ int BravePermissionManager::RequestPermission( void BravePermissionManager::OnPermissionResponse( int request_id, const GURL& origin, - const ResponseCallback& callback, + const base::Callback& callback, blink::mojom::PermissionStatus status) { auto request = pending_requests_.find(request_id); if (request != pending_requests_.end()) { diff --git a/brave/browser/brave_permission_manager.h b/brave/browser/brave_permission_manager.h index 91a4cef830..9dd24788ac 100644 --- a/brave/browser/brave_permission_manager.h +++ b/brave/browser/brave_permission_manager.h @@ -38,7 +38,8 @@ class BravePermissionManager : public atom::AtomPermissionManager { content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - const ResponseCallback& callback) override; + bool user_gesture, + const base::Callback& callback) override; protected: void OnPermissionResponse(int request_id, diff --git a/brave/browser/component_updater/BUILD.gn b/brave/browser/component_updater/BUILD.gn new file mode 100644 index 0000000000..83872bee48 --- /dev/null +++ b/brave/browser/component_updater/BUILD.gn @@ -0,0 +1,19 @@ +source_set("component_updater") { + configs += [ + "//electron/build:electron_config", + ] + + sources = [ + "brave_component_updater_configurator.cc", + "brave_component_updater_configurator.h", + "default_extensions.h", + "extension_installer_traits.cc", + "extension_installer_traits.h", + "widevine_cdm_component_installer.cc", + "widevine_cdm_component_installer.h", + ] + + deps = [ + "//components/component_updater", + ] +} diff --git a/brave/browser/component_updater/brave_component_updater_configurator.cc b/brave/browser/component_updater/brave_component_updater_configurator.cc index 00f2b573e5..83a009cc32 100644 --- a/brave/browser/component_updater/brave_component_updater_configurator.cc +++ b/brave/browser/component_updater/brave_component_updater_configurator.cc @@ -50,9 +50,10 @@ class BraveConfigurator : public update_client::Configurator { net::URLRequestContextGetter* RequestContext() const override; scoped_refptr CreateOutOfProcessPatcher() const override; - bool DeltasEnabled() const override; - bool UseBackgroundDownloader() const override; - bool UseCupSigning() const override; + bool EnabledDeltas() const override; + bool EnabledComponentUpdates() const override; + bool EnabledBackgroundDownloader() const override; + bool EnabledCupSigning() const override; scoped_refptr GetSequencedTaskRunner() const override; PrefService* GetPrefService() const override; @@ -149,21 +150,25 @@ BraveConfigurator::CreateOutOfProcessPatcher() const { return nullptr; } -bool BraveConfigurator::DeltasEnabled() const { +bool BraveConfigurator::EnabledComponentUpdates() const { + return configurator_impl_.EnabledComponentUpdates(); +} + +bool BraveConfigurator::EnabledDeltas() const { // TODO(bbondy): Re-enable // return configurator_impl_.DeltasEnabled(); return false; } -bool BraveConfigurator::UseBackgroundDownloader() const { - return configurator_impl_.UseBackgroundDownloader(); +bool BraveConfigurator::EnabledBackgroundDownloader() const { + return configurator_impl_.EnabledBackgroundDownloader(); } -bool BraveConfigurator::UseCupSigning() const { +bool BraveConfigurator::EnabledCupSigning() const { if (use_brave_server_) { return false; } - return configurator_impl_.UseCupSigning(); + return configurator_impl_.EnabledCupSigning(); } // Returns a task runner to run blocking tasks. The task runner continues to run diff --git a/brave/browser/component_updater/extension_installer_traits.cc b/brave/browser/component_updater/extension_installer_traits.cc index 1be8827b86..b21ce18fdf 100644 --- a/brave/browser/component_updater/extension_installer_traits.cc +++ b/brave/browser/component_updater/extension_installer_traits.cc @@ -66,10 +66,6 @@ ExtensionInstallerTraits::ExtensionInstallerTraits( public_key_(public_key), ready_callback_(ready_callback) { } -bool ExtensionInstallerTraits::CanAutoUpdate() const { - return true; -} - bool ExtensionInstallerTraits::RequiresNetworkEncryption() const { return false; } @@ -87,6 +83,14 @@ void ExtensionInstallerTraits::ComponentReady( ready_callback_.Run(install_dir); } +bool ExtensionInstallerTraits::SupportsGroupPolicyEnabledComponentUpdates() const { + return false; // TODO(bridiver) - true for widevine +} + +std::vector ExtensionInstallerTraits::GetMimeTypes() const { + return std::vector(); +} + // Called during startup and installation before ComponentReady(). bool ExtensionInstallerTraits::VerifyInstallation( const base::DictionaryValue& manifest, diff --git a/brave/browser/component_updater/extension_installer_traits.h b/brave/browser/component_updater/extension_installer_traits.h index a9d91d40dd..c427711774 100644 --- a/brave/browser/component_updater/extension_installer_traits.h +++ b/brave/browser/component_updater/extension_installer_traits.h @@ -35,7 +35,6 @@ class ExtensionInstallerTraits : private: // The following methods override ComponentInstallerTraits. - bool CanAutoUpdate() const override; bool RequiresNetworkEncryption() const override; bool OnCustomInstall(const base::DictionaryValue& manifest, const base::FilePath& install_dir) override; @@ -44,6 +43,8 @@ class ExtensionInstallerTraits : void ComponentReady(const base::Version& version, const base::FilePath& install_dir, std::unique_ptr manifest) override; + bool SupportsGroupPolicyEnabledComponentUpdates() const override; + std::vector GetMimeTypes() const override; base::FilePath GetRelativeInstallDir() const override; void GetHash(std::vector* hash) const override; std::string GetName() const override; diff --git a/brave/browser/component_updater/widevine_cdm_component_installer.cc b/brave/browser/component_updater/widevine_cdm_component_installer.cc index 6299f04d6d..8fcd5927dc 100644 --- a/brave/browser/component_updater/widevine_cdm_component_installer.cc +++ b/brave/browser/component_updater/widevine_cdm_component_installer.cc @@ -216,7 +216,8 @@ class WidevineCdmComponentInstallerTraits private: // The following methods override ComponentInstallerTraits. - bool CanAutoUpdate() const override; + bool SupportsGroupPolicyEnabledComponentUpdates() const override; + std::vector GetMimeTypes() const override; bool RequiresNetworkEncryption() const override; bool OnCustomInstall(const base::DictionaryValue& manifest, const base::FilePath& install_dir) override; @@ -254,10 +255,14 @@ WidevineCdmComponentInstallerTraits::WidevineCdmComponentInstallerTraits( const ReadyCallback& ready_callback) : ready_callback_(ready_callback) { } -bool WidevineCdmComponentInstallerTraits::CanAutoUpdate() const { +bool WidevineCdmComponentInstallerTraits::SupportsGroupPolicyEnabledComponentUpdates() const { return true; } +std::vector WidevineCdmComponentInstallerTraits::GetMimeTypes() const { + return std::vector(); +} + bool WidevineCdmComponentInstallerTraits::RequiresNetworkEncryption() const { return false; } diff --git a/brave/browser/extensions/BUILD.gn b/brave/browser/extensions/BUILD.gn new file mode 100644 index 0000000000..b740ef7ca4 --- /dev/null +++ b/brave/browser/extensions/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright 2014 The Chromium 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("//build/config/features.gni") +import("//build/config/ui.gni") +import("//chrome/common/features.gni") + +assert(enable_extensions) + +static_library("extensions") { + configs += [ + "//electron/build:electron_config", + ] + + sources = [ + "api/guest_view/tab_view/tab_view_internal_api.cc", + "api/guest_view/tab_view/tab_view_internal_api.h", + "//extensions/shell/browser/shell_display_info_provider.cc", + ] + + configs += [ + "//build/config:precompiled_headers", + "//build/config/compiler:wexit_time_destructors", + ] + + public_deps = [ + "//chrome/common/extensions/api", + "//content/public/browser", + ] +} diff --git a/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.cc b/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.cc new file mode 100644 index 0000000000..345b18ddeb --- /dev/null +++ b/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.cc @@ -0,0 +1,38 @@ +// Copyright 2014 The Brave 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 "brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.h" + +#include "atom/browser/extensions/tab_helper.h" +#include "content/public/browser/render_process_host.h" +#include "extensions/browser/guest_view/web_view/web_view_constants.h" +#include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h" +#include "extensions/common/error_utils.h" + +bool TabViewInternalExtensionFunction::PreRunValidation(std::string* error) { + if (!UIThreadExtensionFunction::PreRunValidation(error)) + return false; + + int instance_id = 0; + EXTENSION_FUNCTION_PRERUN_VALIDATE(args_->GetInteger(0, &instance_id)); + guest_ = brave::TabViewGuest::From(render_frame_host()->GetProcess()->GetID(), + instance_id); + if (!guest_) { + *error = "Could not find guest"; + return false; + } + return true; +} + +TabViewInternalGetTabIDFunction::TabViewInternalGetTabIDFunction() { +} + +TabViewInternalGetTabIDFunction::~TabViewInternalGetTabIDFunction() { +} + +ExtensionFunction::ResponseAction TabViewInternalGetTabIDFunction::Run() { + int tab_id = extensions::TabHelper::IdForTab(guest_->web_contents()); + return RespondNow( + OneArgument(base::MakeUnique(tab_id))); +} diff --git a/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.h b/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.h new file mode 100644 index 0000000000..cece0ffc33 --- /dev/null +++ b/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.h @@ -0,0 +1,41 @@ +// Copyright 2014 The Brave 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 BRAVE_BROWSER_EXTENSIONS_API_GUEST_VIEW_TAB_VIEW_TAB_VIEW_INTERNAL_API_H_ +#define BRAVE_BROWSER_EXTENSIONS_API_GUEST_VIEW_TAB_VIEW_TAB_VIEW_INTERNAL_API_H_ + +#include + +#include "base/macros.h" +#include "extensions/browser/api/execute_code_function.h" +#include "extensions/browser/extension_function.h" +#include "brave/browser/guest_view/tab_view/tab_view_guest.h" + +class TabViewInternalExtensionFunction : public UIThreadExtensionFunction { + public: + TabViewInternalExtensionFunction() {} + + protected: + ~TabViewInternalExtensionFunction() override {} + bool PreRunValidation(std::string* error) override; + + brave::TabViewGuest* guest_ = nullptr; +}; + +class TabViewInternalGetTabIDFunction + : public TabViewInternalExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("tabViewInternal.getTabID", + UNKNOWN); + + TabViewInternalGetTabIDFunction(); + + protected: + ~TabViewInternalGetTabIDFunction() override; + ResponseAction Run() override; + + DISALLOW_COPY_AND_ASSIGN(TabViewInternalGetTabIDFunction); +}; + +#endif // BRAVE_BROWSER_EXTENSIONS_API_GUEST_VIEW_TAB_VIEW_TAB_VIEW_INTERNAL_API_H_ diff --git a/brave/browser/guest_view/brave_guest_view_manager_delegate.cc b/brave/browser/guest_view/brave_guest_view_manager_delegate.cc new file mode 100644 index 0000000000..9f55e33940 --- /dev/null +++ b/brave/browser/guest_view/brave_guest_view_manager_delegate.cc @@ -0,0 +1,41 @@ +// Copyright 2015 The Brave 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 "brave/browser/guest_view/brave_guest_view_manager_delegate.h" + +#include "brave/browser/guest_view/tab_view/tab_view_guest.h" +#include "components/guest_view/browser/guest_view_base.h" +#include "components/guest_view/browser/guest_view_manager.h" +#include "components/guest_view/common/guest_view_constants.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/guest_view/extension_options/extension_options_guest.h" +#include "extensions/browser/guest_view/extension_view/extension_view_guest.h" +#include "extensions/browser/guest_view/guest_view_events.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" +#include "extensions/browser/guest_view/web_view/web_view_guest.h" + +using guest_view::GuestViewBase; +using guest_view::GuestViewManager; + +namespace brave { + +BraveGuestViewManagerDelegate::BraveGuestViewManagerDelegate( + content::BrowserContext* context) + : extensions::ExtensionsGuestViewManagerDelegate(context), + context_(context) { +} + +BraveGuestViewManagerDelegate::~BraveGuestViewManagerDelegate() { +} + +void BraveGuestViewManagerDelegate::RegisterAdditionalGuestViewTypes() { + GuestViewManager* manager = GuestViewManager::FromBrowserContext(context_); + manager->RegisterGuestViewType(); + // manager->RegisterGuestViewType(); + // manager->RegisterGuestViewType(); + // manager->RegisterGuestViewType(); + // manager->RegisterGuestViewType(); +} + +} // namespace extensions diff --git a/brave/browser/guest_view/brave_guest_view_manager_delegate.h b/brave/browser/guest_view/brave_guest_view_manager_delegate.h new file mode 100644 index 0000000000..18c64abe86 --- /dev/null +++ b/brave/browser/guest_view/brave_guest_view_manager_delegate.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Chromium 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 BRAVE_BROWSER_GUEST_VIEW_BRAVE_GUEST_VIEW_MANAGER_DELEGATE_H_ +#define BRAVE_BROWSER_GUEST_VIEW_BRAVE_GUEST_VIEW_MANAGER_DELEGATE_H_ + +#include "extensions/browser/guest_view/extensions_guest_view_manager_delegate.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace guest_view { +class GuestViewBase; +} + +namespace brave { + +class BraveGuestViewManagerDelegate + : public extensions::ExtensionsGuestViewManagerDelegate { + public: + explicit BraveGuestViewManagerDelegate(content::BrowserContext* context); + ~BraveGuestViewManagerDelegate() override; + void RegisterAdditionalGuestViewTypes() override; + + private: + content::BrowserContext* const context_; + + DISALLOW_COPY_AND_ASSIGN(BraveGuestViewManagerDelegate); +}; + +} // namespace brave + +#endif // BRAVE_BROWSER_GUEST_VIEW_BRAVE_GUEST_VIEW_MANAGER_DELEGATE_H_ diff --git a/brave/browser/guest_view/tab_view/tab_view_guest.cc b/brave/browser/guest_view/tab_view/tab_view_guest.cc new file mode 100644 index 0000000000..11e266c98c --- /dev/null +++ b/brave/browser/guest_view/tab_view/tab_view_guest.cc @@ -0,0 +1,270 @@ +// Copyright 2014 The Brave 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 "brave/browser/guest_view/tab_view/tab_view_guest.h" + +#include + +#include + +#include "atom/browser/api/atom_api_web_contents.h" +#include "atom/browser/api/atom_api_session.h" +#include "atom/browser/api/event.h" +#include "atom/browser/extensions/tab_helper.h" +#include "base/memory/ptr_util.h" +#include "build/build_config.h" +#include "components/guest_view/browser/guest_view_event.h" +#include "components/guest_view/browser/guest_view_manager.h" +#include "components/guest_view/common/guest_view_constants.h" + +#include "atom/browser/native_window.h" +#include "atom/browser/web_contents_preferences.h" +#include "atom/common/native_mate_converters/gurl_converter.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/guest_host.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "native_mate/dictionary.h" +#include "atom/common/node_includes.h" + +using content::RenderFrameHost; +using content::RenderProcessHost; +using content::WebContents; +using guest_view::GuestViewBase; +using guest_view::GuestViewEvent; +using guest_view::GuestViewManager; + +namespace brave { + +// static +GuestViewBase* TabViewGuest::Create(WebContents* owner_web_contents) { + return new TabViewGuest(owner_web_contents); +} + +// static +const char TabViewGuest::Type[] = "webview"; + +bool TabViewGuest::CanRunInDetachedState() const { + return true; +} + +void TabViewGuest::WebContentsCreated(WebContents* source_contents, + int opener_render_frame_id, + const std::string& frame_name, + const GURL& target_url, + WebContents* new_contents) { + auto* guest = TabViewGuest::FromWebContents(new_contents); + CHECK(guest); + guest->SetOpener(this); + guest->name_ = frame_name; + pending_new_windows_.insert( + std::make_pair(guest, NewWindowInfo(target_url, frame_name))); +} + +WebContents* TabViewGuest::OpenURLFromTab( + WebContents* source, + const content::OpenURLParams& params) { + if (!attached()) { + TabViewGuest* opener = GetOpener(); + // If the guest wishes to navigate away prior to attachment then we save the + // navigation to perform upon attachment. Navigation initializes a lot of + // state that assumes an embedder exists, such as RenderWidgetHostViewGuest. + // Navigation also resumes resource loading. If we were created using + // newwindow (i.e. we have an opener), we don't allow navigation until + // attachment. + if (opener) { + auto it = opener->pending_new_windows_.find(this); + if (it == opener->pending_new_windows_.end()) + return nullptr; + const NewWindowInfo& info = it->second; + NewWindowInfo new_window_info(params.url, info.name); + new_window_info.changed = new_window_info.url != info.url; + it->second = new_window_info; + } + return nullptr; + } + + // bool scheme_is_blocked = true // TODO(bridiver) - block access to chrome:// urls + // (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme( + // url.scheme()) && + // !url.SchemeIs(url::kAboutScheme)) || + // url.SchemeIs(url::kJavaScriptScheme); + + // let the api_web_contents navigate + return web_contents(); +} + +void TabViewGuest::DidInitialize(const base::DictionaryValue& create_params) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + + api_web_contents_ = atom::api::WebContents::CreateFrom(isolate, + web_contents()).get(); + web_contents()->SetDelegate(api_web_contents_); +} + +content::WebContents* TabViewGuest::CreateNewGuestWindow( + const content::WebContents::CreateParams& create_params) { + v8::Isolate* isolate = api_web_contents_->isolate(); + v8::HandleScope handle_scope(isolate); + + content::WebContents::CreateParams params(create_params); + // window options will come from features that needs to be passed through + mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate); + options.Set("isGuest", true); + + if (params.browser_context) { + auto session = atom::api::Session::CreateFrom(isolate, + Profile::FromBrowserContext(params.browser_context)); + options.Set("session", session); + } + + auto guest = Create(owner_web_contents()); + params.guest_delegate = guest; + + mate::Handle new_api_web_contents = + atom::api::WebContents::CreateWithParams(isolate, options, params); + content::WebContents* guest_web_contents = + new_api_web_contents->GetWebContents(); + guest->InitWithWebContents(base::DictionaryValue(), guest_web_contents); + + return guest_web_contents; +} + + +void TabViewGuest::CreateWebContents( + const base::DictionaryValue& create_params, + const WebContentsCreatedCallback& callback) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handle_scope(isolate); + + // window options will come from features that needs to be passed through + mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate); + options.Set("isGuest", true); + std::string partition; + create_params.GetString("partition", &partition); + std::string src; + create_params.GetString("src", &src); + options.Set("delayedLoadUrl", src); + + auto session = atom::api::Session::FromPartition(isolate, partition); + options.Set("session", session); + + auto site_instance = content::SiteInstance::CreateForURL( + session->browser_context(), GURL(src)); + auto browser_context = session->browser_context(); + content::WebContents::CreateParams + params(browser_context, site_instance); + params.guest_delegate = this; + + mate::Handle new_api_web_contents = + atom::api::WebContents::CreateWithParams(isolate, options, params); + content::WebContents* web_contents = new_api_web_contents->GetWebContents(); + + callback.Run(web_contents); +} + +void TabViewGuest::DidAttachToEmbedder() { + api_web_contents_->Emit("did-attach"); + + if (GetOpener()) { + // We need to do a navigation here if the target URL has changed between + // the time the WebContents was created and the time it was attached. + // We also need to do an initial navigation if a RenderView was never + // created for the new window in cases where there is no referrer. + auto it = GetOpener()->pending_new_windows_.find(this); + if (it != GetOpener()->pending_new_windows_.end()) { + const NewWindowInfo& new_window_info = it->second; + if (new_window_info.changed || !web_contents()->HasOpener()) { + content::OpenURLParams params( + new_window_info.url, content::Referrer(), CURRENT_TAB, + ui::PAGE_TRANSITION_LINK, false); + api_web_contents_->OpenURLFromTab(web_contents(), params); + } + // Once a new guest is attached to the DOM of the embedder page, then the + // lifetime of the new guest is no longer managed by the opener guest. + GetOpener()->pending_new_windows_.erase(this); + } + } + api_web_contents_->ResumeLoadingCreatedWebContents(); + web_contents()->WasHidden(); + web_contents()->WasShown(); +} + +bool TabViewGuest::ZoomPropagatesFromEmbedderToGuest() const { + return false; +} + +const char* TabViewGuest::GetAPINamespace() const { + return "webViewInternal"; +} + +int TabViewGuest::GetTaskPrefix() const { + // return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX; + return -1; +} + +void TabViewGuest::GuestReady() { + // we don't use guest only processes + CHECK(!web_contents()->GetRenderProcessHost()->IsForGuestsOnly()); + + web_contents() + ->GetRenderViewHost() + ->GetWidget() + ->GetView() + ->SetBackgroundColorToDefault(); +} + +void TabViewGuest::WillDestroy() { + api_web_contents_->WebContentsDestroyed(); +} + +void TabViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size, + const gfx::Size& new_size) { + api_web_contents_->Emit("size-changed", + old_size.width(), old_size.height(), + new_size.width(), new_size.height()); +} + +bool TabViewGuest::IsAutoSizeSupported() const { + return true; +} + +TabViewGuest::TabViewGuest(WebContents* owner_web_contents) + : GuestView(owner_web_contents) { +} + +TabViewGuest::~TabViewGuest() { +} + +void TabViewGuest::WillAttachToEmbedder() { + // register the guest for event forwarding + v8::Isolate* isolate = api_web_contents_->isolate(); + v8::HandleScope handle_scope(isolate); + + auto add_guest_event = + v8::Local::Cast(mate::Event::Create(isolate).ToV8()); + node::Environment* env = node::Environment::GetCurrent(isolate); + mate::EmitEvent(isolate, + env->process_object(), + "ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST", + add_guest_event, + api_web_contents_, + extensions::TabHelper::IdForTab(web_contents())); + + // update the owner window + auto relay = atom::NativeWindowRelay::FromWebContents( + embedder_web_contents()); + if (relay) { + api_web_contents_->SetOwnerWindow(relay->window.get()); + } +} + +} // namespace brave diff --git a/brave/browser/guest_view/tab_view/tab_view_guest.h b/brave/browser/guest_view/tab_view/tab_view_guest.h new file mode 100644 index 0000000000..cda490af0a --- /dev/null +++ b/brave/browser/guest_view/tab_view/tab_view_guest.h @@ -0,0 +1,88 @@ +// Copyright 2014 The Brave 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 BRAVE_BROWSER_GUEST_VIEW_TAB_VIEW_TAB_VIEW_GUEST_H_ +#define BRAVE_BROWSER_GUEST_VIEW_TAB_VIEW_TAB_VIEW_GUEST_H_ + +#include + +#include "base/macros.h" +#include "components/guest_view/browser/guest_view.h" + +namespace atom { +namespace api { +class WebContents; +} +} + +namespace brave { + +class TabViewGuest : public guest_view::GuestView { + public: + static GuestViewBase* Create(content::WebContents* owner_web_contents); + + static const char Type[]; + private: + explicit TabViewGuest(content::WebContents* owner_web_contents); + + ~TabViewGuest() override; + + void WebContentsCreated(content::WebContents* source_contents, + int opener_render_frame_id, + const std::string& frame_name, + const GURL& target_url, + content::WebContents* new_contents) override; + content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) override; + + + // GuestViewBase implementation. + content::WebContents* CreateNewGuestWindow( + const content::WebContents::CreateParams& create_params) override; + void CreateWebContents( + const base::DictionaryValue& create_params, + const WebContentsCreatedCallback& callback) final; + bool CanRunInDetachedState() const final; + void DidAttachToEmbedder() final; + bool ZoomPropagatesFromEmbedderToGuest() const final; + const char* GetAPINamespace() const final; + void GuestSizeChangedDueToAutoSize(const gfx::Size& old_size, + const gfx::Size& new_size) final; + bool IsAutoSizeSupported() const final; + void WillAttachToEmbedder() final; + int GetTaskPrefix() const final; + void GuestReady() final; + void WillDestroy() final; + void DidInitialize(const base::DictionaryValue& create_params) final; + + atom::api::WebContents* api_web_contents_; + + // Stores the window name of the main frame of the guest. + std::string name_; + + // Stores the src URL of the WebView. + GURL src_; + + // Tracks the name, and target URL of the new window. Once the first + // navigation commits, we no longer track this information. + struct NewWindowInfo { + GURL url; + std::string name; + bool changed; + NewWindowInfo(const GURL& url, const std::string& name) : + url(url), + name(name), + changed(false) {} + }; + + using PendingWindowMap = std::map; + PendingWindowMap pending_new_windows_; + + DISALLOW_COPY_AND_ASSIGN(TabViewGuest); +}; + +} // namespace extensions + +#endif // BRAVE_BROWSER_GUEST_VIEW_TAB_VIEW_TAB_VIEW_GUEST_H_ diff --git a/brave/browser/notifications/platform_notification_service_impl.cc b/brave/browser/notifications/platform_notification_service_impl.cc index 30ce47417a..5d67777f88 100644 --- a/brave/browser/notifications/platform_notification_service_impl.cc +++ b/brave/browser/notifications/platform_notification_service_impl.cc @@ -132,13 +132,14 @@ void PlatformNotificationServiceImpl::DisplayNotification( auto permission_manager = browser_context->GetPermissionManager(); permission_manager->RequestPermission( - content::PermissionType::NOTIFICATIONS, NULL, origin, + content::PermissionType::NOTIFICATIONS, NULL, origin, false, // TODO(bridiver) user gesture base::Bind(&OnPermissionResponse, callback)); } void PlatformNotificationServiceImpl::DisplayPersistentNotification( content::BrowserContext* browser_context, int64_t persistent_notification_id, + const GURL& service_worker_origin, const GURL& origin, const content::PlatformNotificationData& notification_data, const content::NotificationResources& notification_resources) { diff --git a/brave/browser/notifications/platform_notification_service_impl.h b/brave/browser/notifications/platform_notification_service_impl.h index 17de784cf3..58a7d57a27 100644 --- a/brave/browser/notifications/platform_notification_service_impl.h +++ b/brave/browser/notifications/platform_notification_service_impl.h @@ -43,6 +43,7 @@ class PlatformNotificationServiceImpl void DisplayPersistentNotification( content::BrowserContext* browser_context, int64_t persistent_notification_id, + const GURL& service_worker_origin, const GURL& origin, const content::PlatformNotificationData& notification_data, const content::NotificationResources& notification_resources) override; diff --git a/brave/browser/ui/simple_message_box.cc b/brave/browser/ui/simple_message_box.cc new file mode 100644 index 0000000000..fce0c9d395 --- /dev/null +++ b/brave/browser/ui/simple_message_box.cc @@ -0,0 +1,18 @@ +#include "chrome/browser/ui/simple_message_box.h" + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "build/build_config.h" +#include "ui/gfx/native_widget_types.h" + +namespace chrome { + +void ShowWarningMessageBox(gfx::NativeWindow parent, + const base::string16& title, + const base::string16& message) { + //TODO(bridiver) + // atom:Message... +} + +} // namespace chrome diff --git a/brave/common/brave_paths.cc b/brave/common/brave_paths.cc new file mode 100644 index 0000000000..9a4201a5c7 --- /dev/null +++ b/brave/common/brave_paths.cc @@ -0,0 +1,43 @@ +// Copyright (c) 2012 The Chromium 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 "brave/common/brave_paths.h" + +#include "base/lazy_instance.h" +#include "base/path_service.h" +#include "browser/brightray_paths.h" +#include "build/build_config.h" +#include "chrome/common/chrome_paths_internal.h" + +namespace brave { + bool GetDefaultAppDataDirectory(base::FilePath* result) { +#if defined(OS_LINUX) + std::unique_ptr env(base::Environment::Create()); + if (!env) + return false + + result = base::nix::GetXDGDirectory(env.get(), + base::nix::kXdgConfigHomeEnvVar, + base::nix::kDotConfigDir); + if (result.BaseName.value() != base::nix::kDotConfigDir) + return false; + + return true; +#else + return PathService::Get(brightray::DIR_APP_DATA, result); +#endif + } + + bool GetDefaultUserDataDirectory(base::FilePath* result) { +#if defined(OS_MACOSX) + // on mac we SetOverrideVersionedDirectory to fix GetVersionedDirectory + return chrome::GetDefaultUserDataDirectory(result); +#else + if (!GetDefaultUserDataDirectory(result)) { + return false; + } + result->Append(ATOM_PROJECT_NAME); +#endif + } +} diff --git a/brave/common/brave_paths.h b/brave/common/brave_paths.h new file mode 100644 index 0000000000..6b18d65383 --- /dev/null +++ b/brave/common/brave_paths.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium 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 BRAVE_COMMON_BRAVE_PATHS_H__ +#define BRAVE_COMMON_BRAVE_PATHS_H__ + +#include "build/build_config.h" + +namespace base { +class FilePath; +} + +namespace brave { + +bool GetDefaultAppDataDirectory(base::FilePath* result); +bool GetDefaultUserDataDirectory(base::FilePath* path); + +} // namespace brave + +#endif // BRAVE_COMMON_BRAVE_PATHS_H__ diff --git a/brave/common/extensions/api/BUILD.gn b/brave/common/extensions/api/BUILD.gn new file mode 100644 index 0000000000..785fccd0db --- /dev/null +++ b/brave/common/extensions/api/BUILD.gn @@ -0,0 +1,79 @@ +# Copyright 2015 The Chromium 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("//build/json_schema_api.gni") +import("//tools/json_schema_compiler/json_features.gni") +import("schemas.gni") + +# assert(enable_extensions) + +json_schema_api("api") { + sources = schema_sources + schemas = true + bundle = true + bundle_name = "Brave" + + deps = schema_dependencies +} + +# GYP version: extensions/shell/browser/api/api_registration.gyp:shell_api_registration +json_schema_api("api_registration") { + sources = schema_sources + impl_dir = "//electron/brave/common/extensions/api" + bundle_registration = true + bundle_name = "Brave" + + deps = [ + ":api", + ] + + deps += schema_dependencies +} + +json_features("api_features") { + feature_class = "APIFeature" + provider_class = "BraveAPIFeatureProvider" + sources = [ + "../../../../../chrome/common/extensions/api/_api_features.json", + "../../../../../extensions/common/api/_api_features.json", + "_api_features.json", + ] +} + +json_features("permission_features") { + feature_class = "PermissionFeature" + provider_class = "BravePermissionFeatureProvider" + sources = [ + "../../../../../chrome/common/extensions/api/_permission_features.json", + "../../../../../extensions/common/api/_permission_features.json", + "_permission_features.json", + ] +} + +json_features("manifest_features") { + feature_class = "ManifestFeature" + provider_class = "BraveManifestFeatureProvider" + sources = [ + "../../../../../chrome/common/extensions/api/_manifest_features.json", + "../../../../../extensions/common/api/_manifest_features.json", + # "_manifest_features.json", + ] +} + +json_features("behavior_features") { + feature_class = "BehaviorFeature" + provider_class = "BraveBehaviorFeatureProvider" + sources = [ + "../../../../../extensions/common/api/_behavior_features.json", + ] +} + +group("extensions_features") { + public_deps = [ + ":api_features", + ":behavior_features", + ":manifest_features", + ":permission_features", + ] +} diff --git a/brave/common/extensions/api/_api_features.json b/brave/common/extensions/api/_api_features.json new file mode 100644 index 0000000000..79fa1c462c --- /dev/null +++ b/brave/common/extensions/api/_api_features.json @@ -0,0 +1,56 @@ +{ + "ipcRenderer": { + "channel": "stable", + "contexts": [ + "webui", + "content_script", + "blessed_extension", + "unblessed_extension" + ], + "matches": [ + "chrome://brave/*" + ] + }, + "remote": { + "channel": "stable", + "contexts": [ + "webui" + ], + "matches": [ + "chrome://brave/*" + ] + }, + "webFrame": { + "channel": "stable", + "contexts": [ + "webui", + "content_script", + "blessed_extension", + "unblessed_extension" + ], + "matches": [ + "chrome://brave/*" + ] + }, + "contentSettings.get": { + "dependencies": ["permission:contentSettings"], + "contexts": [ + "webui", + "content_script", + "blessed_extension", + "unblessed_extension" + ], + "matches": [ + "chrome://brave/*" + ] + }, + "tabViewInternal": { + "channel": "stable", + "contexts": [ + "webui" + ], + "matches": [ + "chrome://brave/*" + ] + } +} diff --git a/brave/common/extensions/api/_permission_features.json b/brave/common/extensions/api/_permission_features.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/brave/common/extensions/api/_permission_features.json @@ -0,0 +1,2 @@ +{ +} diff --git a/brave/common/extensions/api/empty.json b/brave/common/extensions/api/empty.json new file mode 100644 index 0000000000..b98009f500 --- /dev/null +++ b/brave/common/extensions/api/empty.json @@ -0,0 +1,6 @@ +[ + { + "namespace": "empty", + "description": "empty placeholder" + } +] diff --git a/brave/common/extensions/api/ipc.json b/brave/common/extensions/api/ipc.json new file mode 100644 index 0000000000..47ccb462a6 --- /dev/null +++ b/brave/common/extensions/api/ipc.json @@ -0,0 +1,38 @@ +[ + { + "namespace": "ipcRenderer", + "description": "none", + "functions": [ + { + "name": "send", + "nocompile": true, + "type": "function" + }, + { + "name": "sendSync", + "nocompile": true, + "type": "function" + }, + { + "name": "sendToHost", + "nocompile": true, + "type": "function" + }, + { + "name": "on", + "nocompile": true, + "type": "function" + }, + { + "name": "once", + "nocompile": true, + "type": "function" + }, + { + "name": "emit", + "nocompile": true, + "type": "function" + } + ] + } +] diff --git a/brave/common/extensions/api/remote.json b/brave/common/extensions/api/remote.json new file mode 100644 index 0000000000..acc4ee806c --- /dev/null +++ b/brave/common/extensions/api/remote.json @@ -0,0 +1,13 @@ +[ + { + "namespace": "remote", + "description": "none", + "functions": [ + { + "name": "getCurrentWindow", + "nocompile": true, + "type": "function" + } + ] + } +] diff --git a/brave/common/extensions/api/schemas.gni b/brave/common/extensions/api/schemas.gni new file mode 100644 index 0000000000..c6ed0659a3 --- /dev/null +++ b/brave/common/extensions/api/schemas.gni @@ -0,0 +1,21 @@ +schema_sources = [ + "tab_view_internal.json", +] + +uncompiled_sources = [ + "ipc.json", + "web_frame.json", + "remote.json" +] + +root_namespace = "extensions::api::%(namespace)s" +schema_include_rules = "extensions/common/api:chrome/common/extensions/api:extensions::api::%(namespace)s" +schema_dependencies = [ + "//extensions/common/api", + "//chrome/common/extensions/api", + "//electron/brave/browser/extensions" +] + +set_defaults("json_schema_api") { + configs = [ "//electron/build:electron_config" ] +} diff --git a/brave/common/extensions/api/tab_view_internal.json b/brave/common/extensions/api/tab_view_internal.json new file mode 100644 index 0000000000..93981b045d --- /dev/null +++ b/brave/common/extensions/api/tab_view_internal.json @@ -0,0 +1,35 @@ +[ + { + "namespace": "tabViewInternal", + "description": "none", + "compiler_options": { + "implemented_in": "electron/brave/browser/extensions/api/guest_view/tab_view/tab_view_internal_api.h" + }, + "functions": [ + { + "name": "getTabID", + "type": "function", + "description": "Returns the tabID for the guest webcontents.", + "parameters": [ + { + "type": "integer", + "name": "instanceId", + "description": "The instance ID of the guest process." + }, + { + "type": "function", + "name": "callback", + "description": "Called after the operation is completed.", + "parameters": [ + { + "name": "tabID", + "type": "integer", + "description": "The tabID or -1" + } + ] + } + ] + } + ] + } +] diff --git a/brave/common/extensions/api/web_frame.json b/brave/common/extensions/api/web_frame.json new file mode 100644 index 0000000000..f1905573a2 --- /dev/null +++ b/brave/common/extensions/api/web_frame.json @@ -0,0 +1,61 @@ +[ + { + "namespace": "webFrame", + "description": "none", + "functions": [ + { + "name": "setGlobal", + "nocompile": true, + "type": "function", + "parameters": [ + { + "type": "string", + "name": "path" + }, + { + "type": "any", + "name": "value" + } + ] + }, + { + "name": "setSpellCheckProvider", + "nocompile": true, + "type": "function", + "parameters": [ + { + "type": "string", + "name": "lang" + }, + { + "type": "boolean", + "name": "autoCorrectEnabled" + }, + { + "type": "object", + "name": "spellCheckProvider", + "properties": { + "spellCheck": { + "type": "function", + "parameters": [ + {"name": "word", "type": "string"} + ] + } + } + } + ] + }, + { + "name": "executeJavaScript", + "nocompile": true, + "type": "function", + "parameters": [ + { + "type": "string", + "name": "code" + } + ] + } + ] + } +] diff --git a/brave/renderer/brave_content_renderer_client.cc b/brave/renderer/brave_content_renderer_client.cc index 90b194f371..8e3333258a 100644 --- a/brave/renderer/brave_content_renderer_client.cc +++ b/brave/renderer/brave_content_renderer_client.cc @@ -4,13 +4,15 @@ #include "brave/renderer/brave_content_renderer_client.h" -#include "atom/browser/web_contents_preferences.h" #include "atom/renderer/content_settings_client.h" #include "atom/renderer/content_settings_manager.h" +#include "brave/renderer/printing/brave_print_web_view_helper_delegate.h" #include "chrome/renderer/pepper/pepper_helper.h" #include "content/public/renderer/render_thread.h" #include "components/autofill/content/renderer/autofill_agent.h" #include "components/autofill/content/renderer/password_autofill_agent.h" +#include "components/printing/renderer/print_web_view_helper.h" + #if defined(ENABLE_EXTENSIONS) #include "atom/renderer/extensions/atom_extensions_renderer_client.h" #include "atom/common/extensions/atom_extensions_client.h" @@ -52,11 +54,7 @@ void BraveContentRendererClient::RenderFrameCreated( extensions::AtomExtensionsRendererClient::GetInstance()->RenderFrameCreated( render_frame); #endif - if (atom::WebContentsPreferences::run_node()) { - AtomRendererClient::RenderFrameCreated(render_frame); - } else { - new PepperHelper(render_frame); - } + AtomRendererClient::RenderFrameCreated(render_frame); autofill::PasswordAutofillAgent* password_autofill_agent = new autofill::PasswordAutofillAgent(render_frame); new autofill::AutofillAgent(render_frame, password_autofill_agent, @@ -66,6 +64,9 @@ void BraveContentRendererClient::RenderFrameCreated( void BraveContentRendererClient::RenderViewCreated( content::RenderView* render_view) { AtomRendererClient::RenderViewCreated(render_view); + new printing::PrintWebViewHelper( + render_view, std::unique_ptr( + new BravePrintWebViewHelperDelegate())); #if defined(ENABLE_EXTENSIONS) extensions::AtomExtensionsRendererClient::GetInstance()-> RenderViewCreated(render_view); @@ -78,8 +79,6 @@ void BraveContentRendererClient::RunScriptsAtDocumentStart( extensions::AtomExtensionsRendererClient::GetInstance()-> RunScriptsAtDocumentStart(render_frame); #endif - if (atom::WebContentsPreferences::run_node()) - AtomRendererClient::RunScriptsAtDocumentStart(render_frame); } void BraveContentRendererClient::RunScriptsAtDocumentEnd( @@ -88,19 +87,13 @@ void BraveContentRendererClient::RunScriptsAtDocumentEnd( extensions::AtomExtensionsRendererClient::GetInstance()-> RunScriptsAtDocumentEnd(render_frame); #endif - if (atom::WebContentsPreferences::run_node()) - AtomRendererClient::RunScriptsAtDocumentEnd(render_frame); } bool BraveContentRendererClient::AllowPopup() { - if (atom::WebContentsPreferences::run_node()) { - return false; // TODO(bridiver) - should return setting for allow popups - } - #if defined(ENABLE_EXTENSIONS) return extensions::AtomExtensionsRendererClient::GetInstance()->AllowPopup(); #else - return false; + return false; // TODO(bridiver) - should return setting for allow popups #endif } @@ -110,10 +103,15 @@ bool BraveContentRendererClient::ShouldFork(blink::WebLocalFrame* frame, bool is_initial_navigation, bool is_server_redirect, bool* send_referrer) { - if (atom::WebContentsPreferences::run_node()) { - AtomRendererClient::ShouldFork(frame, url, http_method, - is_initial_navigation, is_server_redirect, send_referrer); - } + if (http_method != "GET") + return false; + +#if defined(ENABLE_EXTENSIONS) + bool should_fork = extensions::AtomExtensionsRendererClient::ShouldFork( + frame, url, is_initial_navigation, is_server_redirect, send_referrer); + if (should_fork) + return true; +#endif // defined(ENABLE_EXTENSIONS) return false; } diff --git a/brave/renderer/extensions/web_frame_bindings.cc b/brave/renderer/extensions/web_frame_bindings.cc index 07f0200bca..c59941dbb6 100644 --- a/brave/renderer/extensions/web_frame_bindings.cc +++ b/brave/renderer/extensions/web_frame_bindings.cc @@ -8,12 +8,17 @@ #include #include "base/strings/utf_string_conversions.h" +#include "content/renderer/browser_plugin/browser_plugin.h" +#include "content/renderer/browser_plugin/browser_plugin_manager.h" #include "extensions/renderer/console.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container.h" #include "extensions/renderer/script_context.h" +#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebView.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebScriptExecutionCallback.h" #include "third_party/WebKit/public/web/WebScriptSource.h" +#include "third_party/WebKit/public/web/WebView.h" namespace brave { @@ -47,6 +52,14 @@ WebFrameBindings::WebFrameBindings(extensions::ScriptContext* context) RouteFunction( "setGlobal", base::Bind(&WebFrameBindings::SetGlobal, base::Unretained(this))); + RouteFunction( + "registerElementResizeCallback", + base::Bind(&WebFrameBindings::RegisterElementResizeCallback, + base::Unretained(this))); + RouteFunction( + "registerEmbedderCustomElement", + base::Bind(&WebFrameBindings::RegisterEmbedderCustomElement, + base::Unretained(this))); } WebFrameBindings::~WebFrameBindings() { @@ -61,6 +74,73 @@ void WebFrameBindings::Invalidate() { ObjectBackedNativeHandler::Invalidate(); } +void WebFrameBindings::RegisterElementResizeCallback( + const v8::FunctionCallbackInfo& args) { + // There are two parameters. + CHECK(args.Length() == 2); + // Element Instance ID. + CHECK(args[0]->IsInt32()); + // Callback function. + CHECK(args[1]->IsFunction()); + + int element_instance_id = args[0]->Int32Value(); + // An element instance ID uniquely identifies a ExtensionsGuestViewContainer + // within a RenderView. + auto* guest_view_container = static_cast( + guest_view::GuestViewContainer::FromID(element_instance_id)); + if (!guest_view_container) + return; + + guest_view_container->RegisterElementResizeCallback( + args[1].As(), args.GetIsolate()); + + args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true)); +} + +// void WebFrameBindings::AddGuest( +// const v8::FunctionCallbackInfo& args) { + + + +// CHECK(args.Length() == 1); + +// int id = args[0]->Int32Value(); +// // This is a workaround for a strange issue on windows with background tabs +// // libchromiumcontent doesn't appear to be making the check for +// // params.disposition == NEW_BACKGROUND_TAB in WebContentsImpl +// // This results in the BrowserPluginGuest trying to access the native +// // window before it's actually ready. +// // +// // It's also possible that the guest is being treated as +// // visible because the "embedder", which is the same for all tabs +// // in the window, is always visible. +// // +// // This hack works around the issue by always +// // marking it as hidden while attaching +// content::BrowserPluginManager::Get()->GetBrowserPlugin(id)-> +// updateVisibility(false); +// content::RenderFrame::FromWebFrame(context()->web_frame())->AttachGuest(id); +// content::BrowserPluginManager::Get()->GetBrowserPlugin(id)-> +// updateVisibility(true); +// } + +void WebFrameBindings::RegisterEmbedderCustomElement( + const v8::FunctionCallbackInfo& args) { + + CHECK(args.Length() >= 1 && + args[0]->IsString()); + + const base::string16 name = base::UTF8ToUTF16(mate::V8ToString(args[0])); + v8::Local options = + static_cast>(args[1]); + + blink::WebExceptionCode c = 0; + args.GetReturnValue().Set( + context()->web_frame()-> + document().registerEmbedderCustomElement(name, options, c)); + +} + void WebFrameBindings::SetSpellCheckProvider( const v8::FunctionCallbackInfo& args) { if (context()->web_frame()->parent()) { @@ -130,4 +210,47 @@ void WebFrameBindings::SetGlobal( } } +// double WebFrame::SetZoomLevel(double level) { +// double ret = web_frame_->view()->setZoomLevel(level); +// return ret; +// } + +// double WebFrame::GetZoomLevel() const { +// return web_frame_->view()->zoomLevel(); +// } + +// double WebFrame::SetZoomFactor(double factor) { +// return blink::WebView::zoomLevelToZoomFactor(SetZoomLevel( +// blink::WebView::zoomFactorToZoomLevel(factor))); +// } + +// double WebFrame::GetZoomFactor() const { +// return blink::WebView::zoomLevelToZoomFactor(GetZoomLevel()); +// } + +// void WebFrame::SetZoomLevelLimits(double min_level, double max_level) { +// web_frame_->view()->zoomLimitsChanged(min_level, max_level); +// } + +// float WebFrame::GetPageScaleFactor() { +// return web_frame_->view()->pageScaleFactor(); +// } + +// void WebFrame::SetPageScaleFactor(float factor) { +// web_frame_->view()->setPageScaleFactor(factor); +// } + +// void WebFrame::SetPageScaleLimits(float min_scale, float max_scale) { +// web_frame_->view()->setDefaultPageScaleLimits(min_scale, max_scale); +// web_frame_->view()->setIgnoreViewportTagScaleLimits(true); +// } + +// float WebFrame::GetTextZoomFactor() { +// return web_frame_->view()->textZoomFactor(); +// } + +// void WebFrame::SetTextZoomFactor(float factor) { +// web_frame_->view()->setTextZoomFactor(factor); +// } + } // namespace brave diff --git a/brave/renderer/extensions/web_frame_bindings.h b/brave/renderer/extensions/web_frame_bindings.h index f285b65695..7589bfd000 100644 --- a/brave/renderer/extensions/web_frame_bindings.h +++ b/brave/renderer/extensions/web_frame_bindings.h @@ -18,6 +18,10 @@ class WebFrameBindings : public extensions::ObjectBackedNativeHandler { virtual ~WebFrameBindings(); void WebFrame(const v8::FunctionCallbackInfo& args); + void RegisterElementResizeCallback( + const v8::FunctionCallbackInfo& args); + void RegisterEmbedderCustomElement( + const v8::FunctionCallbackInfo& args); void SetSpellCheckProvider(const v8::FunctionCallbackInfo& args); void SetGlobal(const v8::FunctionCallbackInfo& args); void ExecuteJavaScript(const v8::FunctionCallbackInfo& args); diff --git a/brave/renderer/printing/brave_print_web_view_helper_delegate.cc b/brave/renderer/printing/brave_print_web_view_helper_delegate.cc new file mode 100644 index 0000000000..8b31a318c4 --- /dev/null +++ b/brave/renderer/printing/brave_print_web_view_helper_delegate.cc @@ -0,0 +1,80 @@ +// Copyright 2015 The Chromium 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 "brave/renderer/printing/brave_print_web_view_helper_delegate.h" + +#include + +#include "base/command_line.h" +#include "base/strings/string_util.h" +#include "chrome/renderer/prerender/prerender_helper.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_view.h" +#include "ipc/ipc_message.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" + +// #if defined(ENABLE_EXTENSIONS) +// #include "chrome/common/extensions/extension_constants.h" +// #include "extensions/common/constants.h" +// #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h" +// #endif // defined(ENABLE_EXTENSIONS) + +BravePrintWebViewHelperDelegate::~BravePrintWebViewHelperDelegate(){ +} + +bool BravePrintWebViewHelperDelegate::CancelPrerender( + content::RenderView* render_view, int routing_id) { + return false; +} + +// Return the PDF object element if |frame| is the out of process PDF extension. +blink::WebElement BravePrintWebViewHelperDelegate::GetPdfElement( + blink::WebLocalFrame* frame) { +// #if defined(ENABLE_EXTENSIONS) +// GURL url = frame->document().url(); +// bool inside_print_preview = +// url.GetOrigin() == GURL(chrome::kChromeUIPrintURL); +// bool inside_pdf_extension = url.SchemeIs(extensions::kExtensionScheme) && +// url.host() == extension_misc::kPdfExtensionId; +// if (inside_print_preview || inside_pdf_extension) { +// // with id="plugin" is created in +// // chrome/browser/resources/pdf/pdf.js. +// auto plugin_element = frame->document().getElementById("plugin"); +// if (!plugin_element.isNull()) { +// return plugin_element; +// } +// NOTREACHED(); +// } +// #endif // defined(ENABLE_EXTENSIONS) + return blink::WebElement(); +} + +bool BravePrintWebViewHelperDelegate::IsPrintPreviewEnabled() { + return false; +} + +bool BravePrintWebViewHelperDelegate::OverridePrint( + blink::WebLocalFrame* frame) { +// #if defined(ENABLE_EXTENSIONS) +// if (!frame->document().isPluginDocument()) +// return false; + +// std::vector mime_handlers = +// extensions::MimeHandlerViewContainer::FromRenderFrame( +// content::RenderFrame::FromWebFrame(frame)); +// if (!mime_handlers.empty()) { +// // This message is handled in chrome/browser/resources/pdf/pdf.js and +// // instructs the PDF plugin to print. This is to make window.print() on a +// // PDF plugin document correctly print the PDF. See +// // https://crbug.com/448720. +// base::DictionaryValue message; +// message.SetString("type", "print"); +// mime_handlers.front()->PostMessageFromValue(message); +// return true; +// } +// #endif // defined(ENABLE_EXTENSIONS) + return false; +} diff --git a/brave/renderer/printing/brave_print_web_view_helper_delegate.h b/brave/renderer/printing/brave_print_web_view_helper_delegate.h new file mode 100644 index 0000000000..a248bf4de3 --- /dev/null +++ b/brave/renderer/printing/brave_print_web_view_helper_delegate.h @@ -0,0 +1,25 @@ +// Copyright 2015 The Chromium 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 BRAVE_RENDERER_PRINTING_BRAVE_PRINT_WEB_VIEW_HELPER_DELEGATE_H_ +#define BRAVE_RENDERER_PRINTING_BRAVE_PRINT_WEB_VIEW_HELPER_DELEGATE_H_ + +#include "components/printing/renderer/print_web_view_helper.h" + +class BravePrintWebViewHelperDelegate + : public printing::PrintWebViewHelper::Delegate { + public: + ~BravePrintWebViewHelperDelegate() override; + + bool CancelPrerender(content::RenderView* render_view, + int routing_id) override; + + blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override; + + bool IsPrintPreviewEnabled() override; + + bool OverridePrint(blink::WebLocalFrame* frame) override; +}; // class BravePrintWebViewHelperDelegate + +#endif // BRAVE_RENDERER_PRINTING_BRAVE_PRINT_WEB_VIEW_HELPER_DELEGATE_H_ diff --git a/brave/renderer/resources/extensions/ipc_renderer_custom_bindings.js b/brave/renderer/resources/extensions/ipc_renderer_custom_bindings.js new file mode 100644 index 0000000000..fc590da664 --- /dev/null +++ b/brave/renderer/resources/extensions/ipc_renderer_custom_bindings.js @@ -0,0 +1,2 @@ +const ipc_utils = require('ipc_utils') +exports.$set('binding', ipc_utils) diff --git a/build/BUILD.gn b/build/BUILD.gn new file mode 100644 index 0000000000..b254b0e235 --- /dev/null +++ b/build/BUILD.gn @@ -0,0 +1,65 @@ +import("//build/config/chrome_build.gni") +import("//build/config/compiler/compiler.gni") +import("//build/util/branding.gni") +import("//chrome/version.gni") + +config("electron_config") { + include_dirs = [ + "//electron", + "//skia/config", + "//third_party/skia/include/core", + "$root_gen_dir/electron", + "//electron/vendor/brightray", + "//electron/vendor/native_mate", + "//electron/vendor/node/src", + "//electron/vendor/node/deps/http_parser", + "//electron/vendor/node/deps/uv/include", + # The `node.h` is using `#include"ares.h"`. + "//electron/vendor/node/deps/cares/include", + "//electron/vendor", + ] + + defines = [ + "ATOM_PRODUCT_NAME=\"$chrome_product_short_name\"", + "NODE_WANT_INTERNALS=1", + "NODE_SHARED_MODE", + "V8_SHARED", + "USING_V8_SHARED", + # "USING_V8_SHARED", + # "V8_SHARED", + # "BUILDING_V8_SHARED", + # "BORINGSSL_SHARED_LIBRARY", + ] + + if (is_clang) { + cflags = [ + "-Wno-error", + "-Wno-unknown-warning-option", + "-Wno-parentheses-equality", + "-Wno-unused-function", + "-Wno-sometimes-uninitialized", + "-Wno-pointer-sign", + "-Wno-sign-compare", + "-Wno-string-plus-int", + "-Wno-unused-variable", + "-Wno-deprecated-declarations", + "-Wno-return-type", + "-Wno-gnu-folding-constant", + "-Wno-shift-negative-value", + "-Wno-varargs", # https://git.io/v6Olj + "-Wno-deprecated-declarations", + "-Wno-deprecated-register", + "-Wno-unused-private-field", + "-Wno-unused-function", + "-Wno-unguarded-availability", + "-Wno-overloaded-virtual", + ] + } + + common_flags = [] + if (is_mac) { + common_flags += [ "-mmacosx-version-min=10.9" ] + } + ldflags = common_flags + cflags_objcc = common_flags +} diff --git a/build/node/BUILD.gn b/build/node/BUILD.gn new file mode 100644 index 0000000000..865a3f7928 --- /dev/null +++ b/build/node/BUILD.gn @@ -0,0 +1,58 @@ +import("//v8/gni/v8.gni") + +config("node_external_config") { + if (is_linux) { + ldflags = + [ rebase_path("$root_out_dir/libnode.so", + root_build_dir) ] + } else if (is_win) { + ldflags = + [ rebase_path("$root_out_dir/node.dll.lib", + root_build_dir) ] + } else if (is_mac) { + ldflags = + [ rebase_path("$root_out_dir/libnode.dylib", + root_build_dir) ] + } +} + +source_set("node") { + public_configs = [ + ":node_external_config", + ] + + public_deps = [ + ":build_node", + "//v8:v8", + "//third_party/boringssl:boringssl", + ] +} + +action("build_node") { + # dummy script - these are actually produced separately through gyp + script = "//electron/tools/node_gyp_build.py" + + outputs = [ + "$root_out_dir/libnode.dylib", + ] +} + +# TODO(bridiver) this isn't used right now because generating the node target wipes out the electron target +action("node_gyp_chromium") { + script = "//build/gyp_chromium" + inputs = [ + "//electron/node.gypi", + "//electron/vendor/node/node.gyp", + ] + outputs = [ + "$root_build_dir/obj/electron/vendor/node/node.ninja", + ] + args = [] + if (is_component_build) { + args += [ "-D", "component=shared_library", ] + } + args += [ + "-I", rebase_path("node.gypi", root_build_dir), + rebase_path("vendor/node/node.gyp", root_build_dir), + ] +} diff --git a/common.gypi b/build/node/node.gypi similarity index 57% rename from common.gypi rename to build/node/node.gypi index 233e77db03..2c8b4b16af 100644 --- a/common.gypi +++ b/build/node/node.gypi @@ -1,23 +1,16 @@ { 'includes': [ - 'toolchain.gypi', - 'vendor/brightray/brightray.gypi', + 'v8.gypi', ], 'variables': { - # Tell crashpad to build as external project. - 'crashpad_dependencies': 'external', - # Required by breakpad. - 'os_bsd': 0, - 'chromeos': 0, - # Reflects node's config.gypi. - 'component%': 'static_library', - 'python': 'python', + 'clang_use_chrome_plugins': 0, 'openssl_fips': '', 'openssl_no_asm': 1, 'use_openssl_def': 0, 'OPENSSL_PRODUCT': 'libopenssl.a', - 'node_release_urlbase': 'https://atom.io/download/atom-shell', - 'node_byteorder': '= 10.7. + 'mac_deployment_target': '10.8', + # Use the standard way of linking with msvc runtime. + 'win_use_allocator_shim': 0, + # The V8 libraries. + 'v8_libraries': '["v8", "v8_snapshot", "v8_nosnapshot", "v8_external_snapshot", "v8_base", "v8_libbase", "v8_libplatform"]', + # The icu libraries. + 'icu_libraries': '["icui18n", "icuuc"]', + 'v8_use_snapshot': 'true', + }, + 'target_defaults': { + 'msvs_disabled_warnings': [ + # class 'std::xx' needs to have dll-interface. Chrome turns this off + # for component builds, and we need to too. + 4251, + # The file contains a character that cannot be represented in these + # current code page + 4819, + # no matching operator delete found; memory will not be freed if + # initialization throws an exception + 4291, + # non dll-interface class used as base for dll-interface class + 4275, + # 'GetVersionExW': was declared deprecated + 4996, + # result of 32-bit shift implicitly converted to 64 bits + 4334, + ], + 'xcode_settings': { + 'WARNING_CFLAGS': [ + '-Wno-deprecated-declarations', + ], + # Use C++11 library. + 'CLANG_CXX_LIBRARY': 'libc++', # -stdlib=libc++ + }, + # Force exporting icu's symbols. + 'defines': [ + 'U_COMBINED_IMPLEMENTATION', + # Defining "U_COMBINED_IMPLEMENTATION" will add "explicit" for some + # constructors, make sure it doesn' happen. + 'UNISTR_FROM_CHAR_EXPLICIT=', + 'UNISTR_FROM_STRING_EXPLICIT=', + 'U_NO_DEFAULT_INCLUDE_UTF_HEADERS=0', + ], + 'defines!': [ + 'U_STATIC_IMPLEMENTATION', + ], + 'conditions': [ + ['OS=="linux" and target_arch=="arm"', { + # Work around ODR violations. + 'ldflags!': [ + '-Wl,--detect-odr-violations', + ], + }], + ], + 'target_conditions': [ + ['_type=="static_library" and _toolset=="target" and OS=="linux"', { + 'standalone_static_library': 1, + }], + ['_target_name in <(v8_libraries) + <(icu_libraries)', { + 'xcode_settings': { + 'DEAD_CODE_STRIPPING': 'NO', # -Wl,-dead_strip + 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'NO', + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'NO', + }, + 'cflags!': [ + '-fvisibility=hidden', + '-fdata-sections', + '-ffunction-sections', + ], + 'cflags_cc!': ['-fvisibility-inlines-hidden'], + }], + ['_target_name in <(v8_libraries) + ["mksnapshot"]', { + 'defines': [ + 'V8_SHARED', + 'BUILDING_V8_SHARED', + ], + }], + ], + }, +} diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn new file mode 100644 index 0000000000..3842418e0c --- /dev/null +++ b/chromium_src/BUILD.gn @@ -0,0 +1,270 @@ +import("//build/config/features.gni") +import("//build/config/ui.gni") + +config("chromium_src_config") { + include_dirs = [ + ".", + "..", + "../..", + "$root_gen_dir/chrome", + "$root_gen_dir/components/strings", + "//skia/config", + "//third_party/skia/include/core", + ] + + if (is_clang) { + cflags = [ + "-Wno-error", + "-Wno-unknown-warning-option", + "-Wno-parentheses-equality", + "-Wno-unused-function", + "-Wno-sometimes-uninitialized", + "-Wno-pointer-sign", + "-Wno-sign-compare", + "-Wno-string-plus-int", + "-Wno-unused-variable", + "-Wno-deprecated-declarations", + "-Wno-return-type", + "-Wno-gnu-folding-constant", + "-Wno-shift-negative-value", + "-Wno-varargs", # https://git.io/v6Olj + "-Wno-deprecated-declarations", + "-Wno-deprecated-register", + "-Wno-unused-private-field", + "-Wno-unused-function", + ] + } +} + +source_set("debug") { + public_configs = [ ":chromium_src_config" ] + + include_dirs = [ + # force this to appear before the chromium root src dir + ".", + ] + + sources = [ + "net/test/embedded_test_server/stream_listen_socket.cc", + "net/test/embedded_test_server/stream_listen_socket.h", + "net/test/embedded_test_server/tcp_listen_socket.cc", + "net/test/embedded_test_server/tcp_listen_socket.h", + ] +} + +source_set("renderer") { + configs += [ ":chromium_src_config" ] + + include_dirs = [ + # force this to appear before the chromium root src dir + ".", + ] + + sources = [] + + deps = [] + + if (enable_plugins) { + sources += [ + # "chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc", + # "chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h", + ] + + deps += [ + "//components/pdf/renderer", + "//components/strings", + "//ppapi/host", + "//ppapi/proxy", + "//ppapi/proxy:ipc", + "//ppapi/shared_impl", + "//third_party/icu", + "//third_party/WebKit/public:blink_headers", + ] + } +} + +source_set("browser") { + configs += [ ":chromium_src_config" ] + + include_dirs = [ + # force this to appear before the chromium root src dir + ".", + ] + + sources = [ + "chrome/browser/browser_process.cc", + "chrome/browser/browser_process.h", + "chrome/browser/chrome_process_finder_win.cc", + "chrome/browser/chrome_process_finder_win.h", + "chrome/browser/chrome_notification_types.h", + "chrome/browser/custom_handlers/protocol_handler_registry.cc", + "chrome/browser/custom_handlers/protocol_handler_registry_factory.cc", + "chrome/browser/extensions/global_shortcut_listener.cc", + "chrome/browser/extensions/global_shortcut_listener.h", + "chrome/browser/extensions/global_shortcut_listener_mac.mm", + "chrome/browser/extensions/global_shortcut_listener_mac.h", + "chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc", + "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h", + # TODO(bridiver) - maybe use chrome? + # "chrome/browser/media/media_capture_devices_dispatcher.cc", + # "chrome/browser/media/media_capture_devices_dispatcher.h", + + "chrome/browser/importer/external_process_importer_client.cc", + "chrome/browser/importer/external_process_importer_client.h", + "chrome/browser/importer/external_process_importer_host.cc", + "chrome/browser/importer/external_process_importer_host.h", + "chrome/browser/importer/firefox_profile_lock.cc", + "chrome/browser/importer/firefox_profile_lock.h", + "chrome/browser/importer/firefox_profile_lock_posix.cc", + "chrome/browser/importer/firefox_profile_lock_win.cc", + "chrome/browser/importer/importer_list.cc", + "chrome/browser/importer/importer_list.h", + "chrome/browser/importer/importer_lock_dialog.h", + "chrome/browser/importer/importer_progress_observer.h", + "chrome/browser/importer/importer_uma.cc", + "chrome/browser/importer/importer_uma.h", + "chrome/browser/importer/in_process_importer_bridge.cc", + "chrome/browser/importer/in_process_importer_bridge.h", + "chrome/browser/importer/profile_writer.h", + "chrome/browser/process_singleton_posix.cc", + # "chrome/browser/process_singleton_win.cc", + "chrome/browser/process_singleton.h", + "chrome/browser/profiles/incognito_helpers.cc", + "chrome/browser/profiles/incognito_helpers.h", + "chrome/browser/profiles/profile_io_data.cc", + "chrome/browser/profiles/profile_io_data.h", + "chrome/browser/profiles/profile.cc", + "chrome/browser/profiles/profile.h", + "chrome/browser/profiles/profile_manager.cc", + "chrome/browser/profiles/profile_manager.h", + "chrome/browser/profiles/profiles_state.cc", + "chrome/browser/profiles/profiles_state.h", + "//chrome/browser/renderer_host/chrome_extension_message_filter.cc", + "//chrome/browser/renderer_host/chrome_extension_message_filter.h", + "//chrome/browser/speech/tts_controller.h", + "//chrome/browser/speech/tts_controller_impl.cc", + "//chrome/browser/speech/tts_controller_impl.h", + "//chrome/browser/speech/tts_linux.cc", + "//chrome/browser/speech/tts_mac.mm", + "//chrome/browser/speech/tts_message_filter.cc", + "//chrome/browser/speech/tts_message_filter.h", + "//chrome/browser/speech/tts_platform.cc", + "//chrome/browser/speech/tts_platform.h", + "//chrome/browser/speech/tts_win.cc", + "chrome/browser/ui/browser_dialogs.h", + "//chrome/browser/ui/cocoa/color_chooser_mac.mm", + "//chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc", + "//chrome/browser/ui/zoom/chrome_zoom_level_prefs.h", + "//chrome/browser/content_settings/cookie_settings_factory.cc", + "//chrome/browser/content_settings/cookie_settings_factory.h", + "chrome/browser/content_settings/host_content_settings_map_factory.cc", + "chrome/browser/content_settings/host_content_settings_map_factory.h", + "//chrome/browser/printing/print_job.cc", + "//chrome/browser/printing/print_job.h", + "//chrome/browser/printing/print_job_manager.cc", + "//chrome/browser/printing/print_job_manager.h", + "//chrome/browser/printing/print_job_worker.cc", + "//chrome/browser/printing/print_job_worker.h", + "//chrome/browser/printing/print_job_worker_owner.cc", + "//chrome/browser/printing/print_job_worker_owner.h", + # "//chrome/browser/printing/print_view_manager.cc", + # "//chrome/browser/printing/print_view_manager.h", + # "//chrome/browser/printing/print_view_manager_base.cc", + # "//chrome/browser/printing/print_view_manager_base.h", + # "//chrome/browser/printing/print_view_manager_basic.cc", + # "//chrome/browser/printing/print_view_manager_basic.h", + # "//chrome/browser/printing/print_view_manager_common.cc", + # "//chrome/browser/printing/print_view_manager_common.h", + # "//chrome/browser/printing/print_view_manager_observer.h", + "//chrome/browser/printing/printer_query.cc", + "//chrome/browser/printing/printer_query.h", + "//chrome/browser/printing/printing_message_filter.cc", + "//chrome/browser/printing/printing_message_filter.h", + "//chrome/browser/tab_contents/tab_util.cc", + "//chrome/browser/tab_contents/tab_util.h", + ] + + public_deps = [] + deps = [ + "//electron/atom/browser", + "//components/certificate_transparency", + "//components/data_usage/core", + "//components/prefs", + "//components/ssl_config", + ] + + if (enable_webrtc) { + sources += [ + "chrome/browser/media/desktop_media_list.h", + "chrome/browser/media/desktop_media_list_observer.h", + "chrome/browser/media/native_desktop_media_list.cc", + "chrome/browser/media/native_desktop_media_list.h", + ] + + public_deps += [ + "//third_party/libyuv", + "//third_party/webrtc/modules/desktop_capture", + ] + } + + if (enable_plugins) { + sources += [ + "//chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc", + "//chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h", + "//chrome/browser/renderer_host/pepper/device_id_fetcher.cc", + "//chrome/browser/renderer_host/pepper/device_id_fetcher.h", + "//chrome/browser/renderer_host/pepper/monitor_finder_mac.h", + "//chrome/browser/renderer_host/pepper/monitor_finder_mac.mm", + "//chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc", + "//chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h", + "//chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc", + "//chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h", + "//chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.cc", + "//chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h", + "//chrome/browser/renderer_host/pepper/pepper_flash_drm_host.cc", + "//chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h", + "//chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc", + "//chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h", + "//chrome/browser/plugins/plugin_info_message_filter.cc", + "//chrome/browser/plugins/plugin_info_message_filter.h", + ] + + if (enable_extensions) { + sources += [ + "chrome/browser/extensions/extension_util.cc", + "chrome/browser/extensions/extension_util.h", + "chrome/browser/extensions/extension_tab_util.cc", + "chrome/browser/extensions/extension_tab_util.h", + "//chrome/browser/extensions/api/messaging/extension_message_port.cc", + "//chrome/browser/extensions/api/messaging/extension_message_port.h", + # "//chrome/browser/extensions/api/messaging/incognito_connectability.cc", + # "//chrome/browser/extensions/api/messaging/incognito_connectability.h", + "//chrome/browser/extensions/api/messaging/message_property_provider.cc", + "//chrome/browser/extensions/api/messaging/message_property_provider.h", + "//chrome/browser/extensions/api/messaging/message_service.cc", + "//chrome/browser/extensions/api/messaging/message_service.h", + "//chrome/browser/extensions/api/messaging/native_message_port.cc", + "//chrome/browser/extensions/api/messaging/native_message_port.h", + "//chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc", + "//chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h", + "//chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc", + "//chrome/browser/extensions/api/messaging/native_messaging_policy_handler.h", + "//chrome/browser/extensions/api/messaging/native_message_process_host.cc", + "//chrome/browser/extensions/api/messaging/native_message_process_host.h", + "//chrome/browser/extensions/api/messaging/native_process_launcher.cc", + "//chrome/browser/extensions/api/messaging/native_process_launcher.h", + "//chrome/browser/extensions/api/messaging/native_process_launcher_posix.cc", + "//chrome/browser/extensions/api/messaging/native_process_launcher_win.cc", + "//chrome/browser/extensions/event_router_forwarder.cc", + "//chrome/browser/extensions/event_router_forwarder.h", + ] + + deps += [ "//extensions/browser" ] + } + + public_deps += [ + "//ppapi/host", + "//ppapi/proxy", + ] + } +} diff --git a/chromium_src/chrome/browser/browser_process.cc b/chromium_src/chrome/browser/browser_process.cc index c522012e98..084a6eedf0 100644 --- a/chromium_src/chrome/browser/browser_process.cc +++ b/chromium_src/chrome/browser/browser_process.cc @@ -4,10 +4,16 @@ #include "chrome/browser/browser_process.h" +#include "atom/browser/api/atom_api_app.h" #include "atom/browser/atom_browser_context.h" #include "base/command_line.h" +#include "base/path_service.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" #include "brave/browser/component_updater/brave_component_updater_configurator.h" +#include "brightray/browser/brightray_paths.h" #include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/profiles/profile_manager.h" #include "components/component_updater/component_updater_service.h" #include "content/public/browser/child_process_security_policy.h" #include "ui/base/idle/idle.h" @@ -16,6 +22,7 @@ #if defined(ENABLE_EXTENSIONS) #include "atom/browser/extensions/atom_extensions_browser_client.h" #include "atom/common/extensions/atom_extensions_client.h" +#include "chrome/browser/extensions/event_router_forwarder.h" #include "extensions/common/constants.h" #include "extensions/common/features/feature_provider.h" #include "ui/base/resource/resource_bundle.h" @@ -24,7 +31,8 @@ BrowserProcess* g_browser_process = NULL; BrowserProcess::BrowserProcess() - : tearing_down_(false) { + : tearing_down_(false), + created_profile_manager_(false) { g_browser_process = this; print_job_manager_.reset(new printing::PrintJobManager); @@ -39,15 +47,12 @@ BrowserProcess::BrowserProcess() content::ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme( extensions::kExtensionResourceScheme); + extension_event_router_forwarder_ = new extensions::EventRouterForwarder; + extensions::ExtensionsClient::Set(new extensions::AtomExtensionsClient()); extensions_browser_client_.reset( new extensions::AtomExtensionsBrowserClient()); extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get()); - // make sure everything is loaded - extensions::FeatureProvider::GetAPIFeatures(); - extensions::FeatureProvider::GetPermissionFeatures(); - extensions::FeatureProvider::GetManifestFeatures(); - extensions::FeatureProvider::GetBehaviorFeatures(); #endif } @@ -55,10 +60,31 @@ BrowserProcess::~BrowserProcess() { g_browser_process = NULL; } +void BrowserProcess::CreateProfileManager() { + DCHECK(!created_profile_manager_ && !profile_manager_); + created_profile_manager_ = true; + + base::FilePath user_data_dir; + PathService::Get(brightray::DIR_USER_DATA, &user_data_dir); + profile_manager_.reset(new ProfileManager(user_data_dir)); +} + +ProfileManager* BrowserProcess::profile_manager() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!created_profile_manager_) + CreateProfileManager(); + return profile_manager_.get(); +} + +rappor::RapporService* BrowserProcess::rappor_service() { + return nullptr; +} + component_updater::ComponentUpdateService* BrowserProcess::component_updater( std::unique_ptr &component_updater, bool use_brave_server) { + DCHECK(thread_checker_.CalledOnValidThread()); if (!component_updater.get()) { auto browser_context = atom::AtomBrowserContext::From("", false); scoped_refptr configurator = @@ -80,8 +106,13 @@ BrowserProcess::brave_component_updater() { } component_updater::ComponentUpdateService* -BrowserProcess::google_component_updater() { - return component_updater(google_component_updater_, false); +BrowserProcess::component_updater() { + return component_updater(component_updater_, false); +} + +extensions::EventRouterForwarder* +BrowserProcess::extension_event_router_forwarder() { + return extension_event_router_forwarder_.get(); } void BrowserProcess::StartTearDown() { @@ -100,3 +131,7 @@ printing::PrintJobManager* BrowserProcess::print_job_manager() { bool BrowserProcess::IsShuttingDown() { return tearing_down_; } + +void BrowserProcess::PreCreateThreads() { +} + diff --git a/chromium_src/chrome/browser/browser_process.h b/chromium_src/chrome/browser/browser_process.h index a0329fa75f..8148a75f5f 100644 --- a/chromium_src/chrome/browser/browser_process.h +++ b/chromium_src/chrome/browser/browser_process.h @@ -14,20 +14,40 @@ #include #include "base/macros.h" +#include "base/threading/thread_checker.h" +#include "net/log/net_log.h" -#if defined(ENABLE_EXTENSIONS) -namespace extensions { -class ExtensionsBrowserClient; +namespace atom { +namespace api { +class App; } +} + namespace component_updater { class ComponentUpdateService; } + +#if defined(ENABLE_EXTENSIONS) +namespace extensions { +class EventRouterForwarder; +class ExtensionsBrowserClient; +} #endif +namespace metrics { +class MetricsService; +} namespace printing { class PrintJobManager; } +namespace rappor { +class RapporService; +} + +class IOThread; +class PrefService; +class ProfileManager; // NOT THREAD SAFE, call only from the main thread. // These functions shouldn't return NULL unless otherwise noted. class BrowserProcess { @@ -35,25 +55,53 @@ class BrowserProcess { BrowserProcess(); ~BrowserProcess(); + // Called before the browser threads are created. + void PreCreateThreads(); + std::string GetApplicationLocale(); printing::PrintJobManager* print_job_manager(); bool IsShuttingDown(); void StartTearDown(); + + void set_app(atom::api::App* app) { app_ = app; } + atom::api::App* app() { return app_; } + + metrics::MetricsService* metrics_service() { return NULL; }; + PrefService* local_state(); + ProfileManager* profile_manager(); + rappor::RapporService* rappor_service(); component_updater::ComponentUpdateService* brave_component_updater(); - component_updater::ComponentUpdateService* google_component_updater(); + component_updater::ComponentUpdateService* component_updater(); + +#if defined(ENABLE_EXTENSIONS) + extensions::EventRouterForwarder* extension_event_router_forwarder(); +#endif private: + base::ThreadChecker thread_checker_; + + void CreateProfileManager(); + std::unique_ptr print_job_manager_; #if defined(ENABLE_EXTENSIONS) + scoped_refptr + extension_event_router_forwarder_; std::unique_ptr extensions_browser_client_; #endif bool tearing_down_; + + bool created_profile_manager_; + std::unique_ptr profile_manager_; + + atom::api::App* app_; // not owned + + net::NetLog net_log_; std::unique_ptr brave_component_updater_; std::unique_ptr - google_component_updater_; + component_updater_; component_updater::ComponentUpdateService* component_updater( std::unique_ptr &, bool use_brave_server); diff --git a/chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.cc new file mode 100644 index 0000000000..deb52894d6 --- /dev/null +++ b/chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.cc @@ -0,0 +1,90 @@ +// Copyright 2015 The Chromium 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 "chrome/browser/content_settings/host_content_settings_map_factory.h" + +#include + +// #include "chrome/browser/prefs/pref_service_syncable_util.h" +// #include "chrome/browser/profiles/off_the_record_profile_impl.h" +#include "chrome/browser/profiles/profile.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/syncable_prefs/pref_service_syncable.h" +#include "content/public/browser/browser_thread.h" + +#if defined(ENABLE_EXTENSIONS) +#include "atom/browser/extensions/atom_extension_system.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extension_system_provider.h" +#include "extensions/browser/extensions_browser_client.h" +#endif + +HostContentSettingsMapFactory::HostContentSettingsMapFactory() + : RefcountedBrowserContextKeyedServiceFactory( + "HostContentSettingsMap", + BrowserContextDependencyManager::GetInstance()) { +#if defined(ENABLE_EXTENSIONS) + DependsOn( + extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); +#endif +} + +HostContentSettingsMapFactory::~HostContentSettingsMapFactory() { +} + +// static +HostContentSettingsMap* HostContentSettingsMapFactory::GetForProfile( + Profile* profile) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + return static_cast( + GetInstance()->GetServiceForBrowserContext(profile, true).get()); +} + +// static +HostContentSettingsMapFactory* HostContentSettingsMapFactory::GetInstance() { + return base::Singleton::get(); +} + +scoped_refptr + HostContentSettingsMapFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + Profile* profile = static_cast(context); + + // If off the record, retrieve the host content settings map of the parent + // profile in order to ensure the preferences have been migrated. + if (profile->IsOffTheRecord()) + GetForProfile(profile->GetOriginalProfile()); + + scoped_refptr settings_map(new HostContentSettingsMap( + profile->GetPrefs(), + profile->IsOffTheRecord(), + false)); + + syncable_prefs::PrefServiceSyncable* pref_service = + profile->GetPrefs(); + if (pref_service) { + pref_service->RegisterMergeDataFinishedCallback( + base::Bind(&HostContentSettingsMap::MigrateDomainScopedSettings, + settings_map->GetWeakPtr(), true /* after_sync */)); + } + +#if defined(ENABLE_EXTENSIONS) + ExtensionService *ext_service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + // This may be null in testing or when the extenion_service hasn't been + // initialized, in which case it will be registered then. + if (ext_service) + ext_service->RegisterContentSettings(settings_map.get()); +#endif // defined(ENABLE_EXTENSIONS) + return settings_map; +} + +content::BrowserContext* HostContentSettingsMapFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return context; +} diff --git a/chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.h b/chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.h new file mode 100644 index 0000000000..4b9344ee01 --- /dev/null +++ b/chromium_src/chrome/browser/content_settings/host_content_settings_map_factory.h @@ -0,0 +1,37 @@ +// Copyright 2015 The Chromium 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 CHROME_BROWSER_CONTENT_SETTINGS_HOST_CONTENT_SETTINGS_MAP_FACTORY_H_ +#define CHROME_BROWSER_CONTENT_SETTINGS_HOST_CONTENT_SETTINGS_MAP_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "components/keyed_service/content/refcounted_browser_context_keyed_service_factory.h" + +class HostContentSettingsMap; +class Profile; + +class HostContentSettingsMapFactory + : public RefcountedBrowserContextKeyedServiceFactory { + public: + static HostContentSettingsMap* GetForProfile(Profile* profile); + static HostContentSettingsMapFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits; + + HostContentSettingsMapFactory(); + ~HostContentSettingsMapFactory() override; + + // RefcountedBrowserContextKeyedServiceFactory methods: + scoped_refptr BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + + DISALLOW_COPY_AND_ASSIGN(HostContentSettingsMapFactory); +}; + +#endif // CHROME_BROWSER_CONTENT_SETTINGS_HOST_CONTENT_SETTINGS_MAP_FACTORY_H_ diff --git a/chromium_src/chrome/browser/extensions/extension_tab_util.cc b/chromium_src/chrome/browser/extensions/extension_tab_util.cc new file mode 100644 index 0000000000..894e85fb08 --- /dev/null +++ b/chromium_src/chrome/browser/extensions/extension_tab_util.cc @@ -0,0 +1,257 @@ +// Copyright (c) 2012 The Chromium 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 "chrome/browser/extensions/extension_tab_util.h" + +#include +#include + +#include "atom/browser/extensions/tab_helper.h" +#include "base/macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +// #include "chrome/browser/extensions/api/tabs/tabs_constants.h" +// #include "chrome/browser/extensions/chrome_extension_function.h" +// #include "chrome/browser/extensions/chrome_extension_function_details.h" +// #include "chrome/browser/extensions/tab_helper.h" +// #include "chrome/browser/extensions/window_controller.h" +// #include "chrome/browser/extensions/window_controller_list.h" +// #include "chrome/browser/memory/tab_manager.h" +#include "chrome/browser/profiles/profile.h" +// #include "chrome/browser/sessions/session_tab_helper.h" +// #include "chrome/browser/ui/browser.h" +// #include "chrome/browser/ui/browser_finder.h" +// #include "chrome/browser/ui/browser_navigator_params.h" +// #include "chrome/browser/ui/browser_window.h" +// #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" +// #include "chrome/browser/ui/singleton_tabs.h" +// #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" +// #include "chrome/browser/ui/tabs/tab_strip_model.h" +// #include "chrome/browser/ui/tabs/tab_utils.h" +#include "chrome/common/extensions/api/tabs.h" +#include "chrome/common/url_constants.h" +#include "components/url_formatter/url_fixer.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/app_window/app_window.h" +#include "extensions/browser/app_window/app_window_registry.h" +#include "extensions/common/constants.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/feature_switch.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handlers/incognito_info.h" +#include "extensions/common/manifest_handlers/options_page_info.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/permissions_data.h" +#include "url/gurl.h" + +using content::NavigationEntry; +using content::WebContents; + +namespace extensions { + +ExtensionTabUtil::OpenTabParams::OpenTabParams() + : create_browser_if_needed(false) { +} + +ExtensionTabUtil::OpenTabParams::~OpenTabParams() { +} + +base::DictionaryValue* ExtensionTabUtil::OpenTab( + ChromeUIThreadExtensionFunction* function, + const OpenTabParams& params, + std::string* error) { + NOTIMPLEMENTED();; + return NULL; +} + +Browser* ExtensionTabUtil::GetBrowserFromWindowID( + ChromeUIThreadExtensionFunction* function, + int window_id, + std::string* error) { + NOTIMPLEMENTED(); + return NULL; +} + +Browser* ExtensionTabUtil::GetBrowserFromWindowID( + const ChromeExtensionFunctionDetails& details, + int window_id, + std::string* error) { + NOTIMPLEMENTED(); + return NULL; +} + +int ExtensionTabUtil::GetWindowId(const Browser* browser) { + NOTIMPLEMENTED(); + return -1; +} + +int ExtensionTabUtil::GetWindowIdOfTabStripModel( + const TabStripModel* tab_strip_model) { + NOTIMPLEMENTED(); + return -1; +} + +int ExtensionTabUtil::GetTabId(const WebContents* web_contents) { + return TabHelper::IdForTab(web_contents); +} + +std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) { + return is_loading ? "loading" : "complete"; +} + +int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) { + return TabHelper::IdForWindowContainingTab(web_contents); +} + +// static +std::unique_ptr ExtensionTabUtil::CreateTabObject( + WebContents* contents, + TabStripModel* tab_strip, + int tab_index, + const Extension* extension) { + NOTIMPLEMENTED(); + return NULL; +} + +std::unique_ptr ExtensionTabUtil::CreateTabList( + const Browser* browser, + const Extension* extension) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +std::unique_ptr ExtensionTabUtil::CreateTabObject( + content::WebContents* contents, + TabStripModel* tab_strip, + int tab_index) { + NOTIMPLEMENTED(); return NULL; +} + +// static +std::unique_ptr ExtensionTabUtil::CreateMutedInfo( + content::WebContents* contents) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension, + content::WebContents* contents, + api::tabs::Tab* tab) { + NOTIMPLEMENTED(); +} + +bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents, + TabStripModel** tab_strip_model, + int* tab_index) { + NOTIMPLEMENTED(); + return false; +} + +bool ExtensionTabUtil::GetDefaultTab(Browser* browser, + WebContents** contents, + int* tab_id) { + NOTIMPLEMENTED(); + return false; +} + +bool ExtensionTabUtil::GetTabById(int tab_id, + content::BrowserContext* browser_context, + bool include_incognito, + Browser** browser, + TabStripModel** tab_strip, + WebContents** contents, + int* tab_index) { + if (tab_id == api::tabs::TAB_ID_NONE) + return false; + Profile* profile = Profile::FromBrowserContext(browser_context); + Profile* incognito_profile = + include_incognito && profile->HasOffTheRecordProfile() ? + profile->GetOffTheRecordProfile() : NULL; + WebContents* target_contents = TabHelper::GetTabById(tab_id, profile); + if (!target_contents) + return false; + + if (contents) + *contents = target_contents; + + return true; +} + +GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string, + const Extension* extension) { + GURL url = GURL(url_string); + if (!url.is_valid()) + url = extension->GetResourceURL(url_string); + + return url; +} + +bool ExtensionTabUtil::IsKillURL(const GURL& url) { + static const char* kill_hosts[] = { + chrome::kChromeUICrashHost, + chrome::kChromeUIDelayedHangUIHost, + chrome::kChromeUIHangUIHost, + chrome::kChromeUIKillHost, + chrome::kChromeUIQuitHost, + chrome::kChromeUIRestartHost, + content::kChromeUIBrowserCrashHost, + content::kChromeUIMemoryExhaustHost, + }; + + // Check a fixed-up URL, to normalize the scheme and parse hosts correctly. + GURL fixed_url = + url_formatter::FixupURL(url.possibly_invalid_spec(), std::string()); + if (!fixed_url.SchemeIs(content::kChromeUIScheme)) + return false; + + base::StringPiece fixed_host = fixed_url.host_piece(); + for (size_t i = 0; i < arraysize(kill_hosts); ++i) { + if (fixed_host == kill_hosts[i]) + return true; + } + + return false; +} + +void ExtensionTabUtil::CreateTab(WebContents* web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) { + NOTIMPLEMENTED(); +} + +// static +void ExtensionTabUtil::ForEachTab( + const base::Callback& callback) { + NOTIMPLEMENTED(); +} + +// static +WindowController* ExtensionTabUtil::GetWindowControllerOfTab( + const WebContents* web_contents) { + NOTIMPLEMENTED(); + return NULL; +} + +bool ExtensionTabUtil::OpenOptionsPage(const Extension* extension, + Browser* browser) { + NOTIMPLEMENTED(); + return false; +} + +// static +bool ExtensionTabUtil::BrowserSupportsTabs(Browser* browser) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace extensions diff --git a/chromium_src/chrome/browser/extensions/extension_tab_util.h b/chromium_src/chrome/browser/extensions/extension_tab_util.h new file mode 100644 index 0000000000..7b34826eef --- /dev/null +++ b/chromium_src/chrome/browser/extensions/extension_tab_util.h @@ -0,0 +1,185 @@ +// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_EXTENSIONS_EXTENSION_TAB_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_TAB_UTIL_H_ + +#include +#include + +#include "base/callback.h" +#include "chrome/common/extensions/api/tabs.h" +#include "ui/base/window_open_disposition.h" + +class Browser; +class ChromeExtensionFunctionDetails; +class ChromeUIThreadExtensionFunction; +class ExtensionFunction; +class GURL; +class Profile; +class TabStripModel; + +namespace base { +class DictionaryValue; +class ListValue; +} + +namespace content { +class BrowserContext; +class WebContents; +} + +namespace gfx { +class Rect; +} + +namespace extensions { + +class Extension; +class WindowController; + +// Provides various utility functions that help manipulate tabs. +class ExtensionTabUtil { + public: + struct OpenTabParams { + OpenTabParams(); + ~OpenTabParams(); + + bool create_browser_if_needed; + std::unique_ptr window_id; + std::unique_ptr opener_tab_id; + std::unique_ptr url; + std::unique_ptr active; + std::unique_ptr pinned; + std::unique_ptr index; + }; + + // Opens a new tab given an extension function |function| and creation + // parameters |params|. Returns a Tab object if successful, or NULL and + // optionally sets |error| if an error occurs. + static base::DictionaryValue* OpenTab( + ChromeUIThreadExtensionFunction* function, + const OpenTabParams& params, + std::string* error); + + static int GetWindowId(const Browser* browser); + static int GetWindowIdOfTabStripModel(const TabStripModel* tab_strip_model); + static int GetTabId(const content::WebContents* web_contents); + static std::string GetTabStatusText(bool is_loading); + static int GetWindowIdOfTab(const content::WebContents* web_contents); + static std::unique_ptr CreateTabList( + const Browser* browser, + const Extension* extension); + + // DEPRECATED: Please consider using ChromeExtensionFunctionDetails instead + // of the deprecated ChromeUIThreadExtensionFunction and use the overload + // below + static Browser* GetBrowserFromWindowID( + ChromeUIThreadExtensionFunction* function, + int window_id, + std::string* error_message); + + static Browser* GetBrowserFromWindowID( + const ChromeExtensionFunctionDetails& details, + int window_id, + std::string* error_message); + + // Creates a Tab object (see chrome/common/extensions/api/tabs.json) with + // information about the state of a browser tab. Depending on the + // permissions of the extension, the object may or may not include sensitive + // data such as the tab's URL. + static std::unique_ptr CreateTabObject( + content::WebContents* web_contents, + const Extension* extension) { + return CreateTabObject(web_contents, nullptr, -1, extension); + } + static std::unique_ptr CreateTabObject( + content::WebContents* web_contents, + TabStripModel* tab_strip, + int tab_index, + const Extension* extension); + + // Creates a Tab object but performs no extension permissions checks; the + // returned object will contain privacy-sensitive data. + static std::unique_ptr CreateTabObject( + content::WebContents* web_contents) { + return CreateTabObject(web_contents, nullptr, -1); + } + static std::unique_ptr CreateTabObject( + content::WebContents* web_contents, + TabStripModel* tab_strip, + int tab_index); + + // Creates a tab MutedInfo object (see chrome/common/extensions/api/tabs.json) + // with information about the mute state of a browser tab. + static std::unique_ptr CreateMutedInfo( + content::WebContents* contents); + + // Removes any privacy-sensitive fields from a Tab object if appropriate, + // given the permissions of the extension and the tab in question. The + // tab object is modified in place. + static void ScrubTabForExtension(const Extension* extension, + content::WebContents* contents, + api::tabs::Tab* tab); + + // Gets the |tab_strip_model| and |tab_index| for the given |web_contents|. + static bool GetTabStripModel(const content::WebContents* web_contents, + TabStripModel** tab_strip_model, + int* tab_index); + static bool GetDefaultTab(Browser* browser, + content::WebContents** contents, + int* tab_id); + // Any out parameter (|browser|, |tab_strip|, |contents|, & |tab_index|) may + // be NULL and will not be set within the function. + static bool GetTabById(int tab_id, + content::BrowserContext* browser_context, + bool incognito_enabled, + Browser** browser, + TabStripModel** tab_strip, + content::WebContents** contents, + int* tab_index); + + // Takes |url_string| and returns a GURL which is either valid and absolute + // or invalid. If |url_string| is not directly interpretable as a valid (it is + // likely a relative URL) an attempt is made to resolve it. |extension| is + // provided so it can be resolved relative to its extension base + // (chrome-extension:///). Using the source frame url would be more + // correct, but because the api shipped with urls resolved relative to their + // extension base, we decided it wasn't worth breaking existing extensions to + // fix. + static GURL ResolvePossiblyRelativeURL(const std::string& url_string, + const Extension* extension); + + // Returns true if navigating to |url| would kill a page or the browser + // itself, whether by simulating a crash, browser quit, thread hang, or + // equivalent. Extensions should be prevented from navigating to such URLs. + static bool IsKillURL(const GURL& url); + + // Opens a tab for the specified |web_contents|. + static void CreateTab(content::WebContents* web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture); + + // Executes the specified callback for all tabs in all browser windows. + static void ForEachTab( + const base::Callback& callback); + + static WindowController* GetWindowControllerOfTab( + const content::WebContents* web_contents); + + // Open the extension's options page. Returns true if an options page was + // successfully opened (though it may not necessarily *load*, e.g. if the + // URL does not exist). + static bool OpenOptionsPage(const Extension* extension, Browser* browser); + + // Returns true if the given Browser can report tabs to extensions. + // Example of Browsers which don't support tabs include apps and devtools. + static bool BrowserSupportsTabs(Browser* browser); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_TAB_UTIL_H_ diff --git a/chromium_src/chrome/browser/extensions/extension_util.cc b/chromium_src/chrome/browser/extensions/extension_util.cc new file mode 100644 index 0000000000..70ca36c1d8 --- /dev/null +++ b/chromium_src/chrome/browser/extensions/extension_util.cc @@ -0,0 +1,250 @@ +// Copyright 2013 The Chromium 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 "chrome/browser/extensions/extension_util.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/metrics/field_trial.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/sync_helper.h" +#include "components/variations/variations_associated_data.h" +#include "content/public/browser/site_instance.h" +#include "extensions/browser/extension_host.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extension_util.h" +#include "extensions/browser/process_manager.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_icon_set.h" +#include "extensions/common/features/behavior_feature.h" +#include "extensions/common/features/feature.h" +#include "extensions/common/features/feature_provider.h" +#include "extensions/common/manifest.h" +#include "extensions/common/manifest_handlers/app_isolation_info.h" +#include "extensions/common/manifest_handlers/incognito_info.h" +#include "extensions/common/permissions/permissions_data.h" +#include "extensions/grit/extensions_browser_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace extensions { +namespace util { + +namespace { + +const char kSupervisedUserExtensionPermissionIncreaseFieldTrialName[] = + "SupervisedUserExtensionPermissionIncrease"; + +// The entry into the prefs used to flag an extension as installed by custodian. +// It is relevant only for supervised users. +const char kWasInstalledByCustodianPrefName[] = "was_installed_by_custodian"; + +// Returns true if |extension| should always be enabled in incognito mode. +bool IsWhitelistedForIncognito(const Extension* extension) { + const Feature* feature = FeatureProvider::GetBehaviorFeature( + BehaviorFeature::kWhitelistedForIncognito); + return feature && feature->IsAvailableToExtension(extension).is_available(); +} + +} // namespace + +bool IsIncognitoEnabled(const std::string& extension_id, + content::BrowserContext* context) { + const Extension* extension = ExtensionRegistry::Get(context)-> + GetExtensionById(extension_id, ExtensionRegistry::ENABLED); + if (extension) { + if (!util::CanBeIncognitoEnabled(extension)) + return false; + // If this is an existing component extension we always allow it to + // work in incognito mode. + if (extension->location() == Manifest::COMPONENT || + extension->location() == Manifest::EXTERNAL_COMPONENT) { + return true; + } + if (IsWhitelistedForIncognito(extension)) + return true; + } + return ExtensionPrefs::Get(context)->IsIncognitoEnabled(extension_id); +} + +void SetIsIncognitoEnabled(const std::string& extension_id, + content::BrowserContext* context, + bool enabled) { + NOTIMPLEMENTED(); +} + +bool CanCrossIncognito(const Extension* extension, + content::BrowserContext* context) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + CHECK(extension); + return IsIncognitoEnabled(extension->id(), context) && + !IncognitoInfo::IsSplitMode(extension); +} + +bool CanLoadInIncognito(const Extension* extension, + content::BrowserContext* context) { + CHECK(extension); + if (extension->is_hosted_app()) + return true; + // Packaged apps and regular extensions need to be enabled specifically for + // incognito (and split mode should be set). + return IncognitoInfo::IsSplitMode(extension) && + IsIncognitoEnabled(extension->id(), context); +} + +bool AllowFileAccess(const std::string& extension_id, + content::BrowserContext* context) { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableExtensionsFileAccessCheck) || + ExtensionPrefs::Get(context)->AllowFileAccess(extension_id); +} + +void SetAllowFileAccess(const std::string& extension_id, + content::BrowserContext* context, + bool allow) { + NOTIMPLEMENTED(); +} + +void SetWasInstalledByCustodian(const std::string& extension_id, + content::BrowserContext* context, + bool installed_by_custodian) { + NOTIMPLEMENTED(); +} + +bool WasInstalledByCustodian(const std::string& extension_id, + content::BrowserContext* context) { + bool installed_by_custodian = false; + ExtensionPrefs* prefs = ExtensionPrefs::Get(context); + prefs->ReadPrefAsBoolean(extension_id, kWasInstalledByCustodianPrefName, + &installed_by_custodian); + return installed_by_custodian; +} + +bool IsAppLaunchable(const std::string& extension_id, + content::BrowserContext* context) { + int reason = ExtensionPrefs::Get(context)->GetDisableReasons(extension_id); + return !((reason & Extension::DISABLE_UNSUPPORTED_REQUIREMENT) || + (reason & Extension::DISABLE_CORRUPTED)); +} + +bool IsAppLaunchableWithoutEnabling(const std::string& extension_id, + content::BrowserContext* context) { + return ExtensionRegistry::Get(context)->GetExtensionById( + extension_id, ExtensionRegistry::ENABLED) != NULL; +} + +bool ShouldSync(const Extension* extension, + content::BrowserContext* context) { + return sync_helper::IsSyncable(extension) && + !ExtensionPrefs::Get(context)->DoNotSync(extension->id()); +} + +bool IsExtensionIdle(const std::string& extension_id, + content::BrowserContext* context) { + std::vector ids_to_check; + ids_to_check.push_back(extension_id); + + const Extension* extension = + ExtensionRegistry::Get(context) + ->GetExtensionById(extension_id, ExtensionRegistry::ENABLED); + if (extension && extension->is_shared_module()) { + // We have to check all the extensions that use this shared module for idle + // to tell whether it is really 'idle'. + NOTIMPLEMENTED(); + } + + ProcessManager* process_manager = ProcessManager::Get(context); + for (std::vector::const_iterator i = ids_to_check.begin(); + i != ids_to_check.end(); + i++) { + const std::string id = (*i); + ExtensionHost* host = process_manager->GetBackgroundHostForExtension(id); + if (host) + return false; + + scoped_refptr site_instance = + process_manager->GetSiteInstanceForURL( + Extension::GetBaseURLFromExtensionId(id)); + if (site_instance && site_instance->HasProcess()) + return false; + + if (!process_manager->GetRenderFrameHostsForExtension(id).empty()) + return false; + } + return true; +} + +GURL GetSiteForExtensionId(const std::string& extension_id, + content::BrowserContext* context) { + return content::SiteInstance::GetSiteForURL( + context, Extension::GetBaseURLFromExtensionId(extension_id)); +} + +std::unique_ptr GetExtensionInfo( + const Extension* extension) { + DCHECK(extension); + std::unique_ptr dict(new base::DictionaryValue); + + dict->SetString("id", extension->id()); + dict->SetString("name", extension->name()); + + // TODO(bridiver) + // GURL icon = extensions::ExtensionIconSource::GetIconURL( + // extension, + // extension_misc::EXTENSION_ICON_SMALLISH, + // ExtensionIconSet::MATCH_BIGGER, + // false, // Not grayscale. + // NULL); // Don't set bool if exists. + // dict->SetString("icon", icon.spec()); + + return dict; +} + +const gfx::ImageSkia& GetDefaultAppIcon() { + return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_APP_DEFAULT_ICON); +} + +const gfx::ImageSkia& GetDefaultExtensionIcon() { + return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_EXTENSION_DEFAULT_ICON); +} + +bool IsNewBookmarkAppsEnabled() { +#if defined(OS_MACOSX) + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableNewBookmarkApps); +#else + return !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableNewBookmarkApps); +#endif +} + +bool CanHostedAppsOpenInWindows() { +#if defined(OS_MACOSX) + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableHostedAppsInWindows); +#else + return true; +#endif +} + +bool IsExtensionSupervised(const Extension* extension, Profile* profile) { + NOTIMPLEMENTED(); + return false; +} + +bool NeedCustodianApprovalForPermissionIncrease(const Profile* profile) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace util +} // namespace extensions diff --git a/chromium_src/chrome/browser/extensions/extension_util.h b/chromium_src/chrome/browser/extensions/extension_util.h new file mode 100644 index 0000000000..0790df209f --- /dev/null +++ b/chromium_src/chrome/browser/extensions/extension_util.h @@ -0,0 +1,132 @@ +// Copyright 2013 The Chromium 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 CHROME_BROWSER_EXTENSIONS_EXTENSION_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_UTIL_H_ + +#include +#include + +#include "url/gurl.h" + +namespace base { +class DictionaryValue; +} + +namespace content { +class BrowserContext; +} + +namespace gfx { +class ImageSkia; +} + +class Profile; + +namespace extensions { + +class Extension; +struct ExtensionInfo; +class PermissionSet; + +namespace util { + +// Returns true if |extension_id| can run in an incognito window. +bool IsIncognitoEnabled(const std::string& extension_id, + content::BrowserContext* context); + +// Sets whether |extension_id| can run in an incognito window. Reloads the +// extension if it's enabled since this permission is applied at loading time +// only. Note that an ExtensionService must exist. +void SetIsIncognitoEnabled(const std::string& extension_id, + content::BrowserContext* context, + bool enabled); + +// Returns true if |extension| can see events and data from another sub-profile +// (incognito to original profile, or vice versa). +bool CanCrossIncognito(const extensions::Extension* extension, + content::BrowserContext* context); + +// Returns true if |extension| can be loaded in incognito. +bool CanLoadInIncognito(const extensions::Extension* extension, + content::BrowserContext* context); + +// Returns true if this extension can inject scripts into pages with file URLs. +bool AllowFileAccess(const std::string& extension_id, + content::BrowserContext* context); + +// Sets whether |extension_id| can inject scripts into pages with file URLs. +// Reloads the extension if it's enabled since this permission is applied at +// loading time only. Note than an ExtensionService must exist. +void SetAllowFileAccess(const std::string& extension_id, + content::BrowserContext* context, + bool allow); + +// Returns true if this extension has been installed by the custodian of +// a supervised user. It is relevant for supervised users and used to block +// them from uninstalling the extension for example. +bool WasInstalledByCustodian(const std::string& extension_id, + content::BrowserContext* context); + +// Sets whether |extension_id| is installed by a custodian. +// This is relevant for supervised users and is used to limit their privileges +// for extensions installed by their custodians (e.g. supervised users cannot +// uninstall such extensions). +void SetWasInstalledByCustodian(const std::string& extension_id, + content::BrowserContext* context, + bool installed_by_custodian); + +// Returns true if |extension_id| can be launched (possibly only after being +// enabled). +bool IsAppLaunchable(const std::string& extension_id, + content::BrowserContext* context); + +// Returns true if |extension_id| can be launched without being enabled first. +bool IsAppLaunchableWithoutEnabling(const std::string& extension_id, + content::BrowserContext* context); + +// Returns true if |extension| should be synced. +bool ShouldSync(const Extension* extension, content::BrowserContext* context); + +// Returns true if |extension_id| is idle and it is safe to perform actions such +// as updating. +bool IsExtensionIdle(const std::string& extension_id, + content::BrowserContext* context); + +// Returns the site of the |extension_id|, given the associated |context|. +// Suitable for use with BrowserContext::GetStoragePartitionForSite(). +GURL GetSiteForExtensionId(const std::string& extension_id, + content::BrowserContext* context); + +// Sets the name, id, and icon resource path of the given extension into the +// returned dictionary. +std::unique_ptr GetExtensionInfo( + const Extension* extension); + +// Returns the default extension/app icon (for extensions or apps that don't +// have one). +const gfx::ImageSkia& GetDefaultExtensionIcon(); +const gfx::ImageSkia& GetDefaultAppIcon(); + +// Returns true if the bookmark apps feature is enabled. +// +// TODO(benwells): http://crbug.com/441128: Remove this entirely once the +// feature is stable. +bool IsNewBookmarkAppsEnabled(); + +// TODO(dominickn): http://crbug.com/517682: Remove this entirely once +// open in window is stable on Mac. +bool CanHostedAppsOpenInWindows(); + +// Returns true for custodian-installed extensions in a supervised profile. +bool IsExtensionSupervised(const Extension* extension, Profile* profile); + +// Returns true if supervised users need approval from their custodian for +// approving escalated permissions on updated extensions. +bool NeedCustodianApprovalForPermissionIncrease(const Profile* profile); + +} // namespace util +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_UTIL_H_ diff --git a/chromium_src/chrome/browser/importer/external_process_importer_client.cc b/chromium_src/chrome/browser/importer/external_process_importer_client.cc index 30dccfc384..7a30e2e9f0 100644 --- a/chromium_src/chrome/browser/importer/external_process_importer_client.cc +++ b/chromium_src/chrome/browser/importer/external_process_importer_client.cc @@ -119,10 +119,6 @@ bool ExternalProcessImporterClient::OnMessageReceived( OnAutofillFormDataImportStart) IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_AutofillFormDataImportGroup, OnAutofillFormDataImportGroup) - IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyCookiesImportStart, - OnCookiesImportStart) - IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyCookiesImportGroup, - OnCookiesImportGroup) #if defined(OS_WIN) IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyIE7PasswordInfo, OnIE7PasswordReceived) @@ -288,24 +284,6 @@ void ExternalProcessImporterClient::OnAutofillFormDataImportGroup( if (autofill_form_data_.size() >= total_autofill_form_data_entry_count_) bridge_->SetAutofillFormData(autofill_form_data_); } -void ExternalProcessImporterClient::OnCookiesImportStart( - size_t total_cookies_count) { - if (cancelled_) - return; - - total_cookies_count_ = total_cookies_count; - cookies_.reserve(total_cookies_count); -} -void ExternalProcessImporterClient::OnCookiesImportGroup( - const std::vector& cookies_group) { - if (cancelled_) - return; - - cookies_.insert(cookies_.end(), cookies_group.begin(), - cookies_group.end()); - if (cookies_.size() >= total_cookies_count_) - bridge_->SetCookies(cookies_); -} #if defined(OS_WIN) void ExternalProcessImporterClient::OnIE7PasswordReceived( @@ -340,9 +318,10 @@ void ExternalProcessImporterClient::NotifyItemFinishedOnIOThread( void ExternalProcessImporterClient::StartProcessOnIOThread( BrowserThread::ID thread_id) { - utility_process_host_ = UtilityProcessHost::Create( - this, BrowserThread::GetMessageLoopProxyForThread(thread_id).get()) - ->AsWeakPtr(); + utility_process_host_ = + UtilityProcessHost::Create( + this, BrowserThread::GetTaskRunnerForThread(thread_id).get()) + ->AsWeakPtr(); utility_process_host_->SetName(l10n_util::GetStringUTF16( IDS_UTILITY_PROCESS_PROFILE_IMPORTER_NAME)); utility_process_host_->DisableSandbox(); diff --git a/chromium_src/chrome/browser/importer/external_process_importer_client.h b/chromium_src/chrome/browser/importer/external_process_importer_client.h index 6f6a50ba76..84882bc6ef 100644 --- a/chromium_src/chrome/browser/importer/external_process_importer_client.h +++ b/chromium_src/chrome/browser/importer/external_process_importer_client.h @@ -101,9 +101,6 @@ class ExternalProcessImporterClient : public content::UtilityProcessHostClient { size_t total_autofill_form_data_entry_count); void OnAutofillFormDataImportGroup(const std::vector< ImporterAutofillFormDataEntry>& autofill_form_data_entry_group); - void OnCookiesImportStart(size_t total_cookies_count); - void OnCookiesImportGroup( - const std::vector& cookies_group); #if defined(OS_WIN) void OnIE7PasswordReceived( const importer::ImporterIE7PasswordInfo& importer_password_info); diff --git a/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc b/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc index c91333c75b..8757d7b1d6 100644 --- a/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc +++ b/chromium_src/chrome/browser/importer/in_process_importer_bridge.cc @@ -290,13 +290,6 @@ void InProcessImporterBridge::SetAutofillFormData( autofill_entries)); } -void InProcessImporterBridge::SetCookies( - const std::vector& cookies) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&ProfileWriter::AddCookies, writer_, cookies)); -} - void InProcessImporterBridge::NotifyStarted() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chromium_src/chrome/browser/importer/in_process_importer_bridge.h b/chromium_src/chrome/browser/importer/in_process_importer_bridge.h index 77d0fa84a8..76d1e7024a 100644 --- a/chromium_src/chrome/browser/importer/in_process_importer_bridge.h +++ b/chromium_src/chrome/browser/importer/in_process_importer_bridge.h @@ -70,8 +70,6 @@ class InProcessImporterBridge : public ImporterBridge { void SetAutofillFormData( const std::vector& entries) override; - void SetCookies(const std::vector& cookies) override; - void NotifyStarted() override; void NotifyItemStarted(importer::ImportItem item) override; void NotifyItemEnded(importer::ImportItem item) override; diff --git a/chromium_src/chrome/browser/importer/profile_writer.h b/chromium_src/chrome/browser/importer/profile_writer.h index aac8ddc887..3bdddbe01c 100644 --- a/chromium_src/chrome/browser/importer/profile_writer.h +++ b/chromium_src/chrome/browser/importer/profile_writer.h @@ -111,8 +111,6 @@ class ProfileWriter : public base::RefCountedThreadSafe { virtual void AddAutofillFormDataEntries( const std::vector& autofill_entries); - virtual void AddCookies(const std::vector& cookies); - void Initialize(atom::api::Importer* importer); void ShowWarningDialog(); diff --git a/chromium_src/chrome/browser/media/media_capture_devices_dispatcher.cc b/chromium_src/chrome/browser/media/media_capture_devices_dispatcher.cc new file mode 100644 index 0000000000..87709c24e5 --- /dev/null +++ b/chromium_src/chrome/browser/media/media_capture_devices_dispatcher.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2012 The Chromium 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 "chrome/browser/media/media_capture_devices_dispatcher.h" + +MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { + return base::Singleton::get(); +} + +MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() {} + +MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} + +bool MediaCaptureDevicesDispatcher::IsInsecureCapturingInProgress( + int render_process_id, + int render_frame_id) { + return false; +} diff --git a/chromium_src/chrome/browser/media/media_capture_devices_dispatcher.h b/chromium_src/chrome/browser/media/media_capture_devices_dispatcher.h new file mode 100644 index 0000000000..f352ed977f --- /dev/null +++ b/chromium_src/chrome/browser/media/media_capture_devices_dispatcher.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ +#define CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ + +#include +#include +#include +#include + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/singleton.h" +#include "base/observer_list.h" +// #include "content/public/browser/media_observer.h" +// #include "content/public/browser/web_contents_delegate.h" +// #include "content/public/common/media_stream_request.h" + + +// This singleton is used to receive updates about media events from the content +// layer. +class MediaCaptureDevicesDispatcher { + public: + static MediaCaptureDevicesDispatcher* GetInstance(); + + // Return true if there is any ongoing insecured capturing. The capturing is + // deemed secure if all connected video sinks are reported secure and the + // extension is trusted. + bool IsInsecureCapturingInProgress(int render_process_id, + int render_frame_id); + + private: + friend struct base::DefaultSingletonTraits; + + MediaCaptureDevicesDispatcher(); + ~MediaCaptureDevicesDispatcher(); + + DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesDispatcher); +}; + +#endif // CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ diff --git a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc b/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc deleted file mode 100644 index 7c9f199459..0000000000 --- a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/browser/printing/pdf_to_emf_converter.h" - -#include - -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "chrome/common/chrome_utility_messages.h" -#include "chrome/common/print_messages.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_data.h" -#include "content/public/browser/utility_process_host.h" -#include "content/public/browser/utility_process_host_client.h" -#include "printing/emf_win.h" -#include "printing/pdf_render_settings.h" - -namespace printing { - -namespace { - -using content::BrowserThread; - -class PdfToEmfConverterImpl; - -// Allows to delete temporary directory after all temporary files created inside -// are closed. Windows cannot delete directory with opened files. Directory is -// used to store PDF and metafiles. PDF should be gone by the time utility -// process exits. Metafiles should be gone when all LazyEmf destroyed. -class RefCountedTempDir - : public base::RefCountedThreadSafe { - public: - RefCountedTempDir() { ignore_result(temp_dir_.CreateUniqueTempDir()); } - bool IsValid() const { return temp_dir_.IsValid(); } - const base::FilePath& GetPath() const { return temp_dir_.path(); } - - private: - friend struct BrowserThread::DeleteOnThread; - friend class base::DeleteHelper; - ~RefCountedTempDir() {} - - base::ScopedTempDir temp_dir_; - DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir); -}; - -typedef std::unique_ptr - ScopedTempFile; - -// Wrapper for Emf to keep only file handle in memory, and load actual data only -// on playback. Emf::InitFromFile() can play metafile directly from disk, but it -// can't open file handles. We need file handles to reliably delete temporary -// files, and to efficiently interact with utility process. -class LazyEmf : public MetafilePlayer { - public: - LazyEmf(const scoped_refptr& temp_dir, ScopedTempFile file) - : temp_dir_(temp_dir), file_(std::move(file)) {} - virtual ~LazyEmf() { Close(); } - - virtual bool SafePlayback(HDC hdc) const override; - virtual bool SaveTo(base::File* file) const override; - - private: - void Close() const; - bool LoadEmf(Emf* emf) const; - - mutable scoped_refptr temp_dir_; - mutable ScopedTempFile file_; // Mutable because of consts in base class. - - DISALLOW_COPY_AND_ASSIGN(LazyEmf); -}; - -// Converts PDF into EMF. -// Class uses 3 threads: UI, IO and FILE. -// Internal workflow is following: -// 1. Create instance on the UI thread. (files_, settings_,) -// 2. Create pdf file on the FILE thread. -// 3. Start utility process and start conversion on the IO thread. -// 4. Utility process returns page count. -// 5. For each page: -// 1. Clients requests page with file handle to a temp file. -// 2. Utility converts the page, save it to the file and reply. -// -// All these steps work sequentially, so no data should be accessed -// simultaneously by several threads. -class PdfToEmfUtilityProcessHostClient - : public content::UtilityProcessHostClient { - public: - PdfToEmfUtilityProcessHostClient( - base::WeakPtr converter, - const PdfRenderSettings& settings); - - void Start(const scoped_refptr& data, - const PdfToEmfConverter::StartCallback& start_callback); - - void GetPage(int page_number, - const PdfToEmfConverter::GetPageCallback& get_page_callback); - - void Stop(); - - // UtilityProcessHostClient implementation. - virtual void OnProcessCrashed(int exit_code) override; - virtual void OnProcessLaunchFailed(int exit_code) override; - virtual bool OnMessageReceived(const IPC::Message& message) override; - - private: - class GetPageCallbackData { - public: - GetPageCallbackData(int page_number, - PdfToEmfConverter::GetPageCallback callback) - : page_number_(page_number), callback_(callback) {} - - GetPageCallbackData(GetPageCallbackData&& other) { - *this = std::move(other); - } - - GetPageCallbackData& operator=(GetPageCallbackData&& rhs) { - page_number_ = rhs.page_number_; - callback_ = rhs.callback_; - emf_ = std::move(rhs.emf_); - return *this; - } - - int page_number() const { return page_number_; } - const PdfToEmfConverter::GetPageCallback& callback() const { - return callback_; - } - ScopedTempFile TakeEmf() { return std::move(emf_); } - void set_emf(ScopedTempFile emf) { emf_ = std::move(emf); } - - private: - int page_number_; - PdfToEmfConverter::GetPageCallback callback_; - ScopedTempFile emf_; - }; - - virtual ~PdfToEmfUtilityProcessHostClient(); - - bool Send(IPC::Message* msg); - - // Message handlers. - void OnProcessStarted(); - void OnPageCount(int page_count); - void OnPageDone(bool success, float scale_factor); - - void OnFailed(); - void OnTempPdfReady(ScopedTempFile pdf); - void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); - - scoped_refptr temp_dir_; - - // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. - base::WeakPtr converter_; - PdfRenderSettings settings_; - scoped_refptr data_; - - // Document loaded callback. - PdfToEmfConverter::StartCallback start_callback_; - - // Process host for IPC. - base::WeakPtr utility_process_host_; - - // Queue of callbacks for GetPage() requests. Utility process should reply - // with PageDone in the same order as requests were received. - // Use containers that keeps element pointers valid after push() and pop(). - typedef std::queue GetPageCallbacks; - GetPageCallbacks get_page_callbacks_; - - DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); -}; - -class PdfToEmfConverterImpl : public PdfToEmfConverter { - public: - PdfToEmfConverterImpl(); - - virtual ~PdfToEmfConverterImpl(); - - virtual void Start(const scoped_refptr& data, - const PdfRenderSettings& conversion_settings, - const StartCallback& start_callback) override; - - virtual void GetPage(int page_number, - const GetPageCallback& get_page_callback) override; - - // Helps to cancel callbacks if this object is destroyed. - void RunCallback(const base::Closure& callback); - - private: - scoped_refptr utility_client_; - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl); -}; - -ScopedTempFile CreateTempFile(scoped_refptr* temp_dir) { - if (!temp_dir->get()) - *temp_dir = new RefCountedTempDir(); - ScopedTempFile file; - if (!(*temp_dir)->IsValid()) - return file; - base::FilePath path; - if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path)) - return file; - file.reset(new base::File(path, - base::File::FLAG_CREATE_ALWAYS | - base::File::FLAG_WRITE | - base::File::FLAG_READ | - base::File::FLAG_DELETE_ON_CLOSE | - base::File::FLAG_TEMPORARY)); - if (!file->IsValid()) - file.reset(); - return file; -} - -ScopedTempFile CreateTempPdfFile( - const scoped_refptr& data, - scoped_refptr* temp_dir) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - - ScopedTempFile pdf_file = CreateTempFile(temp_dir); - if (!pdf_file || - static_cast(data->size()) != - pdf_file->WriteAtCurrentPos(data->front_as(), data->size())) { - pdf_file.reset(); - } - pdf_file->Seek(base::File::FROM_BEGIN, 0); - return pdf_file; -} - -bool LazyEmf::SafePlayback(HDC hdc) const { - Emf emf; - bool result = LoadEmf(&emf) && emf.SafePlayback(hdc); - // TODO(vitalybuka): Fix destruction of metafiles. For some reasons - // instances of Emf are not deleted. crbug.com/411683 - // It's known that the Emf going to be played just once to a printer. So just - // release file here. - Close(); - return result; -} - -bool LazyEmf::SaveTo(base::File* file) const { - Emf emf; - return LoadEmf(&emf) && emf.SaveTo(file); -} - -void LazyEmf::Close() const { - file_.reset(); - temp_dir_ = NULL; -} - -bool LazyEmf::LoadEmf(Emf* emf) const { - file_->Seek(base::File::FROM_BEGIN, 0); - int64_t size = file_->GetLength(); - if (size <= 0) - return false; - std::vector data(size); - if (file_->ReadAtCurrentPos(data.data(), data.size()) != size) - return false; - return emf->InitFromData(data.data(), data.size()); -} - -PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( - base::WeakPtr converter, - const PdfRenderSettings& settings) - : converter_(converter), settings_(settings) { -} - -PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { -} - -void PdfToEmfUtilityProcessHostClient::Start( - const scoped_refptr& data, - const PdfToEmfConverter::StartCallback& start_callback) { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&PdfToEmfUtilityProcessHostClient::Start, - this, - data, - start_callback)); - return; - } - data_ = data; - - // Store callback before any OnFailed() call to make it called on failure. - start_callback_ = start_callback; - - // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load - // gdiplus.dll, change how rendering happens, and not be able to correctly - // generate when sent to a metafile DC. - utility_process_host_ = - content::UtilityProcessHost::Create( - this, base::MessageLoop::current()->task_runner())->AsWeakPtr(); - if (!utility_process_host_) - return OnFailed(); - // Should reply with OnProcessStarted(). - Send(new ChromeUtilityMsg_StartupPing); -} - -void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!utility_process_host_) - return OnFailed(); - - scoped_refptr data = data_; - data_ = NULL; - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&CreateTempPdfFile, data, &temp_dir_), - base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this)); -} - -void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!utility_process_host_ || !pdf) - return OnFailed(); - // Should reply with OnPageCount(). - Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( - IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false), - settings_)); -} - -void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (start_callback_.is_null()) - return OnFailed(); - BrowserThread::PostTask(BrowserThread::UI, - FROM_HERE, - base::Bind(&PdfToEmfConverterImpl::RunCallback, - converter_, - base::Bind(start_callback_, page_count))); - start_callback_.Reset(); -} - -void PdfToEmfUtilityProcessHostClient::GetPage( - int page_number, - const PdfToEmfConverter::GetPageCallback& get_page_callback) { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage, - this, - page_number, - get_page_callback)); - return; - } - - // Store callback before any OnFailed() call to make it called on failure. - get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback)); - - if (!utility_process_host_) - return OnFailed(); - - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&CreateTempFile, &temp_dir_), - base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady, - this, - &get_page_callbacks_.back())); -} - -void PdfToEmfUtilityProcessHostClient::OnTempEmfReady( - GetPageCallbackData* callback_data, - ScopedTempFile emf) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!utility_process_host_ || !emf) - return OnFailed(); - IPC::PlatformFileForTransit transit = - IPC::GetPlatformFileForTransit(emf->GetPlatformFile(), false); - callback_data->set_emf(std::move(emf)); - // Should reply with OnPageDone(). - Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage( - callback_data->page_number(), transit)); -} - -void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success, - float scale_factor) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (get_page_callbacks_.empty()) - return OnFailed(); - GetPageCallbackData& data = get_page_callbacks_.front(); - std::unique_ptr emf; - - if (success) { - ScopedTempFile temp_emf = data.TakeEmf(); - if (!temp_emf) // Unexpected message from utility process. - return OnFailed(); - emf.reset(new LazyEmf(temp_dir_, std::move(temp_emf))); - } - - BrowserThread::PostTask(BrowserThread::UI, - FROM_HERE, - base::Bind(&PdfToEmfConverterImpl::RunCallback, - converter_, - base::Bind(data.callback(), - data.page_number(), - scale_factor, - base::Passed(&emf)))); - get_page_callbacks_.pop(); -} - -void PdfToEmfUtilityProcessHostClient::Stop() { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this)); - return; - } - Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); -} - -void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { - OnFailed(); -} - -void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed(int exit_code) { - OnFailed(); -} - -bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) - IPC_MESSAGE_HANDLER( - ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, - OnPageDone) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) { - if (utility_process_host_) - return utility_process_host_->Send(msg); - delete msg; - return false; -} - -void PdfToEmfUtilityProcessHostClient::OnFailed() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!start_callback_.is_null()) - OnPageCount(0); - while (!get_page_callbacks_.empty()) - OnPageDone(false, 0.0f); - utility_process_host_.reset(); -} - -PdfToEmfConverterImpl::PdfToEmfConverterImpl() : weak_ptr_factory_(this) { -} - -PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { - if (utility_client_.get()) - utility_client_->Stop(); -} - -void PdfToEmfConverterImpl::Start( - const scoped_refptr& data, - const PdfRenderSettings& conversion_settings, - const StartCallback& start_callback) { - DCHECK(!utility_client_.get()); - utility_client_ = new PdfToEmfUtilityProcessHostClient( - weak_ptr_factory_.GetWeakPtr(), conversion_settings); - utility_client_->Start(data, start_callback); -} - -void PdfToEmfConverterImpl::GetPage(int page_number, - const GetPageCallback& get_page_callback) { - utility_client_->GetPage(page_number, get_page_callback); -} - -void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - callback.Run(); -} - -} // namespace - -PdfToEmfConverter::~PdfToEmfConverter() { -} - -// static -std::unique_ptr PdfToEmfConverter::CreateDefault() { - return std::unique_ptr(new PdfToEmfConverterImpl()); -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h b/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h deleted file mode 100644 index bd292dd476..0000000000 --- a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 The Chromium 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 CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ -#define CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ - -#include - -#include "base/callback.h" -#include "base/memory/ref_counted_memory.h" - -namespace base { -class FilePath; -} - -namespace printing { - -class MetafilePlayer; -class PdfRenderSettings; - -class PdfToEmfConverter { - public: - typedef base::Callback StartCallback; - typedef base::Callback emf)> GetPageCallback; - - virtual ~PdfToEmfConverter(); - - static std::unique_ptr CreateDefault(); - - // Starts conversion of PDF provided as |data|. Calls |start_callback| - // with positive |page_count|. |page_count| is 0 if initialization failed. - virtual void Start(const scoped_refptr& data, - const PdfRenderSettings& conversion_settings, - const StartCallback& start_callback) = 0; - - // Requests conversion of the page. |page_number| is 0-base page number in - // PDF provided in Start() call. - // Calls |get_page_callback| after conversion. |emf| of callback in not NULL - // if conversion succeeded. - virtual void GetPage(int page_number, - const GetPageCallback& get_page_callback) = 0; -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ diff --git a/chromium_src/chrome/browser/printing/print_job.cc b/chromium_src/chrome/browser/printing/print_job.cc deleted file mode 100644 index ef82db706e..0000000000 --- a/chromium_src/chrome/browser/printing/print_job.cc +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/printing/print_job.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_restrictions.h" -#include "base/threading/worker_pool.h" -#include "base/timer/timer.h" -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/printing/print_job_worker.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_service.h" -#include "printing/printed_document.h" -#include "printing/printed_page.h" - -#if defined(OS_WIN) -#include "chrome/browser/printing/pdf_to_emf_converter.h" -#include "printing/pdf_render_settings.h" -#endif - - -using base::TimeDelta; - -namespace { - -// Helper function to ensure |owner| is valid until at least |callback| returns. -void HoldRefCallback(const scoped_refptr& owner, - const base::Closure& callback) { - callback.Run(); -} - -} // namespace - -namespace printing { - -PrintJob::PrintJob() - : source_(NULL), - worker_(), - settings_(), - is_job_pending_(false), - is_canceling_(false), - quit_factory_(this) { - // This is normally a UI message loop, but in unit tests, the message loop is - // of the 'default' type. - DCHECK(base::MessageLoopForUI::IsCurrent() || - base::MessageLoop::current()->type() == - base::MessageLoop::TYPE_DEFAULT); -} - -PrintJob::~PrintJob() { - // The job should be finished (or at least canceled) when it is destroyed. - DCHECK(!is_job_pending_); - DCHECK(!is_canceling_); - DCHECK(!worker_ || !worker_->IsRunning()); - DCHECK(RunsTasksOnCurrentThread()); -} - -void PrintJob::Initialize(PrintJobWorkerOwner* job, - PrintedPagesSource* source, - int page_count) { - DCHECK(!source_); - DCHECK(!worker_.get()); - DCHECK(!is_job_pending_); - DCHECK(!is_canceling_); - DCHECK(!document_.get()); - source_ = source; - worker_.reset(job->DetachWorker(this)); - settings_ = job->settings(); - - PrintedDocument* new_doc = - new PrintedDocument(settings_, - source_, - job->cookie(), - content::BrowserThread::GetBlockingPool()); - new_doc->set_page_count(page_count); - UpdatePrintedDocument(new_doc); - - // Don't forget to register to our own messages. - registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(this)); -} - -void PrintJob::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK(RunsTasksOnCurrentThread()); - switch (type) { - case chrome::NOTIFICATION_PRINT_JOB_EVENT: { - OnNotifyPrintJobEvent(*content::Details(details).ptr()); - break; - } - default: { - break; - } - } -} - -void PrintJob::GetSettingsDone(const PrintSettings& new_settings, - PrintingContext::Result result) { - NOTREACHED(); -} - -PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) { - NOTREACHED(); - return NULL; -} - -const PrintSettings& PrintJob::settings() const { - return settings_; -} - -int PrintJob::cookie() const { - if (!document_.get()) - // Always use an invalid cookie in this case. - return 0; - return document_->cookie(); -} - -void PrintJob::StartPrinting() { - DCHECK(RunsTasksOnCurrentThread()); - DCHECK(worker_->IsRunning()); - DCHECK(!is_job_pending_); - if (!worker_->IsRunning() || is_job_pending_) - return; - - // Real work is done in PrintJobWorker::StartPrinting(). - worker_->PostTask(FROM_HERE, - base::Bind(&HoldRefCallback, - base::RetainedRef(this), - base::Bind(&PrintJobWorker::StartPrinting, - base::Unretained(worker_.get()), - base::RetainedRef(document_)))); - // Set the flag right now. - is_job_pending_ = true; - - // Tell everyone! - scoped_refptr details( - new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL)); - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(this), - content::Details(details.get())); -} - -void PrintJob::Stop() { - DCHECK(RunsTasksOnCurrentThread()); - - if (quit_factory_.HasWeakPtrs()) { - // In case we're running a nested message loop to wait for a job to finish, - // and we finished before the timeout, quit the nested loop right away. - Quit(); - quit_factory_.InvalidateWeakPtrs(); - } - - // Be sure to live long enough. - scoped_refptr handle(this); - - if (worker_->IsRunning()) { - ControlledWorkerShutdown(); - } else { - // Flush the cached document. - UpdatePrintedDocument(NULL); - } -} - -void PrintJob::Cancel() { - if (is_canceling_) - return; - is_canceling_ = true; - - // Be sure to live long enough. - scoped_refptr handle(this); - - DCHECK(RunsTasksOnCurrentThread()); - if (worker_ && worker_->IsRunning()) { - // Call this right now so it renders the context invalid. Do not use - // InvokeLater since it would take too much time. - worker_->Cancel(); - } - // Make sure a Cancel() is broadcast. - scoped_refptr details( - new JobEventDetails(JobEventDetails::FAILED, NULL, NULL)); - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(this), - content::Details(details.get())); - Stop(); - is_canceling_ = false; -} - -bool PrintJob::FlushJob(base::TimeDelta timeout) { - // Make sure the object outlive this message loop. - scoped_refptr handle(this); - - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout); - - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - base::MessageLoop::current()->Run(); - - return true; -} - -void PrintJob::DisconnectSource() { - source_ = NULL; - if (document_.get()) - document_->DisconnectSource(); -} - -bool PrintJob::is_job_pending() const { - return is_job_pending_; -} - -PrintedDocument* PrintJob::document() const { - return document_.get(); -} - -#if defined(OS_WIN) - -class PrintJob::PdfToEmfState { - public: - PdfToEmfState(const gfx::Size& page_size, const gfx::Rect& content_area) - : page_count_(0), - current_page_(0), - pages_in_progress_(0), - page_size_(page_size), - content_area_(content_area), - converter_(PdfToEmfConverter::CreateDefault()) {} - - void Start(const scoped_refptr& data, - const PdfRenderSettings& conversion_settings, - const PdfToEmfConverter::StartCallback& start_callback) { - converter_->Start(data, conversion_settings, start_callback); - } - - void GetMorePages( - const PdfToEmfConverter::GetPageCallback& get_page_callback) { - const int kMaxNumberOfTempFilesPerDocument = 3; - while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument && - current_page_ < page_count_) { - ++pages_in_progress_; - converter_->GetPage(current_page_++, get_page_callback); - } - } - - void OnPageProcessed( - const PdfToEmfConverter::GetPageCallback& get_page_callback) { - --pages_in_progress_; - GetMorePages(get_page_callback); - // Release converter if we don't need this any more. - if (!pages_in_progress_ && current_page_ >= page_count_) - converter_.reset(); - } - - void set_page_count(int page_count) { page_count_ = page_count; } - gfx::Size page_size() const { return page_size_; } - gfx::Rect content_area() const { return content_area_; } - - private: - int page_count_; - int current_page_; - int pages_in_progress_; - gfx::Size page_size_; - gfx::Rect content_area_; - std::unique_ptr converter_; -}; - -void PrintJob::StartPdfToEmfConversion( - const scoped_refptr& bytes, - const gfx::Size& page_size, - const gfx::Rect& content_area) { - DCHECK(!ptd_to_emf_state_.get()); - ptd_to_emf_state_.reset(new PdfToEmfState(page_size, content_area)); - const int kPrinterDpi = settings().dpi(); - ptd_to_emf_state_->Start( - bytes, - printing::PdfRenderSettings(content_area, kPrinterDpi, true), - base::Bind(&PrintJob::OnPdfToEmfStarted, this)); -} - -void PrintJob::OnPdfToEmfStarted(int page_count) { - if (page_count <= 0) { - ptd_to_emf_state_.reset(); - Cancel(); - return; - } - ptd_to_emf_state_->set_page_count(page_count); - ptd_to_emf_state_->GetMorePages( - base::Bind(&PrintJob::OnPdfToEmfPageConverted, this)); -} - -void PrintJob::OnPdfToEmfPageConverted(int page_number, - float scale_factor, - std::unique_ptr emf) { - DCHECK(ptd_to_emf_state_); - if (!document_.get() || !emf) { - ptd_to_emf_state_.reset(); - Cancel(); - return; - } - - // Update the rendered document. It will send notifications to the listener. - document_->SetPage(page_number, - std::move(emf), - scale_factor, - ptd_to_emf_state_->page_size(), - ptd_to_emf_state_->content_area()); - - ptd_to_emf_state_->GetMorePages( - base::Bind(&PrintJob::OnPdfToEmfPageConverted, this)); -} - -#endif // OS_WIN - -void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) { - if (document_.get() == new_document) - return; - - document_ = new_document; - - if (document_.get()) { - settings_ = document_->settings(); - } - - if (worker_) { - DCHECK(!is_job_pending_); - // Sync the document with the worker. - worker_->PostTask(FROM_HERE, - base::Bind(&HoldRefCallback, - base::RetainedRef(this), - base::Bind(&PrintJobWorker::OnDocumentChanged, - base::Unretained(worker_.get()), - base::RetainedRef(document_)))); - } -} - -void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) { - switch (event_details.type()) { - case JobEventDetails::FAILED: { - settings_.Clear(); - // No need to cancel since the worker already canceled itself. - Stop(); - break; - } - case JobEventDetails::USER_INIT_DONE: - case JobEventDetails::DEFAULT_INIT_DONE: - case JobEventDetails::USER_INIT_CANCELED: { - DCHECK_EQ(event_details.document(), document_.get()); - break; - } - case JobEventDetails::NEW_DOC: - case JobEventDetails::NEW_PAGE: - case JobEventDetails::JOB_DONE: - case JobEventDetails::ALL_PAGES_REQUESTED: { - // Don't care. - break; - } - case JobEventDetails::DOC_DONE: { - // This will call Stop() and broadcast a JOB_DONE message. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this)); - break; - } - case JobEventDetails::PAGE_DONE: -#if defined(OS_WIN) - ptd_to_emf_state_->OnPageProcessed( - base::Bind(&PrintJob::OnPdfToEmfPageConverted, this)); -#endif // OS_WIN - break; - default: { - NOTREACHED(); - break; - } - } -} - -void PrintJob::OnDocumentDone() { - // Be sure to live long enough. The instance could be destroyed by the - // JOB_DONE broadcast. - scoped_refptr handle(this); - - // Stop the worker thread. - Stop(); - - scoped_refptr details( - new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL)); - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(this), - content::Details(details.get())); -} - -void PrintJob::ControlledWorkerShutdown() { - DCHECK(RunsTasksOnCurrentThread()); - - // The deadlock this code works around is specific to window messaging on - // Windows, so we aren't likely to need it on any other platforms. -#if defined(OS_WIN) - // We could easily get into a deadlock case if worker_->Stop() is used; the - // printer driver created a window as a child of the browser window. By - // canceling the job, the printer driver initiated dialog box is destroyed, - // which sends a blocking message to its parent window. If the browser window - // thread is not processing messages, a deadlock occurs. - // - // This function ensures that the dialog box will be destroyed in a timely - // manner by the mere fact that the thread will terminate. So the potential - // deadlock is eliminated. - worker_->StopSoon(); - - // Delay shutdown until the worker terminates. We want this code path - // to wait on the thread to quit before continuing. - if (worker_->IsRunning()) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&PrintJob::ControlledWorkerShutdown, this), - base::TimeDelta::FromMilliseconds(100)); - return; - } -#endif - - - // Now make sure the thread object is cleaned up. Do this on a worker - // thread because it may block. - base::WorkerPool::PostTaskAndReply( - FROM_HERE, - base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())), - base::Bind(&PrintJob::HoldUntilStopIsCalled, this), - false); - - is_job_pending_ = false; - registrar_.RemoveAll(); - UpdatePrintedDocument(NULL); -} - -void PrintJob::HoldUntilStopIsCalled() { -} - -void PrintJob::Quit() { - base::MessageLoop::current()->QuitWhenIdle(); -} - -// Takes settings_ ownership and will be deleted in the receiving thread. -JobEventDetails::JobEventDetails(Type type, - PrintedDocument* document, - PrintedPage* page) - : document_(document), - page_(page), - type_(type) { -} - -JobEventDetails::~JobEventDetails() { -} - -PrintedDocument* JobEventDetails::document() const { return document_.get(); } - -PrintedPage* JobEventDetails::page() const { return page_.get(); } - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job.h b/chromium_src/chrome/browser/printing/print_job.h deleted file mode 100644 index 420622a7e9..0000000000 --- a/chromium_src/chrome/browser/printing/print_job.h +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_JOB_H_ -#define CHROME_BROWSER_PRINTING_PRINT_JOB_H_ - -#include - -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "chrome/browser/printing/print_job_worker_owner.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" - -class Thread; - -namespace base { -class RefCountedMemory; -} - -namespace printing { - -class JobEventDetails; -class MetafilePlayer; -class PdfToEmfConverter; -class PrintJobWorker; -class PrintedDocument; -class PrintedPage; -class PrintedPagesSource; -class PrinterQuery; - -// Manages the print work for a specific document. Talks to the printer through -// PrintingContext through PrintJobWorker. Hides access to PrintingContext in a -// worker thread so the caller never blocks. PrintJob will send notifications on -// any state change. While printing, the PrintJobManager instance keeps a -// reference to the job to be sure it is kept alive. All the code in this class -// runs in the UI thread. -class PrintJob : public PrintJobWorkerOwner, - public content::NotificationObserver { - public: - // Create a empty PrintJob. When initializing with this constructor, - // post-constructor initialization must be done with Initialize(). - PrintJob(); - - // Grabs the ownership of the PrintJobWorker from another job, which is - // usually a PrinterQuery. Set the expected page count of the print job. - void Initialize(PrintJobWorkerOwner* job, PrintedPagesSource* source, - int page_count); - - // content::NotificationObserver implementation. - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - - // PrintJobWorkerOwner implementation. - virtual void GetSettingsDone(const PrintSettings& new_settings, - PrintingContext::Result result) override; - virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) override; - virtual const PrintSettings& settings() const override; - virtual int cookie() const override; - - // Starts the actual printing. Signals the worker that it should begin to - // spool as soon as data is available. - void StartPrinting(); - - // Asks for the worker thread to finish its queued tasks and disconnects the - // delegate object. The PrintJobManager will remove its reference. This may - // have the side-effect of destroying the object if the caller doesn't have a - // handle to the object. Use PrintJob::is_stopped() to check whether the - // worker thread has actually stopped. - void Stop(); - - // Cancels printing job and stops the worker thread. Takes effect immediately. - void Cancel(); - - // Synchronously wait for the job to finish. It is mainly useful when the - // process is about to be shut down and we're waiting for the spooler to eat - // our data. - bool FlushJob(base::TimeDelta timeout); - - // Disconnects the PrintedPage source (PrintedPagesSource). It is done when - // the source is being destroyed. - void DisconnectSource(); - - // Returns true if the print job is pending, i.e. between a StartPrinting() - // and the end of the spooling. - bool is_job_pending() const; - - // Access the current printed document. Warning: may be NULL. - PrintedDocument* document() const; - -#if defined(OS_WIN) - void StartPdfToEmfConversion( - const scoped_refptr& bytes, - const gfx::Size& page_size, - const gfx::Rect& content_area); - - void OnPdfToEmfStarted(int page_count); - void OnPdfToEmfPageConverted(int page_number, - float scale_factor, - std::unique_ptr emf); - -#endif // OS_WIN - - protected: - virtual ~PrintJob(); - - private: - // Updates document_ to a new instance. - void UpdatePrintedDocument(PrintedDocument* new_document); - - // Processes a NOTIFY_PRINT_JOB_EVENT notification. - void OnNotifyPrintJobEvent(const JobEventDetails& event_details); - - // Releases the worker thread by calling Stop(), then broadcasts a JOB_DONE - // notification. - void OnDocumentDone(); - - // Terminates the worker thread in a very controlled way, to work around any - // eventual deadlock. - void ControlledWorkerShutdown(); - - // Called at shutdown when running a nested message loop. - void Quit(); - - void HoldUntilStopIsCalled(); - - content::NotificationRegistrar registrar_; - - // Source that generates the PrintedPage's (i.e. a WebContents). It will be - // set back to NULL if the source is deleted before this object. - PrintedPagesSource* source_; - - // All the UI is done in a worker thread because many Win32 print functions - // are blocking and enters a message loop without your consent. There is one - // worker thread per print job. - std::unique_ptr worker_; - - // Cache of the print context settings for access in the UI thread. - PrintSettings settings_; - - // The printed document. - scoped_refptr document_; - - // Is the worker thread printing. - bool is_job_pending_; - - // Is Canceling? If so, try to not cause recursion if on FAILED notification, - // the notified calls Cancel() again. - bool is_canceling_; - -#if defined(OS_WIN) - class PdfToEmfState; - std::unique_ptr ptd_to_emf_state_; -#endif // OS_WIN - - // Used at shutdown so that we can quit a nested message loop. - base::WeakPtrFactory quit_factory_; - - DISALLOW_COPY_AND_ASSIGN(PrintJob); -}; - -// Details for a NOTIFY_PRINT_JOB_EVENT notification. The members may be NULL. -class JobEventDetails : public base::RefCountedThreadSafe { - public: - // Event type. - enum Type { - // Print... dialog box has been closed with OK button. - USER_INIT_DONE, - - // Print... dialog box has been closed with CANCEL button. - USER_INIT_CANCELED, - - // An automated initialization has been done, e.g. Init(false, NULL). - DEFAULT_INIT_DONE, - - // A new document started printing. - NEW_DOC, - - // A new page started printing. - NEW_PAGE, - - // A page is done printing. - PAGE_DONE, - - // A document is done printing. The worker thread is still alive. Warning: - // not a good moment to release the handle to PrintJob. - DOC_DONE, - - // The worker thread is finished. A good moment to release the handle to - // PrintJob. - JOB_DONE, - - // All missing pages have been requested. - ALL_PAGES_REQUESTED, - - // An error occured. Printing is canceled. - FAILED, - }; - - JobEventDetails(Type type, PrintedDocument* document, PrintedPage* page); - - // Getters. - PrintedDocument* document() const; - PrintedPage* page() const; - Type type() const { - return type_; - } - - private: - friend class base::RefCountedThreadSafe; - - ~JobEventDetails(); - - scoped_refptr document_; - scoped_refptr page_; - const Type type_; - - DISALLOW_COPY_AND_ASSIGN(JobEventDetails); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_H_ diff --git a/chromium_src/chrome/browser/printing/print_job_manager.cc b/chromium_src/chrome/browser/printing/print_job_manager.cc deleted file mode 100644 index ec08a98923..0000000000 --- a/chromium_src/chrome/browser/printing/print_job_manager.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/printing/print_job_manager.h" - -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/printing/print_job.h" -#include "chrome/browser/printing/printer_query.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_service.h" -#include "printing/printed_document.h" -#include "printing/printed_page.h" - -namespace printing { - -PrintQueriesQueue::PrintQueriesQueue() { -} - -PrintQueriesQueue::~PrintQueriesQueue() { - base::AutoLock lock(lock_); - queued_queries_.clear(); -} - -void PrintQueriesQueue::QueuePrinterQuery(PrinterQuery* job) { - base::AutoLock lock(lock_); - DCHECK(job); - queued_queries_.push_back(make_scoped_refptr(job)); - DCHECK(job->is_valid()); -} - -scoped_refptr PrintQueriesQueue::PopPrinterQuery( - int document_cookie) { - base::AutoLock lock(lock_); - for (PrinterQueries::iterator itr = queued_queries_.begin(); - itr != queued_queries_.end(); ++itr) { - if ((*itr)->cookie() == document_cookie && !(*itr)->is_callback_pending()) { - scoped_refptr current_query(*itr); - queued_queries_.erase(itr); - DCHECK(current_query->is_valid()); - return current_query; - } - } - return NULL; -} - -scoped_refptr PrintQueriesQueue::CreatePrinterQuery( - int render_process_id, - int render_view_id) { - scoped_refptr job = - new printing::PrinterQuery(render_process_id, render_view_id); - return job; -} - -void PrintQueriesQueue::Shutdown() { - PrinterQueries queries_to_stop; - { - base::AutoLock lock(lock_); - queued_queries_.swap(queries_to_stop); - } - // Stop all pending queries, requests to generate print preview do not have - // corresponding PrintJob, so any pending preview requests are not covered - // by PrintJobManager::StopJobs and should be stopped explicitly. - for (PrinterQueries::iterator itr = queries_to_stop.begin(); - itr != queries_to_stop.end(); ++itr) { - (*itr)->PostTask(FROM_HERE, base::Bind(&PrinterQuery::StopWorker, *itr)); - } -} - -PrintJobManager::PrintJobManager() : is_shutdown_(false) { - registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::NotificationService::AllSources()); -} - -PrintJobManager::~PrintJobManager() { -} - -scoped_refptr PrintJobManager::queue() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - if (!queue_.get()) - queue_ = new PrintQueriesQueue(); - return queue_; -} - -void PrintJobManager::Shutdown() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - DCHECK(!is_shutdown_); - is_shutdown_ = true; - registrar_.RemoveAll(); - StopJobs(true); - if (queue_.get()) - queue_->Shutdown(); - queue_ = NULL; -} - -void PrintJobManager::StopJobs(bool wait_for_finish) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - // Copy the array since it can be modified in transit. - PrintJobs to_stop; - to_stop.swap(current_jobs_); - - for (PrintJobs::const_iterator job = to_stop.begin(); job != to_stop.end(); - ++job) { - // Wait for two minutes for the print job to be spooled. - if (wait_for_finish) - (*job)->FlushJob(base::TimeDelta::FromMinutes(2)); - (*job)->Stop(); - } -} - -void PrintJobManager::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - switch (type) { - case chrome::NOTIFICATION_PRINT_JOB_EVENT: { - OnPrintJobEvent(content::Source(source).ptr(), - *content::Details(details).ptr()); - break; - } - default: { - NOTREACHED(); - break; - } - } -} - -void PrintJobManager::OnPrintJobEvent( - PrintJob* print_job, - const JobEventDetails& event_details) { - switch (event_details.type()) { - case JobEventDetails::NEW_DOC: { - DCHECK(current_jobs_.end() == current_jobs_.find(print_job)); - // Causes a AddRef(). - current_jobs_.insert(print_job); - break; - } - case JobEventDetails::JOB_DONE: { - DCHECK(current_jobs_.end() != current_jobs_.find(print_job)); - current_jobs_.erase(print_job); - break; - } - case JobEventDetails::FAILED: { - current_jobs_.erase(print_job); - break; - } - case JobEventDetails::USER_INIT_DONE: - case JobEventDetails::USER_INIT_CANCELED: - case JobEventDetails::DEFAULT_INIT_DONE: - case JobEventDetails::NEW_PAGE: - case JobEventDetails::PAGE_DONE: - case JobEventDetails::DOC_DONE: - case JobEventDetails::ALL_PAGES_REQUESTED: { - // Don't care. - break; - } - default: { - NOTREACHED(); - break; - } - } -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job_manager.h b/chromium_src/chrome/browser/printing/print_job_manager.h deleted file mode 100644 index ddb4e97b42..0000000000 --- a/chromium_src/chrome/browser/printing/print_job_manager.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_ -#define CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_ - -#include -#include -#include - -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "base/threading/non_thread_safe.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" - -namespace printing { - -class JobEventDetails; -class PrintJob; -class PrinterQuery; - -class PrintQueriesQueue : public base::RefCountedThreadSafe { - public: - PrintQueriesQueue(); - - // Queues a semi-initialized worker thread. Can be called from any thread. - // Current use case is queuing from the I/O thread. - // TODO(maruel): Have them vanish after a timeout (~5 minutes?) - void QueuePrinterQuery(PrinterQuery* job); - - // Pops a queued PrintJobWorkerOwner object that was previously queued or - // create new one. Can be called from any thread. - scoped_refptr PopPrinterQuery(int document_cookie); - - // Creates new query. - scoped_refptr CreatePrinterQuery(int render_process_id, - int render_view_id); - - void Shutdown(); - - private: - friend class base::RefCountedThreadSafe; - typedef std::vector > PrinterQueries; - - virtual ~PrintQueriesQueue(); - - // Used to serialize access to queued_workers_. - base::Lock lock_; - - PrinterQueries queued_queries_; - - DISALLOW_COPY_AND_ASSIGN(PrintQueriesQueue); -}; - -class PrintJobManager : public content::NotificationObserver { - public: - PrintJobManager(); - virtual ~PrintJobManager(); - - // On browser quit, we should wait to have the print job finished. - void Shutdown(); - - // content::NotificationObserver - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - - // Returns queries queue. Never returns NULL. Must be called on Browser UI - // Thread. Reference could be stored and used from any thread. - scoped_refptr queue(); - - private: - typedef std::set > PrintJobs; - - // Processes a NOTIFY_PRINT_JOB_EVENT notification. - void OnPrintJobEvent(PrintJob* print_job, - const JobEventDetails& event_details); - - // Stops all printing jobs. If wait_for_finish is true, tries to give jobs - // a chance to complete before stopping them. - void StopJobs(bool wait_for_finish); - - content::NotificationRegistrar registrar_; - - // Current print jobs that are active. - PrintJobs current_jobs_; - - scoped_refptr queue_; - - bool is_shutdown_; - - DISALLOW_COPY_AND_ASSIGN(PrintJobManager); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_ diff --git a/chromium_src/chrome/browser/printing/print_job_worker.cc b/chromium_src/chrome/browser/printing/print_job_worker.cc deleted file mode 100644 index 1ec52f02cc..0000000000 --- a/chromium_src/chrome/browser/printing/print_job_worker.cc +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/printing/print_job_worker.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" -#include "base/values.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/printing/print_job.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "printing/print_job_constants.h" -#include "printing/printed_document.h" -#include "printing/printed_page.h" -#include "printing/printing_utils.h" -#include "ui/base/l10n/l10n_util.h" - -using content::BrowserThread; - -namespace printing { - -namespace { - -// Helper function to ensure |owner| is valid until at least |callback| returns. -void HoldRefCallback(const scoped_refptr& owner, - const base::Closure& callback) { - callback.Run(); -} - -class PrintingContextDelegate : public PrintingContext::Delegate { - public: - PrintingContextDelegate(int render_process_id, int render_view_id); - virtual ~PrintingContextDelegate(); - - virtual gfx::NativeView GetParentView() override; - virtual std::string GetAppLocale() override; - - private: - int render_process_id_; - int render_view_id_; -}; - -PrintingContextDelegate::PrintingContextDelegate(int render_process_id, - int render_view_id) - : render_process_id_(render_process_id), - render_view_id_(render_view_id) { -} - -PrintingContextDelegate::~PrintingContextDelegate() { -} - -gfx::NativeView PrintingContextDelegate::GetParentView() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::RenderViewHost* view = - content::RenderViewHost::FromID(render_process_id_, render_view_id_); - if (!view) - return NULL; - content::WebContents* wc = content::WebContents::FromRenderViewHost(view); - return wc ? wc->GetNativeView() : NULL; -} - -std::string PrintingContextDelegate::GetAppLocale() { - return g_browser_process->GetApplicationLocale(); -} - -void NotificationCallback(PrintJobWorkerOwner* print_job, - JobEventDetails::Type detail_type, - PrintedDocument* document, - PrintedPage* page) { - JobEventDetails* details = new JobEventDetails(detail_type, document, page); - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_EVENT, - // We know that is is a PrintJob object in this circumstance. - content::Source(static_cast(print_job)), - content::Details(details)); -} - -} // namespace - -PrintJobWorker::PrintJobWorker(int render_process_id, - int render_view_id, - PrintJobWorkerOwner* owner) - : owner_(owner), thread_("Printing_Worker"), weak_factory_(this) { - // The object is created in the IO thread. - DCHECK(owner_->RunsTasksOnCurrentThread()); - - printing_context_delegate_.reset( - new PrintingContextDelegate(render_process_id, render_view_id)); - printing_context_ = PrintingContext::Create(printing_context_delegate_.get()); -} - -PrintJobWorker::~PrintJobWorker() { - // The object is normally deleted in the UI thread, but when the user - // cancels printing or in the case of print preview, the worker is destroyed - // on the I/O thread. - DCHECK(owner_->RunsTasksOnCurrentThread()); - Stop(); -} - -void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) { - DCHECK(page_number_ == PageNumber::npos()); - owner_ = new_owner; -} - -void PrintJobWorker::GetSettings( - bool ask_user_for_settings, - int document_page_count, - bool has_selection, - MarginType margin_type) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - DCHECK_EQ(page_number_, PageNumber::npos()); - - // Recursive task processing is needed for the dialog in case it needs to be - // destroyed by a task. - // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed - // on the thread where the PrintDlgEx is called, and definitely both calls - // should happen on the same thread. See http://crbug.com/73466 - // MessageLoop::current()->SetNestableTasksAllowed(true); - printing_context_->set_margin_type(margin_type); - - // When we delegate to a destination, we don't ask the user for settings. - // TODO(mad): Ask the destination for settings. - if (ask_user_for_settings) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&HoldRefCallback, base::RetainedRef(owner_), - base::Bind(&PrintJobWorker::GetSettingsWithUI, - base::Unretained(this), - document_page_count, - has_selection))); - } else { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&HoldRefCallback, base::RetainedRef(owner_), - base::Bind(&PrintJobWorker::UseDefaultSettings, - base::Unretained(this)))); - } -} - -void PrintJobWorker::SetSettings( - std::unique_ptr new_settings) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&HoldRefCallback, - base::RetainedRef(owner_), - base::Bind(&PrintJobWorker::UpdatePrintSettings, - base::Unretained(this), - base::Passed(&new_settings)))); -} - -void PrintJobWorker::UpdatePrintSettings( - std::unique_ptr new_settings) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - PrintingContext::Result result = - printing_context_->UpdatePrintSettings(*new_settings); - GetSettingsDone(result); -} - -void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) { - // Most PrintingContext functions may start a message loop and process - // message recursively, so disable recursive task processing. - // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to - // be called on the same thread as the previous call. See - // http://crbug.com/73466 - // MessageLoop::current()->SetNestableTasksAllowed(false); - - // We can't use OnFailure() here since owner_ may not support notifications. - - // PrintJob will create the new PrintedDocument. - owner_->PostTask(FROM_HERE, - base::Bind(&PrintJobWorkerOwner::GetSettingsDone, - base::RetainedRef(owner_), - printing_context_->settings(), - result)); -} - -void PrintJobWorker::GetSettingsWithUI( - int document_page_count, - bool has_selection) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - printing_context_->AskUserForSettings( - document_page_count, - has_selection, - false, - base::Bind(&PrintJobWorker::GetSettingsWithUIDone, - base::Unretained(this))); -} - -void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) { - PostTask(FROM_HERE, - base::Bind(&HoldRefCallback, - base::RetainedRef(owner_), - base::Bind(&PrintJobWorker::GetSettingsDone, - base::Unretained(this), - result))); -} - -void PrintJobWorker::UseDefaultSettings() { - PrintingContext::Result result = printing_context_->UseDefaultSettings(); - GetSettingsDone(result); -} - -void PrintJobWorker::StartPrinting(PrintedDocument* new_document) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - DCHECK_EQ(page_number_, PageNumber::npos()); - DCHECK_EQ(document_.get(), new_document); - DCHECK(document_.get()); - - if (!document_.get() || page_number_ != PageNumber::npos() || - document_.get() != new_document) { - return; - } - - base::string16 document_name = - printing::SimplifyDocumentTitle(document_->name()); - if (document_name.empty()) { - } - PrintingContext::Result result = - printing_context_->NewDocument(document_name); - if (result != PrintingContext::OK) { - OnFailure(); - return; - } - - // Try to print already cached data. It may already have been generated for - // the print preview. - OnNewPage(); - // Don't touch this anymore since the instance could be destroyed. It happens - // if all the pages are printed a one sweep and the client doesn't have a - // handle to us anymore. There's a timing issue involved between the worker - // thread and the UI thread. Take no chance. -} - -void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - DCHECK_EQ(page_number_, PageNumber::npos()); - - if (page_number_ != PageNumber::npos()) - return; - - document_ = new_document; -} - -void PrintJobWorker::OnNewPage() { - if (!document_.get()) // Spurious message. - return; - - // message_loop() could return NULL when the print job is cancelled. - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - - if (page_number_ == PageNumber::npos()) { - // Find first page to print. - int page_count = document_->page_count(); - if (!page_count) { - // We still don't know how many pages the document contains. We can't - // start to print the document yet since the header/footer may refer to - // the document's page count. - return; - } - // We have enough information to initialize page_number_. - page_number_.Init(document_->settings(), page_count); - } - DCHECK_NE(page_number_, PageNumber::npos()); - - while (true) { - // Is the page available? - scoped_refptr page = document_->GetPage(page_number_.ToInt()); - if (!page.get()) { - // We need to wait for the page to be available. - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(500)); - break; - } - // The page is there, print it. - SpoolPage(page.get()); - ++page_number_; - if (page_number_ == PageNumber::npos()) { - OnDocumentDone(); - // Don't touch this anymore since the instance could be destroyed. - break; - } - } -} - -void PrintJobWorker::Cancel() { - // This is the only function that can be called from any thread. - printing_context_->Cancel(); - // Cannot touch any member variable since we don't know in which thread - // context we run. -} - -bool PrintJobWorker::IsRunning() const { - return thread_.IsRunning(); -} - -bool PrintJobWorker::PostTask(const tracked_objects::Location& from_here, - const base::Closure& task) { - if (task_runner_.get()) - return task_runner_->PostTask(from_here, task); - return false; -} - -void PrintJobWorker::StopSoon() { - thread_.StopSoon(); -} - -void PrintJobWorker::Stop() { - thread_.Stop(); -} - -bool PrintJobWorker::Start() { - bool result = thread_.Start(); - task_runner_ = thread_.task_runner(); - return result; -} - -void PrintJobWorker::OnDocumentDone() { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - DCHECK_EQ(page_number_, PageNumber::npos()); - DCHECK(document_.get()); - - if (printing_context_->DocumentDone() != PrintingContext::OK) { - OnFailure(); - return; - } - - owner_->PostTask(FROM_HERE, - base::Bind(&NotificationCallback, base::RetainedRef(owner_), - JobEventDetails::DOC_DONE, - base::RetainedRef(document_), nullptr)); - - // Makes sure the variables are reinitialized. - document_ = NULL; -} - -void PrintJobWorker::SpoolPage(PrintedPage* page) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - DCHECK_NE(page_number_, PageNumber::npos()); - - // Signal everyone that the page is about to be printed. - owner_->PostTask(FROM_HERE, - base::Bind(&NotificationCallback, base::RetainedRef(owner_), - JobEventDetails::NEW_PAGE, base::RetainedRef(document_), - base::RetainedRef(page))); - - // Preprocess. - if (printing_context_->NewPage() != PrintingContext::OK) { - OnFailure(); - return; - } - - // Actual printing. -#if defined(OS_WIN) || defined(OS_MACOSX) - document_->RenderPrintedPage(*page, printing_context_->context()); -#elif defined(OS_POSIX) - document_->RenderPrintedPage(*page, printing_context_.get()); -#endif - - // Postprocess. - if (printing_context_->PageDone() != PrintingContext::OK) { - OnFailure(); - return; - } - - // Signal everyone that the page is printed. - owner_->PostTask( - FROM_HERE, - base::Bind(&NotificationCallback, base::RetainedRef(owner_), - JobEventDetails::PAGE_DONE, base::RetainedRef(document_), - base::RetainedRef(page))); -} - -void PrintJobWorker::OnFailure() { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - - // We may loose our last reference by broadcasting the FAILED event. - scoped_refptr handle(owner_); - - owner_->PostTask( - FROM_HERE, - base::Bind(&NotificationCallback, base::RetainedRef(owner_), - JobEventDetails::FAILED, - base::RetainedRef(document_), nullptr)); - Cancel(); - - // Makes sure the variables are reinitialized. - document_ = NULL; - page_number_ = PageNumber::npos(); -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job_worker.h b/chromium_src/chrome/browser/printing/print_job_worker.h deleted file mode 100644 index 343c0fa15f..0000000000 --- a/chromium_src/chrome/browser/printing/print_job_worker.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ -#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ - -#include - -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/threading/thread.h" -#include "content/public/browser/browser_thread.h" -#include "printing/page_number.h" -#include "printing/print_job_constants.h" -#include "printing/printing_context.h" - -namespace base { -class DictionaryValue; -} - -namespace printing { - -class PrintJob; -class PrintJobWorkerOwner; -class PrintedDocument; -class PrintedPage; - -// Worker thread code. It manages the PrintingContext, which can be blocking -// and/or run a message loop. This is the object that generates most -// NOTIFY_PRINT_JOB_EVENT notifications, but they are generated through a -// NotificationTask task to be executed from the right thread, the UI thread. -// PrintJob always outlives its worker instance. -class PrintJobWorker { - public: - PrintJobWorker(int render_process_id, - int render_view_id, - PrintJobWorkerOwner* owner); - virtual ~PrintJobWorker(); - - void SetNewOwner(PrintJobWorkerOwner* new_owner); - - // Initializes the print settings. If |ask_user_for_settings| is true, a - // Print... dialog box will be shown to ask the user his preference. - void GetSettings( - bool ask_user_for_settings, - int document_page_count, - bool has_selection, - MarginType margin_type); - - // Set the new print settings. - void SetSettings(std::unique_ptr new_settings); - - // Starts the printing loop. Every pages are printed as soon as the data is - // available. Makes sure the new_document is the right one. - void StartPrinting(PrintedDocument* new_document); - - // Updates the printed document. - void OnDocumentChanged(PrintedDocument* new_document); - - // Dequeues waiting pages. Called when PrintJob receives a - // NOTIFY_PRINTED_DOCUMENT_UPDATED notification. It's time to look again if - // the next page can be printed. - void OnNewPage(); - - // This is the only function that can be called in a thread. - void Cancel(); - - // Returns true if the thread has been started, and not yet stopped. - bool IsRunning() const; - - // Posts the given task to be run. - bool PostTask(const tracked_objects::Location& from_here, - const base::Closure& task); - - // Signals the thread to exit in the near future. - void StopSoon(); - - // Signals the thread to exit and returns once the thread has exited. - void Stop(); - - // Starts the thread. - bool Start(); - - protected: - // Retrieves the context for testing only. - PrintingContext* printing_context() { return printing_context_.get(); } - - private: - // The shared NotificationService service can only be accessed from the UI - // thread, so this class encloses the necessary information to send the - // notification from the right thread. Most NOTIFY_PRINT_JOB_EVENT - // notifications are sent this way, except USER_INIT_DONE, USER_INIT_CANCELED - // and DEFAULT_INIT_DONE. These three are sent through PrintJob::InitDone(). - class NotificationTask; - - // Renders a page in the printer. - void SpoolPage(PrintedPage* page); - - // Closes the job since spooling is done. - void OnDocumentDone(); - - // Discards the current document, the current page and cancels the printing - // context. - void OnFailure(); - - // Asks the user for print settings. Must be called on the UI thread. - // Required on Mac and Linux. Windows can display UI from non-main threads, - // but sticks with this for consistency. - void GetSettingsWithUI( - int document_page_count, - bool has_selection); - - // The callback used by PrintingContext::GetSettingsWithUI() to notify this - // object that the print settings are set. This is needed in order to bounce - // back into the IO thread for GetSettingsDone(). - void GetSettingsWithUIDone(PrintingContext::Result result); - - // Called on the UI thread to update the print settings. - void UpdatePrintSettings(std::unique_ptr new_settings); - - // Reports settings back to owner_. - void GetSettingsDone(PrintingContext::Result result); - - // Use the default settings. When using GTK+ or Mac, this can still end up - // displaying a dialog. So this needs to happen from the UI thread on these - // systems. - void UseDefaultSettings(); - - // Printing context delegate. - std::unique_ptr printing_context_delegate_; - - // Information about the printer setting. - std::unique_ptr printing_context_; - - // The printed document. Only has read-only access. - scoped_refptr document_; - - // The print job owning this worker thread. It is guaranteed to outlive this - // object. - PrintJobWorkerOwner* owner_; - - // Current page number to print. - PageNumber page_number_; - - // Thread to run worker tasks. - base::Thread thread_; - - // Tread-safe pointer to task runner of the |thread_|. - scoped_refptr task_runner_; - - // Used to generate a WeakPtr for callbacks. - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(PrintJobWorker); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ diff --git a/chromium_src/chrome/browser/printing/print_job_worker_owner.cc b/chromium_src/chrome/browser/printing/print_job_worker_owner.cc deleted file mode 100644 index 843ab4616d..0000000000 --- a/chromium_src/chrome/browser/printing/print_job_worker_owner.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/browser/printing/print_job_worker_owner.h" - -#include "base/message_loop/message_loop.h" - -namespace printing { - -PrintJobWorkerOwner::PrintJobWorkerOwner() - : task_runner_(base::MessageLoop::current()->task_runner()) { -} - -PrintJobWorkerOwner::~PrintJobWorkerOwner() { -} - -bool PrintJobWorkerOwner::RunsTasksOnCurrentThread() const { - return task_runner_->RunsTasksOnCurrentThread(); -} - -bool PrintJobWorkerOwner::PostTask(const tracked_objects::Location& from_here, - const base::Closure& task) { - return task_runner_->PostTask(from_here, task); -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job_worker_owner.h b/chromium_src/chrome/browser/printing/print_job_worker_owner.h deleted file mode 100644 index 00a561a39b..0000000000 --- a/chromium_src/chrome/browser/printing/print_job_worker_owner.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__ -#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__ - -#include "base/memory/ref_counted.h" -#include "printing/printing_context.h" - -namespace base { -class MessageLoop; -class SequencedTaskRunner; -} - -namespace tracked_objects { -class Location; -} - -namespace printing { - -class PrintJobWorker; -class PrintSettings; - -class PrintJobWorkerOwner - : public base::RefCountedThreadSafe { - public: - PrintJobWorkerOwner(); - - // Finishes the initialization began by PrintJobWorker::GetSettings(). - // Creates a new PrintedDocument if necessary. Solely meant to be called by - // PrintJobWorker. - virtual void GetSettingsDone(const PrintSettings& new_settings, - PrintingContext::Result result) = 0; - - // Detach the PrintJobWorker associated to this object. - virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) = 0; - - // Access the current settings. - virtual const PrintSettings& settings() const = 0; - - // Cookie uniquely identifying the PrintedDocument and/or loaded settings. - virtual int cookie() const = 0; - - // Returns true if the current thread is a thread on which a task - // may be run, and false if no task will be run on the current - // thread. - bool RunsTasksOnCurrentThread() const; - - // Posts the given task to be run. - bool PostTask(const tracked_objects::Location& from_here, - const base::Closure& task); - - protected: - friend class base::RefCountedThreadSafe; - - virtual ~PrintJobWorkerOwner(); - - // Task runner reference. Used to send notifications in the right - // thread. - scoped_refptr task_runner_; -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__ diff --git a/chromium_src/chrome/browser/printing/print_preview_message_handler.cc b/chromium_src/chrome/browser/printing/print_preview_message_handler.cc deleted file mode 100644 index d1f3660f8d..0000000000 --- a/chromium_src/chrome/browser/printing/print_preview_message_handler.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/printing/print_preview_message_handler.h" - -#include "base/bind.h" -#include "base/memory/shared_memory.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/printing/print_job_manager.h" -#include "chrome/browser/printing/printer_query.h" -#include "chrome/common/print_messages.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "printing/page_size_margins.h" -#include "printing/print_job_constants.h" -#include "printing/pdf_metafile_skia.h" - -#include "atom/common/node_includes.h" - -using content::BrowserThread; -using content::WebContents; - -DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintPreviewMessageHandler); - -namespace { - -void StopWorker(int document_cookie) { - if (document_cookie <= 0) - return; - scoped_refptr queue = - g_browser_process->print_job_manager()->queue(); - scoped_refptr printer_query = - queue->PopPrinterQuery(document_cookie); - if (printer_query.get()) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&printing::PrinterQuery::StopWorker, - printer_query)); - } -} - -char* CopyPDFDataOnIOThread( - const PrintHostMsg_DidPreviewDocument_Params& params) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - std::unique_ptr shared_buf( - new base::SharedMemory(params.metafile_data_handle, true)); - if (!shared_buf->Map(params.data_size)) - return nullptr; - char* memory_pdf_data = static_cast(shared_buf->memory()); - char* pdf_data = new char[params.data_size]; - memcpy(pdf_data, memory_pdf_data, params.data_size); - return pdf_data; -} - -} // namespace - -namespace printing { - - -PrintPreviewMessageHandler::PrintPreviewMessageHandler( - WebContents* web_contents) - : content::WebContentsObserver(web_contents) { - DCHECK(web_contents); -} - -PrintPreviewMessageHandler::~PrintPreviewMessageHandler() { -} - -void PrintPreviewMessageHandler::OnMetafileReadyForPrinting( - const PrintHostMsg_DidPreviewDocument_Params& params) { - // Always try to stop the worker. - StopWorker(params.document_cookie); - - if (params.expected_pages_count <= 0) { - NOTREACHED(); - return; - } - - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::IO, - FROM_HERE, - base::Bind(&CopyPDFDataOnIOThread, params), - base::Bind(&PrintPreviewMessageHandler::RunPrintToPDFCallback, - base::Unretained(this), - params.preview_request_id, - params.data_size)); -} - -void PrintPreviewMessageHandler::OnPrintPreviewFailed(int document_cookie, - int request_id) { - StopWorker(document_cookie); - RunPrintToPDFCallback(request_id, 0, nullptr); -} - -bool PrintPreviewMessageHandler::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message) - IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting, - OnMetafileReadyForPrinting) - IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed, - OnPrintPreviewFailed) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void PrintPreviewMessageHandler::PrintToPDF( - const base::DictionaryValue& options, - const atom::api::WebContents::PrintToPDFCallback& callback) { - int request_id; - options.GetInteger(printing::kPreviewRequestID, &request_id); - print_to_pdf_callback_map_[request_id] = callback; - - content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); - rvh->Send(new PrintMsg_PrintPreview(rvh->GetRoutingID(), options)); -} - -void PrintPreviewMessageHandler::RunPrintToPDFCallback( - int request_id, uint32_t data_size, char* data) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - if (data) { - v8::Local buffer = node::Buffer::New(isolate, - data, static_cast(data_size)).ToLocalChecked(); - print_to_pdf_callback_map_[request_id].Run(v8::Null(isolate), buffer); - } else { - v8::Local error_message = v8::String::NewFromUtf8(isolate, - "Fail to generate PDF"); - print_to_pdf_callback_map_[request_id].Run( - v8::Exception::Error(error_message), v8::Null(isolate)); - } - print_to_pdf_callback_map_.erase(request_id); -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_preview_message_handler.h b/chromium_src/chrome/browser/printing/print_preview_message_handler.h deleted file mode 100644 index 1aac74baa2..0000000000 --- a/chromium_src/chrome/browser/printing/print_preview_message_handler.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_ -#define CHROME_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_ - -#include - -#include "atom/browser/api/atom_api_web_contents.h" -#include "base/compiler_specific.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_contents_user_data.h" - -struct PrintHostMsg_DidPreviewDocument_Params; - -namespace content { -class WebContents; -} - -namespace printing { - -struct PageSizeMargins; - -// Manages the print preview handling for a WebContents. -class PrintPreviewMessageHandler - : public content::WebContentsObserver, - public content::WebContentsUserData { - public: - ~PrintPreviewMessageHandler() override; - - // content::WebContentsObserver implementation. - bool OnMessageReceived(const IPC::Message& message) override; - - void PrintToPDF(const base::DictionaryValue& options, - const atom::api::WebContents::PrintToPDFCallback& callback); - - private: - typedef std::map - PrintToPDFCallbackMap; - - explicit PrintPreviewMessageHandler(content::WebContents* web_contents); - friend class content::WebContentsUserData; - - // Message handlers. - void OnMetafileReadyForPrinting( - const PrintHostMsg_DidPreviewDocument_Params& params); - void OnPrintPreviewFailed(int document_cookie, int request_id); - - void RunPrintToPDFCallback(int request_id, uint32_t data_size, char* data); - - PrintToPDFCallbackMap print_to_pdf_callback_map_; - - DISALLOW_COPY_AND_ASSIGN(PrintPreviewMessageHandler); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_base.cc b/chromium_src/chrome/browser/printing/print_view_manager_base.cc deleted file mode 100644 index 079c5913b5..0000000000 --- a/chromium_src/chrome/browser/printing/print_view_manager_base.cc +++ /dev/null @@ -1,499 +0,0 @@ -// Copyright 2013 The Chromium 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 "chrome/browser/printing/print_view_manager_base.h" - -#include - -#include "base/bind.h" -#include "base/memory/ref_counted_memory.h" -#include "base/strings/utf_string_conversions.h" -#include "base/timer/timer.h" -#include "components/prefs/pref_service.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/printing/print_job.h" -#include "chrome/browser/printing/print_job_manager.h" -#include "chrome/browser/printing/printer_query.h" -#include "chrome/browser/ui/simple_message_box.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/print_messages.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_details.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_source.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "printing/pdf_metafile_skia.h" -#include "printing/printed_document.h" -#include "ui/base/l10n/l10n_util.h" - -#if defined(ENABLE_FULL_PRINTING) -#include "chrome/browser/printing/print_error_dialog.h" -#endif - -using base::TimeDelta; -using content::BrowserThread; - -namespace printing { - -namespace { - -} // namespace - -PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents) - : content::WebContentsObserver(web_contents), - number_pages_(0), - printing_succeeded_(false), - inside_inner_message_loop_(false), - cookie_(0), - queue_(g_browser_process->print_job_manager()->queue()) { - DCHECK(queue_.get()); -#if !defined(OS_MACOSX) - expecting_first_page_ = true; -#endif // OS_MACOSX - printing_enabled_ = true; -} - -PrintViewManagerBase::~PrintViewManagerBase() { - ReleasePrinterQuery(); - DisconnectFromCurrentPrintJob(); -} - -#if !defined(DISABLE_BASIC_PRINTING) -bool PrintViewManagerBase::PrintNow(bool silent, bool print_background) { - return PrintNowInternal(new PrintMsg_PrintPages( - routing_id(), silent, print_background)); -} -#endif // !DISABLE_BASIC_PRINTING - -void PrintViewManagerBase::NavigationStopped() { - // Cancel the current job, wait for the worker to finish. - TerminatePrintJob(true); -} - -void PrintViewManagerBase::RenderProcessGone(base::TerminationStatus status) { - ReleasePrinterQuery(); - - if (!print_job_.get()) - return; - - scoped_refptr document(print_job_->document()); - if (document.get()) { - // If IsComplete() returns false, the document isn't completely rendered. - // Since our renderer is gone, there's nothing to do, cancel it. Otherwise, - // the print job may finish without problem. - TerminatePrintJob(!document->IsComplete()); - } -} - -base::string16 PrintViewManagerBase::RenderSourceName() { - base::string16 name(web_contents()->GetTitle()); - return name; -} - -void PrintViewManagerBase::OnDidGetPrintedPagesCount(int cookie, - int number_pages) { - DCHECK_GT(cookie, 0); - DCHECK_GT(number_pages, 0); - number_pages_ = number_pages; - OpportunisticallyCreatePrintJob(cookie); -} - -void PrintViewManagerBase::OnDidGetDocumentCookie(int cookie) { - cookie_ = cookie; -} - -void PrintViewManagerBase::OnDidPrintPage( - const PrintHostMsg_DidPrintPage_Params& params) { - if (!OpportunisticallyCreatePrintJob(params.document_cookie)) - return; - - PrintedDocument* document = print_job_->document(); - if (!document || params.document_cookie != document->cookie()) { - // Out of sync. It may happen since we are completely asynchronous. Old - // spurious messages can be received if one of the processes is overloaded. - return; - } - -#if defined(OS_MACOSX) - const bool metafile_must_be_valid = true; -#else - const bool metafile_must_be_valid = expecting_first_page_; - expecting_first_page_ = false; -#endif // OS_MACOSX - - base::SharedMemory shared_buf(params.metafile_data_handle, true); - if (metafile_must_be_valid) { - if (!shared_buf.Map(params.data_size)) { - NOTREACHED() << "couldn't map"; - web_contents()->Stop(); - return; - } - } - - std::unique_ptr metafile( - new PdfMetafileSkia(PDF_SKIA_DOCUMENT_TYPE)); - if (metafile_must_be_valid) { - if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) { - NOTREACHED() << "Invalid metafile header"; - web_contents()->Stop(); - return; - } - } - -#if !defined(OS_WIN) - // Update the rendered document. It will send notifications to the listener. - document->SetPage(params.page_number, - std::move(metafile), - params.page_size, - params.content_area); - - ShouldQuitFromInnerMessageLoop(); -#else - if (metafile_must_be_valid) { - scoped_refptr bytes = new base::RefCountedBytes( - reinterpret_cast(shared_buf.memory()), - params.data_size); - - document->DebugDumpData(bytes.get(), FILE_PATH_LITERAL(".pdf")); - print_job_->StartPdfToEmfConversion( - bytes, params.page_size, params.content_area); - } -#endif // !OS_WIN -} - -void PrintViewManagerBase::OnPrintingFailed(int cookie) { - if (cookie != cookie_) { - NOTREACHED(); - return; - } - - ReleasePrinterQuery(); - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_RELEASED, - content::Source(web_contents()), - content::NotificationService::NoDetails()); -} - -void PrintViewManagerBase::OnShowInvalidPrinterSettingsError() { - LOG(ERROR) << "Invalid printer settings"; -} - -bool PrintViewManagerBase::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message) - IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount, - OnDidGetPrintedPagesCount) - IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDocumentCookie, - OnDidGetDocumentCookie) - IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage) - IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed) - IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError, - OnShowInvalidPrinterSettingsError); - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void PrintViewManagerBase::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - switch (type) { - case chrome::NOTIFICATION_PRINT_JOB_EVENT: { - OnNotifyPrintJobEvent(*content::Details(details).ptr()); - break; - } - default: { - NOTREACHED(); - break; - } - } -} - -void PrintViewManagerBase::OnNotifyPrintJobEvent( - const JobEventDetails& event_details) { - switch (event_details.type()) { - case JobEventDetails::FAILED: { - TerminatePrintJob(true); - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_RELEASED, - content::Source(web_contents()), - content::NotificationService::NoDetails()); - break; - } - case JobEventDetails::USER_INIT_DONE: - case JobEventDetails::DEFAULT_INIT_DONE: - case JobEventDetails::USER_INIT_CANCELED: { - NOTREACHED(); - break; - } - case JobEventDetails::ALL_PAGES_REQUESTED: { - ShouldQuitFromInnerMessageLoop(); - break; - } - case JobEventDetails::NEW_DOC: - case JobEventDetails::NEW_PAGE: - case JobEventDetails::PAGE_DONE: - case JobEventDetails::DOC_DONE: { - // Don't care about the actual printing process. - break; - } - case JobEventDetails::JOB_DONE: { - // Printing is done, we don't need it anymore. - // print_job_->is_job_pending() may still be true, depending on the order - // of object registration. - printing_succeeded_ = true; - ReleasePrintJob(); - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_PRINT_JOB_RELEASED, - content::Source(web_contents()), - content::NotificationService::NoDetails()); - break; - } - default: { - NOTREACHED(); - break; - } - } -} - -bool PrintViewManagerBase::RenderAllMissingPagesNow() { - if (!print_job_.get() || !print_job_->is_job_pending()) - return false; - - // We can't print if there is no renderer. - if (!web_contents() || - !web_contents()->GetRenderViewHost() || - !web_contents()->GetRenderViewHost()->IsRenderViewLive()) { - return false; - } - - // Is the document already complete? - if (print_job_->document() && print_job_->document()->IsComplete()) { - printing_succeeded_ = true; - return true; - } - - // WebContents is either dying or a second consecutive request to print - // happened before the first had time to finish. We need to render all the - // pages in an hurry if a print_job_ is still pending. No need to wait for it - // to actually spool the pages, only to have the renderer generate them. Run - // a message loop until we get our signal that the print job is satisfied. - // PrintJob will send a ALL_PAGES_REQUESTED after having received all the - // pages it needs. MessageLoop::current()->Quit() will be called as soon as - // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED - // or in DidPrintPage(). The check is done in - // ShouldQuitFromInnerMessageLoop(). - // BLOCKS until all the pages are received. (Need to enable recursive task) - if (!RunInnerMessageLoop()) { - // This function is always called from DisconnectFromCurrentPrintJob() so we - // know that the job will be stopped/canceled in any case. - return false; - } - return true; -} - -void PrintViewManagerBase::ShouldQuitFromInnerMessageLoop() { - // Look at the reason. - DCHECK(print_job_->document()); - if (print_job_->document() && - print_job_->document()->IsComplete() && - inside_inner_message_loop_) { - // We are in a message loop created by RenderAllMissingPagesNow. Quit from - // it. - base::MessageLoop::current()->QuitWhenIdle(); - inside_inner_message_loop_ = false; - } -} - -bool PrintViewManagerBase::CreateNewPrintJob(PrintJobWorkerOwner* job) { - DCHECK(!inside_inner_message_loop_); - - // Disconnect the current print_job_. - DisconnectFromCurrentPrintJob(); - - // We can't print if there is no renderer. - if (!web_contents()->GetRenderViewHost() || - !web_contents()->GetRenderViewHost()->IsRenderViewLive()) { - return false; - } - - // Ask the renderer to generate the print preview, create the print preview - // view and switch to it, initialize the printer and show the print dialog. - DCHECK(!print_job_.get()); - DCHECK(job); - if (!job) - return false; - - print_job_ = new PrintJob(); - print_job_->Initialize(job, this, number_pages_); - registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(print_job_.get())); - printing_succeeded_ = false; - return true; -} - -void PrintViewManagerBase::DisconnectFromCurrentPrintJob() { - // Make sure all the necessary rendered page are done. Don't bother with the - // return value. - bool result = RenderAllMissingPagesNow(); - - // Verify that assertion. - if (print_job_.get() && - print_job_->document() && - !print_job_->document()->IsComplete()) { - DCHECK(!result); - // That failed. - TerminatePrintJob(true); - } else { - // DO NOT wait for the job to finish. - ReleasePrintJob(); - } -#if !defined(OS_MACOSX) - expecting_first_page_ = true; -#endif // OS_MACOSX -} - -void PrintViewManagerBase::PrintingDone(bool success) { - if (!print_job_.get()) - return; - Send(new PrintMsg_PrintingDone(routing_id(), success)); -} - -void PrintViewManagerBase::TerminatePrintJob(bool cancel) { - if (!print_job_.get()) - return; - - if (cancel) { - // We don't need the metafile data anymore because the printing is canceled. - print_job_->Cancel(); - inside_inner_message_loop_ = false; - } else { - DCHECK(!inside_inner_message_loop_); - DCHECK(!print_job_->document() || print_job_->document()->IsComplete()); - - // WebContents is either dying or navigating elsewhere. We need to render - // all the pages in an hurry if a print job is still pending. This does the - // trick since it runs a blocking message loop: - print_job_->Stop(); - } - ReleasePrintJob(); -} - -void PrintViewManagerBase::ReleasePrintJob() { - if (!print_job_.get()) - return; - - PrintingDone(printing_succeeded_); - - registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(print_job_.get())); - print_job_->DisconnectSource(); - // Don't close the worker thread. - print_job_ = NULL; -} - -bool PrintViewManagerBase::RunInnerMessageLoop() { - // This value may actually be too low: - // - // - If we're looping because of printer settings initialization, the premise - // here is that some poor users have their print server away on a VPN over a - // slow connection. In this situation, the simple fact of opening the printer - // can be dead slow. On the other side, we don't want to die infinitely for a - // real network error. Give the printer 60 seconds to comply. - // - // - If we're looping because of renderer page generation, the renderer could - // be CPU bound, the page overly complex/large or the system just - // memory-bound. - static const int kPrinterSettingsTimeout = 60000; - base::OneShotTimer quit_timer; - quit_timer.Start( - FROM_HERE, TimeDelta::FromMilliseconds(kPrinterSettingsTimeout), - base::MessageLoop::current(), &base::MessageLoop::QuitWhenIdle); - - inside_inner_message_loop_ = true; - - // Need to enable recursive task. - { - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - base::MessageLoop::current()->Run(); - } - - bool success = true; - if (inside_inner_message_loop_) { - // Ok we timed out. That's sad. - inside_inner_message_loop_ = false; - success = false; - } - - return success; -} - -bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) { - if (print_job_.get()) - return true; - - if (!cookie) { - // Out of sync. It may happens since we are completely asynchronous. Old - // spurious message can happen if one of the processes is overloaded. - return false; - } - - // The job was initiated by a script. Time to get the corresponding worker - // thread. - scoped_refptr queued_query = queue_->PopPrinterQuery(cookie); - if (!queued_query.get()) { - NOTREACHED(); - return false; - } - - if (!CreateNewPrintJob(queued_query.get())) { - // Don't kill anything. - return false; - } - - // Settings are already loaded. Go ahead. This will set - // print_job_->is_job_pending() to true. - print_job_->StartPrinting(); - return true; -} - -bool PrintViewManagerBase::PrintNowInternal(IPC::Message* message) { - // Don't print / print preview interstitials. - if (web_contents()->ShowingInterstitialPage()) { - delete message; - return false; - } - return Send(message); -} - -void PrintViewManagerBase::ReleasePrinterQuery() { - if (!cookie_) - return; - - int cookie = cookie_; - cookie_ = 0; - - printing::PrintJobManager* print_job_manager = - g_browser_process->print_job_manager(); - // May be NULL in tests. - if (!print_job_manager) - return; - - scoped_refptr printer_query; - printer_query = queue_->PopPrinterQuery(cookie); - if (!printer_query.get()) - return; - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&PrinterQuery::StopWorker, printer_query.get())); -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_view_manager_base.h b/chromium_src/chrome/browser/printing/print_view_manager_base.h deleted file mode 100644 index 78e5729a5f..0000000000 --- a/chromium_src/chrome/browser/printing/print_view_manager_base.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2013 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ -#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ - -#include "base/memory/ref_counted.h" -#include "components/prefs/pref_member.h" -#include "base/strings/string16.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_contents_user_data.h" -#include "printing/printed_pages_source.h" - -struct PrintHostMsg_DidPrintPage_Params; - -namespace content { -class RenderViewHost; -} - -namespace printing { - -class JobEventDetails; -class MetafilePlayer; -class PrintJob; -class PrintJobWorkerOwner; -class PrintQueriesQueue; - -// Base class for managing the print commands for a WebContents. -class PrintViewManagerBase : public content::NotificationObserver, - public PrintedPagesSource, - public content::WebContentsObserver { - public: - virtual ~PrintViewManagerBase(); - -#if !defined(DISABLE_BASIC_PRINTING) - // Prints the current document immediately. Since the rendering is - // asynchronous, the actual printing will not be completed on the return of - // this function. Returns false if printing is impossible at the moment. - virtual bool PrintNow(bool silent, bool print_background); -#endif // !DISABLE_BASIC_PRINTING - - // PrintedPagesSource implementation. - virtual base::string16 RenderSourceName() override; - - protected: - explicit PrintViewManagerBase(content::WebContents* web_contents); - - // Helper method for Print*Now(). - bool PrintNowInternal(IPC::Message* message); - - // Terminates or cancels the print job if one was pending. - virtual void RenderProcessGone(base::TerminationStatus status) override; - - // content::WebContentsObserver implementation. - virtual bool OnMessageReceived(const IPC::Message& message) override; - - // IPC Message handlers. - virtual void OnPrintingFailed(int cookie); - - private: - // content::NotificationObserver implementation. - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - - // Cancels the print job. - virtual void NavigationStopped() override; - - // IPC Message handlers. - void OnDidGetPrintedPagesCount(int cookie, int number_pages); - void OnDidGetDocumentCookie(int cookie); - void OnDidPrintPage(const PrintHostMsg_DidPrintPage_Params& params); - void OnShowInvalidPrinterSettingsError(); - - // Processes a NOTIFY_PRINT_JOB_EVENT notification. - void OnNotifyPrintJobEvent(const JobEventDetails& event_details); - - // Requests the RenderView to render all the missing pages for the print job. - // No-op if no print job is pending. Returns true if at least one page has - // been requested to the renderer. - bool RenderAllMissingPagesNow(); - - // Quits the current message loop if these conditions hold true: a document is - // loaded and is complete and waiting_for_pages_to_be_rendered_ is true. This - // function is called in DidPrintPage() or on ALL_PAGES_REQUESTED - // notification. The inner message loop is created was created by - // RenderAllMissingPagesNow(). - void ShouldQuitFromInnerMessageLoop(); - - // Creates a new empty print job. It has no settings loaded. If there is - // currently a print job, safely disconnect from it. Returns false if it is - // impossible to safely disconnect from the current print job or it is - // impossible to create a new print job. - bool CreateNewPrintJob(PrintJobWorkerOwner* job); - - // Makes sure the current print_job_ has all its data before continuing, and - // disconnect from it. - void DisconnectFromCurrentPrintJob(); - - // Notify that the printing is done. - void PrintingDone(bool success); - - // Terminates the print job. No-op if no print job has been created. If - // |cancel| is true, cancel it instead of waiting for the job to finish. Will - // call ReleasePrintJob(). - void TerminatePrintJob(bool cancel); - - // Releases print_job_. Correctly deregisters from notifications. No-op if - // no print job has been created. - void ReleasePrintJob(); - - // Runs an inner message loop. It will set inside_inner_message_loop_ to true - // while the blocking inner message loop is running. This is useful in cases - // where the RenderView is about to be destroyed while a printing job isn't - // finished. - bool RunInnerMessageLoop(); - - // In the case of Scripted Printing, where the renderer is controlling the - // control flow, print_job_ is initialized whenever possible. No-op is - // print_job_ is initialized. - bool OpportunisticallyCreatePrintJob(int cookie); - - // Release the PrinterQuery associated with our |cookie_|. - void ReleasePrinterQuery(); - - content::NotificationRegistrar registrar_; - - // Manages the low-level talk to the printer. - scoped_refptr print_job_; - - // Number of pages to print in the print job. - int number_pages_; - - // Indication of success of the print job. - bool printing_succeeded_; - - // Running an inner message loop inside RenderAllMissingPagesNow(). This means - // we are _blocking_ until all the necessary pages have been rendered or the - // print settings are being loaded. - bool inside_inner_message_loop_; - -#if !defined(OS_MACOSX) - // Set to true when OnDidPrintPage() should be expecting the first page. - bool expecting_first_page_; -#endif // OS_MACOSX - - // The document cookie of the current PrinterQuery. - int cookie_; - - // Whether printing is enabled. - bool printing_enabled_; - - scoped_refptr queue_; - - DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBase); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_basic.cc b/chromium_src/chrome/browser/printing/print_view_manager_basic.cc deleted file mode 100644 index d978999a07..0000000000 --- a/chromium_src/chrome/browser/printing/print_view_manager_basic.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 The Chromium 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 "chrome/browser/printing/print_view_manager_basic.h" - -#if defined(OS_ANDROID) -#include "base/file_descriptor_posix.h" -#include "chrome/common/print_messages.h" -#include "printing/printing_context_android.h" -#endif - -DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManagerBasic); - -namespace printing { - -PrintViewManagerBasic::PrintViewManagerBasic(content::WebContents* web_contents) - : PrintViewManagerBase(web_contents) { -} - -PrintViewManagerBasic::~PrintViewManagerBasic() { -} - -#if defined(OS_ANDROID) -void PrintViewManagerBasic::RenderProcessGone(base::TerminationStatus status) { - PrintingContextAndroid::PdfWritingDone(file_descriptor_.fd, false); - file_descriptor_ = base::FileDescriptor(-1, false); - PrintViewManagerBase::RenderProcessGone(status); -} - -void PrintViewManagerBasic::OnPrintingFailed(int cookie) { - PrintingContextAndroid::PdfWritingDone(file_descriptor_.fd, false); - file_descriptor_ = base::FileDescriptor(-1, false); - PrintViewManagerBase::OnPrintingFailed(cookie); -} - -bool PrintViewManagerBasic::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBasic, message) - IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - return handled ? true : PrintViewManagerBase::OnMessageReceived(message); -} -#endif - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_view_manager_basic.h b/chromium_src/chrome/browser/printing/print_view_manager_basic.h deleted file mode 100644 index 553c555cc3..0000000000 --- a/chromium_src/chrome/browser/printing/print_view_manager_basic.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_ -#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_ - -#include "chrome/browser/printing/print_view_manager_base.h" -#include "content/public/browser/web_contents_user_data.h" - -#if defined(OS_ANDROID) -#include "base/file_descriptor_posix.h" -#endif - -namespace printing { - -// Manages the print commands for a WebContents - basic version. -class PrintViewManagerBasic - : public PrintViewManagerBase, - public content::WebContentsUserData { - public: - virtual ~PrintViewManagerBasic(); - -#if defined(OS_ANDROID) - // Sets the file descriptor into which the PDF will be written. - void set_file_descriptor(const base::FileDescriptor& file_descriptor) { - file_descriptor_ = file_descriptor; - } - - // Gets the file descriptor into which the PDF will be written. - base::FileDescriptor file_descriptor() const { return file_descriptor_; } - - // content::WebContentsObserver implementation. - // Terminates or cancels the print job if one was pending. - virtual void RenderProcessGone(base::TerminationStatus status) override; - - // content::WebContentsObserver implementation. - virtual bool OnMessageReceived(const IPC::Message& message) override; -#endif - - private: - explicit PrintViewManagerBasic(content::WebContents* web_contents); - friend class content::WebContentsUserData; - -#if defined(OS_ANDROID) - virtual void OnPrintingFailed(int cookie) override; - - // The file descriptor into which the PDF of the page will be written. - base::FileDescriptor file_descriptor_; -#endif - - DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBasic); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_observer.h b/chromium_src/chrome/browser/printing/print_view_manager_observer.h deleted file mode 100644 index 55cd28c62d..0000000000 --- a/chromium_src/chrome/browser/printing/print_view_manager_observer.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_ -#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_ - -namespace printing { - -// An interface the PrintViewManager uses to notify an observer when the print -// dialog is shown. Register the observer via PrintViewManager::set_observer. -class PrintViewManagerObserver { - public: - // Notifies the observer that the print dialog was shown. - virtual void OnPrintDialogShown() = 0; - - protected: - virtual ~PrintViewManagerObserver() {} -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_ diff --git a/chromium_src/chrome/browser/printing/printer_query.cc b/chromium_src/chrome/browser/printing/printer_query.cc deleted file mode 100644 index 72e2b85f63..0000000000 --- a/chromium_src/chrome/browser/printing/printer_query.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/printing/printer_query.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_restrictions.h" -#include "base/values.h" -#include "chrome/browser/printing/print_job_worker.h" - -namespace printing { - -PrinterQuery::PrinterQuery(int render_process_id, int render_view_id) - : worker_(new PrintJobWorker(render_process_id, render_view_id, this)), - is_print_dialog_box_shown_(false), - cookie_(PrintSettings::NewCookie()), - last_status_(PrintingContext::FAILED) { - DCHECK(base::MessageLoopForIO::IsCurrent()); -} - -PrinterQuery::~PrinterQuery() { - // The job should be finished (or at least canceled) when it is destroyed. - DCHECK(!is_print_dialog_box_shown_); - // If this fires, it is that this pending printer context has leaked. - DCHECK(!worker_.get()); -} - -void PrinterQuery::GetSettingsDone(const PrintSettings& new_settings, - PrintingContext::Result result) { - is_print_dialog_box_shown_ = false; - last_status_ = result; - if (result != PrintingContext::FAILED) { - settings_ = new_settings; - cookie_ = PrintSettings::NewCookie(); - } else { - // Failure. - cookie_ = 0; - } - - if (!callback_.is_null()) { - // This may cause reentrancy like to call StopWorker(). - callback_.Run(); - callback_.Reset(); - } -} - -PrintJobWorker* PrinterQuery::DetachWorker(PrintJobWorkerOwner* new_owner) { - DCHECK(callback_.is_null()); - DCHECK(worker_.get()); - - worker_->SetNewOwner(new_owner); - return worker_.release(); -} - -const PrintSettings& PrinterQuery::settings() const { - return settings_; -} - -int PrinterQuery::cookie() const { - return cookie_; -} - -void PrinterQuery::GetSettings( - GetSettingsAskParam ask_user_for_settings, - int expected_page_count, - bool has_selection, - MarginType margin_type, - const base::Closure& callback) { - DCHECK(RunsTasksOnCurrentThread()); - DCHECK(!is_print_dialog_box_shown_); - - StartWorker(callback); - - // Real work is done in PrintJobWorker::GetSettings(). - is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER; - worker_->PostTask(FROM_HERE, - base::Bind(&PrintJobWorker::GetSettings, - base::Unretained(worker_.get()), - is_print_dialog_box_shown_, - expected_page_count, - has_selection, - margin_type)); -} - -void PrinterQuery::SetSettings(std::unique_ptr new_settings, - const base::Closure& callback) { - StartWorker(callback); - - worker_->PostTask(FROM_HERE, - base::Bind(&PrintJobWorker::SetSettings, - base::Unretained(worker_.get()), - base::Passed(&new_settings))); -} - -void PrinterQuery::StartWorker(const base::Closure& callback) { - DCHECK(callback_.is_null()); - DCHECK(worker_.get()); - - // Lazily create the worker thread. There is one worker thread per print job. - if (!worker_->IsRunning()) - worker_->Start(); - - callback_ = callback; -} - -void PrinterQuery::StopWorker() { - if (worker_.get()) { - // http://crbug.com/66082: We're blocking on the PrinterQuery's worker - // thread. It's not clear to me if this may result in blocking the current - // thread for an unacceptable time. We should probably fix it. - base::ThreadRestrictions::ScopedAllowIO allow_io; - worker_->Stop(); - worker_.reset(); - } -} - -bool PrinterQuery::is_callback_pending() const { - return !callback_.is_null(); -} - -bool PrinterQuery::is_valid() const { - return worker_.get() != NULL; -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/printer_query.h b/chromium_src/chrome/browser/printing/printer_query.h deleted file mode 100644 index d2f017d189..0000000000 --- a/chromium_src/chrome/browser/printing/printer_query.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_ -#define CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_ - -#include - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "chrome/browser/printing/print_job_worker_owner.h" -#include "printing/print_job_constants.h" - -namespace base { -class DictionaryValue; -} - -namespace printing { - -class PrintDestinationInterface; -class PrintJobWorker; - -// Query the printer for settings. -class PrinterQuery : public PrintJobWorkerOwner { - public: - // GetSettings() UI parameter. - enum GetSettingsAskParam { - DEFAULTS, - ASK_USER, - }; - - PrinterQuery(int render_process_id, int render_view_id); - - // PrintJobWorkerOwner implementation. - virtual void GetSettingsDone(const PrintSettings& new_settings, - PrintingContext::Result result) override; - virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) override; - virtual const PrintSettings& settings() const override; - virtual int cookie() const override; - - // Initializes the printing context. It is fine to call this function multiple - // times to reinitialize the settings. |web_contents_observer| can be queried - // to find the owner of the print setting dialog box. It is unused when - // |ask_for_user_settings| is DEFAULTS. - void GetSettings( - GetSettingsAskParam ask_user_for_settings, - int expected_page_count, - bool has_selection, - MarginType margin_type, - const base::Closure& callback); - - // Updates the current settings with |new_settings| dictionary values. - void SetSettings(std::unique_ptr new_settings, - const base::Closure& callback); - - // Stops the worker thread since the client is done with this object. - void StopWorker(); - - // Returns true if a GetSettings() call is pending completion. - bool is_callback_pending() const; - - PrintingContext::Result last_status() const { return last_status_; } - - // Returns if a worker thread is still associated to this instance. - bool is_valid() const; - - private: - virtual ~PrinterQuery(); - - // Lazy create the worker thread. There is one worker thread per print job. - void StartWorker(const base::Closure& callback); - - // All the UI is done in a worker thread because many Win32 print functions - // are blocking and enters a message loop without your consent. There is one - // worker thread per print job. - std::unique_ptr worker_; - - // Cache of the print context settings for access in the UI thread. - PrintSettings settings_; - - // Is the Print... dialog box currently shown. - bool is_print_dialog_box_shown_; - - // Cookie that make this instance unique. - int cookie_; - - // Results from the last GetSettingsDone() callback. - PrintingContext::Result last_status_; - - // Callback waiting to be run. - base::Closure callback_; - - DISALLOW_COPY_AND_ASSIGN(PrinterQuery); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_ diff --git a/chromium_src/chrome/browser/printing/printing_message_filter.cc b/chromium_src/chrome/browser/printing/printing_message_filter.cc deleted file mode 100644 index 31559e2f1a..0000000000 --- a/chromium_src/chrome/browser/printing/printing_message_filter.cc +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/printing/printing_message_filter.h" - -#include - -#include "base/bind.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/printing/print_job_manager.h" -#include "chrome/browser/printing/printer_query.h" -#include "chrome/common/print_messages.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/child_process_host.h" - -#if defined(ENABLE_FULL_PRINTING) -#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" -#endif - -#if defined(OS_CHROMEOS) -#include - -#include - -#include "base/files/file_util.h" -#include "base/lazy_instance.h" -#include "chrome/browser/printing/print_dialog_cloud.h" -#endif - -#if defined(OS_ANDROID) -#include "base/strings/string_number_conversions.h" -#include "chrome/browser/printing/print_view_manager_basic.h" -#include "printing/printing_context_android.h" -#endif - -using content::BrowserThread; - -namespace printing { - -namespace { - -#if defined(OS_CHROMEOS) -typedef std::map SequenceToPathMap; - -struct PrintingSequencePathMap { - SequenceToPathMap map; - int sequence; -}; - -// No locking, only access on the FILE thread. -static base::LazyInstance - g_printing_file_descriptor_map = LAZY_INSTANCE_INITIALIZER; -#endif - -void RenderParamsFromPrintSettings(const PrintSettings& settings, - PrintMsg_Print_Params* params) { - params->page_size = settings.page_setup_device_units().physical_size(); - params->content_size.SetSize( - settings.page_setup_device_units().content_area().width(), - settings.page_setup_device_units().content_area().height()); - params->printable_area.SetRect( - settings.page_setup_device_units().printable_area().x(), - settings.page_setup_device_units().printable_area().y(), - settings.page_setup_device_units().printable_area().width(), - settings.page_setup_device_units().printable_area().height()); - params->margin_top = settings.page_setup_device_units().content_area().y(); - params->margin_left = settings.page_setup_device_units().content_area().x(); - params->dpi = settings.dpi(); - // Currently hardcoded at 72dpi. See PrintSettings' constructor. - params->desired_dpi = settings.desired_dpi(); - // Always use an invalid cookie. - params->document_cookie = 0; - params->selection_only = settings.selection_only(); - params->supports_alpha_blend = settings.supports_alpha_blend(); - params->should_print_backgrounds = settings.should_print_backgrounds(); - params->title = settings.title(); - params->url = settings.url(); -} - -} // namespace - -PrintingMessageFilter::PrintingMessageFilter(int render_process_id) - : BrowserMessageFilter(PrintMsgStart), - render_process_id_(render_process_id), - queue_(g_browser_process->print_job_manager()->queue()) { - DCHECK(queue_.get()); -} - -PrintingMessageFilter::~PrintingMessageFilter() { -} - -void PrintingMessageFilter::OverrideThreadForMessage( - const IPC::Message& message, BrowserThread::ID* thread) { -#if defined(OS_CHROMEOS) - if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || - message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { - *thread = BrowserThread::FILE; - } -#elif defined(OS_ANDROID) - if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || - message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { - *thread = BrowserThread::UI; - } -#endif -} - -bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PrintingMessageFilter, message) -#if defined(OS_WIN) - IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection) -#endif -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) - IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting, - OnAllocateTempFileForPrinting) - IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten, - OnTempFileForPrintingWritten) -#endif - IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings, - OnGetDefaultPrintSettings) - IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint) - IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings, - OnUpdatePrintSettings) -#if defined(ENABLE_FULL_PRINTING) - IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel) -#endif - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -#if defined(OS_WIN) -void PrintingMessageFilter::OnDuplicateSection( - base::SharedMemoryHandle renderer_handle, - base::SharedMemoryHandle* browser_handle) { - // Duplicate the handle in this process right now so the memory is kept alive - // (even if it is not mapped) - base::SharedMemory shared_buf(renderer_handle, true); - shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle); -} -#endif - -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) -void PrintingMessageFilter::OnAllocateTempFileForPrinting( - int render_view_id, - base::FileDescriptor* temp_file_fd, - int* sequence_number) { -#if defined(OS_CHROMEOS) - // TODO(thestig): Use |render_view_id| for Chrome OS. - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - temp_file_fd->fd = *sequence_number = -1; - temp_file_fd->auto_close = false; - - SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; - *sequence_number = g_printing_file_descriptor_map.Get().sequence++; - - base::FilePath path; - if (base::CreateTemporaryFile(&path)) { - int fd = open(path.value().c_str(), O_WRONLY); - if (fd >= 0) { - SequenceToPathMap::iterator it = map->find(*sequence_number); - if (it != map->end()) { - NOTREACHED() << "Sequence number already in use. seq=" << - *sequence_number; - } else { - (*map)[*sequence_number] = path; - temp_file_fd->fd = fd; - temp_file_fd->auto_close = true; - } - } - } -#elif defined(OS_ANDROID) - DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::WebContents* wc = GetWebContentsForRenderView(render_view_id); - if (!wc) - return; - PrintViewManagerBasic* print_view_manager = - PrintViewManagerBasic::FromWebContents(wc); - // The file descriptor is originally created in & passed from the Android - // side, and it will handle the closing. - const base::FileDescriptor& file_descriptor = - print_view_manager->file_descriptor(); - temp_file_fd->fd = file_descriptor.fd; - temp_file_fd->auto_close = false; -#endif -} - -void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, - int sequence_number) { -#if defined(OS_CHROMEOS) - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; - SequenceToPathMap::iterator it = map->find(sequence_number); - if (it == map->end()) { - NOTREACHED() << "Got a sequence that we didn't pass to the " - "renderer: " << sequence_number; - return; - } - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&PrintingMessageFilter::CreatePrintDialogForFile, - this, render_view_id, it->second)); - - // Erase the entry in the map. - map->erase(it); -#elif defined(OS_ANDROID) - DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::WebContents* wc = GetWebContentsForRenderView(render_view_id); - if (!wc) - return; - PrintViewManagerBasic* print_view_manager = - PrintViewManagerBasic::FromWebContents(wc); - const base::FileDescriptor& file_descriptor = - print_view_manager->file_descriptor(); - PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true); - // Invalidate the file descriptor so it doesn't accidentally get reused. - print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false)); -#endif -} -#endif // defined(OS_CHROMEOS) || defined(OS_ANDROID) - -#if defined(OS_CHROMEOS) -void PrintingMessageFilter::CreatePrintDialogForFile( - int render_view_id, - const base::FilePath& path) { - content::WebContents* wc = GetWebContentsForRenderView(render_view_id); - if (!wc) - return; - print_dialog_cloud::CreatePrintDialogForFile( - wc->GetBrowserContext(), - wc->GetTopLevelNativeWindow(), - path, - wc->GetTitle(), - base::string16(), - std::string("application/pdf")); -} -#endif // defined(OS_CHROMEOS) - -content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView( - int render_view_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::RenderViewHost* view = content::RenderViewHost::FromID( - render_process_id_, render_view_id); - return view ? content::WebContents::FromRenderViewHost(view) : NULL; -} - -void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - scoped_refptr printer_query; -#if 0 - if (!profile_io_data_->printing_enabled()->GetValue()) { - // Reply with NULL query. - OnGetDefaultPrintSettingsReply(printer_query, reply_msg); - return; - } -#endif - printer_query = queue_->PopPrinterQuery(0); - if (!printer_query.get()) { - printer_query = - queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id()); - } - - // Loads default settings. This is asynchronous, only the IPC message sender - // will hang until the settings are retrieved. - printer_query->GetSettings( - PrinterQuery::DEFAULTS, - 0, - false, - DEFAULT_MARGINS, - base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, - this, - printer_query, - reply_msg)); -} - -void PrintingMessageFilter::OnGetDefaultPrintSettingsReply( - scoped_refptr printer_query, - IPC::Message* reply_msg) { - PrintMsg_Print_Params params; - if (!printer_query.get() || - printer_query->last_status() != PrintingContext::OK) { - params.Reset(); - } else { - RenderParamsFromPrintSettings(printer_query->settings(), ¶ms); - params.document_cookie = printer_query->cookie(); - } - PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params); - Send(reply_msg); - // If printing was enabled. - if (printer_query.get()) { - // If user hasn't cancelled. - if (printer_query->cookie() && printer_query->settings().dpi()) { - queue_->QueuePrinterQuery(printer_query.get()); - } else { - printer_query->StopWorker(); - } - } -} - -void PrintingMessageFilter::OnScriptedPrint( - const PrintHostMsg_ScriptedPrint_Params& params, - IPC::Message* reply_msg) { - scoped_refptr printer_query = - queue_->PopPrinterQuery(params.cookie); - if (!printer_query.get()) { - printer_query = - queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id()); - } - printer_query->GetSettings( - PrinterQuery::ASK_USER, - params.expected_pages_count, - params.has_selection, - params.margin_type, - base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, - this, - printer_query, - reply_msg)); -} - -void PrintingMessageFilter::OnScriptedPrintReply( - scoped_refptr printer_query, - IPC::Message* reply_msg) { - PrintMsg_PrintPages_Params params; -#if defined(OS_ANDROID) - // We need to save the routing ID here because Send method below deletes the - // |reply_msg| before we can get the routing ID for the Android code. - int routing_id = reply_msg->routing_id(); -#endif - if (printer_query->last_status() != PrintingContext::OK || - !printer_query->settings().dpi()) { - params.Reset(); - } else { - RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); - params.params.document_cookie = printer_query->cookie(); - params.pages = PageRange::GetPages(printer_query->settings().ranges()); - } - PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params); - Send(reply_msg); - if (params.params.dpi && params.params.document_cookie) { -#if defined(OS_ANDROID) - int file_descriptor; - const base::string16& device_name = printer_query->settings().device_name(); - if (base::StringToInt(device_name, &file_descriptor)) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this, - routing_id, file_descriptor)); - } -#endif - queue_->QueuePrinterQuery(printer_query.get()); - } else { - printer_query->StopWorker(); - } -} - -#if defined(OS_ANDROID) -void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::WebContents* wc = GetWebContentsForRenderView(render_view_id); - if (!wc) - return; - PrintViewManagerBasic* print_view_manager = - PrintViewManagerBasic::FromWebContents(wc); - print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false)); -} -#endif - -void PrintingMessageFilter::OnUpdatePrintSettings( - int document_cookie, const base::DictionaryValue& job_settings, - IPC::Message* reply_msg) { - std::unique_ptr new_settings(job_settings.DeepCopy()); - - scoped_refptr printer_query; - printer_query = queue_->PopPrinterQuery(document_cookie); - if (!printer_query.get()) { - int host_id = render_process_id_; - int routing_id = reply_msg->routing_id(); - if (!new_settings->GetInteger(printing::kPreviewInitiatorHostId, - &host_id) || - !new_settings->GetInteger(printing::kPreviewInitiatorRoutingId, - &routing_id)) { - host_id = content::ChildProcessHost::kInvalidUniqueID; - routing_id = content::ChildProcessHost::kInvalidUniqueID; - } - printer_query = queue_->CreatePrinterQuery(host_id, routing_id); - } - printer_query->SetSettings( - std::move(new_settings), - base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this, - printer_query, reply_msg)); -} - -void PrintingMessageFilter::OnUpdatePrintSettingsReply( - scoped_refptr printer_query, - IPC::Message* reply_msg) { - PrintMsg_PrintPages_Params params; - if (!printer_query.get() || - printer_query->last_status() != PrintingContext::OK) { - params.Reset(); - } else { - RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); - params.params.document_cookie = printer_query->cookie(); - params.pages = PageRange::GetPages(printer_query->settings().ranges()); - } - PrintHostMsg_UpdatePrintSettings::WriteReplyParams( - reply_msg, - params, - printer_query.get() && - (printer_query->last_status() == printing::PrintingContext::CANCEL)); - Send(reply_msg); - // If user hasn't cancelled. - if (printer_query.get()) { - if (printer_query->cookie() && printer_query->settings().dpi()) { - queue_->QueuePrinterQuery(printer_query.get()); - } else { - printer_query->StopWorker(); - } - } -} - -} // namespace printing diff --git a/chromium_src/chrome/browser/printing/printing_message_filter.h b/chromium_src/chrome/browser/printing/printing_message_filter.h deleted file mode 100644 index e8536a69c5..0000000000 --- a/chromium_src/chrome/browser/printing/printing_message_filter.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ - -#include - -#include "base/compiler_specific.h" -#include "content/public/browser/browser_message_filter.h" - -#if defined(OS_WIN) -#include "base/memory/shared_memory.h" -#endif - -struct PrintHostMsg_ScriptedPrint_Params; -class Profile; -class ProfileIOData; - -namespace base { -class DictionaryValue; -class FilePath; -} - -namespace content { -class WebContents; -} - -namespace printing { - -class PrintJobManager; -class PrintQueriesQueue; -class PrinterQuery; - -// This class filters out incoming printing related IPC messages for the -// renderer process on the IPC thread. -class PrintingMessageFilter : public content::BrowserMessageFilter { - public: - PrintingMessageFilter(int render_process_id); - - // content::BrowserMessageFilter methods. - void OverrideThreadForMessage( - const IPC::Message& message, - content::BrowserThread::ID* thread) override; - bool OnMessageReceived(const IPC::Message& message) override; - - private: - virtual ~PrintingMessageFilter(); - -#if defined(OS_WIN) - // Used to pass resulting EMF from renderer to browser in printing. - void OnDuplicateSection(base::SharedMemoryHandle renderer_handle, - base::SharedMemoryHandle* browser_handle); -#endif - -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) - // Used to ask the browser allocate a temporary file for the renderer - // to fill in resulting PDF in renderer. - void OnAllocateTempFileForPrinting(int render_view_id, - base::FileDescriptor* temp_file_fd, - int* sequence_number); - void OnTempFileForPrintingWritten(int render_view_id, int sequence_number); -#endif - -#if defined(OS_CHROMEOS) - void CreatePrintDialogForFile(int render_view_id, const base::FilePath& path); -#endif - -#if defined(OS_ANDROID) - // Updates the file descriptor for the PrintViewManagerBasic of a given - // render_view_id. - void UpdateFileDescriptor(int render_view_id, int fd); -#endif - - // Given a render_view_id get the corresponding WebContents. - // Must be called on the UI thread. - content::WebContents* GetWebContentsForRenderView(int render_view_id); - - // GetPrintSettingsForRenderView must be called via PostTask and - // base::Bind. Collapse the settings-specific params into a - // struct to avoid running into issues with too many params - // to base::Bind. - struct GetPrintSettingsForRenderViewParams; - - // Get the default print setting. - void OnGetDefaultPrintSettings(IPC::Message* reply_msg); - void OnGetDefaultPrintSettingsReply(scoped_refptr printer_query, - IPC::Message* reply_msg); - - // The renderer host have to show to the user the print dialog and returns - // the selected print settings. The task is handled by the print worker - // thread and the UI thread. The reply occurs on the IO thread. - void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params, - IPC::Message* reply_msg); - void OnScriptedPrintReply(scoped_refptr printer_query, - IPC::Message* reply_msg); - - // Modify the current print settings based on |job_settings|. The task is - // handled by the print worker thread and the UI thread. The reply occurs on - // the IO thread. - void OnUpdatePrintSettings(int document_cookie, - const base::DictionaryValue& job_settings, - IPC::Message* reply_msg); - void OnUpdatePrintSettingsReply(scoped_refptr printer_query, - IPC::Message* reply_msg); - -#if defined(ENABLE_FULL_PRINTING) - // Check to see if print preview has been cancelled. - void OnCheckForCancel(int32_t preview_ui_id, - int preview_request_id, - bool* cancel); -#endif - - const int render_process_id_; - - scoped_refptr queue_; - - DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter); -}; - -} // namespace printing - -#endif // CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc b/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc deleted file mode 100644 index 311d62192f..0000000000 --- a/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2013 The Chromium 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 "chrome/browser/printing/printing_ui_web_contents_observer.h" - -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/web_contents.h" - -PrintingUIWebContentsObserver::PrintingUIWebContentsObserver( - content::WebContents* web_contents) - : content::WebContentsObserver(web_contents) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); -} - -gfx::NativeView PrintingUIWebContentsObserver::GetParentView() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - return web_contents() ? web_contents()->GetNativeView() : NULL; -} diff --git a/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h b/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h deleted file mode 100644 index de969f5cdb..0000000000 --- a/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Chromium 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 CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ -#define CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ - -#include "content/public/browser/web_contents_observer.h" -#include "ui/gfx/native_widget_types.h" - -// Wrapper used to keep track of the lifetime of a WebContents. -// Lives on the UI thread. -class PrintingUIWebContentsObserver : public content::WebContentsObserver { - public: - explicit PrintingUIWebContentsObserver(content::WebContents* web_contents); - - // Return the parent NativeView of the observed WebContents. - gfx::NativeView GetParentView(); - - private: - DISALLOW_COPY_AND_ASSIGN(PrintingUIWebContentsObserver); -}; - -#endif // CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ diff --git a/chromium_src/chrome/browser/process_singleton_posix.cc b/chromium_src/chrome/browser/process_singleton_posix.cc index bb999fb500..358a25c0be 100644 --- a/chromium_src/chrome/browser/process_singleton_posix.cc +++ b/chromium_src/chrome/browser/process_singleton_posix.cc @@ -222,9 +222,8 @@ int SetupSocketOnly() { int sock = socket(PF_UNIX, SOCK_STREAM, 0); PCHECK(sock >= 0) << "socket() failed"; - int rv = base::SetNonBlocking(sock); - DCHECK_EQ(0, rv) << "Failed to make non-blocking socket."; - rv = SetCloseOnExec(sock); + DCHECK(base::SetNonBlocking(sock)) << "Failed to make non-blocking socket."; + int rv = SetCloseOnExec(sock); DCHECK_EQ(0, rv) << "Failed to set CLOEXEC on socket."; return sock; diff --git a/chromium_src/chrome/browser/profiles/incognito_helpers.cc b/chromium_src/chrome/browser/profiles/incognito_helpers.cc index 875bfd1395..cdd3cafc66 100644 --- a/chromium_src/chrome/browser/profiles/incognito_helpers.cc +++ b/chromium_src/chrome/browser/profiles/incognito_helpers.cc @@ -3,14 +3,13 @@ // found in the LICENSE file. #include "chrome/browser/profiles/incognito_helpers.h" - -#include "brave/browser/brave_browser_context.h" +#include "chrome/browser/profiles/profile.h" namespace chrome { content::BrowserContext* GetBrowserContextRedirectedInIncognito( content::BrowserContext* context) { - return static_cast(context)->original_context(); + return static_cast(context)->GetOriginalProfile(); } content::BrowserContext* GetBrowserContextOwnInstanceInIncognito( diff --git a/chromium_src/chrome/browser/profiles/profile.cc b/chromium_src/chrome/browser/profiles/profile.cc new file mode 100644 index 0000000000..4871dc995d --- /dev/null +++ b/chromium_src/chrome/browser/profiles/profile.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2012 The Chromium 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 "chrome/browser/profiles/profile.h" + +#include + +#include "build/build_config.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/features.h" +#include "chrome/common/pref_names.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/host_zoom_map.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/storage_partition.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" + +#if defined(ENABLE_EXTENSIONS) +#include "extensions/browser/pref_names.h" +#endif + +Profile::Profile(const std::string& partition, bool in_memory, + const base::DictionaryValue& options) + : atom::AtomBrowserContext(partition, in_memory, options), + // restored_last_session_(false), + sent_destroyed_notification_(false) { + // accessibility_pause_level_(0), + // is_guest_profile_(false), + // is_system_profile_(false) { +} + +Profile::~Profile() { +} + +// static +Profile* Profile::FromBrowserContext(content::BrowserContext* browser_context) { + // This is safe; this is the only implementation of the browser context. + return static_cast(browser_context); +} + +// static +Profile* Profile::FromWebUI(content::WebUI* web_ui) { + return FromBrowserContext(web_ui->GetWebContents()->GetBrowserContext()); +} + +ChromeZoomLevelPrefs* Profile::GetZoomLevelPrefs() { + return NULL; +} + +void Profile::MaybeSendDestroyedNotification() { + if (!sent_destroyed_notification_) { + sent_destroyed_notification_ = true; + + NotifyWillBeDestroyed(this); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PROFILE_DESTROYED, + content::Source(this), + content::NotificationService::NoDetails()); + } +} + +bool ProfileCompare::operator()(Profile* a, Profile* b) const { + DCHECK(a && b); + if (a->IsSameProfile(b)) + return false; + return a->GetOriginalProfile() < b->GetOriginalProfile(); +} + +double Profile::GetDefaultZoomLevelForProfile() { + return GetDefaultStoragePartition(this) + ->GetHostZoomMap() + ->GetDefaultZoomLevel(); +} diff --git a/chromium_src/chrome/browser/profiles/profile.h b/chromium_src/chrome/browser/profiles/profile.h index 861d2f589d..f70f262904 100644 --- a/chromium_src/chrome/browser/profiles/profile.h +++ b/chromium_src/chrome/browser/profiles/profile.h @@ -9,152 +9,51 @@ #include -#include "base/containers/hash_tables.h" +#include "atom/browser/atom_browser_context.h" #include "base/logging.h" #include "base/macros.h" #include "build/build_config.h" -// #include "components/domain_reliability/clear_mode.h" +#include "components/syncable_prefs/pref_service_syncable.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" -class ChromeAppCacheService; -class ChromeZoomLevelPrefs; -class DevToolsNetworkControllerHandle; -class ExtensionSpecialStoragePolicy; -class HostContentSettingsMap; -class PrefProxyConfigTracker; -class PrefService; -class PromoCounter; -class ProtocolHandlerRegistry; -class TestingProfile; - -namespace android { -class TabContentsProvider; +namespace autofill { +class AutofillWebDataService; } namespace base { -class SequencedTaskRunner; -class Time; -} - -namespace chrome_browser_net { -class Predictor; -} - -namespace chromeos { -class LibCrosServiceLibraryImpl; -class ResetDefaultProxyConfigServiceTask; +class DictionaryValue; } namespace content { class WebUI; } -namespace storage { -class FileSystemContext; -} - namespace net { -class SSLConfigService; +class URLRequestContextGetter; } namespace user_prefs { class PrefRegistrySyncable; } +class ChromeZoomLevelPrefs; +class PrefChangeRegistrar; + // Instead of adding more members to Profile, consider creating a // KeyedService. See // http://dev.chromium.org/developers/design-documents/profile-architecture -class Profile : public content::BrowserContext { +class Profile : public atom::AtomBrowserContext { public: - enum CreateStatus { - // Profile services were not created due to a local error (e.g., disk full). - CREATE_STATUS_LOCAL_FAIL, - // Profile services were not created due to a remote error (e.g., network - // down during limited-user registration). - CREATE_STATUS_REMOTE_FAIL, - // Profile created but before initializing extensions and promo resources. - CREATE_STATUS_CREATED, - // Profile is created, extensions and promo resources are initialized. - CREATE_STATUS_INITIALIZED, - // Profile creation (supervised-user registration, generally) was canceled - // by the user. - CREATE_STATUS_CANCELED, - MAX_CREATE_STATUS // For histogram display. - }; - - enum CreateMode { - CREATE_MODE_SYNCHRONOUS, - CREATE_MODE_ASYNCHRONOUS - }; - - enum ExitType { - // A normal shutdown. The user clicked exit/closed last window of the - // profile. - EXIT_NORMAL, - - // The exit was the result of the system shutting down. - EXIT_SESSION_ENDED, - - EXIT_CRASHED, - }; - - enum ProfileType { - REGULAR_PROFILE, // Login user's normal profile - INCOGNITO_PROFILE, // Login user's off-the-record profile - GUEST_PROFILE, // Guest session's profile - }; - - class Delegate { - public: - virtual ~Delegate(); - - // Called when creation of the profile is finished. - virtual void OnProfileCreated(Profile* profile, - bool success, - bool is_new_profile) = 0; - }; - - // Key used to bind profile to the widget with which it is associated. - static const char kProfileKey[]; - // Value representing no hosted domain in the kProfileHostedDomain preference. - static const char kNoHostedDomainFound[]; - - Profile(); - ~Profile() override; - - // Profile prefs are registered as soon as the prefs are loaded for the first - // time. - static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - // Create a new profile given a path. If |create_mode| is - // CREATE_MODE_ASYNCHRONOUS then the profile is initialized asynchronously. - static Profile* CreateProfile(const base::FilePath& path, - Delegate* delegate, - CreateMode create_mode); + Profile(const std::string& partition, bool in_memory, + const base::DictionaryValue& options); + ~Profile() override; + static Profile* FromWebUI(content::WebUI* web_ui); // Returns the profile corresponding to the given browser context. static Profile* FromBrowserContext(content::BrowserContext* browser_context); - // Returns the profile corresponding to the given WebUI. - static Profile* FromWebUI(content::WebUI* web_ui); - - // content::BrowserContext implementation ------------------------------------ - - // Typesafe upcast. - virtual TestingProfile* AsTestingProfile(); - - // Returns sequenced task runner where browser context dependent I/O - // operations should be performed. - virtual scoped_refptr GetIOTaskRunner() = 0; - - // Returns the username associated with this profile, if any. In non-test - // implementations, this is usually the Google-services email address. - virtual std::string GetProfileUserName() const = 0; - - // Returns the profile type. - virtual ProfileType GetProfileType() const = 0; - // Return the incognito version of this profile. The returned pointer // is owned by the receiving profile. If the receiving profile is off the // record, the same profile is returned. @@ -164,8 +63,8 @@ class Profile : public content::BrowserContext { // HasOffTheRecordProfile() first. virtual Profile* GetOffTheRecordProfile() = 0; - // Destroys the incognito profile. - virtual void DestroyOffTheRecordProfile() = 0; + // Returns the main request context. + virtual net::URLRequestContextGetter* GetRequestContext() = 0; // True if an incognito profile exists. virtual bool HasOffTheRecordProfile() = 0; @@ -174,187 +73,38 @@ class Profile : public content::BrowserContext { // profile is not incognito. virtual Profile* GetOriginalProfile() = 0; - // Returns whether the profile is supervised (either a legacy supervised - // user or a child account; see SupervisedUserService). - virtual bool IsSupervised() const = 0; - // Returns whether the profile is associated with a child account. - virtual bool IsChild() const = 0; - // Returns whether the profile is a legacy supervised user profile. - virtual bool IsLegacySupervised() const = 0; - - // Accessor. The instance is created upon first access. - virtual ExtensionSpecialStoragePolicy* - GetExtensionSpecialStoragePolicy() = 0; - - // Retrieves a pointer to the PrefService that manages the - // preferences for this user profile. - virtual PrefService* GetPrefs() = 0; - virtual const PrefService* GetPrefs() const = 0; - // Retrieves a pointer to the PrefService that manages the default zoom // level and the per-host zoom levels for this user profile. // TODO(wjmaclean): Remove this when HostZoomMap migrates to StoragePartition. virtual ChromeZoomLevelPrefs* GetZoomLevelPrefs(); - // Retrieves a pointer to the PrefService that manages the preferences - // for OffTheRecord Profiles. This PrefService is lazily created the first - // time that this method is called. - virtual PrefService* GetOffTheRecordPrefs() = 0; - - // Returns the main request context. - virtual net::URLRequestContextGetter* GetRequestContext() = 0; - - // Returns the request context used for extension-related requests. This - // is only used for a separate cookie store currently. - virtual net::URLRequestContextGetter* GetRequestContextForExtensions() = 0; - - // Returns the SSLConfigService for this profile. - virtual net::SSLConfigService* GetSSLConfigService() = 0; - // Return whether 2 profiles are the same. 2 profiles are the same if they // represent the same profile. This can happen if there is pointer equality // or if one profile is the incognito version of another profile (or vice // versa). virtual bool IsSameProfile(Profile* profile) = 0; - // Returns the time the profile was started. This is not the time the profile - // was created, rather it is the time the user started chrome and logged into - // this profile. For the single profile case, this corresponds to the time - // the user started chrome. - virtual base::Time GetStartTime() const = 0; - - // Returns the last directory that was chosen for uploading or opening a file. - virtual base::FilePath last_selected_directory() = 0; - virtual void set_last_selected_directory(const base::FilePath& path) = 0; - -#if defined(OS_CHROMEOS) - enum AppLocaleChangedVia { - // Caused by chrome://settings change. - APP_LOCALE_CHANGED_VIA_SETTINGS, - // Locale has been reverted via LocaleChangeGuard. - APP_LOCALE_CHANGED_VIA_REVERT, - // From login screen. - APP_LOCALE_CHANGED_VIA_LOGIN, - // From login to a public session. - APP_LOCALE_CHANGED_VIA_PUBLIC_SESSION_LOGIN, - // Source unknown. - APP_LOCALE_CHANGED_VIA_UNKNOWN - }; - - // Changes application locale for a profile. - virtual void ChangeAppLocale( - const std::string& locale, AppLocaleChangedVia via) = 0; - - // Called after login. - virtual void OnLogin() = 0; - - // Initializes Chrome OS's preferences. - virtual void InitChromeOSPreferences() = 0; -#endif // defined(OS_CHROMEOS) - - // Returns the helper object that provides the proxy configuration service - // access to the the proxy configuration possibly defined by preferences. - virtual PrefProxyConfigTracker* GetProxyConfigTracker() = 0; - - // Returns the Predictor object used for dns prefetch. - virtual chrome_browser_net::Predictor* GetNetworkPredictor() = 0; - - // Returns the DevToolsNetworkControllerHandle for this profile. - virtual DevToolsNetworkControllerHandle* - GetDevToolsNetworkControllerHandle() = 0; - - // Deletes all network related data since |time|. It deletes transport - // security state since |time| and it also deletes HttpServerProperties data. - // Works asynchronously, however if the |completion| callback is non-null, it - // will be posted on the UI thread once the removal process completes. - // Be aware that theoretically it is possible that |completion| will be - // invoked after the Profile instance has been destroyed. - virtual void ClearNetworkingHistorySince(base::Time time, - const base::Closure& completion) = 0; - - // Returns the home page for this profile. - virtual GURL GetHomePage() = 0; - - // Returns whether or not the profile was created by a version of Chrome - // more recent (or equal to) the one specified. - virtual bool WasCreatedByVersionOrLater(const std::string& version) = 0; - - std::string GetDebugName(); - - // Returns whether it is a guest session. - virtual bool IsGuestSession() const; - - // Returns whether it is a system profile. - virtual bool IsSystemProfile() const; - - // Did the user restore the last session? This is set by SessionRestore. - void set_restored_last_session(bool restored_last_session) { - restored_last_session_ = restored_last_session; - } - bool restored_last_session() const { - return restored_last_session_; - } - - // Sets the ExitType for the profile. This may be invoked multiple times - // during shutdown; only the first such change (the transition from - // EXIT_CRASHED to one of the other values) is written to prefs, any - // later calls are ignored. - // - // NOTE: this is invoked internally on a normal shutdown, but is public so - // that it can be invoked when the user logs out/powers down (WM_ENDSESSION), - // or to handle backgrounding/foregrounding on mobile. - virtual void SetExitType(ExitType exit_type) = 0; - - // Returns how the last session was shutdown. - virtual ExitType GetLastSessionExitType() = 0; - - // Stop sending accessibility events until ResumeAccessibilityEvents(). - // Calls to Pause nest; no events will be sent until the number of - // Resume calls matches the number of Pause calls received. - void PauseAccessibilityEvents() { - accessibility_pause_level_++; - } - - void ResumeAccessibilityEvents() { - DCHECK_GT(accessibility_pause_level_, 0); - accessibility_pause_level_--; - } - - bool ShouldSendAccessibilityEvents() { - return 0 == accessibility_pause_level_; - } - - // Returns whether the profile is new. A profile is new if the browser has - // not been shut down since the profile was created. - bool IsNewProfile(); - - // Checks whether sync is configurable by the user. Returns false if sync is - // disallowed by the command line or controlled by configuration management. - bool IsSyncAllowed(); - // Send NOTIFICATION_PROFILE_DESTROYED for this Profile, if it has not // already been sent. It is necessary because most Profiles are destroyed by // ProfileDestroyer, but in tests, some are not. void MaybeSendDestroyedNotification(); - // Creates an OffTheRecordProfile which points to this Profile. - Profile* CreateOffTheRecordProfile(); - // Convenience method to retrieve the default zoom level for the default // storage partition. double GetDefaultZoomLevelForProfile(); - protected: - void set_is_guest_profile(bool is_guest_profile) { - is_guest_profile_ = is_guest_profile; - } + virtual syncable_prefs::PrefServiceSyncable* GetPrefs() = 0; + + virtual user_prefs::PrefRegistrySyncable* pref_registry() const = 0; + + virtual void AddOverlayPref(const std::string name) = 0; - void set_is_system_profile(bool is_system_profile) { - is_system_profile_ = is_system_profile; - } + virtual scoped_refptr + GetAutofillWebdataService() = 0; + virtual PrefChangeRegistrar* user_prefs_change_registrar() const = 0; private: - bool restored_last_session_; + // bool restored_last_session_; // Used to prevent the notification that this Profile is destroyed from // being sent twice. @@ -364,12 +114,12 @@ class Profile : public content::BrowserContext { // level is zero. PauseAccessibilityEvents and ResumeAccessibilityEvents // increment and decrement the level, respectively, rather than set it to // true or false, so that calls can be nested. - int accessibility_pause_level_; + // int accessibility_pause_level_; - bool is_guest_profile_; + // bool is_guest_profile_; // A non-browsing profile not associated to a user. Sample use: User-Manager. - bool is_system_profile_; + // bool is_system_profile_; DISALLOW_COPY_AND_ASSIGN(Profile); }; diff --git a/chromium_src/chrome/browser/profiles/profile_manager.cc b/chromium_src/chrome/browser/profiles/profile_manager.cc new file mode 100644 index 0000000000..9e220a5cb4 --- /dev/null +++ b/chromium_src/chrome/browser/profiles/profile_manager.cc @@ -0,0 +1,304 @@ +// Copyright (c) 2012 The Chromium 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 "chrome/browser/profiles/profile_manager.h" + +#include + +#include +#include +#include + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/deferred_sequenced_task_runner.h" +#include "base/feature_list.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/trace_event/trace_event.h" +#include "build/build_config.h" +// #include "chrome/browser/bookmarks/bookmark_model_factory.h" +// #include "chrome/browser/bookmarks/startup_task_runner_service_factory.h" +#include "chrome/browser/browser_process.h" +// #include "chrome/browser/chrome_notification_types.h" +// #include "chrome/browser/content_settings/host_content_settings_map_factory.h" +// #include "chrome/browser/download/download_service.h" +// #include "chrome/browser/download/download_service_factory.h" +// #include "chrome/browser/invalidation/profile_invalidation_provider_factory.h" +// #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h" +// #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h" +// #include "chrome/browser/password_manager/password_manager_setting_migrator_service_factory.h" +// #include "chrome/browser/password_manager/password_store_factory.h" +// #include "chrome/browser/prefs/incognito_mode_prefs.h" +// #include "chrome/browser/profiles/bookmark_model_loaded_observer.h" +// #include "chrome/browser/profiles/profile_attributes_entry.h" +// #include "chrome/browser/profiles/profile_attributes_storage.h" +// #include "chrome/browser/profiles/profile_avatar_icon_util.h" +// #include "chrome/browser/profiles/profile_destroyer.h" +// #include "chrome/browser/profiles/profile_info_cache.h" +// #include "chrome/browser/profiles/profile_metrics.h" +#include "chrome/browser/profiles/profiles_state.h" +// #include "chrome/browser/sessions/session_service_factory.h" +// #include "chrome/browser/signin/account_fetcher_service_factory.h" +// #include "chrome/browser/signin/account_reconcilor_factory.h" +// #include "chrome/browser/signin/account_tracker_service_factory.h" +// #include "chrome/browser/signin/cross_device_promo.h" +// #include "chrome/browser/signin/cross_device_promo_factory.h" +// #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h" +// #include "chrome/browser/signin/signin_manager_factory.h" +// #include "chrome/browser/sync/profile_sync_service_factory.h" +// #include "chrome/browser/ui/browser.h" +// #include "chrome/browser/ui/browser_list.h" +// #include "chrome/browser/ui/sync/sync_promo_ui.h" +// #include "chrome/common/chrome_constants.h" +// #include "chrome/common/chrome_paths_internal.h" +// #include "chrome/common/chrome_switches.h" +// #include "chrome/common/logging_chrome.h" +// #include "chrome/common/pref_names.h" +// #include "chrome/common/url_constants.h" +// #include "chrome/grit/generated_resources.h" +// #include "components/bookmarks/browser/bookmark_model.h" +// #include "components/bookmarks/browser/startup_task_runner_service.h" +// #include "components/bookmarks/common/bookmark_pref_names.h" +// #include "components/browser_sync/browser/profile_sync_service.h" +// #include "components/content_settings/core/browser/host_content_settings_map.h" +// #include "components/invalidation/impl/profile_invalidation_provider.h" +// #include "components/invalidation/public/invalidation_service.h" +// #include "components/password_manager/core/browser/password_store.h" +// #include "components/password_manager/sync/browser/password_manager_setting_migrator_service.h" +// #include "components/prefs/pref_service.h" +// #include "components/prefs/scoped_user_pref_update.h" +// #include "components/search_engines/default_search_manager.h" +// #include "components/signin/core/browser/account_fetcher_service.h" +// #include "components/signin/core/browser/account_tracker_service.h" +// #include "components/signin/core/browser/gaia_cookie_manager_service.h" +// #include "components/signin/core/browser/signin_manager.h" +// #include "components/signin/core/common/profile_management_switches.h" +// #include "components/signin/core/common/signin_pref_names.h" +// #include "components/sync/base/stop_source.h" +#include "content/public/browser/browser_thread.h" +// #include "content/public/browser/notification_service.h" +#include "content/public/browser/user_metrics.h" +#include "content/public/common/content_switches.h" +// #include "net/http/http_transaction_factory.h" +// #include "net/url_request/url_request_context.h" +// #include "net/url_request/url_request_context_getter.h" +// #include "net/url_request/url_request_job.h" +// #include "ui/base/l10n/l10n_util.h" + +// #if defined(ENABLE_EXTENSIONS) +// #include "chrome/browser/extensions/extension_service.h" +// #include "extensions/browser/extension_registry.h" +// #include "extensions/browser/extension_system.h" +// #include "extensions/common/extension_set.h" +// #include "extensions/common/manifest.h" +// #endif + +// #if defined(ENABLE_SUPERVISED_USERS) +// #include "chrome/browser/supervised_user/child_accounts/child_account_service.h" +// #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h" +// #include "chrome/browser/supervised_user/supervised_user_service.h" +// #include "chrome/browser/supervised_user/supervised_user_service_factory.h" +// #endif + +// #if defined(OS_ANDROID) +// #include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h" +// #endif + +// #if defined(OS_CHROMEOS) +// #include "chrome/browser/browser_process_platform_part_chromeos.h" +// #include "chrome/browser/chromeos/profiles/profile_helper.h" +// #include "chromeos/chromeos_switches.h" +// #include "chromeos/dbus/cryptohome_client.h" +// #include "chromeos/dbus/dbus_thread_manager.h" +// #include "components/user_manager/user.h" +// #include "components/user_manager/user_manager.h" +// #endif + +// #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS) +// #include "chrome/browser/profiles/profile_statistics.h" +// #include "chrome/browser/profiles/profile_statistics_factory.h" +// #endif + +using base::UserMetricsAction; +using content::BrowserThread; + +ProfileManager::ProfileManager(const base::FilePath& user_data_dir) + : user_data_dir_(user_data_dir) { +} + +ProfileManager::~ProfileManager() { +} + +// static +Profile* ProfileManager::GetLastUsedProfile() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + return profile_manager->GetLastUsedProfile(profile_manager->user_data_dir_); +} + +// static +Profile* ProfileManager::GetLastUsedProfileAllowedByPolicy() { + return GetLastUsedProfile(); +} + +// static +bool ProfileManager::IncognitoModeForced(Profile* profile) { + return false; +} + +// static +std::vector ProfileManager::GetLastOpenedProfiles() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + return profile_manager->GetLastOpenedProfiles( + profile_manager->user_data_dir_); +} + +// static +Profile* ProfileManager::GetPrimaryUserProfile() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + return profile_manager->GetActiveUserOrOffTheRecordProfileFromPath( + profile_manager->user_data_dir()); +} + +// static +Profile* ProfileManager::GetActiveUserProfile() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile* profile = + profile_manager->GetActiveUserOrOffTheRecordProfileFromPath( + profile_manager->user_data_dir()); + // |profile| could be null if the user doesn't have a profile yet and the path + // is on a read-only volume (preventing Chrome from making a new one). + // However, most callers of this function immediately dereference the result + // which would lead to crashes in a variety of call sites. Assert here to + // figure out how common this is. http://crbug.com/383019 + CHECK(profile) << profile_manager->user_data_dir().AsUTF8Unsafe(); + return profile; +} + +Profile* ProfileManager::GetProfile(const base::FilePath& profile_dir) { + TRACE_EVENT0("browser", "ProfileManager::GetProfile"); + + // If the profile is already loaded (e.g., chrome.exe launched twice), just + // return it. + return GetProfileByPath(profile_dir); +} + +bool ProfileManager::IsValidProfile(const void* profile) { + for (ProfilesInfoMap::iterator iter = profiles_info_.begin(); + iter != profiles_info_.end(); ++iter) { + if (iter->second->created) { + Profile* candidate = iter->second->profile.get(); + if (candidate == profile || + (candidate->HasOffTheRecordProfile() && + candidate->GetOffTheRecordProfile() == profile)) { + return true; + } + } + } + return false; +} + +base::FilePath ProfileManager::GetInitialProfileDir() { + base::FilePath relative_profile_dir; + // TODO(mirandac): should not automatically be default profile. + return relative_profile_dir.AppendASCII(""); +} + +Profile* ProfileManager::GetLastUsedProfile( + const base::FilePath& user_data_dir) { + return GetProfile(GetLastUsedProfileDir(user_data_dir)); +} + +base::FilePath ProfileManager::GetLastUsedProfileDir( + const base::FilePath& user_data_dir) { + return user_data_dir.AppendASCII(GetLastUsedProfileName()); +} + +std::string ProfileManager::GetLastUsedProfileName() { + return ""; +} + +std::vector ProfileManager::GetLastOpenedProfiles( + const base::FilePath& user_data_dir) { + std::vector to_return; + to_return.push_back(GetLastUsedProfile(user_data_dir)); + return to_return; +} + +std::vector ProfileManager::GetLoadedProfiles() const { + std::vector profiles; + for (ProfilesInfoMap::const_iterator iter = profiles_info_.begin(); + iter != profiles_info_.end(); ++iter) { + if (iter->second->created) + profiles.push_back(iter->second->profile.get()); + } + return profiles; +} + +Profile* ProfileManager::GetProfileByPathInternal( + const base::FilePath& path) const { + TRACE_EVENT0("browser", "ProfileManager::GetProfileByPathInternal"); + ProfileInfo* profile_info = GetProfileInfoByPath(path); + return profile_info ? profile_info->profile.get() : nullptr; +} + +Profile* ProfileManager::GetProfileByPath(const base::FilePath& path) const { + TRACE_EVENT0("browser", "ProfileManager::GetProfileByPath"); + ProfileInfo* profile_info = GetProfileInfoByPath(path); + return (profile_info && profile_info->created) ? profile_info->profile.get() + : nullptr; +} + +Profile* ProfileManager::GetActiveUserOrOffTheRecordProfileFromPath( + const base::FilePath& user_data_dir) { + base::FilePath default_profile_dir(user_data_dir); + default_profile_dir = default_profile_dir.Append(GetInitialProfileDir()); + return GetProfile(default_profile_dir); +} + +void ProfileManager::AddProfile(Profile* profile) { + DCHECK(profile); + + if (GetProfileByPathInternal(profile->GetPath())) { + NOTREACHED() << "Attempted to add profile with the same path (" << + profile->GetPath().value() << + ") as an already-loaded profile."; + return false; + } + + RegisterProfile(profile, true); +} + +ProfileManager::ProfileInfo* ProfileManager::RegisterProfile( + Profile* profile, + bool created) { + TRACE_EVENT0("browser", "ProfileManager::RegisterProfile"); + ProfileInfo* info = new ProfileInfo(profile, created); + profiles_info_.insert( + std::make_pair(profile->GetPath(), linked_ptr(info))); + return info; +} + +ProfileManager::ProfileInfo* ProfileManager::GetProfileInfoByPath( + const base::FilePath& path) const { + ProfilesInfoMap::const_iterator iter = profiles_info_.find(path); + return (iter == profiles_info_.end()) ? NULL : iter->second.get(); +} + +ProfileManager::ProfileInfo::ProfileInfo( + Profile* profile, + bool created) + : profile(profile), + created(created) { +} + +ProfileManager::ProfileInfo::~ProfileInfo() { + // TODO(bridiver) - should this even be a unique_ptr right now? + profile.release(); +} diff --git a/chromium_src/chrome/browser/profiles/profile_manager.h b/chromium_src/chrome/browser/profiles/profile_manager.h new file mode 100644 index 0000000000..2dc290b4e7 --- /dev/null +++ b/chromium_src/chrome/browser/profiles/profile_manager.h @@ -0,0 +1,164 @@ +// Copyright (c) 2012 The Chromium 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 class keeps track of the currently-active profiles in the runtime. + +#ifndef CHROME_BROWSER_PROFILES_PROFILE_MANAGER_H_ +#define CHROME_BROWSER_PROFILES_PROFILE_MANAGER_H_ + +#include + +#include +#include +#include + +#include "base/containers/hash_tables.h" +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/linked_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/non_thread_safe.h" +#include "build/build_config.h" +#include "chrome/browser/profiles/profile.h" + +class ProfileManager : public base::NonThreadSafe { + public: + explicit ProfileManager(const base::FilePath& user_data_dir); + ~ProfileManager(); + + // Same as instance method but provides the default user_data_dir as well. + // If the Profile is going to be used to open a new window then consider using + // GetLastUsedProfileAllowedByPolicy() instead. + static Profile* GetLastUsedProfile(); + + // Same as GetLastUsedProfile() but returns the incognito Profile if + // incognito mode is forced. This should be used if the last used Profile + // will be used to open new browser windows. + static Profile* GetLastUsedProfileAllowedByPolicy(); + + // Helper function that returns true if incognito mode is forced for |profile| + // (normal mode is not available for browsing). + static bool IncognitoModeForced(Profile* profile); + + // Same as instance method but provides the default user_data_dir as well. + static std::vector GetLastOpenedProfiles(); + + // Get the profile for the user which created the current session. + // Note that in case of a guest account this will return a 'suitable' profile. + // This function is temporary and will soon be moved to ash. As such avoid + // using it at all cost. + // TODO(skuhne): Move into ash's new user management function. + static Profile* GetPrimaryUserProfile(); + + // Get the profile for the currently active user. + // Note that in case of a guest account this will return a 'suitable' profile. + // This function is temporary and will soon be moved to ash. As such avoid + // using it at all cost. + // TODO(skuhne): Move into ash's new user management function. + static Profile* GetActiveUserProfile(); + + // Returns a profile for a specific profile directory within the user data + // dir. This will return an existing profile it had already been created, + // otherwise it will create and manage it. + // Because this method might synchronously create a new profile, it should + // only be called for the initial profile or in tests, where blocking is + // acceptable. Returns null if creation of the new profile fails. + // TODO(bauerb): Migrate calls from other code to GetProfileByPath(), then + // make this method private. + Profile* GetProfile(const base::FilePath& profile_dir); + + // Returns true if the profile pointer is known to point to an existing + // profile. + bool IsValidProfile(const void* profile); + + // Returns the directory where the first created profile is stored, + // relative to the user data directory currently in use. + base::FilePath GetInitialProfileDir(); + + // Get the Profile last used (the Profile to which owns the most recently + // focused window) with this Chrome build. If no signed profile has been + // stored in Local State, hand back the Default profile. + Profile* GetLastUsedProfile(const base::FilePath& user_data_dir); + + // Get the path of the last used profile, or if that's undefined, the default + // profile. + base::FilePath GetLastUsedProfileDir(const base::FilePath& user_data_dir); + + // Get the name of the last used profile, or if that's undefined, the default + // profile. + std::string GetLastUsedProfileName(); + + // Get the Profiles which are currently open, i.e. have open browsers or were + // open the last time Chrome was running. Profiles that fail to initialize are + // skipped. The Profiles appear in the order they were opened. The last used + // profile will be on the list if it is initialized successfully, but its + // index on the list will depend on when it was opened (it is not necessarily + // the last one). + std::vector GetLastOpenedProfiles( + const base::FilePath& user_data_dir); + + // Returns created and fully initialized profiles. Note, profiles order is NOT + // guaranteed to be related with the creation order. + std::vector GetLoadedProfiles() const; + + // If a profile with the given path is currently managed by this object and + // fully initialized, return a pointer to the corresponding Profile object; + // otherwise return null. + Profile* GetProfileByPath(const base::FilePath& path) const; + + void AddProfile(Profile* profile); + + const base::FilePath& user_data_dir() const { return user_data_dir_; } + + private: + + // This struct contains information about profiles which are being loaded or + // were loaded. + struct ProfileInfo { + ProfileInfo(Profile* profile, bool created); + + ~ProfileInfo(); + + std::unique_ptr profile; + // Whether profile has been fully loaded (created and initialized). + bool created; + private: + DISALLOW_COPY_AND_ASSIGN(ProfileInfo); + }; + + // Returns the profile of the active user and / or the off the record profile + // if needed. This adds the profile to the ProfileManager if it doesn't + // already exist. The method will return NULL if the profile doesn't exist + // and we can't create it. + // The profile used can be overridden by using --login-profile on cros. + Profile* GetActiveUserOrOffTheRecordProfileFromPath( + const base::FilePath& user_data_dir); + + // Registers profile with given info. Returns pointer to created ProfileInfo + // entry. + ProfileInfo* RegisterProfile(Profile* profile, bool created); + + // Returns ProfileInfo associated with given |path|, registered earlier with + // RegisterProfile. + ProfileInfo* GetProfileInfoByPath(const base::FilePath& path) const; + + // Returns a registered profile. In contrast to GetProfileByPath(), this will + // also return a profile that is not fully initialized yet, so this method + // should be used carefully. + Profile* GetProfileByPathInternal(const base::FilePath& path) const; + + // The path to the user data directory (DIR_USER_DATA). + const base::FilePath user_data_dir_; + + // Maps profile path to ProfileInfo (if profile has been created). Use + // RegisterProfile() to add into this map. This map owns all loaded profile + // objects in a running instance of Chrome. + typedef std::map > ProfilesInfoMap; + ProfilesInfoMap profiles_info_; + + DISALLOW_COPY_AND_ASSIGN(ProfileManager); +}; + +#endif // CHROME_BROWSER_PROFILES_PROFILE_MANAGER_H_ diff --git a/chromium_src/chrome/browser/profiles/profiles_state.cc b/chromium_src/chrome/browser/profiles/profiles_state.cc new file mode 100644 index 0000000000..d41b5e4e2c --- /dev/null +++ b/chromium_src/chrome/browser/profiles/profiles_state.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Chromium 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 "chrome/browser/profiles/profiles_state.h" + +namespace profiles { + +bool IsMultipleProfilesEnabled() { + return false; +} + +} // namespace profiles diff --git a/chromium_src/chrome/browser/profiles/profiles_state.h b/chromium_src/chrome/browser/profiles/profiles_state.h new file mode 100644 index 0000000000..9d3474e5d2 --- /dev/null +++ b/chromium_src/chrome/browser/profiles/profiles_state.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Chromium 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 CHROME_BROWSER_PROFILES_PROFILES_STATE_H_ +#define CHROME_BROWSER_PROFILES_PROFILES_STATE_H_ + +#include +#include +#include "base/strings/string16.h" +#include "chrome/browser/profiles/avatar_menu.h" + +class Browser; +class PrefRegistrySimple; +class Profile; +class SigninErrorController; +namespace base { class FilePath; } + +namespace profiles { + +// Checks if multiple profiles is enabled. +bool IsMultipleProfilesEnabled(); + +} // namespace profiles + +#endif // CHROME_BROWSER_PROFILES_PROFILES_STATE_H_ diff --git a/chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.cc b/chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.cc deleted file mode 100644 index b6e528e611..0000000000 --- a/chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.cc +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/browser/renderer_host/chrome_extension_message_filter.h" - -#include - -#include "atom/browser/extensions/api/messaging/message_service.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/chrome_notification_types.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/render_process_host.h" -#include "extensions/browser/extension_system.h" -#include "extensions/browser/info_map.h" -#include "extensions/common/api/messaging/message.h" -#include "extensions/common/extension_messages.h" -#include "extensions/common/file_util.h" -#include "extensions/common/manifest_handlers/default_locale_handler.h" -#include "extensions/common/message_bundle.h" - -using content::BrowserThread; - -namespace { - -const uint32_t kFilteredMessageClasses[] = { - ChromeExtensionMsgStart, ExtensionMsgStart, -}; - -} - -ChromeExtensionMessageFilter::ChromeExtensionMessageFilter( - int render_process_id, - content::BrowserContext* browser_context) - : BrowserMessageFilter(kFilteredMessageClasses, - arraysize(kFilteredMessageClasses)), - render_process_id_(render_process_id), - browser_context_(browser_context), - extension_info_map_( - extensions::ExtensionSystem::Get(browser_context)->info_map()) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - notification_registrar_.Add(this, - chrome::NOTIFICATION_PROFILE_DESTROYED, - content::Source(browser_context)); -} - -ChromeExtensionMessageFilter::~ChromeExtensionMessageFilter() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); -} - -bool ChromeExtensionMessageFilter::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension, - OnOpenChannelToExtension) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp, - OnOpenChannelToNativeApp) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenMessagePort, OnOpenMessagePort) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseMessagePort, OnCloseMessagePort) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_PostMessage, OnPostMessage) - IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle, - OnGetExtMessageBundle) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddAPIActionToActivityLog, - OnAddAPIActionToExtensionActivityLog); - IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddDOMActionToActivityLog, - OnAddDOMActionToExtensionActivityLog); - IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddEventToActivityLog, - OnAddEventToExtensionActivityLog); - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - return handled; -} - -void ChromeExtensionMessageFilter::OverrideThreadForMessage( - const IPC::Message& message, BrowserThread::ID* thread) { - switch (message.type()) { - case ExtensionHostMsg_OpenMessagePort::ID: - case ExtensionHostMsg_CloseMessagePort::ID: - case ExtensionHostMsg_PostMessage::ID: - case ExtensionHostMsg_AddAPIActionToActivityLog::ID: - case ExtensionHostMsg_AddDOMActionToActivityLog::ID: - case ExtensionHostMsg_AddEventToActivityLog::ID: - *thread = BrowserThread::UI; - break; - default: - break; - } -} - -void ChromeExtensionMessageFilter::OnDestruct() const { - if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { - delete this; - } else { - BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this); - } -} - -void ChromeExtensionMessageFilter::OnOpenChannelToExtension( - int routing_id, - const ExtensionMsg_ExternalConnectionInfo& info, - const std::string& channel_name, - bool include_tls_channel_id, - int* port_id) { - int port2_id; - extensions::MessageService::AllocatePortIdPair(port_id, &port2_id); - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread, - this, render_process_id_, routing_id, port2_id, info, - channel_name, include_tls_channel_id)); -} - -void ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread( - int source_process_id, int source_routing_id, - int receiver_port_id, - const ExtensionMsg_ExternalConnectionInfo& info, - const std::string& channel_name, - bool include_tls_channel_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (browser_context_) { - extensions::MessageService::Get(browser_context_) - ->OpenChannelToExtension(source_process_id, - source_routing_id, - receiver_port_id, - info.source_id, - info.target_id, - info.source_url, - channel_name, - include_tls_channel_id); - } -} - -void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp( - int routing_id, - const std::string& native_app_name, - int* port_id) { - int port2_id; - extensions::MessageService::AllocatePortIdPair(port_id, &port2_id); - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread, - this, routing_id, port2_id, native_app_name)); -} - -void ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread( - int source_routing_id, - int receiver_port_id, - const std::string& native_app_name) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (browser_context_) { - extensions::MessageService::Get(browser_context_) - ->OpenChannelToNativeApp(render_process_id_, - source_routing_id, - receiver_port_id, - native_app_name); - } -} - -void ChromeExtensionMessageFilter::OnOpenChannelToTab( - int routing_id, - const ExtensionMsg_TabTargetConnectionInfo& info, - const std::string& extension_id, - const std::string& channel_name, - int* port_id) { - int port2_id; - extensions::MessageService::AllocatePortIdPair(port_id, &port2_id); - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread, - this, render_process_id_, routing_id, port2_id, info, - extension_id, channel_name)); -} - -void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread( - int source_process_id, - int source_routing_id, - int receiver_port_id, - const ExtensionMsg_TabTargetConnectionInfo& info, - const std::string& extension_id, - const std::string& channel_name) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (browser_context_) { - extensions::MessageService::Get(browser_context_) - ->OpenChannelToTab(source_process_id, - source_routing_id, - receiver_port_id, - info.tab_id, - info.frame_id, - extension_id, - channel_name); - } -} - -void ChromeExtensionMessageFilter::OnOpenMessagePort(int routing_id, - int port_id) { - if (!browser_context_) - return; - - extensions::MessageService::Get(browser_context_)->OpenPort( - port_id, render_process_id_, routing_id); -} - -void ChromeExtensionMessageFilter::OnCloseMessagePort(int routing_id, - int port_id, - bool force_close) { - if (!browser_context_) - return; - - extensions::MessageService::Get(browser_context_)->ClosePort( - port_id, render_process_id_, routing_id, force_close); -} - -void ChromeExtensionMessageFilter::OnPostMessage( - int port_id, - const extensions::Message& message) { - if (!browser_context_) - return; - - extensions::MessageService::Get(browser_context_)->PostMessage(port_id, message); -} - -void ChromeExtensionMessageFilter::OnGetExtMessageBundle( - const std::string& extension_id, IPC::Message* reply_msg) { - BrowserThread::PostBlockingPoolTask( - FROM_HERE, - base::Bind( - &ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool, - this, extension_id, reply_msg)); -} - -void ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool( - const std::string& extension_id, - IPC::Message* reply_msg) { - DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); - - const extensions::ExtensionSet& extension_set = - extension_info_map_->extensions(); - - std::unique_ptr dictionary_map( - extensions::file_util::LoadMessageBundleSubstitutionMapWithImports( - extension_id, extension_set)); - - ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg, - *dictionary_map); - Send(reply_msg); -} - -void ChromeExtensionMessageFilter::OnAddAPIActionToExtensionActivityLog( - const std::string& extension_id, - const ExtensionHostMsg_APIActionOrEvent_Params& params) { - VLOG(1) << "extension action " << params.api_call << " " << params.arguments; -} - -void ChromeExtensionMessageFilter::OnAddDOMActionToExtensionActivityLog( - const std::string& extension_id, - const ExtensionHostMsg_DOMAction_Params& params) { - VLOG(1) << "dom action " << params.api_call << " " << params.arguments; -} - -void ChromeExtensionMessageFilter::OnAddEventToExtensionActivityLog( - const std::string& extension_id, - const ExtensionHostMsg_APIActionOrEvent_Params& params) { - VLOG(1) << "extension event " << params.api_call << " " << params.arguments; -} - -void ChromeExtensionMessageFilter::Observe( - int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type); - browser_context_ = NULL; -} diff --git a/chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.h b/chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.h deleted file mode 100644 index 6864d12646..0000000000 --- a/chromium_src/chrome/browser/renderer_host/chrome_extension_message_filter.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2014 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_CHROME_EXTENSION_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_RENDERER_HOST_CHROME_EXTENSION_MESSAGE_FILTER_H_ - -#include - -#include "base/macros.h" -#include "base/sequenced_task_runner_helpers.h" -#include "content/public/browser/browser_message_filter.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" - -class GURL; -struct ExtensionMsg_ExternalConnectionInfo; -struct ExtensionMsg_TabTargetConnectionInfo; - -struct ExtensionHostMsg_APIActionOrEvent_Params; -struct ExtensionHostMsg_DOMAction_Params; -struct ExtensionMsg_ExternalConnectionInfo; -struct ExtensionMsg_TabTargetConnectionInfo; - - -namespace base { -class FilePath; -} - -namespace content { -class BrowserContext; -} - -namespace extensions { -class InfoMap; -struct Message; -} - -// This class filters out incoming Chrome-specific IPC messages from the -// extension process on the IPC thread. -class ChromeExtensionMessageFilter : public content::BrowserMessageFilter, - public content::NotificationObserver { - public: - ChromeExtensionMessageFilter(int render_process_id, - content::BrowserContext* browser_context); - - // content::BrowserMessageFilter methods: - bool OnMessageReceived(const IPC::Message& message) override; - void OverrideThreadForMessage(const IPC::Message& message, - content::BrowserThread::ID* thread) override; - void OnDestruct() const override; - - private: - friend class content::BrowserThread; - friend class base::DeleteHelper; - - ~ChromeExtensionMessageFilter() override; - - void OnOpenChannelToExtension(int routing_id, - const ExtensionMsg_ExternalConnectionInfo& info, - const std::string& channel_name, - bool include_tls_channel_id, - int* port_id); - void OpenChannelToExtensionOnUIThread( - int source_process_id, - int source_routing_id, - int receiver_port_id, - const ExtensionMsg_ExternalConnectionInfo& info, - const std::string& channel_name, - bool include_tls_channel_id); - void OnOpenChannelToNativeApp(int routing_id, - const std::string& native_app_name, - int* port_id); - void OpenChannelToNativeAppOnUIThread(int source_routing_id, - int receiver_port_id, - const std::string& native_app_name); - void OnOpenChannelToTab(int routing_id, - const ExtensionMsg_TabTargetConnectionInfo& info, - const std::string& extension_id, - const std::string& channel_name, - int* port_id); - void OpenChannelToTabOnUIThread( - int source_process_id, - int source_routing_id, - int receiver_port_id, - const ExtensionMsg_TabTargetConnectionInfo& info, - const std::string& extension_id, - const std::string& channel_name); - void OnOpenMessagePort(int routing_id, int port_id); - void OnCloseMessagePort(int routing_id, int port_id, bool force_close); - void OnPostMessage(int port_id, const extensions::Message& message); - void OnPostMessageOnUIThread(int port_id, const extensions::Message& message); - void OnGetExtMessageBundle(const std::string& extension_id, - IPC::Message* reply_msg); - void OnGetExtMessageBundleOnBlockingPool( - const std::string& extension_id, - IPC::Message* reply_msg); - - void OnAddAPIActionToExtensionActivityLog( - const std::string& extension_id, - const ExtensionHostMsg_APIActionOrEvent_Params& params); - void OnAddDOMActionToExtensionActivityLog( - const std::string& extension_id, - const ExtensionHostMsg_DOMAction_Params& params); - void OnAddEventToExtensionActivityLog( - const std::string& extension_id, - const ExtensionHostMsg_APIActionOrEvent_Params& params); - - - // content::NotificationObserver implementation. - void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - - const int render_process_id_; - - // The BrowserContext associated with our renderer process. This should only - // be accessed on the UI thread! Furthermore since this class is refcounted it - // may outlive |browser_context_|, so make sure to NULL check if in doubt; async - // calls and the like. - content::BrowserContext* browser_context_; - - scoped_refptr extension_info_map_; - - content::NotificationRegistrar notification_registrar_; - - DISALLOW_COPY_AND_ASSIGN(ChromeExtensionMessageFilter); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_CHROME_EXTENSION_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc b/chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc deleted file mode 100644 index 5364aa0b88..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h" - -#include "build/build_config.h" -#include "chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h" -#include "chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h" -#include "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h" -#include "chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h" -#include "chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h" -#include "content/public/browser/browser_ppapi_host.h" -#include "ppapi/host/message_filter_host.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/host/resource_host.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/shared_impl/ppapi_permissions.h" - -using ppapi::host::MessageFilterHost; -using ppapi::host::ResourceHost; -using ppapi::host::ResourceMessageFilter; - -namespace chrome { - -ChromeBrowserPepperHostFactory::ChromeBrowserPepperHostFactory( - content::BrowserPpapiHost* host) - : host_(host) {} - -ChromeBrowserPepperHostFactory::~ChromeBrowserPepperHostFactory() {} - -std::unique_ptr ChromeBrowserPepperHostFactory::CreateResourceHost( - ppapi::host::PpapiHost* host, - PP_Resource resource, - PP_Instance instance, - const IPC::Message& message) { - DCHECK(host == host_->GetPpapiHost()); - - // Make sure the plugin is giving us a valid instance for this resource. - if (!host_->IsValidInstance(instance)) - return std::unique_ptr(); - - // Private interfaces. - if (host_->GetPpapiHost()->permissions().HasPermission( - ppapi::PERMISSION_PRIVATE)) { - switch (message.type()) { - case PpapiHostMsg_Broker_Create::ID: { - scoped_refptr broker_filter( - new PepperBrokerMessageFilter(instance, host_)); - return std::unique_ptr(new MessageFilterHost( - host_->GetPpapiHost(), instance, resource, broker_filter)); - } - } - } - - // Flash interfaces. - if (host_->GetPpapiHost()->permissions().HasPermission( - ppapi::PERMISSION_FLASH)) { - switch (message.type()) { - case PpapiHostMsg_Flash_Create::ID: - return std::unique_ptr( - new PepperFlashBrowserHost(host_, instance, resource)); - case PpapiHostMsg_FlashClipboard_Create::ID: { - scoped_refptr clipboard_filter( - new PepperFlashClipboardMessageFilter); - return std::unique_ptr(new MessageFilterHost( - host_->GetPpapiHost(), instance, resource, clipboard_filter)); - } - case PpapiHostMsg_FlashDRM_Create::ID: - return std::unique_ptr( - new chrome::PepperFlashDRMHost(host_, instance, resource)); - } - } - - // Permissions for the following interfaces will be checked at the - // time of the corresponding instance's methods calls (because - // permission check can be performed only on the UI - // thread). Currently these interfaces are available only for - // whitelisted apps which may not have access to the other private - // interfaces. - if (message.type() == PpapiHostMsg_IsolatedFileSystem_Create::ID) { - PepperIsolatedFileSystemMessageFilter* isolated_fs_filter = - PepperIsolatedFileSystemMessageFilter::Create(instance, host_); - if (!isolated_fs_filter) - return std::unique_ptr(); - return std::unique_ptr( - new MessageFilterHost(host, instance, resource, isolated_fs_filter)); - } - - return std::unique_ptr(); -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h b/chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h deleted file mode 100644 index 84385140ce..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_CHROME_BROWSER_PEPPER_HOST_FACTORY_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_CHROME_BROWSER_PEPPER_HOST_FACTORY_H_ - -#include "base/macros.h" -#include "ppapi/host/host_factory.h" - -namespace content { -class BrowserPpapiHost; -} // namespace content - -namespace chrome { - -class ChromeBrowserPepperHostFactory : public ppapi::host::HostFactory { - public: - // Non-owning pointer to the filter must outlive this class. - explicit ChromeBrowserPepperHostFactory(content::BrowserPpapiHost* host); - ~ChromeBrowserPepperHostFactory() override; - - std::unique_ptr CreateResourceHost( - ppapi::host::PpapiHost* host, - PP_Resource resource, - PP_Instance instance, - const IPC::Message& message) override; - - private: - // Non-owning pointer. - content::BrowserPpapiHost* host_; - - DISALLOW_COPY_AND_ASSIGN(ChromeBrowserPepperHostFactory); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_CHROME_BROWSER_PEPPER_HOST_FACTORY_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.h b/chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.h deleted file mode 100644 index cc10ee8578..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2014 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_MONITOR_FINDER_MAC_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_MONITOR_FINDER_MAC_H_ - -#include -#include - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" - -namespace chrome { - -// MonitorFinder maps a RenderFrameHost to the display ID on which the widget -// is painting. This class operates on the IO thread while the RenderFrameHost -// is on the UI thread, so the value returned by GetMonitor() may be 0 until -// the information can be retrieved asynchronously. -class MonitorFinder : public base::RefCountedThreadSafe { - public: - MonitorFinder(int process_id, int render_frame_id); - - // Gets the native display ID for the tuple. - int64_t GetMonitor(); - - // Checks if the given |monitor_id| represents a built-in display. - static bool IsMonitorBuiltIn(int64_t monitor_id); - - private: - friend class base::RefCountedThreadSafe; - ~MonitorFinder(); - - // Method run on the UI thread to get the display information. - void FetchMonitorFromWidget(); - - const int process_id_; - const int render_frame_id_; - - base::Lock mutex_; // Protects the two members below. - // Whether one request to FetchMonitorFromWidget() has been made already. - bool request_sent_; - // The native display ID for the RenderFrameHost. - CGDirectDisplayID display_id_; - - DISALLOW_COPY_AND_ASSIGN(MonitorFinder); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_MONITOR_FINDER_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.mm b/chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.mm deleted file mode 100644 index 31f6cfd410..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/monitor_finder_mac.mm +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/browser/renderer_host/pepper/monitor_finder_mac.h" - -#import - -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_frame_host.h" - -namespace chrome { - -MonitorFinder::MonitorFinder(int process_id, int render_frame_id) - : process_id_(process_id), - render_frame_id_(render_frame_id), - request_sent_(false), - display_id_(kCGNullDirectDisplay) {} - -MonitorFinder::~MonitorFinder() {} - -int64_t MonitorFinder::GetMonitor() { - { - // The plugin may call this method several times, so avoid spamming the UI - // thread with requests by only allowing one outstanding request at a time. - base::AutoLock lock(mutex_); - if (request_sent_) - return display_id_; - request_sent_ = true; - } - - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::Bind(&MonitorFinder::FetchMonitorFromWidget, this)); - return display_id_; -} - -// static -bool MonitorFinder::IsMonitorBuiltIn(int64_t display_id) { - return CGDisplayIsBuiltin(display_id); -} - -void MonitorFinder::FetchMonitorFromWidget() { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - content::RenderFrameHost* rfh = - content::RenderFrameHost::FromID(process_id_, render_frame_id_); - if (!rfh) - return; - - gfx::NativeView native_view = rfh->GetNativeView(); - NSWindow* window = [native_view window]; - NSScreen* screen = [window screen]; - CGDirectDisplayID display_id = - [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue]; - - base::AutoLock lock(mutex_); - request_sent_ = false; - display_id_ = display_id; -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc b/chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc deleted file mode 100644 index c77c8f00f8..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h" - -#include - -#include "content/public/browser/browser_ppapi_host.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_process_host.h" -#include "ipc/ipc_message_macros.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "url/gurl.h" - -using content::BrowserPpapiHost; -using content::BrowserThread; - -namespace chrome { - -PepperBrokerMessageFilter::PepperBrokerMessageFilter(PP_Instance instance, - BrowserPpapiHost* host) - : document_url_(host->GetDocumentURLForInstance(instance)) { - int unused; - host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused); -} - -PepperBrokerMessageFilter::~PepperBrokerMessageFilter() {} - -scoped_refptr -PepperBrokerMessageFilter::OverrideTaskRunnerForMessage( - const IPC::Message& message) { - return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); -} - -int32_t PepperBrokerMessageFilter::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperBrokerMessageFilter, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Broker_IsAllowed, - OnIsAllowed) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperBrokerMessageFilter::OnIsAllowed( - ppapi::host::HostMessageContext* context) { - return PP_OK; -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h b/chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h deleted file mode 100644 index 44627c6a35..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_BROKER_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_BROKER_MESSAGE_FILTER_H_ - -#include "base/compiler_specific.h" -#include "ppapi/c/pp_instance.h" -#include "ppapi/host/resource_message_filter.h" -#include "url/gurl.h" - -namespace content { -class BrowserPpapiHost; -} - -namespace ppapi { -namespace host { -struct HostMessageContext; -} -} - -namespace chrome { - -// This filter handles messages for the PepperBrokerHost on the UI thread. -class PepperBrokerMessageFilter : public ppapi::host::ResourceMessageFilter { - public: - PepperBrokerMessageFilter(PP_Instance instance, - content::BrowserPpapiHost* host); - - private: - ~PepperBrokerMessageFilter() override; - - // ppapi::host::ResourceMessageFilter overrides. - scoped_refptr OverrideTaskRunnerForMessage( - const IPC::Message& message) override; - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - int32_t OnIsAllowed(ppapi::host::HostMessageContext* context); - - int render_process_id_; - GURL document_url_; - - DISALLOW_COPY_AND_ASSIGN(PepperBrokerMessageFilter); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_BROKER_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc b/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc deleted file mode 100644 index 96fe8dc8e3..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h" - -#include "base/time/time.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_ppapi_host.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_process_host.h" -#include "ipc/ipc_message_macros.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/c/private/ppb_flash.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/proxy/resource_message_params.h" -#include "ppapi/shared_impl/time_conversion.h" -#include "url/gurl.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_MACOSX) -#include -#endif - -using content::BrowserPpapiHost; -using content::BrowserThread; - -namespace chrome { - -PepperFlashBrowserHost::PepperFlashBrowserHost(BrowserPpapiHost* host, - PP_Instance instance, - PP_Resource resource) - : ResourceHost(host->GetPpapiHost(), instance, resource), - host_(host), - weak_factory_(this) { - int unused; - host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused); -} - -PepperFlashBrowserHost::~PepperFlashBrowserHost() {} - -int32_t PepperFlashBrowserHost::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashBrowserHost, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Flash_UpdateActivity, - OnUpdateActivity) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_GetLocalTimeZoneOffset, - OnGetLocalTimeZoneOffset) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( - PpapiHostMsg_Flash_GetLocalDataRestrictions, OnGetLocalDataRestrictions) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperFlashBrowserHost::OnUpdateActivity( - ppapi::host::HostMessageContext* host_context) { -#if defined(OS_WIN) - // Reading then writing back the same value to the screensaver timeout system - // setting resets the countdown which prevents the screensaver from turning - // on "for a while". As long as the plugin pings us with this message faster - // than the screensaver timeout, it won't go on. - int value = 0; - if (SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &value, 0)) - SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, value, NULL, 0); -#elif defined(OS_MACOSX) -// UpdateSystemActivity(OverallAct); -#else -// TODO(brettw) implement this for other platforms. -#endif - return PP_OK; -} - -int32_t PepperFlashBrowserHost::OnGetLocalTimeZoneOffset( - ppapi::host::HostMessageContext* host_context, - const base::Time& t) { - // The reason for this processing being in the browser process is that on - // Linux, the localtime calls require filesystem access prohibited by the - // sandbox. - host_context->reply_msg = PpapiPluginMsg_Flash_GetLocalTimeZoneOffsetReply( - ppapi::PPGetLocalTimeZoneOffset(t)); - return PP_OK; -} - -int32_t PepperFlashBrowserHost::OnGetLocalDataRestrictions( - ppapi::host::HostMessageContext* context) { - // Getting the Flash LSO settings requires using the CookieSettings which - // belong to the profile which lives on the UI thread. We lazily initialize - // |cookie_settings_| by grabbing the reference from the UI thread and then - // call |GetLocalDataRestrictions| with it. - GURL document_url = host_->GetDocumentURLForInstance(pp_instance()); - GURL plugin_url = host_->GetPluginURLForInstance(pp_instance()); - GetLocalDataRestrictions(context->MakeReplyMessageContext(), - document_url, - plugin_url); - return PP_OK_COMPLETIONPENDING; -} - -void PepperFlashBrowserHost::GetLocalDataRestrictions( - ppapi::host::ReplyMessageContext reply_context, - const GURL& document_url, - const GURL& plugin_url) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - PP_FlashLSORestrictions restrictions = PP_FLASHLSORESTRICTIONS_NONE; - SendReply(reply_context, - PpapiPluginMsg_Flash_GetLocalDataRestrictionsReply( - static_cast(restrictions))); -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h b/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h deleted file mode 100644 index 40a03a1ff5..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_BROWSER_HOST_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_BROWSER_HOST_H_ - -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/resource_host.h" - -namespace base { -class Time; -} - -namespace content { -class BrowserPpapiHost; -class ResourceContext; -} - -class GURL; - -namespace chrome { - -class PepperFlashBrowserHost : public ppapi::host::ResourceHost { - public: - PepperFlashBrowserHost(content::BrowserPpapiHost* host, - PP_Instance instance, - PP_Resource resource); - ~PepperFlashBrowserHost() override; - - // ppapi::host::ResourceHost override. - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - int32_t OnUpdateActivity(ppapi::host::HostMessageContext* host_context); - int32_t OnGetLocalTimeZoneOffset( - ppapi::host::HostMessageContext* host_context, - const base::Time& t); - int32_t OnGetLocalDataRestrictions(ppapi::host::HostMessageContext* context); - - void GetLocalDataRestrictions(ppapi::host::ReplyMessageContext reply_context, - const GURL& document_url, - const GURL& plugin_url); - - content::BrowserPpapiHost* host_; - int render_process_id_; - // For fetching the Flash LSO settings. - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(PepperFlashBrowserHost); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_BROWSER_HOST_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.cc b/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.cc deleted file mode 100644 index 4e66d772ec..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.cc +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h" - -#include - -#include "base/pickle.h" -#include "base/strings/utf_string_conversions.h" -#include "content/public/browser/browser_thread.h" -#include "ipc/ipc_message.h" -#include "ipc/ipc_message_macros.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/c/private/ppb_flash_clipboard.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/proxy/resource_message_params.h" -#include "ui/base/clipboard/scoped_clipboard_writer.h" - -using content::BrowserThread; - -namespace chrome { - -namespace { - -const size_t kMaxClipboardWriteSize = 1000000; - -ui::ClipboardType ConvertClipboardType(uint32_t type) { - switch (type) { - case PP_FLASH_CLIPBOARD_TYPE_STANDARD: - return ui::CLIPBOARD_TYPE_COPY_PASTE; - case PP_FLASH_CLIPBOARD_TYPE_SELECTION: - return ui::CLIPBOARD_TYPE_SELECTION; - } - NOTREACHED(); - return ui::CLIPBOARD_TYPE_COPY_PASTE; -} - -// Functions to pack/unpack custom data from a pickle. See the header file for -// more detail on custom formats in Pepper. -// TODO(raymes): Currently pepper custom formats are stored in their own -// native format type. However we should be able to store them in the same way -// as "Web Custom" formats are. This would allow clipboard data to be shared -// between pepper applications and web applications. However currently web apps -// assume all data that is placed on the clipboard is UTF16 and pepper allows -// arbitrary data so this change would require some reworking of the chrome -// clipboard interface for custom data. -bool JumpToFormatInPickle(const base::string16& format, - base::PickleIterator* iter) { - uint32_t size = 0; - if (!iter->ReadUInt32(&size)) - return false; - for (uint32_t i = 0; i < size; ++i) { - base::string16 stored_format; - if (!iter->ReadString16(&stored_format)) - return false; - if (stored_format == format) - return true; - int skip_length; - if (!iter->ReadLength(&skip_length)) - return false; - if (!iter->SkipBytes(skip_length)) - return false; - } - return false; -} - -bool IsFormatAvailableInPickle(const base::string16& format, - const base::Pickle& pickle) { - base::PickleIterator iter(pickle); - return JumpToFormatInPickle(format, &iter); -} - -std::string ReadDataFromPickle(const base::string16& format, - const base::Pickle& pickle) { - std::string result; - base::PickleIterator iter(pickle); - if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result)) - return std::string(); - return result; -} - -bool WriteDataToPickle(const std::map& data, - base::Pickle* pickle) { - pickle->WriteUInt32(data.size()); - for (std::map::const_iterator it = data.begin(); - it != data.end(); - ++it) { - if (!pickle->WriteString16(it->first)) - return false; - if (!pickle->WriteString(it->second)) - return false; - } - return true; -} - -} // namespace - -PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {} - -PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {} - -scoped_refptr -PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage( - const IPC::Message& msg) { - // Clipboard writes should always occur on the UI thread due to the - // restrictions of various platform APIs. In general, the clipboard is not - // thread-safe, so all clipboard calls should be serviced from the UI thread. - if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID) - return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); - -// Windows needs clipboard reads to be serviced from the IO thread because -// these are sync IPCs which can result in deadlocks with plugins if serviced -// from the UI thread. Note that Windows clipboard calls ARE thread-safe so it -// is ok for reads and writes to be serviced from different threads. -#if !defined(OS_WIN) - return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); -#else - return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); -#endif -} - -int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( - PpapiHostMsg_FlashClipboard_RegisterCustomFormat, - OnMsgRegisterCustomFormat) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( - PpapiHostMsg_FlashClipboard_IsFormatAvailable, OnMsgIsFormatAvailable) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_ReadData, - OnMsgReadData) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_WriteData, - OnMsgWriteData) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( - PpapiHostMsg_FlashClipboard_GetSequenceNumber, OnMsgGetSequenceNumber) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat( - ppapi::host::HostMessageContext* host_context, - const std::string& format_name) { - uint32_t format = custom_formats_.RegisterFormat(format_name); - if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID) - return PP_ERROR_FAILED; - host_context->reply_msg = - PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format); - return PP_OK; -} - -int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable( - ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type, - uint32_t format) { - if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { - NOTIMPLEMENTED(); - return PP_ERROR_FAILED; - } - - ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - ui::ClipboardType type = ConvertClipboardType(clipboard_type); - bool available = false; - switch (format) { - case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { - bool plain = clipboard->IsFormatAvailable( - ui::Clipboard::GetPlainTextFormatType(), type); - bool plainw = clipboard->IsFormatAvailable( - ui::Clipboard::GetPlainTextWFormatType(), type); - available = plain || plainw; - break; - } - case PP_FLASH_CLIPBOARD_FORMAT_HTML: - available = clipboard->IsFormatAvailable( - ui::Clipboard::GetHtmlFormatType(), type); - break; - case PP_FLASH_CLIPBOARD_FORMAT_RTF: - available = - clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type); - break; - case PP_FLASH_CLIPBOARD_FORMAT_INVALID: - break; - default: - if (custom_formats_.IsFormatRegistered(format)) { - std::string format_name = custom_formats_.GetFormatName(format); - std::string clipboard_data; - clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(), - &clipboard_data); - base::Pickle pickle(clipboard_data.data(), clipboard_data.size()); - available = - IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle); - } - break; - } - - return available ? PP_OK : PP_ERROR_FAILED; -} - -int32_t PepperFlashClipboardMessageFilter::OnMsgReadData( - ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type, - uint32_t format) { - if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { - NOTIMPLEMENTED(); - return PP_ERROR_FAILED; - } - - ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - ui::ClipboardType type = ConvertClipboardType(clipboard_type); - std::string clipboard_string; - int32_t result = PP_ERROR_FAILED; - switch (format) { - case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { - if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(), - type)) { - base::string16 text; - clipboard->ReadText(type, &text); - if (!text.empty()) { - result = PP_OK; - clipboard_string = base::UTF16ToUTF8(text); - break; - } - } - // If the PlainTextW format isn't available or is empty, take the - // ASCII text format. - if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), - type)) { - result = PP_OK; - clipboard->ReadAsciiText(type, &clipboard_string); - } - break; - } - case PP_FLASH_CLIPBOARD_FORMAT_HTML: { - if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(), - type)) { - break; - } - - base::string16 html; - std::string url; - uint32_t fragment_start; - uint32_t fragment_end; - clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end); - result = PP_OK; - clipboard_string = base::UTF16ToUTF8( - html.substr(fragment_start, fragment_end - fragment_start)); - break; - } - case PP_FLASH_CLIPBOARD_FORMAT_RTF: { - if (!clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), - type)) { - break; - } - result = PP_OK; - clipboard->ReadRTF(type, &clipboard_string); - break; - } - case PP_FLASH_CLIPBOARD_FORMAT_INVALID: - break; - default: { - if (custom_formats_.IsFormatRegistered(format)) { - base::string16 format_name = - base::UTF8ToUTF16(custom_formats_.GetFormatName(format)); - std::string clipboard_data; - clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(), - &clipboard_data); - base::Pickle pickle(clipboard_data.data(), clipboard_data.size()); - if (IsFormatAvailableInPickle(format_name, pickle)) { - result = PP_OK; - clipboard_string = ReadDataFromPickle(format_name, pickle); - } - } - break; - } - } - - if (result == PP_OK) { - host_context->reply_msg = - PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string); - } - return result; -} - -int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData( - ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type, - const std::vector& formats, - const std::vector& data) { - if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { - NOTIMPLEMENTED(); - return PP_ERROR_FAILED; - } - if (formats.size() != data.size()) - return PP_ERROR_FAILED; - - ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - ui::ClipboardType type = ConvertClipboardType(clipboard_type); - // If no formats are passed in clear the clipboard. - if (formats.size() == 0) { - clipboard->Clear(type); - return PP_OK; - } - - ui::ScopedClipboardWriter scw(type); - std::map custom_data_map; - int32_t res = PP_OK; - for (uint32_t i = 0; i < formats.size(); ++i) { - if (data[i].length() > kMaxClipboardWriteSize) { - res = PP_ERROR_NOSPACE; - break; - } - - switch (formats[i]) { - case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: - scw.WriteText(base::UTF8ToUTF16(data[i])); - break; - case PP_FLASH_CLIPBOARD_FORMAT_HTML: - scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string()); - break; - case PP_FLASH_CLIPBOARD_FORMAT_RTF: - scw.WriteRTF(data[i]); - break; - case PP_FLASH_CLIPBOARD_FORMAT_INVALID: - res = PP_ERROR_BADARGUMENT; - break; - default: - if (custom_formats_.IsFormatRegistered(formats[i])) { - std::string format_name = custom_formats_.GetFormatName(formats[i]); - custom_data_map[base::UTF8ToUTF16(format_name)] = data[i]; - } else { - // Invalid format. - res = PP_ERROR_BADARGUMENT; - break; - } - } - - if (res != PP_OK) - break; - } - - if (custom_data_map.size() > 0) { - base::Pickle pickle; - if (WriteDataToPickle(custom_data_map, &pickle)) { - scw.WritePickledData(pickle, - ui::Clipboard::GetPepperCustomDataFormatType()); - } else { - res = PP_ERROR_BADARGUMENT; - } - } - - if (res != PP_OK) { - // Need to clear the objects so nothing is written. - scw.Reset(); - } - - return res; -} - -int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber( - ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type) { - if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { - NOTIMPLEMENTED(); - return PP_ERROR_FAILED; - } - - ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - ui::ClipboardType type = ConvertClipboardType(clipboard_type); - int64_t sequence_number = clipboard->GetSequenceNumber(type); - host_context->reply_msg = - PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number); - return PP_OK; -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h b/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h deleted file mode 100644 index 4c146dd5da..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_CLIPBOARD_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_CLIPBOARD_MESSAGE_FILTER_H_ - -#include -#include - -#include "ppapi/host/resource_message_filter.h" -#include "ppapi/shared_impl/flash_clipboard_format_registry.h" - -namespace ppapi { -namespace host { -struct HostMessageContext; -} -} - -namespace ui { -class ScopedClipboardWriter; -} - -namespace chrome { - -// Resource message filter for accessing the clipboard in Pepper. Pepper -// supports reading/writing custom formats from the clipboard. Currently, all -// custom formats that are read/written from the clipboard through pepper are -// stored in a single real clipboard format (in the same way the "web custom" -// clipboard formats are). This is done so that we don't have to have use real -// clipboard types for each custom clipboard format which may be a limited -// resource on a particular platform. -class PepperFlashClipboardMessageFilter - : public ppapi::host::ResourceMessageFilter { - public: - PepperFlashClipboardMessageFilter(); - - protected: - // ppapi::host::ResourceMessageFilter overrides. - scoped_refptr OverrideTaskRunnerForMessage( - const IPC::Message& msg) override; - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - ~PepperFlashClipboardMessageFilter() override; - - int32_t OnMsgRegisterCustomFormat( - ppapi::host::HostMessageContext* host_context, - const std::string& format_name); - int32_t OnMsgIsFormatAvailable(ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type, - uint32_t format); - int32_t OnMsgReadData(ppapi::host::HostMessageContext* host_context, - uint32_t clipoard_type, - uint32_t format); - int32_t OnMsgWriteData(ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type, - const std::vector& formats, - const std::vector& data); - int32_t OnMsgGetSequenceNumber(ppapi::host::HostMessageContext* host_context, - uint32_t clipboard_type); - - int32_t WriteClipboardDataItem(uint32_t format, - const std::string& data, - ui::ScopedClipboardWriter* scw); - - ppapi::FlashClipboardFormatRegistry custom_formats_; - - DISALLOW_COPY_AND_ASSIGN(PepperFlashClipboardMessageFilter); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_CLIPBOARD_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.cc b/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.cc deleted file mode 100644 index 34a7ff881f..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.cc +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h" - -#include - -#if defined(OS_WIN) -#include -#endif - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/strings/string_number_conversions.h" -#include "build/build_config.h" -#include "content/public/browser/browser_ppapi_host.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_security_policy.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/common/pepper_plugin_info.h" -#include "net/base/network_interfaces.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" - -#if defined(USE_AURA) -#include "ui/aura/window.h" -#include "ui/aura/window_tree_host.h" -#endif - -#if defined(OS_MACOSX) -#include "chrome/browser/renderer_host/pepper/monitor_finder_mac.h" -#endif - -using content::BrowserPpapiHost; - -namespace chrome { - -namespace { - -const char kVoucherFilename[] = "plugin.vch"; - -#if defined(OS_WIN) -bool GetSystemVolumeSerialNumber(std::string* number) { - // Find the system root path (e.g: C:\). - wchar_t system_path[MAX_PATH + 1]; - if (!GetSystemDirectoryW(system_path, MAX_PATH)) - return false; - - wchar_t* first_slash = wcspbrk(system_path, L"\\/"); - if (first_slash != NULL) - *(first_slash + 1) = 0; - - DWORD number_local = 0; - if (!GetVolumeInformationW(system_path, NULL, 0, &number_local, NULL, NULL, - NULL, 0)) - return false; - - *number = base::IntToString(std::abs(static_cast(number_local))); - return true; -} -#endif - -} - -#if defined(OS_WIN) -// Helper class to get the UI thread which monitor is showing the -// window associated with the instance's render view. Since we get -// called by the IO thread and we cannot block, the first answer is -// of GetMonitor() may be NULL, but eventually it will contain the -// right monitor. -class MonitorFinder : public base::RefCountedThreadSafe { - public: - MonitorFinder(int process_id, int render_frame_id) - : process_id_(process_id), - render_frame_id_(render_frame_id), - monitor_(NULL), - request_sent_(0) {} - - int64_t GetMonitor() { - // We use |request_sent_| as an atomic boolean so that we - // never have more than one task posted at a given time. We - // do this because we don't know how often our client is going - // to call and we can't cache the |monitor_| value. - if (InterlockedCompareExchange(&request_sent_, 1, 0) == 0) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::Bind(&MonitorFinder::FetchMonitorFromWidget, this)); - } - return reinterpret_cast(monitor_); - } - - private: - friend class base::RefCountedThreadSafe; - ~MonitorFinder() {} - - void FetchMonitorFromWidget() { - InterlockedExchange(&request_sent_, 0); - content::RenderFrameHost* rfh = - content::RenderFrameHost::FromID(process_id_, render_frame_id_); - if (!rfh) - return; - gfx::NativeView native_view = rfh->GetNativeView(); -#if defined(USE_AURA) - aura::WindowTreeHost* host = native_view->GetHost(); - if (!host) - return; - HWND window = host->GetAcceleratedWidget(); -#else - HWND window = native_view; -#endif - HMONITOR monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - InterlockedExchangePointer(reinterpret_cast(&monitor_), - monitor); - } - - const int process_id_; - const int render_frame_id_; - volatile HMONITOR monitor_; - volatile long request_sent_; -}; -#elif !defined(OS_MACOSX) -// TODO(cpu): Support Linux someday. -class MonitorFinder : public base::RefCountedThreadSafe { - public: - MonitorFinder(int, int) {} - int64_t GetMonitor() { return 0; } - - private: - friend class base::RefCountedThreadSafe; - ~MonitorFinder() {} -}; -#endif - -PepperFlashDRMHost::PepperFlashDRMHost(BrowserPpapiHost* host, - PP_Instance instance, - PP_Resource resource) - : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource), - weak_factory_(this) { - // Grant permissions to read the flash voucher file. - int render_process_id; - int render_frame_id; - bool success = host->GetRenderFrameIDsForInstance( - instance, &render_process_id, &render_frame_id); - base::FilePath plugin_dir = host->GetPluginPath().DirName(); - DCHECK(!plugin_dir.empty() && success); - base::FilePath voucher_file = plugin_dir.AppendASCII(kVoucherFilename); - content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( - render_process_id, voucher_file); - - monitor_finder_ = new MonitorFinder(render_process_id, render_frame_id); - monitor_finder_->GetMonitor(); -} - -PepperFlashDRMHost::~PepperFlashDRMHost() {} - -int32_t PepperFlashDRMHost::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashDRMHost, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FlashDRM_GetDeviceID, - OnHostMsgGetDeviceID) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FlashDRM_GetHmonitor, - OnHostMsgGetHmonitor) - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FlashDRM_MonitorIsExternal, - OnHostMsgMonitorIsExternal) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperFlashDRMHost::OnHostMsgGetDeviceID( - ppapi::host::HostMessageContext* context) { - static std::string id; -#if defined(OS_WIN) - if (id.empty() && !GetSystemVolumeSerialNumber(&id)) - id = net::GetHostName(); -#else - if (id.empty()) - id = net::GetHostName(); -#endif - context->reply_msg = PpapiPluginMsg_FlashDRM_GetDeviceIDReply(id); - return PP_OK; -} - -int32_t PepperFlashDRMHost::OnHostMsgGetHmonitor( - ppapi::host::HostMessageContext* context) { - int64_t monitor_id = monitor_finder_->GetMonitor(); - if (monitor_id) { - context->reply_msg = PpapiPluginMsg_FlashDRM_GetHmonitorReply(monitor_id); - return PP_OK; - } - return PP_ERROR_FAILED; -} - -int32_t PepperFlashDRMHost::OnHostMsgMonitorIsExternal( - ppapi::host::HostMessageContext* context) { - int64_t monitor_id = monitor_finder_->GetMonitor(); - if (!monitor_id) - return PP_ERROR_FAILED; - - PP_Bool is_external = PP_FALSE; -#if defined(OS_MACOSX) - if (!MonitorFinder::IsMonitorBuiltIn(monitor_id)) - is_external = PP_TRUE; -#endif - context->reply_msg = - PpapiPluginMsg_FlashDRM_MonitorIsExternalReply(is_external); - return PP_OK; -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h b/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h deleted file mode 100644 index 91bba9631e..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_drm_host.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_DRM_HOST_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_DRM_HOST_H_ - -#include - -#include - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/resource_host.h" - -namespace content { -class BrowserPpapiHost; -} - -namespace IPC { -class Message; -} - -namespace chrome { -class MonitorFinder; - -class PepperFlashDRMHost : public ppapi::host::ResourceHost { - public: - PepperFlashDRMHost(content::BrowserPpapiHost* host, - PP_Instance instance, - PP_Resource resource); - ~PepperFlashDRMHost() override; - - // ResourceHost override. - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - // IPC message handler. - int32_t OnHostMsgGetDeviceID(ppapi::host::HostMessageContext* context); - int32_t OnHostMsgGetHmonitor(ppapi::host::HostMessageContext* context); - int32_t OnHostMsgMonitorIsExternal(ppapi::host::HostMessageContext* context); - - scoped_refptr monitor_finder_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(PepperFlashDRMHost); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_DRM_HOST_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc b/chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc deleted file mode 100644 index 10c07906af..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2013 The Chromium 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 "chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h" - -#include "content/public/browser/browser_ppapi_host.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_security_policy.h" -#include "content/public/browser/render_view_host.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/shared_impl/file_system_util.h" -#include "storage/browser/fileapi/isolated_context.h" - -namespace chrome { - -// static -PepperIsolatedFileSystemMessageFilter* -PepperIsolatedFileSystemMessageFilter::Create(PP_Instance instance, - content::BrowserPpapiHost* host) { - int render_process_id; - int unused_render_frame_id; - if (!host->GetRenderFrameIDsForInstance( - instance, &render_process_id, &unused_render_frame_id)) { - return NULL; - } - return new PepperIsolatedFileSystemMessageFilter( - render_process_id, - host->GetProfileDataDirectory(), - host->GetDocumentURLForInstance(instance), - host->GetPpapiHost()); -} - -PepperIsolatedFileSystemMessageFilter::PepperIsolatedFileSystemMessageFilter( - int render_process_id, - const base::FilePath& profile_directory, - const GURL& document_url, - ppapi::host::PpapiHost* ppapi_host) - : render_process_id_(render_process_id), - profile_directory_(profile_directory), - document_url_(document_url), - ppapi_host_(ppapi_host) { -} - -PepperIsolatedFileSystemMessageFilter:: - ~PepperIsolatedFileSystemMessageFilter() {} - -scoped_refptr -PepperIsolatedFileSystemMessageFilter::OverrideTaskRunnerForMessage( - const IPC::Message& msg) { - // In order to reach ExtensionSystem, we need to get ProfileManager first. - // ProfileManager lives in UI thread, so we need to do this in UI thread. - return content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::UI); -} - -int32_t PepperIsolatedFileSystemMessageFilter::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperIsolatedFileSystemMessageFilter, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( - PpapiHostMsg_IsolatedFileSystem_BrowserOpen, - OnOpenFileSystem) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperIsolatedFileSystemMessageFilter::OnOpenFileSystem( - ppapi::host::HostMessageContext* context, - PP_IsolatedFileSystemType_Private type) { - switch (type) { - case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_INVALID: - case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX: - break; - case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE: - return OpenPluginPrivateFileSystem(context); - } - NOTREACHED(); - context->reply_msg = - PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(std::string()); - return PP_ERROR_FAILED; -} - -int32_t PepperIsolatedFileSystemMessageFilter::OpenPluginPrivateFileSystem( - ppapi::host::HostMessageContext* context) { - DCHECK(ppapi_host_); - // Only plugins with private permission can open the filesystem. - if (!ppapi_host_->permissions().HasPermission(ppapi::PERMISSION_PRIVATE)) - return PP_ERROR_NOACCESS; - - const std::string& root_name = ppapi::IsolatedFileSystemTypeToRootName( - PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); - const std::string& fsid = - storage::IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath( - storage::kFileSystemTypePluginPrivate, root_name, base::FilePath()); - - // Grant full access of isolated filesystem to renderer process. - content::ChildProcessSecurityPolicy* policy = - content::ChildProcessSecurityPolicy::GetInstance(); - policy->GrantCreateReadWriteFileSystem(render_process_id_, fsid); - - context->reply_msg = PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(fsid); - return PP_OK; -} - -} // namespace chrome diff --git a/chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h b/chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h deleted file mode 100644 index 6a24feadd1..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_ISOLATED_FILE_SYSTEM_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_ISOLATED_FILE_SYSTEM_MESSAGE_FILTER_H_ - -#include -#include - -#include "base/files/file_path.h" -#include "ppapi/c/pp_instance.h" -#include "ppapi/c/pp_resource.h" -#include "ppapi/c/private/ppb_isolated_file_system_private.h" -#include "ppapi/host/resource_host.h" -#include "ppapi/host/resource_message_filter.h" -#include "url/gurl.h" - -class Profile; - -namespace content { -class BrowserPpapiHost; -} - -namespace ppapi { -namespace host { -struct HostMessageContext; -} // namespace host -} // namespace ppapi - -namespace chrome { - -class PepperIsolatedFileSystemMessageFilter - : public ppapi::host::ResourceMessageFilter { - public: - static PepperIsolatedFileSystemMessageFilter* Create( - PP_Instance instance, - content::BrowserPpapiHost* host); - - // ppapi::host::ResourceMessageFilter implementation. - scoped_refptr OverrideTaskRunnerForMessage( - const IPC::Message& msg) override; - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - PepperIsolatedFileSystemMessageFilter(int render_process_id, - const base::FilePath& profile_directory, - const GURL& document_url, - ppapi::host::PpapiHost* ppapi_host_); - - ~PepperIsolatedFileSystemMessageFilter() override; - - // Returns filesystem id of isolated filesystem if valid, or empty string - // otherwise. This must run on the UI thread because ProfileManager only - // allows access on that thread. - - int32_t OnOpenFileSystem(ppapi::host::HostMessageContext* context, - PP_IsolatedFileSystemType_Private type); - int32_t OpenPluginPrivateFileSystem(ppapi::host::HostMessageContext* context); - - const int render_process_id_; - // Keep a copy from original thread. - const base::FilePath profile_directory_; - const GURL document_url_; - - // Not owned by this object. - ppapi::host::PpapiHost* ppapi_host_; - - DISALLOW_COPY_AND_ASSIGN(PepperIsolatedFileSystemMessageFilter); -}; - -} // namespace chrome - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_ISOLATED_FILE_SYSTEM_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc b/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc deleted file mode 100644 index c926b9e94a..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h" - -#include "base/bind.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/common/webplugininfo.h" -#include "content/public/browser/plugin_service.h" - -using content::PluginService; -using content::WebPluginInfo; -using content::BrowserThread; - -WidevineCdmMessageFilter::WidevineCdmMessageFilter( - int render_process_id, - content::BrowserContext* browser_context) - : BrowserMessageFilter(ChromeMsgStart), - render_process_id_(render_process_id), - browser_context_(browser_context) { -} - -bool WidevineCdmMessageFilter::OnMessageReceived(const IPC::Message& message) { - IPC_BEGIN_MESSAGE_MAP(WidevineCdmMessageFilter, message) -#if defined(ENABLE_PEPPER_CDMS) - IPC_MESSAGE_HANDLER( - ChromeViewHostMsg_IsInternalPluginAvailableForMimeType, - OnIsInternalPluginAvailableForMimeType) -#endif - IPC_MESSAGE_UNHANDLED(return false) - IPC_END_MESSAGE_MAP() - return true; -} - -#if defined(ENABLE_PEPPER_CDMS) -void WidevineCdmMessageFilter::OnIsInternalPluginAvailableForMimeType( - const std::string& mime_type, - bool* is_available, - std::vector* additional_param_names, - std::vector* additional_param_values) { - std::vector plugins; - PluginService::GetInstance()->GetInternalPlugins(&plugins); - - for (size_t i = 0; i < plugins.size(); ++i) { - const WebPluginInfo& plugin = plugins[i]; - const std::vector& mime_types = - plugin.mime_types; - for (size_t j = 0; j < mime_types.size(); ++j) { - - if (mime_types[j].mime_type == mime_type) { - *is_available = true; - *additional_param_names = mime_types[j].additional_param_names; - *additional_param_values = mime_types[j].additional_param_values; - return; - } - } - } - - *is_available = false; -} -#endif // defined(ENABLE_PEPPER_CDMS) - -void WidevineCdmMessageFilter::OnDestruct() const { - BrowserThread::DeleteOnUIThread::Destruct(this); -} - -WidevineCdmMessageFilter::~WidevineCdmMessageFilter() { -} diff --git a/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h b/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h deleted file mode 100644 index b8f3d562ac..0000000000 --- a/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_WIDEVINE_CDM_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_RENDERER_HOST_PEPPER_WIDEVINE_CDM_MESSAGE_FILTER_H_ - -#include "chrome/common/widevine_cdm_messages.h" -#include "content/public/browser/browser_message_filter.h" - -namespace content { -class BrowserContext; -} - -class WidevineCdmMessageFilter : public content::BrowserMessageFilter { - public: - explicit WidevineCdmMessageFilter(int render_process_id, - content::BrowserContext* browser_context); - bool OnMessageReceived(const IPC::Message& message) override; - void OnDestruct() const override; - - private: - friend class content::BrowserThread; - friend class base::DeleteHelper; - - virtual ~WidevineCdmMessageFilter(); - - #if defined(ENABLE_PEPPER_CDMS) - // Returns whether any internal plugin supporting |mime_type| is registered - // and enabled. Does not determine whether the plugin can actually be - // instantiated (e.g. whether it has all its dependencies). - // When the returned *|is_available| is true, |additional_param_names| and - // |additional_param_values| contain the name-value pairs, if any, specified - // for the *first* non-disabled plugin found that is registered for - // |mime_type|. - void OnIsInternalPluginAvailableForMimeType( - const std::string& mime_type, - bool* is_available, - std::vector* additional_param_names, - std::vector* additional_param_values); -#endif - - int render_process_id_; - content::BrowserContext* browser_context_; - - DISALLOW_COPY_AND_ASSIGN(WidevineCdmMessageFilter); -}; - -#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_WIDEVINE_CDM_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/speech/tts_controller.h b/chromium_src/chrome/browser/speech/tts_controller.h deleted file mode 100644 index 0587a1b8cb..0000000000 --- a/chromium_src/chrome/browser/speech/tts_controller.h +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_ -#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_ - -#include -#include -#include -#include -#include - -#include "base/memory/singleton.h" -#include "base/memory/weak_ptr.h" -#include "url/gurl.h" - -class Utterance; -class TtsPlatformImpl; - -namespace base { -class Value; -} - -namespace content { -class BrowserContext; -} - -// Events sent back from the TTS engine indicating the progress. -enum TtsEventType { - TTS_EVENT_START, - TTS_EVENT_END, - TTS_EVENT_WORD, - TTS_EVENT_SENTENCE, - TTS_EVENT_MARKER, - TTS_EVENT_INTERRUPTED, - TTS_EVENT_CANCELLED, - TTS_EVENT_ERROR, - TTS_EVENT_PAUSE, - TTS_EVENT_RESUME -}; - -enum TtsGenderType { - TTS_GENDER_NONE, - TTS_GENDER_MALE, - TTS_GENDER_FEMALE -}; - -// Returns true if this event type is one that indicates an utterance -// is finished and can be destroyed. -bool IsFinalTtsEventType(TtsEventType event_type); - -// The continuous parameters that apply to a given utterance. -struct UtteranceContinuousParameters { - UtteranceContinuousParameters(); - - double rate; - double pitch; - double volume; -}; - -// Information about one voice. -struct VoiceData { - VoiceData(); - ~VoiceData(); - - std::string name; - std::string lang; - TtsGenderType gender; - std::string extension_id; - std::set events; - - // If true, the synthesis engine is a remote network resource. - // It may be higher latency and may incur bandwidth costs. - bool remote; - - // If true, this is implemented by this platform's subclass of - // TtsPlatformImpl. If false, this is implemented by an extension. - bool native; - std::string native_voice_identifier; -}; - -// Interface that delegates TTS requests to user-installed extensions. -class TtsEngineDelegate { - public: - virtual ~TtsEngineDelegate() {} - - // Return a list of all available voices registered. - virtual void GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) = 0; - - // Speak the given utterance by sending an event to the given TTS engine. - virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0; - - // Stop speaking the given utterance by sending an event to the target - // associated with this utterance. - virtual void Stop(Utterance* utterance) = 0; - - // Pause in the middle of speaking this utterance. - virtual void Pause(Utterance* utterance) = 0; - - // Resume speaking this utterance. - virtual void Resume(Utterance* utterance) = 0; - - // Load the built-in component extension for ChromeOS. - virtual bool LoadBuiltInTtsExtension( - content::BrowserContext* browser_context) = 0; -}; - -// Class that wants to receive events on utterances. -class UtteranceEventDelegate { - public: - virtual ~UtteranceEventDelegate() {} - virtual void OnTtsEvent(Utterance* utterance, - TtsEventType event_type, - int char_index, - const std::string& error_message) = 0; -}; - -// Class that wants to be notified when the set of -// voices has changed. -class VoicesChangedDelegate { - public: - virtual ~VoicesChangedDelegate() {} - virtual void OnVoicesChanged() = 0; -}; - -// One speech utterance. -class Utterance { - public: - // Construct an utterance given a profile and a completion task to call - // when the utterance is done speaking. Before speaking this utterance, - // its other parameters like text, rate, pitch, etc. should all be set. - explicit Utterance(content::BrowserContext* browser_context); - ~Utterance(); - - // Sends an event to the delegate. If the event type is TTS_EVENT_END - // or TTS_EVENT_ERROR, deletes the utterance. If |char_index| is -1, - // uses the last good value. - void OnTtsEvent(TtsEventType event_type, - int char_index, - const std::string& error_message); - - // Finish an utterance without sending an event to the delegate. - void Finish(); - - // Getters and setters for the text to speak and other speech options. - void set_text(const std::string& text) { text_ = text; } - const std::string& text() const { return text_; } - - void set_options(const base::Value* options); - const base::Value* options() const { return options_.get(); } - - void set_src_extension_id(const std::string& src_extension_id) { - src_extension_id_ = src_extension_id; - } - const std::string& src_extension_id() { return src_extension_id_; } - - void set_src_id(int src_id) { src_id_ = src_id; } - int src_id() { return src_id_; } - - void set_src_url(const GURL& src_url) { src_url_ = src_url; } - const GURL& src_url() { return src_url_; } - - void set_voice_name(const std::string& voice_name) { - voice_name_ = voice_name; - } - const std::string& voice_name() const { return voice_name_; } - - void set_lang(const std::string& lang) { - lang_ = lang; - } - const std::string& lang() const { return lang_; } - - void set_gender(TtsGenderType gender) { - gender_ = gender; - } - TtsGenderType gender() const { return gender_; } - - void set_continuous_parameters(const UtteranceContinuousParameters& params) { - continuous_parameters_ = params; - } - const UtteranceContinuousParameters& continuous_parameters() { - return continuous_parameters_; - } - - void set_can_enqueue(bool can_enqueue) { can_enqueue_ = can_enqueue; } - bool can_enqueue() const { return can_enqueue_; } - - void set_required_event_types(const std::set& types) { - required_event_types_ = types; - } - const std::set& required_event_types() const { - return required_event_types_; - } - - void set_desired_event_types(const std::set& types) { - desired_event_types_ = types; - } - const std::set& desired_event_types() const { - return desired_event_types_; - } - - const std::string& extension_id() const { return extension_id_; } - void set_extension_id(const std::string& extension_id) { - extension_id_ = extension_id; - } - - UtteranceEventDelegate* event_delegate() const { - return event_delegate_.get(); - } - void set_event_delegate( - base::WeakPtr event_delegate) { - event_delegate_ = event_delegate; - } - - // Getters and setters for internal state. - content::BrowserContext* browser_context() const { return browser_context_; } - int id() const { return id_; } - bool finished() const { return finished_; } - - private: - // The BrowserContext that initiated this utterance. - content::BrowserContext* browser_context_; - - // The extension ID of the extension providing TTS for this utterance, or - // empty if native TTS is being used. - std::string extension_id_; - - // The unique ID of this utterance, used to associate callback functions - // with utterances. - int id_; - - // The id of the next utterance, so we can associate requests with - // responses. - static int next_utterance_id_; - - // The text to speak. - std::string text_; - - // The full options arg passed to tts.speak, which may include fields - // other than the ones we explicitly parse, below. - std::unique_ptr options_; - - // The extension ID of the extension that called speak() and should - // receive events. - std::string src_extension_id_; - - // The source extension's ID of this utterance, so that it can associate - // events with the appropriate callback. - int src_id_; - - // The URL of the page where the source extension called speak. - GURL src_url_; - - // The delegate to be called when an utterance event is fired. - base::WeakPtr event_delegate_; - - // The parsed options. - std::string voice_name_; - std::string lang_; - TtsGenderType gender_; - UtteranceContinuousParameters continuous_parameters_; - bool can_enqueue_; - std::set required_event_types_; - std::set desired_event_types_; - - // The index of the current char being spoken. - int char_index_; - - // True if this utterance received an event indicating it's done. - bool finished_; -}; - -// Singleton class that manages text-to-speech for the TTS and TTS engine -// extension APIs, maintaining a queue of pending utterances and keeping -// track of all state. -class TtsController { - public: - // Get the single instance of this class. - static TtsController* GetInstance(); - - // Returns true if we're currently speaking an utterance. - virtual bool IsSpeaking() = 0; - - // Speak the given utterance. If the utterance's can_enqueue flag is true - // and another utterance is in progress, adds it to the end of the queue. - // Otherwise, interrupts any current utterance and speaks this one - // immediately. - virtual void SpeakOrEnqueue(Utterance* utterance) = 0; - - // Stop all utterances and flush the queue. Implies leaving pause mode - // as well. - virtual void Stop() = 0; - - // Pause the speech queue. Some engines may support pausing in the middle - // of an utterance. - virtual void Pause() = 0; - - // Resume speaking. - virtual void Resume() = 0; - - // Handle events received from the speech engine. Events are forwarded to - // the callback function, and in addition, completion and error events - // trigger finishing the current utterance and starting the next one, if - // any. - virtual void OnTtsEvent(int utterance_id, - TtsEventType event_type, - int char_index, - const std::string& error_message) = 0; - - // Return a list of all available voices, including the native voice, - // if supported, and all voices registered by extensions. - virtual void GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) = 0; - - // Called by the extension system or platform implementation when the - // list of voices may have changed and should be re-queried. - virtual void VoicesChanged() = 0; - - // Add a delegate that wants to be notified when the set of voices changes. - virtual void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0; - - // Remove delegate that wants to be notified when the set of voices changes. - virtual void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0; - - // Set the delegate that processes TTS requests with user-installed - // extensions. - virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) = 0; - - // Get the delegate that processes TTS requests with user-installed - // extensions. - virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0; - - // For unit testing. - virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) = 0; - virtual int QueueSize() = 0; - - protected: - virtual ~TtsController() {} -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_ diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.cc b/chromium_src/chrome/browser/speech/tts_controller_impl.cc deleted file mode 100644 index 610ce16567..0000000000 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.cc +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/browser/speech/tts_controller_impl.h" - -#include -#include - -#include "base/values.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/speech/tts_platform.h" - -namespace { -// A value to be used to indicate that there is no char index available. -const int kInvalidCharIndex = -1; - -// Given a language/region code of the form 'fr-FR', returns just the basic -// language portion, e.g. 'fr'. -std::string TrimLanguageCode(std::string lang) { - if (lang.size() >= 5 && lang[2] == '-') - return lang.substr(0, 2); - else - return lang; -} - -} // namespace - -bool IsFinalTtsEventType(TtsEventType event_type) { - return (event_type == TTS_EVENT_END || - event_type == TTS_EVENT_INTERRUPTED || - event_type == TTS_EVENT_CANCELLED || - event_type == TTS_EVENT_ERROR); -} - -// -// UtteranceContinuousParameters -// - - -UtteranceContinuousParameters::UtteranceContinuousParameters() - : rate(-1), - pitch(-1), - volume(-1) {} - - -// -// VoiceData -// - - -VoiceData::VoiceData() - : gender(TTS_GENDER_NONE), - remote(false), - native(false) {} - -VoiceData::~VoiceData() {} - - -// -// Utterance -// - -// static -int Utterance::next_utterance_id_ = 0; - -Utterance::Utterance(content::BrowserContext* browser_context) - : browser_context_(browser_context), - id_(next_utterance_id_++), - src_id_(-1), - gender_(TTS_GENDER_NONE), - can_enqueue_(false), - char_index_(0), - finished_(false) { - options_.reset(new base::DictionaryValue()); -} - -Utterance::~Utterance() { - DCHECK(finished_); -} - -void Utterance::OnTtsEvent(TtsEventType event_type, - int char_index, - const std::string& error_message) { - if (char_index >= 0) - char_index_ = char_index; - if (IsFinalTtsEventType(event_type)) - finished_ = true; - - if (event_delegate_) - event_delegate_->OnTtsEvent(this, event_type, char_index, error_message); - if (finished_) - event_delegate_.reset(); -} - -void Utterance::Finish() { - finished_ = true; -} - -void Utterance::set_options(const base::Value* options) { - options_.reset(options->DeepCopy()); -} - -TtsController* TtsController::GetInstance() { - return TtsControllerImpl::GetInstance(); -} - -// -// TtsControllerImpl -// - -// static -TtsControllerImpl* TtsControllerImpl::GetInstance() { - return base::Singleton::get(); -} - -TtsControllerImpl::TtsControllerImpl() - : current_utterance_(NULL), - paused_(false), - platform_impl_(NULL), - tts_engine_delegate_(NULL) { -} - -TtsControllerImpl::~TtsControllerImpl() { - if (current_utterance_) { - current_utterance_->Finish(); - delete current_utterance_; - } - - // Clear any queued utterances too. - ClearUtteranceQueue(false); // Don't sent events. -} - -void TtsControllerImpl::SpeakOrEnqueue(Utterance* utterance) { - // If we're paused and we get an utterance that can't be queued, - // flush the queue but stay in the paused state. - if (paused_ && !utterance->can_enqueue()) { - Stop(); - paused_ = true; - delete utterance; - return; - } - - if (paused_ || (IsSpeaking() && utterance->can_enqueue())) { - utterance_queue_.push(utterance); - } else { - Stop(); - SpeakNow(utterance); - } -} - -void TtsControllerImpl::SpeakNow(Utterance* utterance) { - // Ensure we have all built-in voices loaded. This is a no-op if already - // loaded. - bool loaded_built_in = - GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context()); - - // Get all available voices and try to find a matching voice. - std::vector voices; - GetVoices(utterance->browser_context(), &voices); - int index = GetMatchingVoice(utterance, voices); - - VoiceData voice; - if (index != -1) { - // Select the matching voice. - voice = voices[index]; - } else { - // However, if no match was found on a platform without native tts voices, - // attempt to get a voice based only on the current locale without respect - // to any supplied voice names. - std::vector native_voices; - - if (GetPlatformImpl()->PlatformImplAvailable()) - GetPlatformImpl()->GetVoices(&native_voices); - - if (native_voices.empty() && !voices.empty()) { - // TODO(dtseng): Notify extension caller of an error. - utterance->set_voice_name(""); - // TODO(gaochun): Replace the global variable g_browser_process with - // GetContentClient()->browser() to eliminate the dependency of browser - // once TTS implementation was moved to content. - utterance->set_lang(g_browser_process->GetApplicationLocale()); - index = GetMatchingVoice(utterance, voices); - - // If even that fails, just take the first available voice. - if (index == -1) - index = 0; - voice = voices[index]; - } else { - // Otherwise, simply give native voices a chance to handle this utterance. - voice.native = true; - } - } - - GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice); - - if (!voice.native) { -#if !defined(OS_ANDROID) - DCHECK(!voice.extension_id.empty()); - current_utterance_ = utterance; - utterance->set_extension_id(voice.extension_id); - if (tts_engine_delegate_) - tts_engine_delegate_->Speak(utterance, voice); - bool sends_end_event = - voice.events.find(TTS_EVENT_END) != voice.events.end(); - if (!sends_end_event) { - utterance->Finish(); - delete utterance; - current_utterance_ = NULL; - SpeakNextUtterance(); - } -#endif - } else { - // It's possible for certain platforms to send start events immediately - // during |speak|. - current_utterance_ = utterance; - GetPlatformImpl()->clear_error(); - bool success = GetPlatformImpl()->Speak( - utterance->id(), - utterance->text(), - utterance->lang(), - voice, - utterance->continuous_parameters()); - if (!success) - current_utterance_ = NULL; - - // If the native voice wasn't able to process this speech, see if - // the browser has built-in TTS that isn't loaded yet. - if (!success && loaded_built_in) { - utterance_queue_.push(utterance); - return; - } - - if (!success) { - utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex, - GetPlatformImpl()->error()); - delete utterance; - return; - } - } -} - -void TtsControllerImpl::Stop() { - paused_ = false; - if (current_utterance_ && !current_utterance_->extension_id().empty()) { -#if !defined(OS_ANDROID) - if (tts_engine_delegate_) - tts_engine_delegate_->Stop(current_utterance_); -#endif - } else { - GetPlatformImpl()->clear_error(); - GetPlatformImpl()->StopSpeaking(); - } - - if (current_utterance_) - current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex, - std::string()); - FinishCurrentUtterance(); - ClearUtteranceQueue(true); // Send events. -} - -void TtsControllerImpl::Pause() { - paused_ = true; - if (current_utterance_ && !current_utterance_->extension_id().empty()) { -#if !defined(OS_ANDROID) - if (tts_engine_delegate_) - tts_engine_delegate_->Pause(current_utterance_); -#endif - } else if (current_utterance_) { - GetPlatformImpl()->clear_error(); - GetPlatformImpl()->Pause(); - } -} - -void TtsControllerImpl::Resume() { - paused_ = false; - if (current_utterance_ && !current_utterance_->extension_id().empty()) { -#if !defined(OS_ANDROID) - if (tts_engine_delegate_) - tts_engine_delegate_->Resume(current_utterance_); -#endif - } else if (current_utterance_) { - GetPlatformImpl()->clear_error(); - GetPlatformImpl()->Resume(); - } else { - SpeakNextUtterance(); - } -} - -void TtsControllerImpl::OnTtsEvent(int utterance_id, - TtsEventType event_type, - int char_index, - const std::string& error_message) { - // We may sometimes receive completion callbacks "late", after we've - // already finished the utterance (for example because another utterance - // interrupted or we got a call to Stop). This is normal and we can - // safely just ignore these events. - if (!current_utterance_ || utterance_id != current_utterance_->id()) { - return; - } - current_utterance_->OnTtsEvent(event_type, char_index, error_message); - if (current_utterance_->finished()) { - FinishCurrentUtterance(); - SpeakNextUtterance(); - } -} - -void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) { -#if !defined(OS_ANDROID) - if (browser_context && tts_engine_delegate_) - tts_engine_delegate_->GetVoices(browser_context, out_voices); -#endif - - TtsPlatformImpl* platform_impl = GetPlatformImpl(); - if (platform_impl) { - // Ensure we have all built-in voices loaded. This is a no-op if already - // loaded. - platform_impl->LoadBuiltInTtsExtension(browser_context); - if (platform_impl->PlatformImplAvailable()) - platform_impl->GetVoices(out_voices); - } -} - -bool TtsControllerImpl::IsSpeaking() { - return current_utterance_ != NULL || GetPlatformImpl()->IsSpeaking(); -} - -void TtsControllerImpl::FinishCurrentUtterance() { - if (current_utterance_) { - if (!current_utterance_->finished()) - current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex, - std::string()); - delete current_utterance_; - current_utterance_ = NULL; - } -} - -void TtsControllerImpl::SpeakNextUtterance() { - if (paused_) - return; - - // Start speaking the next utterance in the queue. Keep trying in case - // one fails but there are still more in the queue to try. - while (!utterance_queue_.empty() && !current_utterance_) { - Utterance* utterance = utterance_queue_.front(); - utterance_queue_.pop(); - SpeakNow(utterance); - } -} - -void TtsControllerImpl::ClearUtteranceQueue(bool send_events) { - while (!utterance_queue_.empty()) { - Utterance* utterance = utterance_queue_.front(); - utterance_queue_.pop(); - if (send_events) - utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex, - std::string()); - else - utterance->Finish(); - delete utterance; - } -} - -void TtsControllerImpl::SetPlatformImpl( - TtsPlatformImpl* platform_impl) { - platform_impl_ = platform_impl; -} - -int TtsControllerImpl::QueueSize() { - return static_cast(utterance_queue_.size()); -} - -TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() { - if (!platform_impl_) - platform_impl_ = TtsPlatformImpl::GetInstance(); - return platform_impl_; -} - -int TtsControllerImpl::GetMatchingVoice( - const Utterance* utterance, std::vector& voices) { - // Make two passes: the first time, do strict language matching - // ('fr-FR' does not match 'fr-CA'). The second time, do prefix - // language matching ('fr-FR' matches 'fr' and 'fr-CA') - for (int pass = 0; pass < 2; ++pass) { - for (size_t i = 0; i < voices.size(); ++i) { - const VoiceData& voice = voices[i]; - - if (!utterance->extension_id().empty() && - utterance->extension_id() != voice.extension_id) { - continue; - } - - if (!voice.name.empty() && - !utterance->voice_name().empty() && - voice.name != utterance->voice_name()) { - continue; - } - if (!voice.lang.empty() && !utterance->lang().empty()) { - std::string voice_lang = voice.lang; - std::string utterance_lang = utterance->lang(); - if (pass == 1) { - voice_lang = TrimLanguageCode(voice_lang); - utterance_lang = TrimLanguageCode(utterance_lang); - } - if (voice_lang != utterance_lang) { - continue; - } - } - if (voice.gender != TTS_GENDER_NONE && - utterance->gender() != TTS_GENDER_NONE && - voice.gender != utterance->gender()) { - continue; - } - - if (utterance->required_event_types().size() > 0) { - bool has_all_required_event_types = true; - for (std::set::const_iterator iter = - utterance->required_event_types().begin(); - iter != utterance->required_event_types().end(); - ++iter) { - if (voice.events.find(*iter) == voice.events.end()) { - has_all_required_event_types = false; - break; - } - } - if (!has_all_required_event_types) - continue; - } - - return static_cast(i); - } - } - - return -1; -} - -void TtsControllerImpl::VoicesChanged() { - for (std::set::iterator iter = - voices_changed_delegates_.begin(); - iter != voices_changed_delegates_.end(); ++iter) { - (*iter)->OnVoicesChanged(); - } -} - -void TtsControllerImpl::AddVoicesChangedDelegate( - VoicesChangedDelegate* delegate) { - voices_changed_delegates_.insert(delegate); -} - -void TtsControllerImpl::RemoveVoicesChangedDelegate( - VoicesChangedDelegate* delegate) { - voices_changed_delegates_.erase(delegate); -} - -void TtsControllerImpl::SetTtsEngineDelegate( - TtsEngineDelegate* delegate) { - tts_engine_delegate_ = delegate; -} - -TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() { - return tts_engine_delegate_; -} diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.h b/chromium_src/chrome/browser/speech/tts_controller_impl.h deleted file mode 100644 index 749c60ad6d..0000000000 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2014 The Chromium 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 CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ -#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ - -#include -#include -#include -#include -#include - -#include "base/memory/singleton.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/speech/tts_controller.h" -#include "url/gurl.h" - -namespace content { -class BrowserContext; -} - -// Singleton class that manages text-to-speech for the TTS and TTS engine -// extension APIs, maintaining a queue of pending utterances and keeping -// track of all state. -class TtsControllerImpl : public TtsController { - public: - // Get the single instance of this class. - static TtsControllerImpl* GetInstance(); - - // TtsController methods - virtual bool IsSpeaking() override; - virtual void SpeakOrEnqueue(Utterance* utterance) override; - virtual void Stop() override; - virtual void Pause() override; - virtual void Resume() override; - virtual void OnTtsEvent(int utterance_id, - TtsEventType event_type, - int char_index, - const std::string& error_message) override; - virtual void GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) override; - virtual void VoicesChanged() override; - virtual void AddVoicesChangedDelegate( - VoicesChangedDelegate* delegate) override; - virtual void RemoveVoicesChangedDelegate( - VoicesChangedDelegate* delegate) override; - virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override; - virtual TtsEngineDelegate* GetTtsEngineDelegate() override; - virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) override; - virtual int QueueSize() override; - - protected: - TtsControllerImpl(); - virtual ~TtsControllerImpl(); - - private: - // Get the platform TTS implementation (or injected mock). - TtsPlatformImpl* GetPlatformImpl(); - - // Start speaking the given utterance. Will either take ownership of - // |utterance| or delete it if there's an error. Returns true on success. - void SpeakNow(Utterance* utterance); - - // Clear the utterance queue. If send_events is true, will send - // TTS_EVENT_CANCELLED events on each one. - void ClearUtteranceQueue(bool send_events); - - // Finalize and delete the current utterance. - void FinishCurrentUtterance(); - - // Start speaking the next utterance in the queue. - void SpeakNextUtterance(); - - // Given an utterance and a vector of voices, return the - // index of the voice that best matches the utterance. - int GetMatchingVoice(const Utterance* utterance, - std::vector& voices); - - friend struct base::DefaultSingletonTraits; - - // The current utterance being spoken. - Utterance* current_utterance_; - - // Whether the queue is paused or not. - bool paused_; - - // A queue of utterances to speak after the current one finishes. - std::queue utterance_queue_; - - // A set of delegates that want to be notified when the voices change. - std::set voices_changed_delegates_; - - // A pointer to the platform implementation of text-to-speech, for - // dependency injection. - TtsPlatformImpl* platform_impl_; - - // The delegate that processes TTS requests with user-installed extensions. - TtsEngineDelegate* tts_engine_delegate_; - - DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl); -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ diff --git a/chromium_src/chrome/browser/speech/tts_linux.cc b/chromium_src/chrome/browser/speech/tts_linux.cc deleted file mode 100644 index d0e0e2ee82..0000000000 --- a/chromium_src/chrome/browser/speech/tts_linux.cc +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 - -#include -#include - -#include "base/command_line.h" -#include "base/debug/leak_annotations.h" -#include "base/memory/singleton.h" -#include "base/synchronization/lock.h" -#include "chrome/browser/speech/tts_platform.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/common/content_switches.h" - -#include "library_loaders/libspeechd.h" - -using content::BrowserThread; - -namespace { - -const char kNotSupportedError[] = - "Native speech synthesis not supported on this platform."; - -struct SPDChromeVoice { - std::string name; - std::string module; -}; - -} // namespace - -class TtsPlatformImplLinux : public TtsPlatformImpl { - public: - bool PlatformImplAvailable() override; - bool Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - bool StopSpeaking() override; - void Pause() override; - void Resume() override; - bool IsSpeaking() override; - void GetVoices(std::vector* out_voices) override; - - void OnSpeechEvent(SPDNotificationType type); - - // Get the single instance of this class. - static TtsPlatformImplLinux* GetInstance(); - - private: - TtsPlatformImplLinux(); - ~TtsPlatformImplLinux() override; - - // Initiate the connection with the speech dispatcher. - void Initialize(); - - // Resets the connection with speech dispatcher. - void Reset(); - - static void NotificationCallback(size_t msg_id, - size_t client_id, - SPDNotificationType type); - - static void IndexMarkCallback(size_t msg_id, - size_t client_id, - SPDNotificationType state, - char* index_mark); - - static SPDNotificationType current_notification_; - - base::Lock initialization_lock_; - LibSpeechdLoader libspeechd_loader_; - SPDConnection* conn_; - - // These apply to the current utterance only. - std::string utterance_; - int utterance_id_; - - // Map a string composed of a voicename and module to the voicename. Used to - // uniquely identify a voice across all available modules. - std::unique_ptr > all_native_voices_; - - friend struct base::DefaultSingletonTraits; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux); -}; - -// static -SPDNotificationType TtsPlatformImplLinux::current_notification_ = - SPD_EVENT_END; - -TtsPlatformImplLinux::TtsPlatformImplLinux() - : utterance_id_(0) { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher)) - return; - - BrowserThread::PostTask(BrowserThread::FILE, - FROM_HERE, - base::Bind(&TtsPlatformImplLinux::Initialize, - base::Unretained(this))); -} - -void TtsPlatformImplLinux::Initialize() { - base::AutoLock lock(initialization_lock_); - - if (!libspeechd_loader_.Load("libspeechd.so.2")) - return; - - { - // spd_open has memory leaks which are hard to suppress. - // http://crbug.com/317360 - ANNOTATE_SCOPED_MEMORY_LEAK; - conn_ = libspeechd_loader_.spd_open( - "chrome", "extension_api", NULL, SPD_MODE_THREADED); - } - if (!conn_) - return; - - // Register callbacks for all events. - conn_->callback_begin = - conn_->callback_end = - conn_->callback_cancel = - conn_->callback_pause = - conn_->callback_resume = - &NotificationCallback; - - conn_->callback_im = &IndexMarkCallback; - - libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_END); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME); -} - -TtsPlatformImplLinux::~TtsPlatformImplLinux() { - base::AutoLock lock(initialization_lock_); - if (conn_) { - libspeechd_loader_.spd_close(conn_); - conn_ = NULL; - } -} - -void TtsPlatformImplLinux::Reset() { - base::AutoLock lock(initialization_lock_); - if (conn_) - libspeechd_loader_.spd_close(conn_); - conn_ = libspeechd_loader_.spd_open( - "chrome", "extension_api", NULL, SPD_MODE_THREADED); -} - -bool TtsPlatformImplLinux::PlatformImplAvailable() { - if (!initialization_lock_.Try()) - return false; - bool result = libspeechd_loader_.loaded() && (conn_ != NULL); - initialization_lock_.Release(); - return result; -} - -bool TtsPlatformImplLinux::Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) { - if (!PlatformImplAvailable()) { - error_ = kNotSupportedError; - return false; - } - - // Speech dispatcher's speech params are around 3x at either limit. - float rate = params.rate > 3 ? 3 : params.rate; - rate = params.rate < 0.334 ? 0.334 : rate; - float pitch = params.pitch > 3 ? 3 : params.pitch; - pitch = params.pitch < 0.334 ? 0.334 : pitch; - - std::map::iterator it = - all_native_voices_->find(voice.name); - if (it != all_native_voices_->end()) { - libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str()); - libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str()); - } - - // Map our multiplicative range to Speech Dispatcher's linear range. - // .334 = -100. - // 3 = 100. - libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3)); - libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3)); - - // Support languages other than the default - if (!lang.empty()) - libspeechd_loader_.spd_set_language(conn_, lang.c_str()); - - utterance_ = utterance; - utterance_id_ = utterance_id; - - if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) { - Reset(); - return false; - } - return true; -} - -bool TtsPlatformImplLinux::StopSpeaking() { - if (!PlatformImplAvailable()) - return false; - if (libspeechd_loader_.spd_stop(conn_) == -1) { - Reset(); - return false; - } - return true; -} - -void TtsPlatformImplLinux::Pause() { - if (!PlatformImplAvailable()) - return; - libspeechd_loader_.spd_pause(conn_); -} - -void TtsPlatformImplLinux::Resume() { - if (!PlatformImplAvailable()) - return; - libspeechd_loader_.spd_resume(conn_); -} - -bool TtsPlatformImplLinux::IsSpeaking() { - return current_notification_ == SPD_EVENT_BEGIN; -} - -void TtsPlatformImplLinux::GetVoices( - std::vector* out_voices) { - if (!all_native_voices_.get()) { - all_native_voices_.reset(new std::map()); - char** modules = libspeechd_loader_.spd_list_modules(conn_); - if (!modules) - return; - for (int i = 0; modules[i]; i++) { - char* module = modules[i]; - libspeechd_loader_.spd_set_output_module(conn_, module); - SPDVoice** native_voices = - libspeechd_loader_.spd_list_synthesis_voices(conn_); - if (!native_voices) { - free(module); - continue; - } - for (int j = 0; native_voices[j]; j++) { - SPDVoice* native_voice = native_voices[j]; - SPDChromeVoice native_data; - native_data.name = native_voice->name; - native_data.module = module; - std::string key; - key.append(native_data.name); - key.append(" "); - key.append(native_data.module); - all_native_voices_->insert( - std::pair(key, native_data)); - free(native_voices[j]); - } - free(modules[i]); - } - } - - for (std::map::iterator it = - all_native_voices_->begin(); - it != all_native_voices_->end(); - it++) { - out_voices->push_back(VoiceData()); - VoiceData& voice = out_voices->back(); - voice.native = true; - voice.name = it->first; - voice.events.insert(TTS_EVENT_START); - voice.events.insert(TTS_EVENT_END); - voice.events.insert(TTS_EVENT_CANCELLED); - voice.events.insert(TTS_EVENT_MARKER); - voice.events.insert(TTS_EVENT_PAUSE); - voice.events.insert(TTS_EVENT_RESUME); - } -} - -void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) { - TtsController* controller = TtsController::GetInstance(); - switch (type) { - case SPD_EVENT_BEGIN: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string()); - break; - case SPD_EVENT_RESUME: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string()); - break; - case SPD_EVENT_END: - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_END, utterance_.size(), std::string()); - break; - case SPD_EVENT_PAUSE: - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_PAUSE, utterance_.size(), std::string()); - break; - case SPD_EVENT_CANCEL: - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_CANCELLED, 0, std::string()); - break; - case SPD_EVENT_INDEX_MARK: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string()); - break; - } -} - -// static -void TtsPlatformImplLinux::NotificationCallback( - size_t msg_id, size_t client_id, SPDNotificationType type) { - // We run Speech Dispatcher in threaded mode, so these callbacks should always - // be in a separate thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - current_notification_ = type; - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&TtsPlatformImplLinux::OnSpeechEvent, - base::Unretained(TtsPlatformImplLinux::GetInstance()), - type)); - } -} - -// static -void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id, - size_t client_id, - SPDNotificationType state, - char* index_mark) { - // TODO(dtseng): index_mark appears to specify an index type supplied by a - // client. Need to explore how this is used before hooking it up with existing - // word, sentence events. - // We run Speech Dispatcher in threaded mode, so these callbacks should always - // be in a separate thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - current_notification_ = state; - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TtsPlatformImplLinux::OnSpeechEvent, - base::Unretained(TtsPlatformImplLinux::GetInstance()), - state)); - } -} - -// static -TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() { - return base::Singleton< - TtsPlatformImplLinux, - base::LeakySingletonTraits>::get(); -} - -// static -TtsPlatformImpl* TtsPlatformImpl::GetInstance() { - return TtsPlatformImplLinux::GetInstance(); -} diff --git a/chromium_src/chrome/browser/speech/tts_mac.mm b/chromium_src/chrome/browser/speech/tts_mac.mm deleted file mode 100644 index aafbd46925..0000000000 --- a/chromium_src/chrome/browser/speech/tts_mac.mm +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 - -#include "base/mac/scoped_nsobject.h" -#include "base/memory/singleton.h" -#include "base/strings/sys_string_conversions.h" -#include "base/values.h" -#include "chrome/browser/speech/tts_controller.h" -#include "chrome/browser/speech/tts_platform.h" - -#import - -class TtsPlatformImplMac; - -@interface ChromeTtsDelegate : NSObject { - @private - TtsPlatformImplMac* ttsImplMac_; // weak. -} - -- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac; - -@end - -// Subclass of NSSpeechSynthesizer that takes an utterance -// string on initialization, retains it and only allows it -// to be spoken once. -// -// We construct a new NSSpeechSynthesizer for each utterance, for -// two reasons: -// 1. To associate delegate callbacks with a particular utterance, -// without assuming anything undocumented about the protocol. -// 2. To work around http://openradar.appspot.com/radar?id=2854403, -// where Nuance voices don't retain the utterance string and -// crash when trying to call willSpeakWord. -@interface SingleUseSpeechSynthesizer : NSSpeechSynthesizer { - @private - base::scoped_nsobject utterance_; - bool didSpeak_; -} - -- (id)initWithUtterance:(NSString*)utterance; -- (bool)startSpeakingRetainedUtterance; -- (bool)startSpeakingString:(NSString*)utterance; - -@end - -class TtsPlatformImplMac : public TtsPlatformImpl { - public: - virtual bool PlatformImplAvailable() override { - return true; - } - - virtual bool Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - - virtual bool StopSpeaking() override; - - virtual void Pause() override; - - virtual void Resume() override; - - virtual bool IsSpeaking() override; - - virtual void GetVoices(std::vector* out_voices) override; - - // Called by ChromeTtsDelegate when we get a callback from the - // native speech engine. - void OnSpeechEvent(NSSpeechSynthesizer* sender, - TtsEventType event_type, - int char_index, - const std::string& error_message); - - // Get the single instance of this class. - static TtsPlatformImplMac* GetInstance(); - - private: - TtsPlatformImplMac(); - virtual ~TtsPlatformImplMac(); - - base::scoped_nsobject speech_synthesizer_; - base::scoped_nsobject delegate_; - int utterance_id_; - std::string utterance_; - int last_char_index_; - bool paused_; - - friend struct base::DefaultSingletonTraits; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac); -}; - -// static -TtsPlatformImpl* TtsPlatformImpl::GetInstance() { - return TtsPlatformImplMac::GetInstance(); -} - -bool TtsPlatformImplMac::Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) { - // TODO: convert SSML to SAPI xml. http://crbug.com/88072 - utterance_ = utterance; - paused_ = false; - - NSString* utterance_nsstring = - [NSString stringWithUTF8String:utterance_.c_str()]; - - // Deliberately construct a new speech synthesizer every time Speak is - // called, otherwise there's no way to know whether calls to the delegate - // apply to the current utterance or a previous utterance. In - // experimentation, the overhead of constructing and destructing a - // NSSpeechSynthesizer is minimal. - speech_synthesizer_.reset( - [[SingleUseSpeechSynthesizer alloc] - initWithUtterance:utterance_nsstring]); - [speech_synthesizer_ setDelegate:delegate_]; - - if (!voice.native_voice_identifier.empty()) { - NSString* native_voice_identifier = - [NSString stringWithUTF8String:voice.native_voice_identifier.c_str()]; - [speech_synthesizer_ setVoice:native_voice_identifier]; - } - - utterance_id_ = utterance_id; - - // TODO: support languages other than the default: crbug.com/88059 - - if (params.rate >= 0.0) { - // The TTS api defines rate via words per minute. Let 200 be the default. - [speech_synthesizer_ - setObject:[NSNumber numberWithInt:params.rate * 200] - forProperty:NSSpeechRateProperty error:nil]; - } - - if (params.pitch >= 0.0) { - // The input is a float from 0.0 to 2.0, with 1.0 being the default. - // Get the default pitch for this voice and modulate it by 50% - 150%. - NSError* errorCode; - NSNumber* defaultPitchObj = - [speech_synthesizer_ objectForProperty:NSSpeechPitchBaseProperty - error:&errorCode]; - int defaultPitch = defaultPitchObj ? [defaultPitchObj intValue] : 48; - int newPitch = static_cast(defaultPitch * (0.5 * params.pitch + 0.5)); - [speech_synthesizer_ - setObject:[NSNumber numberWithInt:newPitch] - forProperty:NSSpeechPitchBaseProperty error:nil]; - } - - if (params.volume >= 0.0) { - [speech_synthesizer_ - setObject: [NSNumber numberWithFloat:params.volume] - forProperty:NSSpeechVolumeProperty error:nil]; - } - - bool success = [speech_synthesizer_ startSpeakingRetainedUtterance]; - if (success) { - TtsController* controller = TtsController::GetInstance(); - controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, ""); - } - return success; -} - -bool TtsPlatformImplMac::StopSpeaking() { - if (speech_synthesizer_.get()) { - [speech_synthesizer_ stopSpeaking]; - speech_synthesizer_.reset(nil); - } - paused_ = false; - return true; -} - -void TtsPlatformImplMac::Pause() { - if (speech_synthesizer_.get() && utterance_id_ && !paused_) { - [speech_synthesizer_ pauseSpeakingAtBoundary:NSSpeechImmediateBoundary]; - paused_ = true; - TtsController::GetInstance()->OnTtsEvent( - utterance_id_, TTS_EVENT_PAUSE, last_char_index_, ""); - } -} - -void TtsPlatformImplMac::Resume() { - if (speech_synthesizer_.get() && utterance_id_ && paused_) { - [speech_synthesizer_ continueSpeaking]; - paused_ = false; - TtsController::GetInstance()->OnTtsEvent( - utterance_id_, TTS_EVENT_RESUME, last_char_index_, ""); - } -} - -bool TtsPlatformImplMac::IsSpeaking() { - if (speech_synthesizer_) - return [speech_synthesizer_ isSpeaking]; - return false; -} - -void TtsPlatformImplMac::GetVoices(std::vector* outVoices) { - NSArray* voices = [NSSpeechSynthesizer availableVoices]; - - // Create a new temporary array of the available voices with - // the default voice first. - NSMutableArray* orderedVoices = - [NSMutableArray arrayWithCapacity:[voices count]]; - NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice]; - if (defaultVoice) { - [orderedVoices addObject:defaultVoice]; - } - for (NSString* voiceIdentifier in voices) { - if (![voiceIdentifier isEqualToString:defaultVoice]) - [orderedVoices addObject:voiceIdentifier]; - } - - for (NSString* voiceIdentifier in orderedVoices) { - outVoices->push_back(VoiceData()); - VoiceData& data = outVoices->back(); - - NSDictionary* attributes = - [NSSpeechSynthesizer attributesForVoice:voiceIdentifier]; - NSString* name = [attributes objectForKey:NSVoiceName]; - NSString* gender = [attributes objectForKey:NSVoiceGender]; - NSString* localeIdentifier = - [attributes objectForKey:NSVoiceLocaleIdentifier]; - - data.native = true; - data.native_voice_identifier = base::SysNSStringToUTF8(voiceIdentifier); - data.name = base::SysNSStringToUTF8(name); - - NSDictionary* localeComponents = - [NSLocale componentsFromLocaleIdentifier:localeIdentifier]; - NSString* language = [localeComponents objectForKey:NSLocaleLanguageCode]; - NSString* country = [localeComponents objectForKey:NSLocaleCountryCode]; - if (language && country) { - data.lang = - [[NSString stringWithFormat:@"%@-%@", language, country] UTF8String]; - } else { - data.lang = base::SysNSStringToUTF8(language); - } - if ([gender isEqualToString:NSVoiceGenderMale]) - data.gender = TTS_GENDER_MALE; - else if ([gender isEqualToString:NSVoiceGenderFemale]) - data.gender = TTS_GENDER_FEMALE; - else - data.gender = TTS_GENDER_NONE; - data.events.insert(TTS_EVENT_START); - data.events.insert(TTS_EVENT_END); - data.events.insert(TTS_EVENT_WORD); - data.events.insert(TTS_EVENT_ERROR); - data.events.insert(TTS_EVENT_CANCELLED); - data.events.insert(TTS_EVENT_INTERRUPTED); - data.events.insert(TTS_EVENT_PAUSE); - data.events.insert(TTS_EVENT_RESUME); - } -} - -void TtsPlatformImplMac::OnSpeechEvent( - NSSpeechSynthesizer* sender, - TtsEventType event_type, - int char_index, - const std::string& error_message) { - // Don't send events from an utterance that's already completed. - // This depends on the fact that we construct a new NSSpeechSynthesizer - // each time we call Speak. - if (sender != speech_synthesizer_.get()) - return; - - if (event_type == TTS_EVENT_END) - char_index = utterance_.size(); - TtsController* controller = TtsController::GetInstance(); -controller->OnTtsEvent( - utterance_id_, event_type, char_index, error_message); - last_char_index_ = char_index; -} - -TtsPlatformImplMac::TtsPlatformImplMac() { - utterance_id_ = -1; - paused_ = false; - - delegate_.reset([[ChromeTtsDelegate alloc] initWithPlatformImplMac:this]); -} - -TtsPlatformImplMac::~TtsPlatformImplMac() { -} - -// static -TtsPlatformImplMac* TtsPlatformImplMac::GetInstance() { - return base::Singleton::get(); -} - -@implementation ChromeTtsDelegate - -- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac { - if ((self = [super init])) { - ttsImplMac_ = ttsImplMac; - } - return self; -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender - didFinishSpeaking:(BOOL)finished_speaking { - ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_END, 0, ""); -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender - willSpeakWord:(NSRange)character_range - ofString:(NSString*)string { - ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_WORD, - character_range.location, ""); -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender - didEncounterErrorAtIndex:(NSUInteger)character_index - ofString:(NSString*)string - message:(NSString*)message { - std::string message_utf8 = base::SysNSStringToUTF8(message); - ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index, - message_utf8); -} - -@end - -@implementation SingleUseSpeechSynthesizer - -- (id)initWithUtterance:(NSString*)utterance { - self = [super init]; - if (self) { - utterance_.reset([utterance retain]); - didSpeak_ = false; - } - return self; -} - -- (bool)startSpeakingRetainedUtterance { - CHECK(!didSpeak_); - CHECK(utterance_); - didSpeak_ = true; - return [super startSpeakingString:utterance_]; -} - -- (bool)startSpeakingString:(NSString*)utterance { - CHECK(false); - return false; -} - -@end diff --git a/chromium_src/chrome/browser/speech/tts_message_filter.cc b/chromium_src/chrome/browser/speech/tts_message_filter.cc deleted file mode 100644 index 1a198d9e26..0000000000 --- a/chromium_src/chrome/browser/speech/tts_message_filter.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/browser/speech/tts_message_filter.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/render_process_host.h" - -using content::BrowserThread; - -TtsMessageFilter::TtsMessageFilter(int render_process_id, - content::BrowserContext* browser_context) - : BrowserMessageFilter(TtsMsgStart), - render_process_id_(render_process_id), - browser_context_(browser_context), - weak_ptr_factory_(this) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->AddVoicesChangedDelegate(this); - - // Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero - // until all WeakPtr's are invalidated. - AddRef(); -} - -void TtsMessageFilter::OverrideThreadForMessage( - const IPC::Message& message, BrowserThread::ID* thread) { - switch (message.type()) { - case TtsHostMsg_InitializeVoiceList::ID: - case TtsHostMsg_Speak::ID: - case TtsHostMsg_Pause::ID: - case TtsHostMsg_Resume::ID: - case TtsHostMsg_Cancel::ID: - *thread = BrowserThread::UI; - break; - } -} - -bool TtsMessageFilter::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(TtsMessageFilter, message) - IPC_MESSAGE_HANDLER(TtsHostMsg_InitializeVoiceList, OnInitializeVoiceList) - IPC_MESSAGE_HANDLER(TtsHostMsg_Speak, OnSpeak) - IPC_MESSAGE_HANDLER(TtsHostMsg_Pause, OnPause) - IPC_MESSAGE_HANDLER(TtsHostMsg_Resume, OnResume) - IPC_MESSAGE_HANDLER(TtsHostMsg_Cancel, OnCancel) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void TtsMessageFilter::OnChannelClosing() { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&TtsMessageFilter::OnChannelClosingInUIThread, this)); -} - -void TtsMessageFilter::OnDestruct() const { - BrowserThread::DeleteOnUIThread::Destruct(this); -} - -void TtsMessageFilter::OnInitializeVoiceList() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController* tts_controller = TtsController::GetInstance(); - std::vector voices; - tts_controller->GetVoices(browser_context_, &voices); - - std::vector out_voices; - out_voices.resize(voices.size()); - for (size_t i = 0; i < voices.size(); ++i) { - TtsVoice& out_voice = out_voices[i]; - out_voice.voice_uri = voices[i].name; - out_voice.name = voices[i].name; - out_voice.lang = voices[i].lang; - out_voice.local_service = !voices[i].remote; - out_voice.is_default = (i == 0); - } - Send(new TtsMsg_SetVoiceList(out_voices)); -} - -void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - std::unique_ptr utterance(new Utterance(browser_context_)); - utterance->set_src_id(request.id); - utterance->set_text(request.text); - utterance->set_lang(request.lang); - utterance->set_voice_name(request.voice); - utterance->set_can_enqueue(true); - - UtteranceContinuousParameters params; - params.rate = request.rate; - params.pitch = request.pitch; - params.volume = request.volume; - utterance->set_continuous_parameters(params); - - utterance->set_event_delegate(weak_ptr_factory_.GetWeakPtr()); - - TtsController::GetInstance()->SpeakOrEnqueue(utterance.release()); -} - -void TtsMessageFilter::OnPause() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->Pause(); -} - -void TtsMessageFilter::OnResume() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->Resume(); -} - -void TtsMessageFilter::OnCancel() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->Stop(); -} - -void TtsMessageFilter::OnTtsEvent(Utterance* utterance, - TtsEventType event_type, - int char_index, - const std::string& error_message) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - switch (event_type) { - case TTS_EVENT_START: - Send(new TtsMsg_DidStartSpeaking(utterance->src_id())); - break; - case TTS_EVENT_END: - Send(new TtsMsg_DidFinishSpeaking(utterance->src_id())); - break; - case TTS_EVENT_WORD: - Send(new TtsMsg_WordBoundary(utterance->src_id(), char_index)); - break; - case TTS_EVENT_SENTENCE: - Send(new TtsMsg_SentenceBoundary(utterance->src_id(), char_index)); - break; - case TTS_EVENT_MARKER: - Send(new TtsMsg_MarkerEvent(utterance->src_id(), char_index)); - break; - case TTS_EVENT_INTERRUPTED: - Send(new TtsMsg_WasInterrupted(utterance->src_id())); - break; - case TTS_EVENT_CANCELLED: - Send(new TtsMsg_WasCancelled(utterance->src_id())); - break; - case TTS_EVENT_ERROR: - Send(new TtsMsg_SpeakingErrorOccurred( - utterance->src_id(), error_message)); - break; - case TTS_EVENT_PAUSE: - Send(new TtsMsg_DidPauseSpeaking(utterance->src_id())); - break; - case TTS_EVENT_RESUME: - Send(new TtsMsg_DidResumeSpeaking(utterance->src_id())); - break; - } -} - -void TtsMessageFilter::OnVoicesChanged() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - OnInitializeVoiceList(); -} - -void TtsMessageFilter::OnChannelClosingInUIThread() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); - - weak_ptr_factory_.InvalidateWeakPtrs(); - Release(); // Balanced in TtsMessageFilter(). -} - -TtsMessageFilter::~TtsMessageFilter() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!weak_ptr_factory_.HasWeakPtrs()); - TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); -} \ No newline at end of file diff --git a/chromium_src/chrome/browser/speech/tts_message_filter.h b/chromium_src/chrome/browser/speech/tts_message_filter.h deleted file mode 100644 index b095651611..0000000000 --- a/chromium_src/chrome/browser/speech/tts_message_filter.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_ - -#include "base/memory/weak_ptr.h" -#include "chrome/browser/speech/tts_controller.h" -#include "chrome/common/tts_messages.h" -#include "content/public/browser/browser_message_filter.h" - -namespace content { -class BrowserContext; -} - -class TtsMessageFilter - : public content::BrowserMessageFilter, - public UtteranceEventDelegate, - public VoicesChangedDelegate { - public: - explicit TtsMessageFilter(int render_process_id, - content::BrowserContext* browser_context); - - // content::BrowserMessageFilter implementation. - void OverrideThreadForMessage( - const IPC::Message& message, - content::BrowserThread::ID* thread) override; - bool OnMessageReceived(const IPC::Message& message) override; - void OnChannelClosing() override; - void OnDestruct() const override; - - // UtteranceEventDelegate implementation. - void OnTtsEvent(Utterance* utterance, - TtsEventType event_type, - int char_index, - const std::string& error_message) override; - - // VoicesChangedDelegate implementation. - void OnVoicesChanged() override; - - private: - friend class content::BrowserThread; - friend class base::DeleteHelper; - - virtual ~TtsMessageFilter(); - - void OnInitializeVoiceList(); - void OnSpeak(const TtsUtteranceRequest& utterance); - void OnPause(); - void OnResume(); - void OnCancel(); - - void OnChannelClosingInUIThread(); - - int render_process_id_; - content::BrowserContext* browser_context_; - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter); -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/speech/tts_platform.cc b/chromium_src/chrome/browser/speech/tts_platform.cc deleted file mode 100644 index 220e005f18..0000000000 --- a/chromium_src/chrome/browser/speech/tts_platform.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/browser/speech/tts_platform.h" - -#include - -bool TtsPlatformImpl::LoadBuiltInTtsExtension( - content::BrowserContext* browser_context) { - return false; -} - -std::string TtsPlatformImpl::error() { - return error_; -} - -void TtsPlatformImpl::clear_error() { - error_ = std::string(); -} - -void TtsPlatformImpl::set_error(const std::string& error) { - error_ = error; -} - -void TtsPlatformImpl::WillSpeakUtteranceWithVoice(const Utterance* utterance, - const VoiceData& voice_data) { -} \ No newline at end of file diff --git a/chromium_src/chrome/browser/speech/tts_platform.h b/chromium_src/chrome/browser/speech/tts_platform.h deleted file mode 100644 index f33eab1c18..0000000000 --- a/chromium_src/chrome/browser/speech/tts_platform.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_ -#define CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_ - -#include - -#include "chrome/browser/speech/tts_controller.h" - -// Abstract class that defines the native platform TTS interface, -// subclassed by specific implementations on Win, Mac, etc. -class TtsPlatformImpl { - public: - static TtsPlatformImpl* GetInstance(); - - // Returns true if this platform implementation is supported and available. - virtual bool PlatformImplAvailable() = 0; - - // Some platforms may provide a built-in TTS extension. Returns true - // if the extension was not previously loaded and is now loading, and - // false if it's already loaded or if there's no extension to load. - // Will call TtsController::RetrySpeakingQueuedUtterances when - // the extension finishes loading. - virtual bool LoadBuiltInTtsExtension( - content::BrowserContext* browser_context); - - // Speak the given utterance with the given parameters if possible, - // and return true on success. Utterance will always be nonempty. - // If rate, pitch, or volume are -1.0, they will be ignored. - // - // The TtsController will only try to speak one utterance at - // a time. If it wants to interrupt speech, it will always call Stop - // before speaking again. - virtual bool Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) = 0; - - // Stop speaking immediately and return true on success. - virtual bool StopSpeaking() = 0; - - // Returns whether any speech is on going. - virtual bool IsSpeaking() = 0; - - // Append information about voices provided by this platform implementation - // to |out_voices|. - virtual void GetVoices(std::vector* out_voices) = 0; - - // Pause the current utterance, if any, until a call to Resume, - // Speak, or StopSpeaking. - virtual void Pause() = 0; - - // Resume speaking the current utterance, if it was paused. - virtual void Resume() = 0; - - // Allows the platform to monitor speech commands and the voices used - // for each one. - virtual void WillSpeakUtteranceWithVoice(const Utterance* utterance, - const VoiceData& voice_data); - - virtual std::string error(); - virtual void clear_error(); - virtual void set_error(const std::string& error); - - protected: - TtsPlatformImpl() {} - - // On some platforms this may be a leaky singleton - do not rely on the - // destructor being called! http://crbug.com/122026 - virtual ~TtsPlatformImpl() {} - - std::string error_; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl); -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_ \ No newline at end of file diff --git a/chromium_src/chrome/browser/speech/tts_win.cc b/chromium_src/chrome/browser/speech/tts_win.cc deleted file mode 100644 index bc9411a8c1..0000000000 --- a/chromium_src/chrome/browser/speech/tts_win.cc +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 -#include - -#include "base/memory/singleton.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "base/win/scoped_comptr.h" -#include "chrome/browser/speech/tts_controller.h" -#include "chrome/browser/speech/tts_platform.h" - -class TtsPlatformImplWin : public TtsPlatformImpl { - public: - bool PlatformImplAvailable() override { - return true; - } - - bool Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - - bool StopSpeaking() override; - - void Pause() override; - - void Resume() override; - - bool IsSpeaking() override; - - void GetVoices(std::vector* out_voices) override; - - // Get the single instance of this class. - static TtsPlatformImplWin* GetInstance(); - - static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param); - - private: - TtsPlatformImplWin(); - ~TtsPlatformImplWin() override {} - - void OnSpeechEvent(); - - base::win::ScopedComPtr speech_synthesizer_; - - // These apply to the current utterance only. - std::wstring utterance_; - int utterance_id_; - int prefix_len_; - ULONG stream_number_; - int char_position_; - bool paused_; - - friend struct base::DefaultSingletonTraits; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin); -}; - -// static -TtsPlatformImpl* TtsPlatformImpl::GetInstance() { - return TtsPlatformImplWin::GetInstance(); -} - -bool TtsPlatformImplWin::Speak( - int utterance_id, - const std::string& src_utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) { - std::wstring prefix; - std::wstring suffix; - - if (!speech_synthesizer_.get()) - return false; - - // TODO(dmazzoni): support languages other than the default: crbug.com/88059 - - if (params.rate >= 0.0) { - // Map our multiplicative range of 0.1x to 10.0x onto Microsoft's - // linear range of -10 to 10: - // 0.1 -> -10 - // 1.0 -> 0 - // 10.0 -> 10 - speech_synthesizer_->SetRate(static_cast(10 * log10(params.rate))); - } - - if (params.pitch >= 0.0) { - // The TTS api allows a range of -10 to 10 for speech pitch. - // TODO(dtseng): cleanup if we ever use any other properties that - // require xml. - std::wstring pitch_value = - base::IntToString16(static_cast(params.pitch * 10 - 10)); - prefix = L""; - suffix = L""; - } - - if (params.volume >= 0.0) { - // The TTS api allows a range of 0 to 100 for speech volume. - speech_synthesizer_->SetVolume(static_cast(params.volume * 100)); - } - - // TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072 - - utterance_ = base::UTF8ToWide(src_utterance); - utterance_id_ = utterance_id; - char_position_ = 0; - std::wstring merged_utterance = prefix + utterance_ + suffix; - prefix_len_ = prefix.size(); - - HRESULT result = speech_synthesizer_->Speak( - merged_utterance.c_str(), - SPF_ASYNC, - &stream_number_); - return (result == S_OK); -} - -bool TtsPlatformImplWin::StopSpeaking() { - if (speech_synthesizer_.get()) { - // Clear the stream number so that any further events relating to this - // utterance are ignored. - stream_number_ = 0; - - if (IsSpeaking()) { - // Stop speech by speaking the empty string with the purge flag. - speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); - } - if (paused_) { - speech_synthesizer_->Resume(); - paused_ = false; - } - } - return true; -} - -void TtsPlatformImplWin::Pause() { - if (speech_synthesizer_.get() && utterance_id_ && !paused_) { - speech_synthesizer_->Pause(); - paused_ = true; - TtsController::GetInstance()->OnTtsEvent( - utterance_id_, TTS_EVENT_PAUSE, char_position_, ""); - } -} - -void TtsPlatformImplWin::Resume() { - if (speech_synthesizer_.get() && utterance_id_ && paused_) { - speech_synthesizer_->Resume(); - paused_ = false; - TtsController::GetInstance()->OnTtsEvent( - utterance_id_, TTS_EVENT_RESUME, char_position_, ""); - } -} - -bool TtsPlatformImplWin::IsSpeaking() { - if (speech_synthesizer_.get()) { - SPVOICESTATUS status; - HRESULT result = speech_synthesizer_->GetStatus(&status, NULL); - if (result == S_OK) { - if (status.dwRunningState == 0 || // 0 == waiting to speak - status.dwRunningState == SPRS_IS_SPEAKING) { - return true; - } - } - } - return false; -} - -void TtsPlatformImplWin::GetVoices( - std::vector* out_voices) { - // TODO: get all voices, not just default voice. - // http://crbug.com/88059 - out_voices->push_back(VoiceData()); - VoiceData& voice = out_voices->back(); - voice.native = true; - voice.name = "native"; - voice.events.insert(TTS_EVENT_START); - voice.events.insert(TTS_EVENT_END); - voice.events.insert(TTS_EVENT_MARKER); - voice.events.insert(TTS_EVENT_WORD); - voice.events.insert(TTS_EVENT_SENTENCE); - voice.events.insert(TTS_EVENT_PAUSE); - voice.events.insert(TTS_EVENT_RESUME); -} - -void TtsPlatformImplWin::OnSpeechEvent() { - TtsController* controller = TtsController::GetInstance(); - SPEVENT event; - while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) { - if (event.ulStreamNum != stream_number_) - continue; - - switch (event.eEventId) { - case SPEI_START_INPUT_STREAM: - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_START, 0, std::string()); - break; - case SPEI_END_INPUT_STREAM: - char_position_ = utterance_.size(); - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_END, char_position_, std::string()); - break; - case SPEI_TTS_BOOKMARK: - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_MARKER, char_position_, std::string()); - break; - case SPEI_WORD_BOUNDARY: - char_position_ = static_cast(event.lParam) - prefix_len_; - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_WORD, char_position_, - std::string()); - break; - case SPEI_SENTENCE_BOUNDARY: - char_position_ = static_cast(event.lParam) - prefix_len_; - controller->OnTtsEvent( - utterance_id_, TTS_EVENT_SENTENCE, char_position_, - std::string()); - break; - default: - break; - } - } -} - -TtsPlatformImplWin::TtsPlatformImplWin() - : utterance_id_(0), - prefix_len_(0), - stream_number_(0), - char_position_(0), - paused_(false) { - speech_synthesizer_.CreateInstance(CLSID_SpVoice); - if (speech_synthesizer_.get()) { - ULONGLONG event_mask = - SPFEI(SPEI_START_INPUT_STREAM) | - SPFEI(SPEI_TTS_BOOKMARK) | - SPFEI(SPEI_WORD_BOUNDARY) | - SPFEI(SPEI_SENTENCE_BOUNDARY) | - SPFEI(SPEI_END_INPUT_STREAM); - speech_synthesizer_->SetInterest(event_mask, event_mask); - speech_synthesizer_->SetNotifyCallbackFunction( - TtsPlatformImplWin::SpeechEventCallback, 0, 0); - } -} - -// static -TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() { - return base::Singleton>::get(); -} - -// static -void TtsPlatformImplWin::SpeechEventCallback( - WPARAM w_param, LPARAM l_param) { - GetInstance()->OnSpeechEvent(); -} diff --git a/chromium_src/chrome/browser/ui/browser_otr_state.cc b/chromium_src/chrome/browser/ui/browser_otr_state.cc new file mode 100644 index 0000000000..d2685cc964 --- /dev/null +++ b/chromium_src/chrome/browser/ui/browser_otr_state.cc @@ -0,0 +1,13 @@ +// Copyright (c) 2012 The Chromium 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 "chrome/browser/ui/browser_otr_state.h" + +namespace chrome { + +bool IsIncognitoSessionActive() { + return true; +} + +} // namespace chrome diff --git a/chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm b/chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm deleted file mode 100644 index 6183dd5d5b..0000000000 --- a/chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 - -#include "base/logging.h" -#import "base/mac/scoped_nsobject.h" -#include "chrome/browser/ui/browser_dialogs.h" -#include "content/public/browser/color_chooser.h" -#include "content/public/browser/web_contents.h" -#include "skia/ext/skia_utils_mac.h" - -class ColorChooserMac; - -// A Listener class to act as a event target for NSColorPanel and send -// the results to the C++ class, ColorChooserMac. -@interface ColorPanelCocoa : NSObject { - @private - // We don't call DidChooseColor if the change wasn't caused by the user - // interacting with the panel. - BOOL nonUserChange_; - ColorChooserMac* chooser_; // weak, owns this -} - -- (id)initWithChooser:(ColorChooserMac*)chooser; - -// Called from NSColorPanel. -- (void)didChooseColor:(NSColorPanel*)panel; - -// Sets color to the NSColorPanel as a non user change. -- (void)setColor:(NSColor*)color; - -@end - -class ColorChooserMac : public content::ColorChooser { - public: - static ColorChooserMac* Open(content::WebContents* web_contents, - SkColor initial_color); - - ColorChooserMac(content::WebContents* tab, SkColor initial_color); - virtual ~ColorChooserMac(); - - // Called from ColorPanelCocoa. - void DidChooseColorInColorPanel(SkColor color); - void DidCloseColorPabel(); - - virtual void End() override; - virtual void SetSelectedColor(SkColor color) override; - - private: - static ColorChooserMac* current_color_chooser_; - - // The web contents invoking the color chooser. No ownership because it will - // outlive this class. - content::WebContents* web_contents_; - base::scoped_nsobject panel_; -}; - -ColorChooserMac* ColorChooserMac::current_color_chooser_ = NULL; - -// static -ColorChooserMac* ColorChooserMac::Open(content::WebContents* web_contents, - SkColor initial_color) { - if (current_color_chooser_) - current_color_chooser_->End(); - DCHECK(!current_color_chooser_); - current_color_chooser_ = - new ColorChooserMac(web_contents, initial_color); - return current_color_chooser_; -} - -ColorChooserMac::ColorChooserMac(content::WebContents* web_contents, - SkColor initial_color) - : web_contents_(web_contents) { - panel_.reset([[ColorPanelCocoa alloc] initWithChooser:this]); - [panel_ setColor:skia::SkColorToDeviceNSColor(initial_color)]; - [[NSColorPanel sharedColorPanel] makeKeyAndOrderFront:nil]; -} - -ColorChooserMac::~ColorChooserMac() { - // Always call End() before destroying. - DCHECK(!panel_); -} - -void ColorChooserMac::DidChooseColorInColorPanel(SkColor color) { - if (web_contents_) - web_contents_->DidChooseColorInColorChooser(color); -} - -void ColorChooserMac::DidCloseColorPabel() { - End(); -} - -void ColorChooserMac::End() { - panel_.reset(); - DCHECK(current_color_chooser_ == this); - current_color_chooser_ = NULL; - if (web_contents_) - web_contents_->DidEndColorChooser(); -} - -void ColorChooserMac::SetSelectedColor(SkColor color) { - [panel_ setColor:skia::SkColorToDeviceNSColor(color)]; -} - -@implementation ColorPanelCocoa - -- (id)initWithChooser:(ColorChooserMac*)chooser { - if ((self = [super init])) { - chooser_ = chooser; - NSColorPanel* panel = [NSColorPanel sharedColorPanel]; - [panel setShowsAlpha:NO]; - [panel setDelegate:self]; - [panel setTarget:self]; - [panel setAction:@selector(didChooseColor:)]; - } - return self; -} - -- (void)dealloc { - NSColorPanel* panel = [NSColorPanel sharedColorPanel]; - if ([panel delegate] == self) { - [panel setDelegate:nil]; - [panel setTarget:nil]; - [panel setAction:nil]; - } - - [super dealloc]; -} - -- (void)windowWillClose:(NSNotification*)notification { - nonUserChange_ = NO; - chooser_->DidCloseColorPabel(); -} - -- (void)didChooseColor:(NSColorPanel*)panel { - if (nonUserChange_) { - nonUserChange_ = NO; - return; - } - chooser_->DidChooseColorInColorPanel(skia::NSDeviceColorToSkColor( - [[panel color] colorUsingColorSpaceName:NSDeviceRGBColorSpace])); - nonUserChange_ = NO; -} - -- (void)setColor:(NSColor*)color { - nonUserChange_ = YES; - [[NSColorPanel sharedColorPanel] setColor:color]; -} - -namespace chrome { - -content::ColorChooser* ShowColorChooser(content::WebContents* web_contents, - SkColor initial_color) { - return ColorChooserMac::Open(web_contents, initial_color); -} - -} // namepace chrome - -@end diff --git a/chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc new file mode 100644 index 0000000000..9b00c661c6 --- /dev/null +++ b/chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc @@ -0,0 +1,192 @@ +// Copyright 2016 Chrome 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 "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" + +#include "base/files/file_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/url_constants.h" +#include "content/browser/webui/web_ui_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/url_data_source.h" +#include "content/public/browser/web_ui.h" +#include "content/public/browser/web_ui_controller.h" +#include "net/base/filename_util.h" +#include "net/base/mime_util.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request.h" +#include "url/gurl.h" + +using content::BrowserThread; +using content::WebUI; +using content::WebUIController; + +namespace { + +const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n"; + +class BraveDataSource : public content::URLDataSource, + public net::URLFetcherDelegate { + public: + BraveDataSource( + net::URLRequestContextGetter* request_context) + : request_context_(request_context) { + } + + // content::URLDataSource implementation. + std::string GetSource() const override { + return "brave"; + } + + void StartDataRequest(const std::string& path, + int render_process_id, + int render_frame_id, + const GotDataCallback& callback) override { + GURL url = GURL("file:///" + path); + if (!url.is_valid()) { + DLOG(WARNING) << "Invalid webui resource: brave://" << path; + callback.Run( + new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound))); + return; + } + net::URLFetcher* fetcher = + net::URLFetcher::Create(url, net::URLFetcher::GET, this).release(); + pending_[fetcher] = callback; + fetcher->SetRequestContext(request_context_.get()); + fetcher->Start(); + } + + void OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK(source); + PendingRequestsMap::iterator it = pending_.find(source); + DCHECK(it != pending_.end()); + std::string response; + source->GetResponseAsString(&response); + delete source; + it->second.Run(base::RefCountedString::TakeString(&response)); + pending_.erase(it); + } + + std::string GetMimeType(const std::string& path) const override { + const GURL url("file:///" + path); + + std::string mime_type; + base::FilePath file_path; + if (!net::FileURLToFilePath(url, &file_path) || + !net::GetMimeTypeFromFile(file_path, &mime_type)) { + DLOG(WARNING) << "Could not get mime type for: file://" << path; + } + + return mime_type; + } + + bool ShouldAddContentSecurityPolicy() const override { + return false; + } + + bool ShouldDenyXFrameOptions() const override { + return false; + } + + bool ShouldServeMimeTypeAsContentTypeHeader() const override { + return true; + } + + bool ShouldServiceRequest(const net::URLRequest* request) const override { + GURL url = request->url(); + return url.SchemeIs(content::kChromeUIScheme) && url.host() == "brave"; + } + + private: + ~BraveDataSource() override {} + scoped_refptr request_context_; + + using PendingRequestsMap = std::map; + PendingRequestsMap pending_; + + DISALLOW_COPY_AND_ASSIGN(BraveDataSource); +}; + +class BraveWebUIController : public WebUIController { + public: + BraveWebUIController(WebUI* web_ui) : WebUIController(web_ui) { + Profile* profile = Profile::FromWebUI(web_ui); + content::URLDataSource::Add( + profile, + new BraveDataSource(profile->GetRequestContext())); + } + bool OverrideHandleWebUIMessage(const GURL& source_url, + const std::string& message, + const base::ListValue& args) override { + return false; + } + + void RenderViewCreated(content::RenderViewHost* render_view_host) {} +}; + +typedef WebUIController* (*WebUIFactoryFunction)(WebUI* web_ui, + const GURL& url); + +// Template for defining WebUIFactoryFunction. +template +WebUIController* NewWebUI(WebUI* web_ui, const GURL& url) { + return new T(web_ui); +} + +WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, + Profile* profile, + const GURL& url) { + if (!url.SchemeIs(content::kChromeUIScheme)) { + return NULL; + } + + if (url.host() == "brave") { + return &NewWebUI; + } + + return NULL; +} + +} // namespace + +WebUI::TypeID ChromeWebUIControllerFactory::GetWebUIType( + content::BrowserContext* browser_context, const GURL& url) const { + Profile* profile = Profile::FromBrowserContext(browser_context); + WebUIFactoryFunction function = GetWebUIFactoryFunction(NULL, profile, url); + return function ? reinterpret_cast(function) : WebUI::kNoWebUI; +} + +bool ChromeWebUIControllerFactory::UseWebUIForURL( + content::BrowserContext* browser_context, const GURL& url) const { + return GetWebUIType(browser_context, url) != WebUI::kNoWebUI; +} + +bool ChromeWebUIControllerFactory::UseWebUIBindingsForURL( + content::BrowserContext* browser_context, const GURL& url) const { + return UseWebUIForURL(browser_context, url); +} + +WebUIController* +ChromeWebUIControllerFactory::CreateWebUIControllerForURL( + WebUI* web_ui, + const GURL& url) const { + Profile* profile = Profile::FromWebUI(web_ui); + WebUIFactoryFunction function = GetWebUIFactoryFunction(web_ui, profile, url); + if (!function) + return NULL; + + return (*function)(web_ui, url); +} + +// static +ChromeWebUIControllerFactory* ChromeWebUIControllerFactory::GetInstance() { + return base::Singleton::get(); +} + +ChromeWebUIControllerFactory::ChromeWebUIControllerFactory() { +} + +ChromeWebUIControllerFactory::~ChromeWebUIControllerFactory() { +} diff --git a/chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h b/chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h new file mode 100644 index 0000000000..76f54af412 --- /dev/null +++ b/chromium_src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h @@ -0,0 +1,40 @@ +// Copyright 2016 Chrome 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 CHROME_BROWSER_UI_WEBUI_CHROME_WEB_UI_CONTROLLER_FACTORY_H_ +#define CHROME_BROWSER_UI_WEBUI_CHROME_WEB_UI_CONTROLLER_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "content/public/browser/web_ui.h" +#include "content/public/browser/web_ui_controller_factory.h" +#include "ui/base/layout.h" + +class ChromeWebUIControllerFactory : public content::WebUIControllerFactory { + public: + content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context, + const GURL& url) const override; + bool UseWebUIForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + bool UseWebUIBindingsForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + content::WebUIController* CreateWebUIControllerForURL( + content::WebUI* web_ui, + const GURL& url) const override; + + static ChromeWebUIControllerFactory* GetInstance(); + + protected: + ChromeWebUIControllerFactory(); + ~ChromeWebUIControllerFactory() override; + + private: + friend struct base::DefaultSingletonTraits; + + DISALLOW_COPY_AND_ASSIGN(ChromeWebUIControllerFactory); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_CHROME_WEB_UI_CONTROLLER_FACTORY_H_ + + diff --git a/chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc b/chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc deleted file mode 100644 index 65576ae8c3..0000000000 --- a/chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h" - -#include - -#include "base/bind.h" -#include "base/strings/string_number_conversions.h" -#include "base/values.h" -#include "chrome/common/chrome_constants.h" -#include "chrome/common/pref_names.h" -#include "components/prefs/json_pref_store.h" -#include "components/prefs/pref_filter.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/prefs/pref_service_factory.h" -#include "components/prefs/scoped_user_pref_update.h" -#include "components/zoom/zoom_event_manager.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/host_zoom_map.h" -#include "content/public/common/page_zoom.h" - -namespace { - -std::string GetHash(const base::FilePath& relative_path) { - size_t int_key = BASE_HASH_NAMESPACE::hash()(relative_path); - return base::SizeTToString(int_key); -} - -} // namespace - -ChromeZoomLevelPrefs::ChromeZoomLevelPrefs( - PrefService* pref_service, - const base::FilePath& profile_path, - const base::FilePath& partition_path, - base::WeakPtr zoom_event_manager) - : pref_service_(pref_service), - zoom_event_manager_(zoom_event_manager), - host_zoom_map_(nullptr) { - DCHECK(pref_service_); - - DCHECK(!partition_path.empty()); - DCHECK((partition_path == profile_path) || - profile_path.IsParent(partition_path)); - // Create a partition_key string with no '.'s in it. For the default - // StoragePartition, this string will always be "0". - base::FilePath partition_relative_path; - profile_path.AppendRelativePath(partition_path, &partition_relative_path); - partition_key_ = GetHash(partition_relative_path); - -} - -ChromeZoomLevelPrefs::~ChromeZoomLevelPrefs() { -} - -std::string ChromeZoomLevelPrefs::GetHashForTesting( - const base::FilePath& relative_path) { - return GetHash(relative_path); -} - -void ChromeZoomLevelPrefs::SetDefaultZoomLevelPref(double level) { - if (content::ZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel())) - return; - - DictionaryPrefUpdate update(pref_service_, prefs::kPartitionDefaultZoomLevel); - update->SetDouble(partition_key_, level); - // For unregistered paths, OnDefaultZoomLevelChanged won't be called, so - // set this manually. - host_zoom_map_->SetDefaultZoomLevel(level); - default_zoom_changed_callbacks_.Notify(); - if (zoom_event_manager_) - zoom_event_manager_->OnDefaultZoomLevelChanged(); -} - -double ChromeZoomLevelPrefs::GetDefaultZoomLevelPref() const { - double default_zoom_level = 0.0; - - const base::DictionaryValue* default_zoom_level_dictionary = - pref_service_->GetDictionary(prefs::kPartitionDefaultZoomLevel); - // If no default has been previously set, the default returned is the - // value used to initialize default_zoom_level in this function. - default_zoom_level_dictionary->GetDouble(partition_key_, &default_zoom_level); - return default_zoom_level; -} - -std::unique_ptr -ChromeZoomLevelPrefs::RegisterDefaultZoomLevelCallback( - const base::Closure& callback) { - return default_zoom_changed_callbacks_.Add(callback); -} - -void ChromeZoomLevelPrefs::OnZoomLevelChanged( - const content::HostZoomMap::ZoomLevelChange& change) { - // If there's a manager to aggregate ZoomLevelChanged events, pass this event - // along. Since we already hold a subscription to our associated HostZoomMap, - // we don't need to create a separate subscription for this. - if (zoom_event_manager_) - zoom_event_manager_->OnZoomLevelChanged(change); - - if (change.mode != content::HostZoomMap::ZOOM_CHANGED_FOR_HOST) - return; - double level = change.zoom_level; - DictionaryPrefUpdate update(pref_service_, - prefs::kPartitionPerHostZoomLevels); - base::DictionaryValue* host_zoom_dictionaries = update.Get(); - DCHECK(host_zoom_dictionaries); - - bool modification_is_removal = - content::ZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel()); - - base::DictionaryValue* host_zoom_dictionary = nullptr; - if (!host_zoom_dictionaries->GetDictionary(partition_key_, - &host_zoom_dictionary)) { - host_zoom_dictionary = new base::DictionaryValue(); - host_zoom_dictionaries->Set(partition_key_, host_zoom_dictionary); - } - - if (modification_is_removal) - host_zoom_dictionary->RemoveWithoutPathExpansion(change.host, nullptr); - else - host_zoom_dictionary->SetDoubleWithoutPathExpansion(change.host, level); -} - -// TODO(wjmaclean): Remove the dictionary_path once the migration code is -// removed. crbug.com/420643 -void ChromeZoomLevelPrefs::ExtractPerHostZoomLevels( - const base::DictionaryValue* host_zoom_dictionary, - bool sanitize_partition_host_zoom_levels) { - std::vector keys_to_remove; - std::unique_ptr host_zoom_dictionary_copy = - host_zoom_dictionary->DeepCopyWithoutEmptyChildren(); - for (base::DictionaryValue::Iterator i(*host_zoom_dictionary_copy); - !i.IsAtEnd(); - i.Advance()) { - const std::string& host(i.key()); - double zoom_level = 0; - - bool has_valid_zoom_level = i.value().GetAsDouble(&zoom_level); - - // Filter out A) the empty host, B) zoom levels equal to the default; and - // remember them, so that we can later erase them from Prefs. - // Values of type A and B could have been stored due to crbug.com/364399. - // Values of type B could further have been stored before the default zoom - // level was set to its current value. In either case, SetZoomLevelForHost - // will ignore type B values, thus, to have consistency with HostZoomMap's - // internal state, these values must also be removed from Prefs. - if (host.empty() || !has_valid_zoom_level || - content::ZoomValuesEqual(zoom_level, - host_zoom_map_->GetDefaultZoomLevel())) { - keys_to_remove.push_back(host); - continue; - } - - host_zoom_map_->SetZoomLevelForHost(host, zoom_level); - } - - // We don't bother sanitizing non-partition dictionaries as they will be - // discarded in the migration process. Note: since the structure of partition - // per-host zoom level dictionaries is different from the legacy profile - // per-host zoom level dictionaries, the following code will fail if run - // on the legacy dictionaries. - if (!sanitize_partition_host_zoom_levels) - return; - - // Sanitize prefs to remove entries that match the default zoom level and/or - // have an empty host. - { - DictionaryPrefUpdate update(pref_service_, - prefs::kPartitionPerHostZoomLevels); - base::DictionaryValue* host_zoom_dictionaries = update.Get(); - base::DictionaryValue* host_zoom_dictionary = nullptr; - host_zoom_dictionaries->GetDictionary(partition_key_, - &host_zoom_dictionary); - for (const std::string& s : keys_to_remove) - host_zoom_dictionary->RemoveWithoutPathExpansion(s, nullptr); - } -} - -void ChromeZoomLevelPrefs::InitHostZoomMap( - content::HostZoomMap* host_zoom_map) { - // This init function must be called only once. - DCHECK(!host_zoom_map_); - DCHECK(host_zoom_map); - host_zoom_map_ = host_zoom_map; - - // Initialize the default zoom level. - host_zoom_map_->SetDefaultZoomLevel(GetDefaultZoomLevelPref()); - - // Initialize the HostZoomMap with per-host zoom levels from the persisted - // zoom-level preference values. - const base::DictionaryValue* host_zoom_dictionaries = - pref_service_->GetDictionary(prefs::kPartitionPerHostZoomLevels); - const base::DictionaryValue* host_zoom_dictionary = nullptr; - if (host_zoom_dictionaries->GetDictionary(partition_key_, - &host_zoom_dictionary)) { - // Since we're calling this before setting up zoom_subscription_ below we - // don't need to worry that host_zoom_dictionary is indirectly affected - // by calls to HostZoomMap::SetZoomLevelForHost(). - ExtractPerHostZoomLevels(host_zoom_dictionary, - true /* sanitize_partition_host_zoom_levels */); - } - zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback(base::Bind( - &ChromeZoomLevelPrefs::OnZoomLevelChanged, base::Unretained(this))); -} diff --git a/chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.h b/chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.h deleted file mode 100644 index 4799e23f1c..0000000000 --- a/chromium_src/chrome/browser/ui/zoom/chrome_zoom_level_prefs.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2014 The Chromium 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 CHROME_BROWSER_UI_ZOOM_CHROME_ZOOM_LEVEL_PREFS_H_ -#define CHROME_BROWSER_UI_ZOOM_CHROME_ZOOM_LEVEL_PREFS_H_ - -#include "base/callback.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/observer_list.h" -#include "components/prefs/json_pref_store.h" -#include "components/prefs/pref_change_registrar.h" -#include "components/prefs/pref_service.h" -#include "components/prefs/pref_store.h" -#include "content/public/browser/host_zoom_map.h" -#include "content/public/browser/zoom_level_delegate.h" - -namespace base { -class DictionaryValue; -} - -namespace zoom { -class ZoomEventManager; -} - -// A class to manage per-partition default and per-host zoom levels in Chrome's -// preference system. It implements an interface between the content/ zoom -// levels in HostZoomMap and Chrome's preference system. All changes -// to the per-partition default zoom levels from chrome/ flow through this -// class. Any changes to per-host levels are updated when HostZoomMap calls -// OnZoomLevelChanged. -class ChromeZoomLevelPrefs : public content::ZoomLevelDelegate { - public: - typedef base::CallbackList::Subscription - DefaultZoomLevelSubscription; - - // Initialize the pref_service and the partition_key via the constructor, - // as these concepts won't be available in the content base class - // ZoomLevelDelegate, which will define the InitHostZoomMap interface. - // |pref_service_| must outlive this class. - ChromeZoomLevelPrefs( - PrefService* pref_service, - const base::FilePath& profile_path, - const base::FilePath& partition_path, - base::WeakPtr zoom_event_manager); - ~ChromeZoomLevelPrefs() override; - - static std::string GetHashForTesting(const base::FilePath& relative_path); - - void SetDefaultZoomLevelPref(double level); - double GetDefaultZoomLevelPref() const; - std::unique_ptr - RegisterDefaultZoomLevelCallback(const base::Closure& callback); - - void ExtractPerHostZoomLevels( - const base::DictionaryValue* host_zoom_dictionary, - bool sanitize_partition_host_zoom_levels); - - // content::ZoomLevelDelegate - void InitHostZoomMap(content::HostZoomMap* host_zoom_map) override; - - private: - // This is a callback function that receives notifications from HostZoomMap - // when per-host zoom levels change. It is used to update the per-host - // zoom levels (if any) managed by this class (for its associated partition). - void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change); - - PrefService* pref_service_; - base::WeakPtr zoom_event_manager_; - content::HostZoomMap* host_zoom_map_; - std::unique_ptr zoom_subscription_; - std::string partition_key_; - base::CallbackList default_zoom_changed_callbacks_; - - DISALLOW_COPY_AND_ASSIGN(ChromeZoomLevelPrefs); -}; - -#endif // CHROME_BROWSER_UI_ZOOM_CHROME_ZOOM_LEVEL_PREFS_H_ diff --git a/chromium_src/chrome/common/chrome_constants.cc b/chromium_src/chrome/common/chrome_constants.cc deleted file mode 100644 index 43a6ccdc7c..0000000000 --- a/chromium_src/chrome/common/chrome_constants.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/chrome_constants.h" - -#define FPL FILE_PATH_LITERAL - -namespace chrome { - -#if defined(OS_MACOSX) -const base::FilePath::CharType kFrameworkName[] = - FPL(ATOM_PRODUCT_NAME " Framework.framework"); -#endif // OS_MACOSX - -// filenames -const base::FilePath::CharType kCacheDirname[] = FPL("Cache"); -const base::FilePath::CharType kChannelIDFilename[] = FPL("Origin Bound Certs"); -const base::FilePath::CharType kCookieFilename[] = FPL("Cookies"); -const base::FilePath::CharType kCRLSetFilename[] = - FPL("Certificate Revocation Lists"); -const base::FilePath::CharType kCustomDictionaryFileName[] = - FPL("Custom Dictionary.txt"); -const base::FilePath::CharType kExtensionActivityLogFilename[] = - FPL("Extension Activity"); -const base::FilePath::CharType kExtensionsCookieFilename[] = - FPL("Extension Cookies"); -const base::FilePath::CharType kFirstRunSentinel[] = FPL("First Run"); -const base::FilePath::CharType kGCMStoreDirname[] = FPL("GCM Store"); -const base::FilePath::CharType kLocalStateFilename[] = FPL("Local State"); -const base::FilePath::CharType kLocalStorePoolName[] = FPL("LocalStorePool"); -const base::FilePath::CharType kMediaCacheDirname[] = FPL("Media Cache"); -const base::FilePath::CharType kNetworkPersistentStateFilename[] = - FPL("Network Persistent State"); -const base::FilePath::CharType kOfflinePageArchviesDirname[] = - FPL("Offline Pages/archives"); -const base::FilePath::CharType kOfflinePageMetadataDirname[] = - FPL("Offline Pages/metadata"); -const base::FilePath::CharType kPreferencesFilename[] = FPL("Preferences"); -const base::FilePath::CharType kProtectedPreferencesFilenameDeprecated[] = - FPL("Protected Preferences"); -const base::FilePath::CharType kReadmeFilename[] = FPL("README"); -const base::FilePath::CharType kResetPromptMementoFilename[] = - FPL("Reset Prompt Memento"); -const base::FilePath::CharType kSafeBrowsingBaseFilename[] = - FPL("Safe Browsing"); -const base::FilePath::CharType kSecurePreferencesFilename[] = - FPL("Secure Preferences"); -const base::FilePath::CharType kServiceStateFileName[] = FPL("Service State"); -const base::FilePath::CharType kSingletonCookieFilename[] = - FPL("SingletonCookie"); -const base::FilePath::CharType kSingletonLockFilename[] = FPL("SingletonLock"); -const base::FilePath::CharType kSingletonSocketFilename[] = - FPL("SingletonSocket"); -const base::FilePath::CharType kSupervisedUserSettingsFilename[] = - FPL("Managed Mode Settings"); -const base::FilePath::CharType kThemePackFilename[] = FPL("Cached Theme.pak"); -const base::FilePath::CharType kThemePackMaterialDesignFilename[] = - FPL("Cached Theme Material Design.pak"); -const base::FilePath::CharType kWebAppDirname[] = FPL("Web Applications"); - -// File name of the Pepper Flash plugin on different platforms. -const base::FilePath::CharType kPepperFlashPluginFilename[] = -#if defined(OS_MACOSX) - FPL("PepperFlashPlayer.plugin"); -#elif defined(OS_WIN) - FPL("pepflashplayer.dll"); -#else // OS_LINUX, etc. - FPL("libpepflashplayer.so"); -#endif - -} // namespace chrome - -#undef FPL diff --git a/chromium_src/chrome/common/chrome_constants.h b/chromium_src/chrome/common/chrome_constants.h deleted file mode 100644 index 2df506ac5d..0000000000 --- a/chromium_src/chrome/common/chrome_constants.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 handful of resource-like constants related to the Chrome application. - -#ifndef CHROME_COMMON_CHROME_CONSTANTS_H_ -#define CHROME_COMMON_CHROME_CONSTANTS_H_ - -#include "base/files/file_path.h" - -namespace chrome { - -#if defined(OS_MACOSX) -// NOTE: if you change the value of kFrameworkName, please don't forget to -// update components/test/run_all_unittests.cc as well. -// TODO(tfarina): Remove the comment above, when you fix components to use plist -// on Mac. -extern const base::FilePath::CharType kFrameworkName[]; -#endif // OS_MACOSX - -// filenames -extern const base::FilePath::CharType kCacheDirname[]; -extern const base::FilePath::CharType kChannelIDFilename[]; -extern const base::FilePath::CharType kCookieFilename[]; -extern const base::FilePath::CharType kCRLSetFilename[]; -extern const base::FilePath::CharType kCustomDictionaryFileName[]; -extern const base::FilePath::CharType kExtensionActivityLogFilename[]; -extern const base::FilePath::CharType kExtensionsCookieFilename[]; -extern const base::FilePath::CharType kFirstRunSentinel[]; -extern const base::FilePath::CharType kGCMStoreDirname[]; -extern const base::FilePath::CharType kLocalStateFilename[]; -extern const base::FilePath::CharType kLocalStorePoolName[]; -extern const base::FilePath::CharType kMediaCacheDirname[]; -extern const base::FilePath::CharType kNetworkPersistentStateFilename[]; -extern const base::FilePath::CharType kOfflinePageArchviesDirname[]; -extern const base::FilePath::CharType kOfflinePageMetadataDirname[]; -extern const base::FilePath::CharType kPreferencesFilename[]; -extern const base::FilePath::CharType kProtectedPreferencesFilenameDeprecated[]; -extern const base::FilePath::CharType kReadmeFilename[]; -extern const base::FilePath::CharType kResetPromptMementoFilename[]; -extern const base::FilePath::CharType kSafeBrowsingBaseFilename[]; -extern const base::FilePath::CharType kSecurePreferencesFilename[]; -extern const base::FilePath::CharType kServiceStateFileName[]; -extern const base::FilePath::CharType kSingletonCookieFilename[]; -extern const base::FilePath::CharType kSingletonLockFilename[]; -extern const base::FilePath::CharType kSingletonSocketFilename[]; -extern const base::FilePath::CharType kSupervisedUserSettingsFilename[]; -extern const base::FilePath::CharType kThemePackFilename[]; -extern const base::FilePath::CharType kThemePackMaterialDesignFilename[]; -extern const base::FilePath::CharType kWebAppDirname[]; - -// File name of the Pepper Flash plugin on different platforms. -extern const base::FilePath::CharType kPepperFlashPluginFilename[]; - -} // namespace chrome - -#endif // CHROME_COMMON_CHROME_CONSTANTS_H_ diff --git a/chromium_src/chrome/common/chrome_paths.cc b/chromium_src/chrome/common/chrome_paths.cc deleted file mode 100644 index d373315beb..0000000000 --- a/chromium_src/chrome/common/chrome_paths.cc +++ /dev/null @@ -1,613 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/chrome_paths.h" - -#include "base/files/file_util.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/sys_info.h" -#include "base/threading/thread_restrictions.h" -#include "base/version.h" -#include "chrome/common/chrome_constants.h" -#include "chrome/common/chrome_paths_internal.h" -#include "chrome/common/widevine_cdm_constants.h" -#include "third_party/widevine/cdm/stub/widevine_cdm_version.h" -#include "third_party/widevine/cdm/widevine_cdm_common.h" - -#if defined(OS_ANDROID) -#include "base/android/path_utils.h" -#include "base/base_paths_android.h" -// ui/base must only be used on Android. See BUILD.gn for dependency info. -#include "ui/base/ui_base_paths.h" // nogncheck -#endif - -#if defined(OS_MACOSX) -#include "base/mac/foundation_util.h" -#endif - -#if defined(OS_WIN) -#include "base/win/registry.h" -#endif - -namespace { - -// The Pepper Flash plugins are in a directory with this name. -const base::FilePath::CharType kPepperFlashBaseDirectory[] = - FILE_PATH_LITERAL("PepperFlash"); - -#if defined(OS_MACOSX) && !defined(OS_IOS) -const base::FilePath::CharType kPepperFlashSystemBaseDirectory[] = - FILE_PATH_LITERAL("Internet Plug-Ins/PepperFlashPlayer"); -const base::FilePath::CharType kFlashSystemBaseDirectory[] = - FILE_PATH_LITERAL("Internet Plug-Ins"); -const base::FilePath::CharType kFlashSystemPluginName[] = - FILE_PATH_LITERAL("Flash Player.plugin"); -#endif - -const base::FilePath::CharType kInternalNaClPluginFileName[] = - FILE_PATH_LITERAL("internal-nacl-plugin"); - -#if defined(OS_LINUX) -// The path to the external extension .json files. -// /usr/share seems like a good choice, see: http://www.pathname.com/fhs/ -const base::FilePath::CharType kFilepathSinglePrefExtensions[] = -#if defined(GOOGLE_CHROME_BUILD) - FILE_PATH_LITERAL("/usr/share/google-chrome/extensions"); -#else - FILE_PATH_LITERAL("/usr/share/chromium/extensions"); -#endif // defined(GOOGLE_CHROME_BUILD) - -// The path to the hint file that tells the pepper plugin loader -// where it can find the latest component updated flash. -const base::FilePath::CharType kComponentUpdatedFlashHint[] = - FILE_PATH_LITERAL("latest-component-updated-flash"); -#endif // defined(OS_LINUX) - -static base::LazyInstance - g_invalid_specified_user_data_dir = LAZY_INSTANCE_INITIALIZER; - -// Gets the path for internal plugins. -bool GetInternalPluginsDirectory(base::FilePath* result) { -#if defined(OS_MACOSX) && !defined(OS_IOS) - // If called from Chrome, get internal plugins from a subdirectory of the - // framework. - if (base::mac::AmIBundled()) { - *result = chrome::GetFrameworkBundlePath(); - DCHECK(!result->empty()); - *result = result->Append("Internet Plug-Ins"); - return true; - } - // In tests, just look in the module directory (below). -#endif - - // The rest of the world expects plugins in the module directory. - return PathService::Get(base::DIR_MODULE, result); -} - -#if defined(OS_WIN) -// Gets the Flash path if installed on the system. |is_npapi| determines whether -// to return the NPAPI of the PPAPI version of the system plugin. -bool GetSystemFlashFilename(base::FilePath* out_path, bool is_npapi) { - const wchar_t kNpapiFlashRegistryRoot[] = - L"SOFTWARE\\Macromedia\\FlashPlayerPlugin"; - const wchar_t kPepperFlashRegistryRoot[] = - L"SOFTWARE\\Macromedia\\FlashPlayerPepper"; - const wchar_t kFlashPlayerPathValueName[] = L"PlayerPath"; - - base::win::RegKey path_key( - HKEY_LOCAL_MACHINE, - is_npapi ? kNpapiFlashRegistryRoot : kPepperFlashRegistryRoot, KEY_READ); - base::string16 path_str; - if (FAILED(path_key.ReadValue(kFlashPlayerPathValueName, &path_str))) - return false; - - *out_path = base::FilePath(path_str); - return true; -} -#endif - -} // namespace - -namespace chrome { - -bool PathProvider(int key, base::FilePath* result) { - // Some keys are just aliases... - switch (key) { - case chrome::DIR_APP: - return PathService::Get(base::DIR_MODULE, result); - case chrome::DIR_LOGS: -#ifdef NDEBUG - // Release builds write to the data dir - return PathService::Get(chrome::DIR_USER_DATA, result); -#else - // Debug builds write next to the binary (in the build tree) -#if defined(OS_MACOSX) - if (!PathService::Get(base::DIR_EXE, result)) - return false; - if (base::mac::AmIBundled()) { - // If we're called from chrome, dump it beside the app (outside the - // app bundle), if we're called from a unittest, we'll already - // outside the bundle so use the exe dir. - // exe_dir gave us .../Chromium.app/Contents/MacOS/Chromium. - *result = result->DirName(); - *result = result->DirName(); - *result = result->DirName(); - } - return true; -#else - return PathService::Get(base::DIR_EXE, result); -#endif // defined(OS_MACOSX) -#endif // NDEBUG - case chrome::FILE_RESOURCE_MODULE: - return PathService::Get(base::FILE_MODULE, result); - } - - // Assume that we will not need to create the directory if it does not exist. - // This flag can be set to true for the cases where we want to create it. - bool create_dir = false; - - base::FilePath cur; - switch (key) { - case chrome::DIR_USER_DATA: - if (!GetDefaultUserDataDirectory(&cur)) { - NOTREACHED(); - return false; - } - create_dir = true; - break; - case chrome::DIR_USER_DOCUMENTS: - if (!GetUserDocumentsDirectory(&cur)) - return false; - create_dir = true; - break; - case chrome::DIR_USER_MUSIC: - if (!GetUserMusicDirectory(&cur)) - return false; - break; - case chrome::DIR_USER_PICTURES: - if (!GetUserPicturesDirectory(&cur)) - return false; - break; - case chrome::DIR_USER_VIDEOS: - if (!GetUserVideosDirectory(&cur)) - return false; - break; - case chrome::DIR_DEFAULT_DOWNLOADS_SAFE: -#if defined(OS_WIN) || defined(OS_LINUX) - if (!GetUserDownloadsDirectorySafe(&cur)) - return false; - break; -#else - // Fall through for all other platforms. -#endif - case chrome::DIR_DEFAULT_DOWNLOADS: -#if defined(OS_ANDROID) - if (!base::android::GetDownloadsDirectory(&cur)) - return false; -#else - if (!GetUserDownloadsDirectory(&cur)) - return false; - // Do not create the download directory here, we have done it twice now - // and annoyed a lot of users. -#endif - break; - case chrome::DIR_CRASH_DUMPS: -#if defined(OS_CHROMEOS) - // ChromeOS uses a separate directory. See http://crosbug.com/25089 - cur = base::FilePath("/var/log/chrome"); -#elif defined(OS_ANDROID) - if (!base::android::GetCacheDirectory(&cur)) - return false; -#else - // The crash reports are always stored relative to the default user data - // directory. This avoids the problem of having to re-initialize the - // exception handler after parsing command line options, which may - // override the location of the app's profile directory. - if (!GetDefaultUserDataDirectory(&cur)) - return false; -#endif -#if defined(OS_MACOSX) - cur = cur.Append(FILE_PATH_LITERAL("Crashpad")); -#else - cur = cur.Append(FILE_PATH_LITERAL("Crash Reports")); -#endif - create_dir = true; - break; -#if defined(OS_WIN) - case chrome::DIR_WATCHER_DATA: - // The watcher data is always stored relative to the default user data - // directory. This allows the watcher to be initialized before - // command-line options have been parsed. - if (!GetDefaultUserDataDirectory(&cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("Diagnostics")); - break; -#endif - case chrome::DIR_RESOURCES: -#if defined(OS_MACOSX) - cur = base::mac::FrameworkBundlePath(); - cur = cur.Append(FILE_PATH_LITERAL("Resources")); -#else - if (!PathService::Get(chrome::DIR_APP, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("resources")); -#endif - break; - case chrome::DIR_INSPECTOR: - if (!PathService::Get(chrome::DIR_RESOURCES, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("inspector")); - break; - case chrome::DIR_APP_DICTIONARIES: -#if defined(OS_POSIX) - // We can't write into the EXE dir on Linux, so keep dictionaries - // alongside the safe browsing database in the user data dir. - // And we don't want to write into the bundle on the Mac, so push - // it to the user data dir there also. - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; -#else - if (!PathService::Get(base::DIR_EXE, &cur)) - return false; -#endif - cur = cur.Append(FILE_PATH_LITERAL("Dictionaries")); - create_dir = true; - break; - case chrome::DIR_INTERNAL_PLUGINS: - if (!GetInternalPluginsDirectory(&cur)) - return false; - break; - case chrome::DIR_PEPPER_FLASH_PLUGIN: - if (!GetInternalPluginsDirectory(&cur)) - return false; - cur = cur.Append(kPepperFlashBaseDirectory); - break; - case chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(kPepperFlashBaseDirectory); - break; - case chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN: -#if defined(OS_WIN) - if (!GetSystemFlashFilename(&cur, false)) - return false; -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (!GetLocalLibraryDirectory(&cur)) - return false; - cur = cur.Append(kPepperFlashSystemBaseDirectory); - cur = cur.Append(chrome::kPepperFlashPluginFilename); -#else - // Chrome on iOS does not supports PPAPI binaries, return false. - // TODO(wfh): If Adobe release PPAPI binaries for Linux, add support here. - return false; -#endif - break; - case chrome::FILE_FLASH_SYSTEM_PLUGIN: -#if defined(OS_WIN) - if (!GetSystemFlashFilename(&cur, true)) - return false; -#elif defined(OS_MACOSX) && !defined(OS_IOS) - if (!GetLocalLibraryDirectory(&cur)) - return false; - cur = cur.Append(kFlashSystemBaseDirectory); - cur = cur.Append(kFlashSystemPluginName); -#else - // Chrome on other platforms does not supports system NPAPI binaries. - return false; -#endif - break; - case chrome::FILE_LOCAL_STATE: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(chrome::kLocalStateFilename); - break; - case chrome::FILE_RECORDED_SCRIPT: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("script.log")); - break; - case chrome::FILE_PEPPER_FLASH_PLUGIN: - if (!PathService::Get(chrome::DIR_PEPPER_FLASH_PLUGIN, &cur)) - return false; - cur = cur.Append(chrome::kPepperFlashPluginFilename); - break; - // TODO(teravest): Remove this case once the internal NaCl plugin is gone. - // We currently need a path here to look up whether the plugin is disabled - // and what its permissions are. - case chrome::FILE_NACL_PLUGIN: - if (!GetInternalPluginsDirectory(&cur)) - return false; - cur = cur.Append(kInternalNaClPluginFileName); - break; - // PNaCl is currenly installable via the component updater or by being - // simply built-in. DIR_PNACL_BASE is used as the base directory for - // installation via component updater. DIR_PNACL_COMPONENT will be - // the final location of pnacl, which is a subdir of DIR_PNACL_BASE. - case chrome::DIR_PNACL_BASE: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("pnacl")); - break; - // Where PNaCl files are ultimately located. The default finds the files - // inside the InternalPluginsDirectory / build directory, as if it - // was shipped along with chrome. The value can be overridden - // if it is installed via component updater. - case chrome::DIR_PNACL_COMPONENT: -#if defined(OS_MACOSX) - // PNaCl really belongs in the InternalPluginsDirectory but actually - // copying it there would result in the files also being shipped, which - // we don't want yet. So for now, just find them in the directory where - // they get built. - if (!PathService::Get(base::DIR_EXE, &cur)) - return false; - if (base::mac::AmIBundled()) { - // If we're called from chrome, it's beside the app (outside the - // app bundle), if we're called from a unittest, we'll already be - // outside the bundle so use the exe dir. - // exe_dir gave us .../Chromium.app/Contents/MacOS/Chromium. - cur = cur.DirName(); - cur = cur.DirName(); - cur = cur.DirName(); - } -#else - if (!GetInternalPluginsDirectory(&cur)) - return false; -#endif - cur = cur.Append(FILE_PATH_LITERAL("pnacl")); - break; -#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) -#if defined(WIDEVINE_CDM_IS_COMPONENT) - case chrome::DIR_COMPONENT_WIDEVINE_CDM: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.AppendASCII(kWidevineCdmBaseDirectory); - break; -#endif // defined(WIDEVINE_CDM_IS_COMPONENT) - // TODO(xhwang): FILE_WIDEVINE_CDM_ADAPTER has different meanings. - // In the component case, this is the source adapter. Otherwise, it is the - // actual Pepper module that gets loaded. - case chrome::FILE_WIDEVINE_CDM_ADAPTER: - if (!GetInternalPluginsDirectory(&cur)) - return false; - cur = cur.AppendASCII(kWidevineCdmAdapterFileName); - break; -#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) - case chrome::FILE_RESOURCES_PACK: -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (base::mac::AmIBundled()) { - cur = base::mac::FrameworkBundlePath(); - cur = cur.Append(FILE_PATH_LITERAL("Resources")) - .Append(FILE_PATH_LITERAL("resources.pak")); - break; - } -#elif defined(OS_ANDROID) - if (!PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &cur)) - return false; -#else - // If we're not bundled on mac or Android, resources.pak should be next - // to the binary (e.g., for unit tests). - if (!PathService::Get(base::DIR_MODULE, &cur)) - return false; -#endif - cur = cur.Append(FILE_PATH_LITERAL("resources.pak")); - break; - case chrome::DIR_RESOURCES_EXTENSION: - if (!PathService::Get(base::DIR_MODULE, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("resources")) - .Append(FILE_PATH_LITERAL("extension")); - break; -#if defined(OS_CHROMEOS) - case chrome::DIR_CHROMEOS_WALLPAPERS: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("wallpapers")); - break; - case chrome::DIR_CHROMEOS_WALLPAPER_THUMBNAILS: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("wallpaper_thumbnails")); - break; - case chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("custom_wallpapers")); - break; -#endif -#if defined(ENABLE_SUPERVISED_USERS) -#if defined(OS_LINUX) - case chrome::DIR_SUPERVISED_USERS_DEFAULT_APPS: - if (!PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("managed_users")); - break; -#endif - case chrome::DIR_SUPERVISED_USER_INSTALLED_WHITELISTS: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("SupervisedUserInstalledWhitelists")); - break; -#endif - // The following are only valid in the development environment, and - // will fail if executed from an installed executable (because the - // generated path won't exist). - case chrome::DIR_GEN_TEST_DATA: -#if defined(OS_ANDROID) - // On Android, our tests don't have permission to write to DIR_MODULE. - // gtest/test_runner.py pushes data to external storage. - if (!PathService::Get(base::DIR_ANDROID_EXTERNAL_STORAGE, &cur)) - return false; -#else - if (!PathService::Get(base::DIR_MODULE, &cur)) - return false; -#endif - cur = cur.Append(FILE_PATH_LITERAL("test_data")); - if (!base::PathExists(cur)) // We don't want to create this. - return false; - break; - case chrome::DIR_TEST_DATA: - if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("chrome")); - cur = cur.Append(FILE_PATH_LITERAL("test")); - cur = cur.Append(FILE_PATH_LITERAL("data")); - if (!base::PathExists(cur)) // We don't want to create this. - return false; - break; - case chrome::DIR_TEST_TOOLS: - if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("chrome")); - cur = cur.Append(FILE_PATH_LITERAL("tools")); - cur = cur.Append(FILE_PATH_LITERAL("test")); - if (!base::PathExists(cur)) // We don't want to create this - return false; - break; -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) - case chrome::DIR_POLICY_FILES: { -#if defined(GOOGLE_CHROME_BUILD) - cur = base::FilePath(FILE_PATH_LITERAL("/etc/opt/chrome/policies")); -#else - cur = base::FilePath(FILE_PATH_LITERAL("/etc/chromium/policies")); -#endif - break; - } -#endif -#if defined(OS_MACOSX) && !defined(OS_IOS) - case chrome::DIR_USER_LIBRARY: { - if (!GetUserLibraryDirectory(&cur)) - return false; - if (!base::PathExists(cur)) // We don't want to create this. - return false; - break; - } - case chrome::DIR_USER_APPLICATIONS: { - if (!GetUserApplicationsDirectory(&cur)) - return false; - if (!base::PathExists(cur)) // We don't want to create this. - return false; - break; - } -#endif -#if defined(OS_CHROMEOS) || (defined(OS_LINUX) && defined(CHROMIUM_BUILD)) || \ - (defined(OS_MACOSX) && !defined(OS_IOS)) - case chrome::DIR_USER_EXTERNAL_EXTENSIONS: { - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("External Extensions")); - break; - } -#endif -#if defined(OS_LINUX) - case chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS: { - cur = base::FilePath(kFilepathSinglePrefExtensions); - break; - } -#endif - case chrome::DIR_EXTERNAL_EXTENSIONS: -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (!chrome::GetGlobalApplicationSupportDirectory(&cur)) - return false; - - cur = cur.Append(FILE_PATH_LITERAL("Google")) - .Append(FILE_PATH_LITERAL("Chrome")) - .Append(FILE_PATH_LITERAL("External Extensions")); - create_dir = false; -#else - if (!PathService::Get(base::DIR_MODULE, &cur)) - return false; - - cur = cur.Append(FILE_PATH_LITERAL("extensions")); - create_dir = true; -#endif - break; - - case chrome::DIR_DEFAULT_APPS: -#if defined(OS_MACOSX) - cur = base::mac::FrameworkBundlePath(); - cur = cur.Append(FILE_PATH_LITERAL("Default Apps")); -#else - if (!PathService::Get(chrome::DIR_APP, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("default_apps")); -#endif - break; - -#if defined(OS_LINUX) || (defined(OS_MACOSX) && !defined(OS_IOS)) - case chrome::DIR_NATIVE_MESSAGING: -#if defined(OS_MACOSX) -#if defined(GOOGLE_CHROME_BUILD) - cur = base::FilePath(FILE_PATH_LITERAL( - "/Library/Google/Chrome/NativeMessagingHosts")); -#else - cur = base::FilePath(FILE_PATH_LITERAL( - "/Library/Application Support/Chromium/NativeMessagingHosts")); -#endif -#else // defined(OS_MACOSX) -#if defined(GOOGLE_CHROME_BUILD) - cur = base::FilePath(FILE_PATH_LITERAL( - "/etc/opt/chrome/native-messaging-hosts")); -#else - cur = base::FilePath(FILE_PATH_LITERAL( - "/etc/chromium/native-messaging-hosts")); -#endif -#endif // !defined(OS_MACOSX) - break; - - case chrome::DIR_USER_NATIVE_MESSAGING: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("NativeMessagingHosts")); - break; -#endif // defined(OS_LINUX) || (defined(OS_MACOSX) && !defined(OS_IOS)) -#if !defined(OS_ANDROID) - case chrome::DIR_GLOBAL_GCM_STORE: - if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) - return false; - cur = cur.Append(kGCMStoreDirname); - break; -#endif // !defined(OS_ANDROID) -#if defined(OS_LINUX) - case chrome::FILE_COMPONENT_FLASH_HINT: - if (!PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN, - &cur)) { - return false; - } - cur = cur.Append(kComponentUpdatedFlashHint); - break; -#endif // defined(OS_LINUX) - - default: - return false; - } - - // TODO(bauerb): http://crbug.com/259796 - base::ThreadRestrictions::ScopedAllowIO allow_io; - if (create_dir && !base::PathExists(cur) && - !base::CreateDirectory(cur)) - return false; - - *result = cur; - return true; -} - -// This cannot be done as a static initializer sadly since Visual Studio will -// eliminate this object file if there is no direct entry point into it. -void RegisterPathProvider() { - PathService::RegisterProvider(PathProvider, PATH_START, PATH_END); -} - -void SetInvalidSpecifiedUserDataDir(const base::FilePath& user_data_dir) { - g_invalid_specified_user_data_dir.Get() = user_data_dir; -} - -const base::FilePath& GetInvalidSpecifiedUserDataDir() { - return g_invalid_specified_user_data_dir.Get(); -} - -} // namespace chrome diff --git a/chromium_src/chrome/common/chrome_paths.h b/chromium_src/chrome/common/chrome_paths.h deleted file mode 100644 index 581fdc06f7..0000000000 --- a/chromium_src/chrome/common/chrome_paths.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_COMMON_CHROME_PATHS_H__ -#define CHROME_COMMON_CHROME_PATHS_H__ - -#include "build/build_config.h" - -namespace base { -class FilePath; -} - -// This file declares path keys for the chrome module. These can be used with -// the PathService to access various special directories and files. - -namespace chrome { - -enum { - PATH_START = 1000, - - DIR_APP = PATH_START, // Directory where dlls and data reside. - DIR_LOGS, // Directory where logs should be written. - DIR_USER_DATA, // Directory where user data can be written. - DIR_CRASH_DUMPS, // Directory where crash dumps are written. -#if defined(OS_WIN) - DIR_WATCHER_DATA, // Directory where the Chrome watcher stores - // data. -#endif - DIR_RESOURCES, // Directory containing separate file resources - // used by Chrome at runtime. - DIR_INSPECTOR, // Directory where web inspector is located. - DIR_APP_DICTIONARIES, // Directory where the global dictionaries are. - DIR_USER_DOCUMENTS, // Directory for a user's "My Documents". - DIR_USER_MUSIC, // Directory for a user's music. - DIR_USER_PICTURES, // Directory for a user's pictures. - DIR_USER_VIDEOS, // Directory for a user's videos. - DIR_DEFAULT_DOWNLOADS_SAFE, // Directory for a user's - // "My Documents/Downloads", (Windows) or - // "Downloads". (Linux) - DIR_DEFAULT_DOWNLOADS, // Directory for a user's downloads. - DIR_INTERNAL_PLUGINS, // Directory where internal plugins reside. -#if defined(OS_POSIX) && !defined(OS_MACOSX) - DIR_POLICY_FILES, // Directory for system-wide read-only - // policy files that allow sys-admins - // to set policies for chrome. This directory - // contains subdirectories. -#endif -#if defined(OS_MACOSX) && !defined(OS_IOS) - DIR_USER_APPLICATIONS, // ~/Applications - DIR_USER_LIBRARY, // ~/Library -#endif -#if defined(OS_CHROMEOS) || (defined(OS_LINUX) && defined(CHROMIUM_BUILD)) || \ - (defined(OS_MACOSX) && !defined(OS_IOS)) - DIR_USER_EXTERNAL_EXTENSIONS, // Directory for per-user external extensions - // on Chrome Mac and Chromium Linux. - // On Chrome OS, this path is used for OEM - // customization. Getting this path does not - // create it. -#endif - -#if defined(OS_LINUX) - DIR_STANDALONE_EXTERNAL_EXTENSIONS, // Directory for 'per-extension' - // definition manifest files that - // describe extensions which are to be - // installed when chrome is run. -#endif - DIR_EXTERNAL_EXTENSIONS, // Directory where installer places .crx files. - - DIR_DEFAULT_APPS, // Directory where installer places .crx files - // to be installed when chrome is first run. - DIR_PEPPER_FLASH_PLUGIN, // Directory to the bundled Pepper Flash plugin, - // containing the plugin and the manifest. - DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN, // Base directory of the Pepper - // Flash plugins downloaded by the - // component updater. - FILE_RESOURCE_MODULE, // Full path and filename of the module that - // contains embedded resources (version, - // strings, images, etc.). - FILE_LOCAL_STATE, // Path and filename to the file in which - // machine/installation-specific state is saved. - FILE_RECORDED_SCRIPT, // Full path to the script.log file that - // contains recorded browser events for - // playback. - FILE_PEPPER_FLASH_PLUGIN, // Full path to the bundled Pepper Flash plugin - // file. - FILE_PEPPER_FLASH_SYSTEM_PLUGIN, // Full path to the system version of the - // Pepper Flash plugin, downloadable from - // Adobe website. Querying this path might - // succeed no matter the file exists or not. - FILE_FLASH_SYSTEM_PLUGIN, // Full path to the system version of NPAPI - // Flash plugin, downloadable from Adobe - // website. Querying this path might succeed no - // matter the file exists or not. - FILE_NACL_PLUGIN, // Full path to the internal NaCl plugin file. - DIR_PNACL_BASE, // Full path to the base dir for PNaCl. - DIR_PNACL_COMPONENT, // Full path to the latest PNaCl version - // (subdir of DIR_PNACL_BASE). - DIR_COMPONENT_WIDEVINE_CDM, // Directory that contains component-updated - // Widevine CDM files. - FILE_WIDEVINE_CDM_ADAPTER, // Full path to the Widevine CDM adapter file. - FILE_RESOURCES_PACK, // Full path to the .pak file containing - // binary data (e.g., html files and images - // used by internal pages). - DIR_RESOURCES_EXTENSION, // Full path to extension resources. -#if defined(OS_CHROMEOS) - DIR_CHROMEOS_WALLPAPERS, // Directory where downloaded chromeos - // wallpapers reside. - DIR_CHROMEOS_WALLPAPER_THUMBNAILS, // Directory where downloaded chromeos - // wallpaper thumbnails reside. - DIR_CHROMEOS_CUSTOM_WALLPAPERS, // Directory where custom wallpapers - // reside. -#endif - DIR_SUPERVISED_USERS_DEFAULT_APPS, // Directory where installer places .crx - // files to be installed when managed user - // session starts. - DIR_SUPERVISED_USER_INSTALLED_WHITELISTS, // Directory where sanitized - // supervised user whitelists are - // installed. -#if defined(OS_LINUX) || (defined(OS_MACOSX) && !defined(OS_IOS)) - DIR_NATIVE_MESSAGING, // System directory where native messaging host - // manifest files are stored. - DIR_USER_NATIVE_MESSAGING, // Directory with Native Messaging Hosts - // installed per-user. -#endif -#if !defined(OS_ANDROID) - DIR_GLOBAL_GCM_STORE, // Directory where the global GCM instance - // stores its data. -#endif - - // Valid only in development environment; TODO(darin): move these - DIR_GEN_TEST_DATA, // Directory where generated test data resides. - DIR_TEST_DATA, // Directory where unit test data resides. - DIR_TEST_TOOLS, // Directory where unit test tools reside. -#if defined(OS_LINUX) - FILE_COMPONENT_FLASH_HINT, // A file in a known location that points to - // the component updated flash plugin. -#endif // defined(OS_LINUX) - - PATH_END -}; - -// Call once to register the provider for the path keys defined above. -void RegisterPathProvider(); - -// Get or set the invalid user data dir that was originally specified. -void SetInvalidSpecifiedUserDataDir(const base::FilePath& user_data_dir); -const base::FilePath& GetInvalidSpecifiedUserDataDir(); - -} // namespace chrome - -#endif // CHROME_COMMON_CHROME_PATHS_H__ diff --git a/chromium_src/chrome/common/chrome_paths_internal.h b/chromium_src/chrome/common/chrome_paths_internal.h deleted file mode 100644 index 7061828773..0000000000 --- a/chromium_src/chrome/common/chrome_paths_internal.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_COMMON_CHROME_PATHS_INTERNAL_H_ -#define CHROME_COMMON_CHROME_PATHS_INTERNAL_H_ - -#include - -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#if defined(__OBJC__) -@class NSBundle; -#else -class NSBundle; -#endif -#endif - -namespace base { -class FilePath; -} - -namespace chrome { - -// Get the path to the user's data directory, regardless of whether -// DIR_USER_DATA has been overridden by a command-line option. -bool GetDefaultUserDataDirectory(base::FilePath* result); - -// Get the path to the user's cache directory. This is normally the -// same as the profile directory, but on Linux it can also be -// $XDG_CACHE_HOME and on Mac it can be under ~/Library/Caches. -// Note that the Chrome cache directories are actually subdirectories -// of this directory, with names like "Cache" and "Media Cache". -// This will always fill in |result| with a directory, sometimes -// just |profile_dir|. -void GetUserCacheDirectory(const base::FilePath& profile_dir, base::FilePath* result); - -// Get the path to the user's documents directory. -bool GetUserDocumentsDirectory(base::FilePath* result); - -#if defined(OS_WIN) || defined(OS_LINUX) -// Gets the path to a safe default download directory for a user. -bool GetUserDownloadsDirectorySafe(base::FilePath* result); -#endif - -// Get the path to the user's downloads directory. -bool GetUserDownloadsDirectory(base::FilePath* result); - -// Gets the path to the user's music directory. -bool GetUserMusicDirectory(base::FilePath* result); - -// Gets the path to the user's pictures directory. -bool GetUserPicturesDirectory(base::FilePath* result); - -// Gets the path to the user's videos directory. -bool GetUserVideosDirectory(base::FilePath* result); - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// The "versioned directory" is a directory in the browser .app bundle. It -// contains the bulk of the application, except for the things that the system -// requires be located at spepcific locations. The versioned directory is -// in the .app at Contents/Versions/w.x.y.z. -base::FilePath GetVersionedDirectory(); - -// This overrides the directory returned by |GetVersionedDirectory()|, to be -// used when |GetVersionedDirectory()| can't automatically determine the proper -// location. This is the case when the browser didn't load itself but by, e.g., -// the app mode loader. This should be called before |ChromeMain()|. This takes -// ownership of the object |path| and the caller must not delete it. -void SetOverrideVersionedDirectory(const base::FilePath* path); - -// Most of the application is further contained within the framework. The -// framework bundle is located within the versioned directory at a specific -// path. The only components in the versioned directory not included in the -// framework are things that also depend on the framework, such as the helper -// app bundle. -base::FilePath GetFrameworkBundlePath(); - -// Get the local library directory. -bool GetLocalLibraryDirectory(base::FilePath* result); - -// Get the user library directory. -bool GetUserLibraryDirectory(base::FilePath* result); - -// Get the user applications directory. -bool GetUserApplicationsDirectory(base::FilePath* result); - -// Get the global Application Support directory (under /Library/). -bool GetGlobalApplicationSupportDirectory(base::FilePath* result); - -// Returns the NSBundle for the outer browser application, even when running -// inside the helper. In unbundled applications, such as tests, returns nil. -NSBundle* OuterAppBundle(); - -// Get the user data directory for the Chrome browser bundle at |bundle|. -// |bundle| should be the same value that would be returned from +[NSBundle -// mainBundle] if Chrome were launched normaly. This is used by app shims, -// which run from a bundle which isn't Chrome itself, but which need access to -// the user data directory to connect to a UNIX-domain socket therein. -// Returns false if there was a problem fetching the app data directory. -bool GetUserDataDirectoryForBrowserBundle(NSBundle* bundle, - base::FilePath* result); - -#endif // OS_MACOSX && !OS_IOS - -// Checks if the |process_type| has the rights to access the profile. -bool ProcessNeedsProfileDir(const std::string& process_type); - -#if defined(OS_WIN) -// Populates |crash_dir| with the default crash dump location regardless of -// whether DIR_USER_DATA or DIR_CRASH_DUMPS has been overridden. -bool GetDefaultCrashDumpLocation(base::FilePath* crash_dir); -#endif - -} // namespace chrome - -#endif // CHROME_COMMON_CHROME_PATHS_INTERNAL_H_ diff --git a/chromium_src/chrome/common/chrome_paths_linux.cc b/chromium_src/chrome/common/chrome_paths_linux.cc deleted file mode 100644 index 745bc03adb..0000000000 --- a/chromium_src/chrome/common/chrome_paths_linux.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/chrome_paths_internal.h" - -#include - -#include "base/base_paths.h" -#include "base/environment.h" -#include "base/files/file_util.h" -#include "base/nix/xdg_util.h" -#include "base/path_service.h" -#include "chrome/common/chrome_paths.h" - -namespace chrome { - -using base::nix::GetXDGDirectory; -using base::nix::GetXDGUserDirectory; -using base::nix::kDotConfigDir; -using base::nix::kXdgConfigHomeEnvVar; - -namespace { - -const char kDownloadsDir[] = "Downloads"; -const char kMusicDir[] = "Music"; -const char kPicturesDir[] = "Pictures"; -const char kVideosDir[] = "Videos"; - -// Generic function for GetUser{Music,Pictures,Video}Directory. -bool GetUserMediaDirectory(const std::string& xdg_name, - const std::string& fallback_name, - base::FilePath* result) { -#if defined(OS_CHROMEOS) - // No local media directories on CrOS. - return false; -#else - *result = GetXDGUserDirectory(xdg_name.c_str(), fallback_name.c_str()); - - base::FilePath home; - PathService::Get(base::DIR_HOME, &home); - if (*result != home) { - base::FilePath desktop; - if (!PathService::Get(base::DIR_USER_DESKTOP, &desktop)) - return false; - if (*result != desktop) { - return true; - } - } - - *result = home.Append(fallback_name); - return true; -#endif -} - -} // namespace - -// See http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html -// for a spec on where config files go. The net effect for most -// systems is we use ~/.config/chromium/ for Chromium and -// ~/.config/google-chrome/ for official builds. -// (This also helps us sidestep issues with other apps grabbing ~/.chromium .) -bool GetDefaultUserDataDirectory(base::FilePath* result) { - std::unique_ptr env(base::Environment::Create()); - base::FilePath config_dir(GetXDGDirectory(env.get(), - kXdgConfigHomeEnvVar, - kDotConfigDir)); -#if defined(GOOGLE_CHROME_BUILD) - *result = config_dir.Append("google-chrome"); -#else - *result = config_dir.Append("chromium"); -#endif - return true; -} - -void GetUserCacheDirectory(const base::FilePath& profile_dir, - base::FilePath* result) { - // See http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - // for a spec on where cache files go. Our rule is: - // - if the user-data-dir in the standard place, - // use same subdirectory of the cache directory. - // (this maps ~/.config/google-chrome to ~/.cache/google-chrome as well - // as the same thing for ~/.config/chromium) - // - otherwise, use the profile dir directly. - - // Default value in cases where any of the following fails. - *result = profile_dir; - - std::unique_ptr env(base::Environment::Create()); - - base::FilePath cache_dir; - if (!PathService::Get(base::DIR_CACHE, &cache_dir)) - return; - base::FilePath config_dir(GetXDGDirectory(env.get(), - kXdgConfigHomeEnvVar, - kDotConfigDir)); - - if (!config_dir.AppendRelativePath(profile_dir, &cache_dir)) - return; - - *result = cache_dir; -} - -bool GetUserDocumentsDirectory(base::FilePath* result) { - *result = GetXDGUserDirectory("DOCUMENTS", "Documents"); - return true; -} - -bool GetUserDownloadsDirectorySafe(base::FilePath* result) { - base::FilePath home; - PathService::Get(base::DIR_HOME, &home); - *result = home.Append(kDownloadsDir); - return true; -} - -bool GetUserDownloadsDirectory(base::FilePath* result) { - *result = GetXDGUserDirectory("DOWNLOAD", kDownloadsDir); - return true; -} - -// We respect the user's preferred pictures location, unless it is -// ~ or their desktop directory, in which case we default to ~/Music. -bool GetUserMusicDirectory(base::FilePath* result) { - return GetUserMediaDirectory("MUSIC", kMusicDir, result); -} - -// We respect the user's preferred pictures location, unless it is -// ~ or their desktop directory, in which case we default to ~/Pictures. -bool GetUserPicturesDirectory(base::FilePath* result) { - return GetUserMediaDirectory("PICTURES", kPicturesDir, result); -} - -// We respect the user's preferred pictures location, unless it is -// ~ or their desktop directory, in which case we default to ~/Videos. -bool GetUserVideosDirectory(base::FilePath* result) { - return GetUserMediaDirectory("VIDEOS", kVideosDir, result); -} - -bool ProcessNeedsProfileDir(const std::string& process_type) { - // For now we have no reason to forbid this on Linux as we don't - // have the roaming profile troubles there. Moreover the Linux breakpad needs - // profile dir access in all process if enabled on Linux. - return true; -} - -} // namespace chrome diff --git a/chromium_src/chrome/common/chrome_paths_mac.mm b/chromium_src/chrome/common/chrome_paths_mac.mm deleted file mode 100644 index bbfd6c9b23..0000000000 --- a/chromium_src/chrome/common/chrome_paths_mac.mm +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/chrome_paths_internal.h" - -#import -#include - -#include - -#include "base/base_paths.h" -#include "base/logging.h" -#import "base/mac/foundation_util.h" -#import "base/mac/scoped_nsautorelease_pool.h" -#include "base/memory/free_deleter.h" -#include "base/path_service.h" -#include "chrome/common/chrome_constants.h" - -namespace { - -#if !defined(OS_IOS) -const base::FilePath* g_override_versioned_directory = NULL; - -// Return a retained (NOT autoreleased) NSBundle* as the internal -// implementation of chrome::OuterAppBundle(), which should be the only -// caller. -NSBundle* OuterAppBundleInternal() { - base::mac::ScopedNSAutoreleasePool pool; - - if (!base::mac::AmIBundled()) { - // If unbundled (as in a test), there's no app bundle. - return nil; - } - - if (!base::mac::IsBackgroundOnlyProcess()) { - // Shortcut: in the browser process, just return the main app bundle. - return [[NSBundle mainBundle] retain]; - } - - // From C.app/Contents/Versions/1.2.3.4, go up three steps to get to C.app. - base::FilePath versioned_dir = chrome::GetVersionedDirectory(); - base::FilePath outer_app_dir = versioned_dir.DirName().DirName().DirName(); - const char* outer_app_dir_c = outer_app_dir.value().c_str(); - NSString* outer_app_dir_ns = [NSString stringWithUTF8String:outer_app_dir_c]; - - return [[NSBundle bundleWithPath:outer_app_dir_ns] retain]; -} -#endif // !defined(OS_IOS) - -char* ProductDirNameForBundle(NSBundle* chrome_bundle) { - const char* product_dir_name = NULL; -#if !defined(OS_IOS) - base::mac::ScopedNSAutoreleasePool pool; - - NSString* product_dir_name_ns = - [chrome_bundle objectForInfoDictionaryKey:@"CrProductDirName"]; - product_dir_name = [product_dir_name_ns fileSystemRepresentation]; -#else - DCHECK(!chrome_bundle); -#endif - - if (!product_dir_name) { -#if defined(GOOGLE_CHROME_BUILD) - product_dir_name = "Google/Chrome"; -#else - product_dir_name = "Chromium"; -#endif - } - - // Leaked, but the only caller initializes a static with this result, so it - // only happens once, and that's OK. - return strdup(product_dir_name); -} - -// ProductDirName returns the name of the directory inside -// ~/Library/Application Support that should hold the product application -// data. This can be overridden by setting the CrProductDirName key in the -// outer browser .app's Info.plist. The default is "Google/Chrome" for -// officially-branded builds, and "Chromium" for unbranded builds. For the -// official canary channel, the Info.plist will have CrProductDirName set -// to "Google/Chrome Canary". -std::string ProductDirName() { -#if defined(OS_IOS) - static const char* product_dir_name = ProductDirNameForBundle(nil); -#else - // Use OuterAppBundle() to get the main app's bundle. This key needs to live - // in the main app's bundle because it will be set differently on the canary - // channel, and the autoupdate system dictates that there can be no - // differences between channels within the versioned directory. This would - // normally use base::mac::FrameworkBundle(), but that references the - // framework bundle within the versioned directory. Ordinarily, the profile - // should not be accessed from non-browser processes, but those processes do - // attempt to get the profile directory, so direct them to look in the outer - // browser .app's Info.plist for the CrProductDirName key. - static const char* product_dir_name = - ProductDirNameForBundle(chrome::OuterAppBundle()); -#endif - return std::string(product_dir_name); -} - -bool GetDefaultUserDataDirectoryForProduct(const std::string& product_dir, - base::FilePath* result) { - bool success = false; - if (result && PathService::Get(base::DIR_APP_DATA, result)) { - *result = result->Append(product_dir); - success = true; - } - return success; -} - -} // namespace - -namespace chrome { - -bool GetDefaultUserDataDirectory(base::FilePath* result) { - return GetDefaultUserDataDirectoryForProduct(ProductDirName(), result); -} - -bool GetUserDocumentsDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSDocumentDirectory, result); -} - -void GetUserCacheDirectory(const base::FilePath& profile_dir, - base::FilePath* result) { - // If the profile directory is under ~/Library/Application Support, - // use a suitable cache directory under ~/Library/Caches. For - // example, a profile directory of ~/Library/Application - // Support/Google/Chrome/MyProfileName would use the cache directory - // ~/Library/Caches/Google/Chrome/MyProfileName. - - // Default value in cases where any of the following fails. - *result = profile_dir; - - base::FilePath app_data_dir; - if (!PathService::Get(base::DIR_APP_DATA, &app_data_dir)) - return; - base::FilePath cache_dir; - if (!PathService::Get(base::DIR_CACHE, &cache_dir)) - return; - if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir)) - return; - - *result = cache_dir; -} - -bool GetUserDownloadsDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSDownloadsDirectory, result); -} - -bool GetUserMusicDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSMusicDirectory, result); -} - -bool GetUserPicturesDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSPicturesDirectory, result); -} - -bool GetUserVideosDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSMoviesDirectory, result); -} - -#if !defined(OS_IOS) - -base::FilePath GetVersionedDirectory() { - if (g_override_versioned_directory) - return *g_override_versioned_directory; - - // Start out with the path to the running executable. - base::FilePath path; - PathService::Get(base::FILE_EXE, &path); - - // One step up to MacOS, another to Contents. - path = path.DirName().DirName(); - DCHECK_EQ(path.BaseName().value(), "Contents"); - - if (base::mac::IsBackgroundOnlyProcess()) { - // path identifies the helper .app's Contents directory in the browser - // .app's versioned directory. Go up two steps to get to the browser - // .app's versioned directory. - path = path.DirName().DirName(); - } else { - // Go into the versioned directory. - path = path.Append("Frameworks"); - } - - return path; -} - -void SetOverrideVersionedDirectory(const base::FilePath* path) { - if (path != g_override_versioned_directory) { - delete g_override_versioned_directory; - g_override_versioned_directory = path; - } -} - -base::FilePath GetFrameworkBundlePath() { - // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really - // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating - // that it may be more efficient than +bundleForClass:. +bundleForClass: - // itself takes 1-2ms. Getting an NSBundle from a path, on the other hand, - // essentially takes no time at all, at least when the bundle has already - // been loaded as it will have been in this case. The FilePath operations - // needed to compute the framework's path are also effectively free, so that - // is the approach that is used here. NSBundle is also documented as being - // not thread-safe, and thread safety may be a concern here. - - // The framework bundle is at a known path and name from the browser .app's - // versioned directory. - return GetVersionedDirectory().Append(kFrameworkName); -} - -bool GetLocalLibraryDirectory(base::FilePath* result) { - return base::mac::GetLocalDirectory(NSLibraryDirectory, result); -} - -bool GetUserLibraryDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSLibraryDirectory, result); -} - -bool GetUserApplicationsDirectory(base::FilePath* result) { - return base::mac::GetUserDirectory(NSApplicationDirectory, result); -} - -bool GetGlobalApplicationSupportDirectory(base::FilePath* result) { - return base::mac::GetLocalDirectory(NSApplicationSupportDirectory, result); -} - -NSBundle* OuterAppBundle() { - // Cache this. Foundation leaks it anyway, and this should be the only call - // to OuterAppBundleInternal(). - static NSBundle* bundle = OuterAppBundleInternal(); - return bundle; -} - -bool GetUserDataDirectoryForBrowserBundle(NSBundle* bundle, - base::FilePath* result) { - std::unique_ptr - product_dir_name(ProductDirNameForBundle(bundle)); - return GetDefaultUserDataDirectoryForProduct(product_dir_name.get(), result); -} - -#endif // !defined(OS_IOS) - -bool ProcessNeedsProfileDir(const std::string& process_type) { - // For now we have no reason to forbid this on other MacOS as we don't - // have the roaming profile troubles there. - return true; -} - -} // namespace chrome diff --git a/chromium_src/chrome/common/chrome_paths_win.cc b/chromium_src/chrome/common/chrome_paths_win.cc deleted file mode 100644 index 89c2ae48ea..0000000000 --- a/chromium_src/chrome/common/chrome_paths_win.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/chrome_paths_internal.h" - -#include -#include -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "base/win/scoped_co_mem.h" -#include "chrome/common/chrome_constants.h" - -namespace chrome { - -namespace { - -// Generic function to call SHGetFolderPath(). -bool GetUserDirectory(int csidl_folder, base::FilePath* result) { - // We need to go compute the value. It would be nice to support paths - // with names longer than MAX_PATH, but the system functions don't seem - // to be designed for it either, with the exception of GetTempPath - // (but other things will surely break if the temp path is too long, - // so we don't bother handling it. - wchar_t path_buf[MAX_PATH]; - path_buf[0] = 0; - if (FAILED(SHGetFolderPath(NULL, csidl_folder, NULL, - SHGFP_TYPE_CURRENT, path_buf))) { - return false; - } - *result = base::FilePath(path_buf); - return true; -} - -} // namespace - -bool GetDefaultUserDataDirectory(base::FilePath* result) { - return PathService::Get(base::DIR_LOCAL_APP_DATA, result); -} - -void GetUserCacheDirectory(const base::FilePath& profile_dir, - base::FilePath* result) { - // This function does more complicated things on Mac/Linux. - *result = profile_dir; -} - -bool GetUserDocumentsDirectory(base::FilePath* result) { - return GetUserDirectory(CSIDL_MYDOCUMENTS, result); -} - -// Return a default path for downloads that is safe. -// We just use 'Downloads' under DIR_USER_DOCUMENTS. Localizing -// 'downloads' is not a good idea because Chrome's UI language -// can be changed. -bool GetUserDownloadsDirectorySafe(base::FilePath* result) { - if (!GetUserDocumentsDirectory(result)) - return false; - - *result = result->Append(L"Downloads"); - return true; -} - -// On Vista and higher, use the downloads known folder. Since it can be -// relocated to point to a "dangerous" folder, callers should validate that the -// returned path is not dangerous before using it. -bool GetUserDownloadsDirectory(base::FilePath* result) { - typedef HRESULT (WINAPI *GetKnownFolderPath)( - REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*); - GetKnownFolderPath f = reinterpret_cast( - GetProcAddress(GetModuleHandle(L"shell32.dll"), "SHGetKnownFolderPath")); - base::win::ScopedCoMem path_buf; - if (f && SUCCEEDED(f(FOLDERID_Downloads, 0, NULL, &path_buf))) { - *result = base::FilePath(std::wstring(path_buf)); - return true; - } - return GetUserDownloadsDirectorySafe(result); -} - -bool GetUserMusicDirectory(base::FilePath* result) { - return GetUserDirectory(CSIDL_MYMUSIC, result); -} - -bool GetUserPicturesDirectory(base::FilePath* result) { - return GetUserDirectory(CSIDL_MYPICTURES, result); -} - -bool GetUserVideosDirectory(base::FilePath* result) { - return GetUserDirectory(CSIDL_MYVIDEO, result); -} - -bool ProcessNeedsProfileDir(const std::string& process_type) { - // On windows we don't want subprocesses other than the browser process and - // service processes to be able to use the profile directory because if it - // lies on a network share the sandbox will prevent us from accessing it. - - if (process_type.empty()) - return true; - - return false; -} - -} // namespace chrome diff --git a/chromium_src/chrome/common/chrome_utility_messages.h b/chromium_src/chrome/common/chrome_utility_messages.h deleted file mode 100644 index efcd468c51..0000000000 --- a/chromium_src/chrome/common/chrome_utility_messages.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Multiply-included message file, so no include guard. - -#if defined(OS_WIN) -#include -#endif // defined(OS_WIN) - -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/strings/string16.h" -#include "base/values.h" -#include "ipc/ipc_message_macros.h" -#include "ipc/ipc_platform_file.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/ipc/gfx_param_traits.h" - -// Singly-included section for typedefs. -#ifndef CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_ -#define CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_ - -#if defined(OS_WIN) -// A vector of filters, each being a tuple containing a display string (i.e. -// "Text Files") and a filter pattern (i.e. "*.txt"). -typedef std::vector> - GetOpenFileNameFilter; -#endif // OS_WIN - -#endif // CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_ - -#define IPC_MESSAGE_START ChromeUtilityMsgStart - - -#if defined(OS_WIN) -IPC_STRUCT_BEGIN(ChromeUtilityMsg_GetSaveFileName_Params) - IPC_STRUCT_MEMBER(HWND, owner) - IPC_STRUCT_MEMBER(DWORD, flags) - IPC_STRUCT_MEMBER(GetOpenFileNameFilter, filters) - IPC_STRUCT_MEMBER(int, one_based_filter_index) - IPC_STRUCT_MEMBER(base::FilePath, suggested_filename) - IPC_STRUCT_MEMBER(base::FilePath, initial_directory) - IPC_STRUCT_MEMBER(base::string16, default_extension) -IPC_STRUCT_END() -#endif // OS_WIN - -//------------------------------------------------------------------------------ -// Utility process messages: -// These are messages from the browser to the utility process. - -// Tell the utility process to parse a JSON string into a Value object. -IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_ParseJSON, - std::string /* JSON to parse */) - -// Tell the utility process to decode the given image data. -IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_DecodeImage, - std::vector /* encoded image contents */, - bool /* shrink image if needed for IPC msg limit */) - -// Tell the utility process to decode the given JPEG image data with a robust -// libjpeg codec. -IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_RobustJPEGDecodeImage, - std::vector) // encoded image contents - -// Tell the utility process to patch the given |input_file| using |patch_file| -// and place the output in |output_file|. The patch should use the bsdiff -// algorithm (Courgette's version). -IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_PatchFileBsdiff, - base::FilePath /* input_file */, - base::FilePath /* patch_file */, - base::FilePath /* output_file */) - -// Tell the utility process to patch the given |input_file| using |patch_file| -// and place the output in |output_file|. The patch should use the Courgette -// algorithm. -IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_PatchFileCourgette, - base::FilePath /* input_file */, - base::FilePath /* patch_file */, - base::FilePath /* output_file */) - - -// Requests the utility process to respond with a -// ChromeUtilityHostMsg_ProcessStarted message once it has started. This may -// be used if the host process needs a handle to the running utility process. -IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_StartupPing) - - -#if defined(OS_WIN) -// Invokes ui::base::win::OpenFileViaShell from the utility process. -IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_OpenFileViaShell, - base::FilePath /* full_path */) - -// Invokes ui::base::win::OpenFolderViaShell from the utility process. -IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_OpenFolderViaShell, - base::FilePath /* full_path */) - -// Instructs the utility process to invoke GetOpenFileName. |owner| is the -// parent of the modal dialog, |flags| are OFN_* flags. |filter| constrains the -// user's file choices. |initial_directory| and |filename| select the directory -// to be displayed and the file to be initially selected. -// -// Either ChromeUtilityHostMsg_GetOpenFileName_Failed or -// ChromeUtilityHostMsg_GetOpenFileName_Result will be returned when the -// operation completes whether due to error or user action. -IPC_MESSAGE_CONTROL5(ChromeUtilityMsg_GetOpenFileName, - HWND /* owner */, - DWORD /* flags */, - GetOpenFileNameFilter /* filter */, - base::FilePath /* initial_directory */, - base::FilePath /* filename */) -IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_GetSaveFileName, - ChromeUtilityMsg_GetSaveFileName_Params /* params */) -#endif // defined(OS_WIN) - - -//------------------------------------------------------------------------------ -// Utility process host messages: -// These are messages from the utility process to the browser. - -// Reply when the utility process successfully parsed a JSON string. -// -// WARNING: The result can be of any Value subclass type, but we can't easily -// pass indeterminate value types by const object reference with our IPC macros, -// so we put the result Value into a ListValue. Handlers should examine the -// first (and only) element of the ListValue for the actual result. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ParseJSON_Succeeded, - base::ListValue) - -// Reply when the utility process failed in parsing a JSON string. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ParseJSON_Failed, - std::string /* error message, if any*/) - -// Reply when the utility process has failed while unpacking and parsing a -// web resource. |error_message| is a user-readable explanation of what -// went wrong. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_UnpackWebResource_Failed, - std::string /* error_message, if any */) - -// Reply when the utility process has succeeded in decoding the image. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DecodeImage_Succeeded, - SkBitmap) // decoded image - -// Reply when an error occurred decoding the image. -IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_DecodeImage_Failed) - -// Reply when a file has been patched. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_PatchFile_Finished, int /* result */) - - -// Reply when the utility process has started. -IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_ProcessStarted) - - -#if defined(OS_WIN) -IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetOpenFileName_Failed) -IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GetOpenFileName_Result, - base::FilePath /* directory */, - std::vector /* filenames */) -IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetSaveFileName_Failed) -IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GetSaveFileName_Result, - base::FilePath /* path */, - int /* one_based_filter_index */) -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_BuildDirectWriteFontCache, - base::FilePath /* cache file path */) -#endif // defined(OS_WIN) diff --git a/chromium_src/chrome/common/cloud_devices_urls.cc b/chromium_src/chrome/common/cloud_devices_urls.cc deleted file mode 100644 index 3eefe02ee5..0000000000 --- a/chromium_src/chrome/common/cloud_devices_urls.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/common/cloud_devices_urls.h" - -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "net/base/url_util.h" - -namespace cloud_devices { - -namespace { - -// Url must not be matched by "urls" section of -// cloud_print_app/manifest.json. If it's matched, print driver dialog will -// open sign-in page in separate window. -const char kCloudPrintURL[] = "https://www.google.com/cloudprint"; - -} - -// Returns the root service URL for the cloud print service. The default is to -// point at the Google Cloud Print service. This can be overridden by the -// command line or by the user preferences. -GURL GetCloudPrintURL() { - return GURL(kCloudPrintURL); -} - -GURL GetCloudPrintRelativeURL(const std::string& relative_path) { - GURL url = GetCloudPrintURL(); - std::string path; - static const char kURLPathSeparator[] = "/"; - base::TrimString(url.path(), kURLPathSeparator, &path); - std::string trimmed_path; - base::TrimString(relative_path, kURLPathSeparator, &trimmed_path); - path += kURLPathSeparator; - path += trimmed_path; - GURL::Replacements replacements; - replacements.SetPathStr(path); - return url.ReplaceComponents(replacements); -} - -} // namespace cloud_devices diff --git a/chromium_src/chrome/common/cloud_devices_urls.h b/chromium_src/chrome/common/cloud_devices_urls.h deleted file mode 100644 index 9ab2da4d6b..0000000000 --- a/chromium_src/chrome/common/cloud_devices_urls.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Electron: this is stubbed to remove a dependency -// on google_apis and components/cloud_devices - -#ifndef COMPONENTS_CLOUD_DEVICES_COMMON_CLOUD_DEVICES_URLS_H_ -#define COMPONENTS_CLOUD_DEVICES_COMMON_CLOUD_DEVICES_URLS_H_ - -#include - -#include "url/gurl.h" - -namespace cloud_devices { - -GURL GetCloudPrintRelativeURL(const std::string& relative_path); - -} // namespace cloud_devices - -#endif // COMPONENTS_CLOUD_DEVICES_COMMON_CLOUD_DEVICES_URLS_H_ diff --git a/chromium_src/chrome/common/common_param_traits_macros.h b/chromium_src/chrome/common/common_param_traits_macros.h deleted file mode 100644 index d5b75a4b22..0000000000 --- a/chromium_src/chrome/common/common_param_traits_macros.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Singly or multiply-included shared traits file depending upon circumstances. -// This allows the use of IPC serialization macros in more than one IPC message -// file. -#ifndef CHROME_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ -#define CHROME_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ - -#include "components/content_settings/core/common/content_settings.h" -#include "ipc/ipc_message_macros.h" - -IPC_ENUM_TRAITS_MAX_VALUE(ContentSetting, CONTENT_SETTING_NUM_SETTINGS - 1) - -#endif // CHROME_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ diff --git a/chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc b/chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc deleted file mode 100644 index d3b8963219..0000000000 --- a/chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.cc +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" - -#include - -#include - -#include "base/files/file_util.h" -#include "base/lazy_instance.h" -#include "base/macros.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "chrome/grit/generated_resources.h" -#include "content/public/common/url_constants.h" -#include "extensions/common/error_utils.h" -#include "extensions/common/extension.h" -#include "extensions/common/extension_resource.h" -#include "extensions/common/host_id.h" -#include "extensions/common/manifest_constants.h" -#include "extensions/common/manifest_handlers/permissions_parser.h" -#include "extensions/common/permissions/permissions_data.h" -#include "extensions/common/url_pattern.h" -#include "extensions/common/url_pattern_set.h" -#include "ui/base/l10n/l10n_util.h" -#include "url/gurl.h" - -namespace extensions { - -namespace keys = extensions::manifest_keys; -namespace values = manifest_values; -namespace errors = manifest_errors; - -namespace { - -// Helper method that loads either the include_globs or exclude_globs list -// from an entry in the content_script lists of the manifest. -bool LoadGlobsHelper(const base::DictionaryValue* content_script, - int content_script_index, - const char* globs_property_name, - base::string16* error, - void(UserScript::*add_method)(const std::string& glob), - UserScript* instance) { - if (!content_script->HasKey(globs_property_name)) - return true; // they are optional - - const base::ListValue* list = NULL; - if (!content_script->GetList(globs_property_name, &list)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidGlobList, - base::IntToString(content_script_index), - globs_property_name); - return false; - } - - for (size_t i = 0; i < list->GetSize(); ++i) { - std::string glob; - if (!list->GetString(i, &glob)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidGlob, base::IntToString(content_script_index), - globs_property_name, base::SizeTToString(i)); - return false; - } - - (instance->*add_method)(glob); - } - - return true; -} - -// Helper method that loads a UserScript object from a dictionary in the -// content_script list of the manifest. -bool LoadUserScriptFromDictionary(const base::DictionaryValue* content_script, - int definition_index, - Extension* extension, - base::string16* error, - UserScript* result) { - // run_at - if (content_script->HasKey(keys::kRunAt)) { - std::string run_location; - if (!content_script->GetString(keys::kRunAt, &run_location)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidRunAt, - base::IntToString(definition_index)); - return false; - } - - if (run_location == values::kRunAtDocumentStart) { - result->set_run_location(UserScript::DOCUMENT_START); - } else if (run_location == values::kRunAtDocumentEnd) { - result->set_run_location(UserScript::DOCUMENT_END); - } else if (run_location == values::kRunAtDocumentIdle) { - result->set_run_location(UserScript::DOCUMENT_IDLE); - } else { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidRunAt, - base::IntToString(definition_index)); - return false; - } - } - - // all frames - if (content_script->HasKey(keys::kAllFrames)) { - bool all_frames = false; - if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidAllFrames, base::IntToString(definition_index)); - return false; - } - result->set_match_all_frames(all_frames); - } - - // match about blank - if (content_script->HasKey(keys::kMatchAboutBlank)) { - bool match_about_blank = false; - if (!content_script->GetBoolean(keys::kMatchAboutBlank, - &match_about_blank)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatchAboutBlank, base::IntToString(definition_index)); - return false; - } - result->set_match_about_blank(match_about_blank); - } - - // matches (required) - const base::ListValue* matches = NULL; - if (!content_script->GetList(keys::kMatches, &matches)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatches, - base::IntToString(definition_index)); - return false; - } - - if (matches->GetSize() == 0) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatchCount, - base::IntToString(definition_index)); - return false; - } - for (size_t j = 0; j < matches->GetSize(); ++j) { - std::string match_str; - if (!matches->GetString(j, &match_str)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatch, base::IntToString(definition_index), - base::SizeTToString(j), errors::kExpectString); - return false; - } - - URLPattern pattern(UserScript::ValidUserScriptSchemes( - PermissionsData::CanExecuteScriptEverywhere(extension))); - - URLPattern::ParseResult parse_result = pattern.Parse(match_str); - if (parse_result != URLPattern::PARSE_SUCCESS) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidMatch, base::IntToString(definition_index), - base::SizeTToString(j), - URLPattern::GetParseResultString(parse_result)); - return false; - } - - // TODO(aboxhall): check for webstore - if (!PermissionsData::CanExecuteScriptEverywhere(extension) && - pattern.scheme() != content::kChromeUIScheme) { - // Exclude SCHEME_CHROMEUI unless it's been explicitly requested. - // If the --extensions-on-chrome-urls flag has not been passed, requesting - // a chrome:// url will cause a parse failure above, so there's no need to - // check the flag here. - pattern.SetValidSchemes( - pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI); - } - - if (pattern.MatchesScheme(url::kFileScheme) && - !PermissionsData::CanExecuteScriptEverywhere(extension)) { - extension->set_wants_file_access(true); - if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) { - pattern.SetValidSchemes( - pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); - } - } - - result->add_url_pattern(pattern); - } - - // exclude_matches - if (content_script->HasKey(keys::kExcludeMatches)) { // optional - const base::ListValue* exclude_matches = NULL; - if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidExcludeMatches, - base::IntToString(definition_index)); - return false; - } - - for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { - std::string match_str; - if (!exclude_matches->GetString(j, &match_str)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidExcludeMatch, base::IntToString(definition_index), - base::SizeTToString(j), errors::kExpectString); - return false; - } - - int valid_schemes = UserScript::ValidUserScriptSchemes( - PermissionsData::CanExecuteScriptEverywhere(extension)); - URLPattern pattern(valid_schemes); - - URLPattern::ParseResult parse_result = pattern.Parse(match_str); - if (parse_result != URLPattern::PARSE_SUCCESS) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidExcludeMatch, base::IntToString(definition_index), - base::SizeTToString(j), - URLPattern::GetParseResultString(parse_result)); - return false; - } - - result->add_exclude_url_pattern(pattern); - } - } - - // include/exclude globs (mostly for Greasemonkey compatibility) - if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, - error, &UserScript::add_glob, result)) { - return false; - } - - if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, - error, &UserScript::add_exclude_glob, result)) { - return false; - } - - // js and css keys - const base::ListValue* js = NULL; - if (content_script->HasKey(keys::kJs) && - !content_script->GetList(keys::kJs, &js)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidJsList, - base::IntToString(definition_index)); - return false; - } - - const base::ListValue* css = NULL; - if (content_script->HasKey(keys::kCss) && - !content_script->GetList(keys::kCss, &css)) { - *error = ErrorUtils:: - FormatErrorMessageUTF16(errors::kInvalidCssList, - base::IntToString(definition_index)); - return false; - } - - // The manifest needs to have at least one js or css user script definition. - if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kMissingFile, - base::IntToString(definition_index)); - return false; - } - - if (js) { - for (size_t script_index = 0; script_index < js->GetSize(); - ++script_index) { - const base::Value* value; - std::string relative; - if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidJs, base::IntToString(definition_index), - base::SizeTToString(script_index)); - return false; - } - GURL url = extension->GetResourceURL(relative); - ExtensionResource resource = extension->GetResource(relative); - result->js_scripts().push_back(UserScript::File( - resource.extension_root(), resource.relative_path(), url)); - } - } - - if (css) { - for (size_t script_index = 0; script_index < css->GetSize(); - ++script_index) { - const base::Value* value; - std::string relative; - if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidCss, base::IntToString(definition_index), - base::SizeTToString(script_index)); - return false; - } - GURL url = extension->GetResourceURL(relative); - ExtensionResource resource = extension->GetResource(relative); - result->css_scripts().push_back(UserScript::File( - resource.extension_root(), resource.relative_path(), url)); - } - } - - return true; -} - -// Returns false and sets the error if script file can't be loaded, -// or if it's not UTF-8 encoded. -static bool IsScriptValid(const base::FilePath& path, - const base::FilePath& relative_path, - int message_id, - std::string* error) { - std::string content; - if (!base::PathExists(path) || - !base::ReadFileToString(path, &content)) { - *error = l10n_util::GetStringFUTF8( - message_id, - relative_path.LossyDisplayName()); - return false; - } - - if (!base::IsStringUTF8(content)) { - *error = l10n_util::GetStringFUTF8( - IDS_EXTENSION_BAD_FILE_ENCODING, - relative_path.LossyDisplayName()); - return false; - } - - return true; -} - -struct EmptyUserScriptList { - UserScriptList user_script_list; -}; - -static base::LazyInstance g_empty_script_list = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -ContentScriptsInfo::ContentScriptsInfo() { -} - -ContentScriptsInfo::~ContentScriptsInfo() { -} - -// static -const UserScriptList& ContentScriptsInfo::GetContentScripts( - const Extension* extension) { - ContentScriptsInfo* info = static_cast( - extension->GetManifestData(keys::kContentScripts)); - return info ? info->content_scripts - : g_empty_script_list.Get().user_script_list; -} - -// static -bool ContentScriptsInfo::ExtensionHasScriptAtURL(const Extension* extension, - const GURL& url) { - const UserScriptList& content_scripts = GetContentScripts(extension); - for (UserScriptList::const_iterator iter = content_scripts.begin(); - iter != content_scripts.end(); ++iter) { - if (iter->MatchesURL(url)) - return true; - } - return false; -} - -// static -URLPatternSet ContentScriptsInfo::GetScriptableHosts( - const Extension* extension) { - const UserScriptList& content_scripts = GetContentScripts(extension); - URLPatternSet scriptable_hosts; - for (UserScriptList::const_iterator content_script = - content_scripts.begin(); - content_script != content_scripts.end(); - ++content_script) { - URLPatternSet::const_iterator pattern = - content_script->url_patterns().begin(); - for (; pattern != content_script->url_patterns().end(); ++pattern) - scriptable_hosts.AddPattern(*pattern); - } - return scriptable_hosts; -} - -ContentScriptsHandler::ContentScriptsHandler() { -} - -ContentScriptsHandler::~ContentScriptsHandler() { -} - -const std::vector ContentScriptsHandler::Keys() const { - static const char* const keys[] = { - keys::kContentScripts - }; - return std::vector(keys, keys + arraysize(keys)); -} - -bool ContentScriptsHandler::Parse(Extension* extension, base::string16* error) { - std::unique_ptr content_scripts_info( - new ContentScriptsInfo); - const base::ListValue* scripts_list = NULL; - if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) { - *error = base::ASCIIToUTF16(errors::kInvalidContentScriptsList); - return false; - } - - for (size_t i = 0; i < scripts_list->GetSize(); ++i) { - const base::DictionaryValue* script_dict = NULL; - if (!scripts_list->GetDictionary(i, &script_dict)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kInvalidContentScript, base::SizeTToString(i)); - return false; - } - - UserScript user_script; - if (!LoadUserScriptFromDictionary(script_dict, - i, - extension, - error, - &user_script)) { - return false; // Failed to parse script context definition. - } - - user_script.set_host_id(HostID(HostID::EXTENSIONS, extension->id())); - if (extension->converted_from_user_script()) { - user_script.set_emulate_greasemonkey(true); - // Greasemonkey matches all frames. - user_script.set_match_all_frames(true); - } - user_script.set_id(UserScript::GenerateUserScriptID()); - content_scripts_info->content_scripts.push_back(user_script); - } - extension->SetManifestData(keys::kContentScripts, - content_scripts_info.release()); - PermissionsParser::SetScriptableHosts( - extension, ContentScriptsInfo::GetScriptableHosts(extension)); - return true; -} - -bool ContentScriptsHandler::Validate( - const Extension* extension, - std::string* error, - std::vector* warnings) const { - // Validate that claimed script resources actually exist, - // and are UTF-8 encoded. - ExtensionResource::SymlinkPolicy symlink_policy; - if ((extension->creation_flags() & - Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) { - symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE; - } else { - symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT; - } - - const UserScriptList& content_scripts = - ContentScriptsInfo::GetContentScripts(extension); - for (size_t i = 0; i < content_scripts.size(); ++i) { - const UserScript& script = content_scripts[i]; - - for (size_t j = 0; j < script.js_scripts().size(); j++) { - const UserScript::File& js_script = script.js_scripts()[j]; - const base::FilePath& path = ExtensionResource::GetFilePath( - js_script.extension_root(), js_script.relative_path(), - symlink_policy); - if (!IsScriptValid(path, js_script.relative_path(), - IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error)) - return false; - } - - for (size_t j = 0; j < script.css_scripts().size(); j++) { - const UserScript::File& css_script = script.css_scripts()[j]; - const base::FilePath& path = ExtensionResource::GetFilePath( - css_script.extension_root(), css_script.relative_path(), - symlink_policy); - if (!IsScriptValid(path, css_script.relative_path(), - IDS_EXTENSION_LOAD_CSS_FAILED, error)) - return false; - } - } - - return true; -} - -} // namespace extensions diff --git a/chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.h b/chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.h deleted file mode 100644 index f633d63b89..0000000000 --- a/chromium_src/chrome/common/extensions/manifest_handlers/content_scripts_handler.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_CONTENT_SCRIPTS_HANDLER_H_ -#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_CONTENT_SCRIPTS_HANDLER_H_ - -#include - -#include "base/macros.h" -#include "extensions/common/extension.h" -#include "extensions/common/manifest_handler.h" -#include "extensions/common/user_script.h" - -namespace extensions { - -class URLPatternSet; - -struct ContentScriptsInfo : public Extension::ManifestData { - ContentScriptsInfo(); - ~ContentScriptsInfo() override; - - // Paths to the content scripts the extension contains (possibly empty). - UserScriptList content_scripts; - - // Returns the content scripts for the extension (if the extension has - // no content scripts, this returns an empty list). - static const UserScriptList& GetContentScripts(const Extension* extension); - - // Returns the list of hosts that this extension can run content scripts on. - static URLPatternSet GetScriptableHosts(const Extension* extension); - - // Returns true if the extension has a content script declared at |url|. - static bool ExtensionHasScriptAtURL(const Extension* extension, - const GURL& url); -}; - -// Parses the "content_scripts" manifest key. -class ContentScriptsHandler : public ManifestHandler { - public: - ContentScriptsHandler(); - ~ContentScriptsHandler() override; - - bool Parse(Extension* extension, base::string16* error) override; - bool Validate(const Extension* extension, - std::string* error, - std::vector* warnings) const override; - - private: - const std::vector Keys() const override; - - DISALLOW_COPY_AND_ASSIGN(ContentScriptsHandler); -}; - -} // namespace extensions - -#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_CONTENT_SCRIPTS_HANDLER_H_ diff --git a/chromium_src/chrome/common/importer/importer_bridge.h b/chromium_src/chrome/common/importer/importer_bridge.h index 0992868ad1..9d44288233 100644 --- a/chromium_src/chrome/common/importer/importer_bridge.h +++ b/chromium_src/chrome/common/importer/importer_bridge.h @@ -76,8 +76,6 @@ class ImporterBridge : public base::RefCountedThreadSafe { virtual void SetAutofillFormData( const std::vector& entries) = 0; - virtual void SetCookies(const std::vector& cookies) = 0; - // Notifies the coordinator that the import operation has begun. virtual void NotifyStarted() = 0; diff --git a/chromium_src/chrome/common/importer/profile_import_process_messages.h b/chromium_src/chrome/common/importer/profile_import_process_messages.h index 962e1344b8..4d2862b422 100644 --- a/chromium_src/chrome/common/importer/profile_import_process_messages.h +++ b/chromium_src/chrome/common/importer/profile_import_process_messages.h @@ -107,12 +107,6 @@ IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_AutofillFormDataImportStart, IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_AutofillFormDataImportGroup, std::vector) -IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyCookiesImportStart, - int /* total number of cookies */) - -IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyCookiesImportGroup, - std::vector) - #if defined(OS_WIN) IPC_MESSAGE_CONTROL1(ProfileImportProcessHostMsg_NotifyIE7PasswordInfo, importer::ImporterIE7PasswordInfo) // password_info diff --git a/chromium_src/chrome/common/ini_parser.cc b/chromium_src/chrome/common/ini_parser.cc deleted file mode 100644 index 91f2d7ad02..0000000000 --- a/chromium_src/chrome/common/ini_parser.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/common/ini_parser.h" - -#include - -#include "base/logging.h" -#include "base/strings/string_tokenizer.h" - -INIParser::INIParser() : used_(false) {} - -INIParser::~INIParser() {} - -void INIParser::Parse(const std::string& content) { - DCHECK(!used_); - used_ = true; - base::StringTokenizer tokenizer(content, "\r\n"); - - std::string current_section; - while (tokenizer.GetNext()) { - std::string line = tokenizer.token(); - if (line.empty()) { - // Skips the empty line. - continue; - } - if (line[0] == '#' || line[0] == ';') { - // This line is a comment. - continue; - } - if (line[0] == '[') { - // It is a section header. - current_section = line.substr(1); - size_t end = current_section.rfind(']'); - if (end != std::string::npos) - current_section.erase(end); - } else { - std::string key, value; - size_t equal = line.find('='); - if (equal != std::string::npos) { - key = line.substr(0, equal); - value = line.substr(equal + 1); - HandleTriplet(current_section, key, value); - } - } - } -} - -DictionaryValueINIParser::DictionaryValueINIParser() {} - -DictionaryValueINIParser::~DictionaryValueINIParser() {} - -void DictionaryValueINIParser::HandleTriplet(const std::string& section, - const std::string& key, - const std::string& value) { - - // Checks whether the section and key contain a '.' character. - // Those sections and keys break DictionaryValue's path format when not - // using the *WithoutPathExpansion methods. - if (section.find('.') == std::string::npos && - key.find('.') == std::string::npos) - root_.SetString(section + "." + key, value); -} diff --git a/chromium_src/chrome/common/ini_parser.h b/chromium_src/chrome/common/ini_parser.h deleted file mode 100644 index 2f8ef16079..0000000000 --- a/chromium_src/chrome/common/ini_parser.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 The Chromium 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 CHROME_COMMON_INI_PARSER_H_ -#define CHROME_COMMON_INI_PARSER_H_ - -#include - -#include "base/macros.h" -#include "base/values.h" - -// Parses INI files in a string. Users should in inherit from this class. -// This is a very basic INI parser with these characteristics: -// - Ignores blank lines. -// - Ignores comment lines beginning with '#' or ';'. -// - Duplicate key names in the same section will simply cause repeated calls -// to HandleTriplet with the same |section| and |key| parameters. -// - No escape characters supported. -// - Global properties result in calls to HandleTriplet with an empty string in -// the |section| argument. -// - Section headers begin with a '[' character. It is recommended, but -// not required to close the header bracket with a ']' character. All -// characters after a closing ']' character is ignored. -// - Key value pairs are indicated with an '=' character. Whitespace is not -// ignored. Quoting is not supported. Everything before the first '=' -// is considered the |key|, and everything after is the |value|. -class INIParser { - public: - INIParser(); - virtual ~INIParser(); - - // May only be called once per instance. - void Parse(const std::string& content); - - private: - virtual void HandleTriplet(const std::string& section, - const std::string& key, - const std::string& value) = 0; - - bool used_; -}; - -// Parsed values are stored as strings at the "section.key" path. Triplets with -// |section| or |key| parameters containing '.' are ignored. -class DictionaryValueINIParser : public INIParser { - public: - DictionaryValueINIParser(); - ~DictionaryValueINIParser() override; - - const base::DictionaryValue& root() const { return root_; } - - private: - // INIParser implementation. - void HandleTriplet(const std::string& section, - const std::string& key, - const std::string& value) override; - - base::DictionaryValue root_; - - DISALLOW_COPY_AND_ASSIGN(DictionaryValueINIParser); -}; - -#endif // CHROME_COMMON_INI_PARSER_H_ diff --git a/chromium_src/chrome/common/pref_names.cc b/chromium_src/chrome/common/pref_names.cc deleted file mode 100644 index ce9f0c3e62..0000000000 --- a/chromium_src/chrome/common/pref_names.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/pref_names.h" - -namespace prefs { - -const char kSelectFileLastDirectory[] = "selectfile.last_directory"; -const char kDownloadDefaultDirectory[] = "download.default_directory"; -const char kDevToolsFileSystemPaths[] = "devtools.file_system_paths"; -const char kApplicationLocale[] = "intl.app_locale"; - -// List of protocol handlers. -const char kRegisteredProtocolHandlers[] = - "custom_handlers.registered_protocol_handlers"; - -// List of protocol handlers the user has requested not to be asked about again. -const char kIgnoredProtocolHandlers[] = - "custom_handlers.ignored_protocol_handlers"; - -// List of protocol handlers registered by policy. -const char kPolicyRegisteredProtocolHandlers[] = - "custom_handlers.policy.registered_protocol_handlers"; - -// List of protocol handlers the policy has requested to be ignored. -const char kPolicyIgnoredProtocolHandlers[] = - "custom_handlers.policy.ignored_protocol_handlers"; - -// Whether user-specified handlers for protocols and content types can be -// specified. -const char kCustomHandlersEnabled[] = "custom_handlers.enabled"; - -// Double that indicates the default zoom level. -const char kPartitionDefaultZoomLevel[] = "partition.default_zoom_level"; - -// Dictionary that maps hostnames to zoom levels. Hosts not in this pref will -// be displayed at the default zoom level. -const char kPartitionPerHostZoomLevels[] = "partition.per_host_zoom_levels"; - -} // namespace prefs diff --git a/chromium_src/chrome/common/pref_names.h b/chromium_src/chrome/common/pref_names.h deleted file mode 100644 index db48074af7..0000000000 --- a/chromium_src/chrome/common/pref_names.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 names of various preferences, for easier changing. - -namespace prefs { - -extern const char kSelectFileLastDirectory[]; -extern const char kDownloadDefaultDirectory[]; -extern const char kDevToolsFileSystemPaths[]; -extern const char kApplicationLocale[]; - -extern const char kRegisteredProtocolHandlers[]; -extern const char kIgnoredProtocolHandlers[]; -extern const char kPolicyRegisteredProtocolHandlers[]; -extern const char kPolicyIgnoredProtocolHandlers[]; -extern const char kCustomHandlersEnabled[]; - -extern const char kPartitionDefaultZoomLevel[]; -extern const char kPartitionPerHostZoomLevels[]; -} // namespace prefs diff --git a/chromium_src/chrome/common/print_messages.cc b/chromium_src/chrome/common/print_messages.cc deleted file mode 100644 index 6ccc8d23a9..0000000000 --- a/chromium_src/chrome/common/print_messages.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/common/print_messages.h" - -#include "base/strings/string16.h" -#include "ui/gfx/geometry/size.h" - -PrintMsg_Print_Params::PrintMsg_Print_Params() - : page_size(), - content_size(), - printable_area(), - margin_top(0), - margin_left(0), - dpi(0), - min_shrink(0), - max_shrink(0), - desired_dpi(0), - document_cookie(0), - selection_only(false), - supports_alpha_blend(false), - print_scaling_option(blink::WebPrintScalingOptionSourceSize), - title(), - url(), - should_print_backgrounds(false) { -} - -PrintMsg_Print_Params::~PrintMsg_Print_Params() {} - -void PrintMsg_Print_Params::Reset() { - page_size = gfx::Size(); - content_size = gfx::Size(); - printable_area = gfx::Rect(); - margin_top = 0; - margin_left = 0; - dpi = 0; - min_shrink = 0; - max_shrink = 0; - desired_dpi = 0; - document_cookie = 0; - selection_only = false; - supports_alpha_blend = false; - print_scaling_option = blink::WebPrintScalingOptionSourceSize; - title.clear(); - url.clear(); - should_print_backgrounds = false; -} - -PrintMsg_PrintPages_Params::PrintMsg_PrintPages_Params() - : pages() { -} - -PrintMsg_PrintPages_Params::~PrintMsg_PrintPages_Params() {} - -void PrintMsg_PrintPages_Params::Reset() { - params.Reset(); - pages = std::vector(); -} - -PrintHostMsg_RequestPrintPreview_Params:: - PrintHostMsg_RequestPrintPreview_Params() - : is_modifiable(false), - webnode_only(false), - has_selection(false), - selection_only(false) { -} - -PrintHostMsg_RequestPrintPreview_Params:: - ~PrintHostMsg_RequestPrintPreview_Params() {} - -PrintHostMsg_SetOptionsFromDocument_Params:: - PrintHostMsg_SetOptionsFromDocument_Params() - : is_scaling_disabled(false), - copies(0), - duplex(printing::UNKNOWN_DUPLEX_MODE) { -} - -PrintHostMsg_SetOptionsFromDocument_Params:: - ~PrintHostMsg_SetOptionsFromDocument_Params() { -} diff --git a/chromium_src/chrome/common/print_messages.h b/chromium_src/chrome/common/print_messages.h deleted file mode 100644 index e1646f2054..0000000000 --- a/chromium_src/chrome/common/print_messages.h +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// IPC messages for printing. -// Multiply-included message file, hence no include guard. - -#include -#include - -#include "base/memory/shared_memory.h" -#include "base/values.h" -#include "ipc/ipc_message_macros.h" -#include "printing/page_range.h" -#include "printing/page_size_margins.h" -#include "printing/print_job_constants.h" -#include "third_party/WebKit/public/web/WebPrintScalingOption.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/geometry/rect.h" - -#if defined(OS_WIN) -#include "ipc/ipc_platform_file.h" -#include "printing/backend/print_backend.h" -#include "printing/page_range.h" -#include "printing/pdf_render_settings.h" -#endif - -#ifndef CHROME_COMMON_PRINT_MESSAGES_H_ -#define CHROME_COMMON_PRINT_MESSAGES_H_ - -struct PrintMsg_Print_Params { - PrintMsg_Print_Params(); - ~PrintMsg_Print_Params(); - - // Resets the members of the struct to 0. - void Reset(); - - gfx::Size page_size; - gfx::Size content_size; - gfx::Rect printable_area; - int margin_top; - int margin_left; - double dpi; - double min_shrink; - double max_shrink; - int desired_dpi; - int document_cookie; - bool selection_only; - bool supports_alpha_blend; - int preview_request_id; - blink::WebPrintScalingOption print_scaling_option; - bool print_to_pdf; - base::string16 title; - base::string16 url; - bool should_print_backgrounds; -}; - -struct PrintMsg_PrintPages_Params { - PrintMsg_PrintPages_Params(); - ~PrintMsg_PrintPages_Params(); - - // Resets the members of the struct to 0. - void Reset(); - - PrintMsg_Print_Params params; - std::vector pages; -}; - -struct PrintHostMsg_RequestPrintPreview_Params { - PrintHostMsg_RequestPrintPreview_Params(); - ~PrintHostMsg_RequestPrintPreview_Params(); - bool is_modifiable; - bool webnode_only; - bool has_selection; - bool selection_only; -}; - -struct PrintHostMsg_SetOptionsFromDocument_Params { - PrintHostMsg_SetOptionsFromDocument_Params(); - ~PrintHostMsg_SetOptionsFromDocument_Params(); - - bool is_scaling_disabled; - int copies; - printing::DuplexMode duplex; - printing::PageRanges page_ranges; -}; - -#endif // CHROME_COMMON_PRINT_MESSAGES_H_ - -#define IPC_MESSAGE_START PrintMsgStart - -IPC_ENUM_TRAITS_MAX_VALUE(printing::MarginType, - printing::MARGIN_TYPE_LAST) -IPC_ENUM_TRAITS_MAX_VALUE(blink::WebPrintScalingOption, - blink::WebPrintScalingOptionLast) - -IPC_ENUM_TRAITS_MIN_MAX_VALUE(printing::DuplexMode, - printing::UNKNOWN_DUPLEX_MODE, - printing::SHORT_EDGE) - -// Parameters for a render request. -IPC_STRUCT_TRAITS_BEGIN(PrintMsg_Print_Params) - // Physical size of the page, including non-printable margins, - // in pixels according to dpi. - IPC_STRUCT_TRAITS_MEMBER(page_size) - - // In pixels according to dpi_x and dpi_y. - IPC_STRUCT_TRAITS_MEMBER(content_size) - - // Physical printable area of the page in pixels according to dpi. - IPC_STRUCT_TRAITS_MEMBER(printable_area) - - // The y-offset of the printable area, in pixels according to dpi. - IPC_STRUCT_TRAITS_MEMBER(margin_top) - - // The x-offset of the printable area, in pixels according to dpi. - IPC_STRUCT_TRAITS_MEMBER(margin_left) - - // Specifies dots per inch. - IPC_STRUCT_TRAITS_MEMBER(dpi) - - // Minimum shrink factor. See PrintSettings::min_shrink for more information. - IPC_STRUCT_TRAITS_MEMBER(min_shrink) - - // Maximum shrink factor. See PrintSettings::max_shrink for more information. - IPC_STRUCT_TRAITS_MEMBER(max_shrink) - - // Desired apparent dpi on paper. - IPC_STRUCT_TRAITS_MEMBER(desired_dpi) - - // Cookie for the document to ensure correctness. - IPC_STRUCT_TRAITS_MEMBER(document_cookie) - - // Should only print currently selected text. - IPC_STRUCT_TRAITS_MEMBER(selection_only) - - // Does the printer support alpha blending? - IPC_STRUCT_TRAITS_MEMBER(supports_alpha_blend) - - // Specifies the page scaling option for preview printing. - IPC_STRUCT_TRAITS_MEMBER(print_scaling_option) - - // Title string to be printed as header if requested by the user. - IPC_STRUCT_TRAITS_MEMBER(title) - - // URL string to be printed as footer if requested by the user. - IPC_STRUCT_TRAITS_MEMBER(url) - - // True if print backgrounds is requested by the user. - IPC_STRUCT_TRAITS_MEMBER(should_print_backgrounds) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_BEGIN(PrintMsg_PrintPage_Params) - // Parameters to render the page as a printed page. It must always be the same - // value for all the document. - IPC_STRUCT_MEMBER(PrintMsg_Print_Params, params) - - // The page number is the indicator of the square that should be rendered - // according to the layout specified in PrintMsg_Print_Params. - IPC_STRUCT_MEMBER(int, page_number) -IPC_STRUCT_END() - -IPC_STRUCT_TRAITS_BEGIN(printing::PageRange) - IPC_STRUCT_TRAITS_MEMBER(from) - IPC_STRUCT_TRAITS_MEMBER(to) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_RequestPrintPreview_Params) - IPC_STRUCT_TRAITS_MEMBER(is_modifiable) - IPC_STRUCT_TRAITS_MEMBER(webnode_only) - IPC_STRUCT_TRAITS_MEMBER(has_selection) - IPC_STRUCT_TRAITS_MEMBER(selection_only) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_SetOptionsFromDocument_Params) - // Specifies whether print scaling is enabled or not. - IPC_STRUCT_TRAITS_MEMBER(is_scaling_disabled) - - // Specifies number of copies to be printed. - IPC_STRUCT_TRAITS_MEMBER(copies) - - // Specifies paper handling option. - IPC_STRUCT_TRAITS_MEMBER(duplex) - - // Specifies page range to be printed. - IPC_STRUCT_TRAITS_MEMBER(page_ranges) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(printing::PageSizeMargins) - IPC_STRUCT_TRAITS_MEMBER(content_width) - IPC_STRUCT_TRAITS_MEMBER(content_height) - IPC_STRUCT_TRAITS_MEMBER(margin_left) - IPC_STRUCT_TRAITS_MEMBER(margin_right) - IPC_STRUCT_TRAITS_MEMBER(margin_top) - IPC_STRUCT_TRAITS_MEMBER(margin_bottom) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(PrintMsg_PrintPages_Params) - // Parameters to render the page as a printed page. It must always be the same - // value for all the document. - IPC_STRUCT_TRAITS_MEMBER(params) - - // If empty, this means a request to render all the printed pages. - IPC_STRUCT_TRAITS_MEMBER(pages) -IPC_STRUCT_TRAITS_END() - -// Parameters to describe a rendered page. -IPC_STRUCT_BEGIN(PrintHostMsg_DidPrintPage_Params) - // A shared memory handle to the EMF data. This data can be quite large so a - // memory map needs to be used. - IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) - - // Size of the metafile data. - IPC_STRUCT_MEMBER(uint32_t, data_size) - - // Cookie for the document to ensure correctness. - IPC_STRUCT_MEMBER(int, document_cookie) - - // Page number. - IPC_STRUCT_MEMBER(int, page_number) - - // Shrink factor used to render this page. - IPC_STRUCT_MEMBER(double, actual_shrink) - - // The size of the page the page author specified. - IPC_STRUCT_MEMBER(gfx::Size, page_size) - - // The printable area the page author specified. - IPC_STRUCT_MEMBER(gfx::Rect, content_area) -IPC_STRUCT_END() - -// Parameters for the IPC message ViewHostMsg_ScriptedPrint -IPC_STRUCT_BEGIN(PrintHostMsg_ScriptedPrint_Params) - IPC_STRUCT_MEMBER(int, cookie) - IPC_STRUCT_MEMBER(int, expected_pages_count) - IPC_STRUCT_MEMBER(bool, has_selection) - IPC_STRUCT_MEMBER(printing::MarginType, margin_type) -IPC_STRUCT_END() - -// Parameters to describe a rendered document. -IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewDocument_Params) - // A shared memory handle to metafile data. - IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) - - // Size of metafile data. - IPC_STRUCT_MEMBER(uint32_t, data_size) - - // Cookie for the document to ensure correctness. - IPC_STRUCT_MEMBER(int, document_cookie) - - // Store the expected pages count. - IPC_STRUCT_MEMBER(int, expected_pages_count) - - // Whether the preview can be modified. - IPC_STRUCT_MEMBER(bool, modifiable) - - // The id of the preview request. - IPC_STRUCT_MEMBER(int, preview_request_id) -IPC_STRUCT_END() - - -// Messages sent from the browser to the renderer. - -// Tells the render view to switch the CSS to print media type, renders every -// requested pages and switch back the CSS to display media type. -IPC_MESSAGE_ROUTED2(PrintMsg_PrintPages, - bool /* silent print */, - bool /* print page's background */) - -// Tells the render view that printing is done so it can clean up. -IPC_MESSAGE_ROUTED1(PrintMsg_PrintingDone, - bool /* success */) - -// Tells the render view to switch the CSS to print media type, renders every -// requested pages for print preview using the given |settings|. This gets -// called multiple times as the user updates settings. -IPC_MESSAGE_ROUTED1(PrintMsg_PrintPreview, - base::DictionaryValue /* settings */) - -// Messages sent from the renderer to the browser. - -#if defined(OS_WIN) -// Duplicates a shared memory handle from the renderer to the browser. Then -// the renderer can flush the handle. -IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_DuplicateSection, - base::SharedMemoryHandle /* renderer handle */, - base::SharedMemoryHandle /* browser handle */) -#endif - -// Tells the browser that the renderer is done calculating the number of -// rendered pages according to the specified settings. -IPC_MESSAGE_ROUTED2(PrintHostMsg_DidGetPrintedPagesCount, - int /* rendered document cookie */, - int /* number of rendered pages */) - -// Sends the document cookie of the current printer query to the browser. -IPC_MESSAGE_ROUTED1(PrintHostMsg_DidGetDocumentCookie, - int /* rendered document cookie */) - -// Tells the browser that the print dialog has been shown. -IPC_MESSAGE_ROUTED0(PrintHostMsg_DidShowPrintDialog) - -// Sends back to the browser the rendered "printed page" that was requested by -// a ViewMsg_PrintPage message or from scripted printing. The memory handle in -// this message is already valid in the browser process. -IPC_MESSAGE_ROUTED1(PrintHostMsg_DidPrintPage, - PrintHostMsg_DidPrintPage_Params /* page content */) - -// The renderer wants to know the default print settings. -IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_GetDefaultPrintSettings, - PrintMsg_Print_Params /* default_settings */) - -// The renderer wants to update the current print settings with new -// |job_settings|. -IPC_SYNC_MESSAGE_ROUTED2_2(PrintHostMsg_UpdatePrintSettings, - int /* document_cookie */, - base::DictionaryValue /* job_settings */, - PrintMsg_PrintPages_Params /* current_settings */, - bool /* canceled */) - -// It's the renderer that controls the printing process when it is generated -// by javascript. This step is about showing UI to the user to select the -// final print settings. The output parameter is the same as -// ViewMsg_PrintPages which is executed implicitly. -IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_ScriptedPrint, - PrintHostMsg_ScriptedPrint_Params, - PrintMsg_PrintPages_Params - /* settings chosen by the user*/) - -// Asks the browser to do print preview. -IPC_MESSAGE_ROUTED1(PrintHostMsg_RequestPrintPreview, - PrintHostMsg_RequestPrintPreview_Params /* params */) - -// This is sent when there are invalid printer settings. -IPC_MESSAGE_ROUTED0(PrintHostMsg_ShowInvalidPrinterSettingsError) - -// Tell the browser printing failed. -IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintingFailed, - int /* document cookie */) - -// Sends back to the browser the complete rendered document (non-draft mode, -// used for printing) that was requested by a PrintMsg_PrintPreview message. -// The memory handle in this message is already valid in the browser process. -IPC_MESSAGE_ROUTED1(PrintHostMsg_MetafileReadyForPrinting, - PrintHostMsg_DidPreviewDocument_Params /* params */) - -IPC_MESSAGE_ROUTED2(PrintHostMsg_PrintPreviewFailed, - int /* document cookie */, - int /* request_id */); - -#if defined(OS_WIN) -// Tell the utility process to start rendering the given PDF into a metafile. -// Utility process would be alive until -// ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop message. -IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles, - IPC::PlatformFileForTransit, /* input_file */ - printing::PdfRenderSettings /* settings */) - -// Requests conversion of the next page. -IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage, - int /* page_number */, - IPC::PlatformFileForTransit /* output_file */) - -// Requests utility process to stop conversion and exit. -IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop) - -// Reply when the utility process loaded PDF. |page_count| is 0, if loading -// failed. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, - int /* page_count */) - -// Reply when the utility process rendered the PDF page. -IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, - bool /* success */, - float /* scale_factor */) - -// Notify the browser to set print presets based on source PDF document. -IPC_MESSAGE_ROUTED1(PrintHostMsg_SetOptionsFromDocument, - PrintHostMsg_SetOptionsFromDocument_Params /* params */) - -#endif diff --git a/chromium_src/chrome/common/tts_messages.h b/chromium_src/chrome/common/tts_messages.h deleted file mode 100644 index 201da370a3..0000000000 --- a/chromium_src/chrome/common/tts_messages.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Multiply-included message file, hence no include guard. - -#include - -#include "chrome/common/tts_utterance_request.h" -#include "ipc/ipc_message_macros.h" -#include "ipc/ipc_param_traits.h" - -#define IPC_MESSAGE_START TtsMsgStart - -IPC_STRUCT_TRAITS_BEGIN(TtsUtteranceRequest) -IPC_STRUCT_TRAITS_MEMBER(id) -IPC_STRUCT_TRAITS_MEMBER(text) -IPC_STRUCT_TRAITS_MEMBER(lang) -IPC_STRUCT_TRAITS_MEMBER(voice) -IPC_STRUCT_TRAITS_MEMBER(volume) -IPC_STRUCT_TRAITS_MEMBER(rate) -IPC_STRUCT_TRAITS_MEMBER(pitch) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(TtsVoice) -IPC_STRUCT_TRAITS_MEMBER(voice_uri) -IPC_STRUCT_TRAITS_MEMBER(name) -IPC_STRUCT_TRAITS_MEMBER(lang) -IPC_STRUCT_TRAITS_MEMBER(local_service) -IPC_STRUCT_TRAITS_MEMBER(is_default) -IPC_STRUCT_TRAITS_END() - -// Renderer -> Browser messages. - -IPC_MESSAGE_CONTROL0(TtsHostMsg_InitializeVoiceList) -IPC_MESSAGE_CONTROL1(TtsHostMsg_Speak, - TtsUtteranceRequest) -IPC_MESSAGE_CONTROL0(TtsHostMsg_Pause) -IPC_MESSAGE_CONTROL0(TtsHostMsg_Resume) -IPC_MESSAGE_CONTROL0(TtsHostMsg_Cancel) - -// Browser -> Renderer messages. - -IPC_MESSAGE_CONTROL1(TtsMsg_SetVoiceList, - std::vector) -IPC_MESSAGE_CONTROL1(TtsMsg_DidStartSpeaking, - int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_DidFinishSpeaking, - int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_DidPauseSpeaking, - int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_DidResumeSpeaking, - int /* utterance id */) -IPC_MESSAGE_CONTROL2(TtsMsg_WordBoundary, - int /* utterance id */, - int /* char index */) -IPC_MESSAGE_CONTROL2(TtsMsg_SentenceBoundary, - int /* utterance id */, - int /* char index */) -IPC_MESSAGE_CONTROL2(TtsMsg_MarkerEvent, - int /* utterance id */, - int /* char index */) -IPC_MESSAGE_CONTROL1(TtsMsg_WasInterrupted, - int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_WasCancelled, - int /* utterance id */) -IPC_MESSAGE_CONTROL2(TtsMsg_SpeakingErrorOccurred, - int /* utterance id */, - std::string /* error message */) diff --git a/chromium_src/chrome/common/tts_utterance_request.cc b/chromium_src/chrome/common/tts_utterance_request.cc deleted file mode 100644 index a2e3e7fcea..0000000000 --- a/chromium_src/chrome/common/tts_utterance_request.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/common/tts_utterance_request.h" - -TtsUtteranceRequest::TtsUtteranceRequest() - : id(0), - volume(1.0), - rate(1.0), - pitch(1.0) { -} - -TtsUtteranceRequest::~TtsUtteranceRequest() { -} - -TtsVoice::TtsVoice() - : local_service(true), - is_default(false) { -} - -TtsVoice::~TtsVoice() { -} - -TtsUtteranceResponse::TtsUtteranceResponse() - : id(0) { -} - -TtsUtteranceResponse::~TtsUtteranceResponse() { -} \ No newline at end of file diff --git a/chromium_src/chrome/common/tts_utterance_request.h b/chromium_src/chrome/common/tts_utterance_request.h deleted file mode 100644 index a4b4cab68c..0000000000 --- a/chromium_src/chrome/common/tts_utterance_request.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_ -#define CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_ - -#include - -#include "base/macros.h" -#include "base/strings/string16.h" - -struct TtsUtteranceRequest { - TtsUtteranceRequest(); - ~TtsUtteranceRequest(); - - int id; - std::string text; - std::string lang; - std::string voice; - float volume; - float rate; - float pitch; -}; - -struct TtsVoice { - TtsVoice(); - ~TtsVoice(); - - std::string voice_uri; - std::string name; - std::string lang; - bool local_service; - bool is_default; -}; - -struct TtsUtteranceResponse { - TtsUtteranceResponse(); - ~TtsUtteranceResponse(); - - int id; -}; - -#endif // CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_ diff --git a/chromium_src/chrome/common/widevine_cdm_constants.cc b/chromium_src/chrome/common/widevine_cdm_constants.cc deleted file mode 100644 index c8655f8d4f..0000000000 --- a/chromium_src/chrome/common/widevine_cdm_constants.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/common/widevine_cdm_constants.h" - -#include "build/build_config.h" -#include "ppapi/shared_impl/ppapi_permissions.h" - -const char kWidevineCdmPluginExtension[] = ""; - -const int32_t kWidevineCdmPluginPermissions = ppapi::PERMISSION_DEV | - ppapi::PERMISSION_PRIVATE; diff --git a/chromium_src/chrome/common/widevine_cdm_constants.h b/chromium_src/chrome/common/widevine_cdm_constants.h deleted file mode 100644 index 020601d236..0000000000 --- a/chromium_src/chrome/common/widevine_cdm_constants.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_COMMON_WIDEVINE_CDM_CONSTANTS_H_ -#define CHROME_COMMON_WIDEVINE_CDM_CONSTANTS_H_ - -#include "base/macros.h" -#include "base/files/file_path.h" - -extern const char kWidevineCdmPluginExtension[]; - -// Permission bits for Widevine CDM plugin. -extern const int32_t kWidevineCdmPluginPermissions; - -#endif // CHROME_COMMON_WIDEVINE_CDM_CONSTANTS_H_ diff --git a/chromium_src/chrome/common/widevine_cdm_messages.h b/chromium_src/chrome/common/widevine_cdm_messages.h deleted file mode 100644 index 17c908776b..0000000000 --- a/chromium_src/chrome/common/widevine_cdm_messages.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Multiply-included message file, hence no include guard. - -#include - -#include "ipc/ipc_message_macros.h" -// #include "ipc/ipc_param_traits.h" - -#define IPC_MESSAGE_START ChromeMsgStart - -// Renderer -> Browser messages. - -#if defined(ENABLE_PEPPER_CDMS) -// Returns whether any internal plugin supporting |mime_type| is registered and -// enabled. Does not determine whether the plugin can actually be instantiated -// (e.g. whether it has all its dependencies). -// When the returned *|is_available| is true, |additional_param_names| and -// |additional_param_values| contain the name-value pairs, if any, specified -// for the *first* non-disabled plugin found that is registered for |mime_type|. -IPC_SYNC_MESSAGE_CONTROL1_3( - ChromeViewHostMsg_IsInternalPluginAvailableForMimeType, - std::string /* mime_type */, - bool /* is_available */, - std::vector /* additional_param_names */, - std::vector /* additional_param_values */) -#endif - -// Browser -> Renderer messages. diff --git a/chromium_src/chrome/renderer/extensions/tabs_custom_bindings.cc b/chromium_src/chrome/renderer/extensions/tabs_custom_bindings.cc deleted file mode 100644 index 2459f96d06..0000000000 --- a/chromium_src/chrome/renderer/extensions/tabs_custom_bindings.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/extensions/tabs_custom_bindings.h" - -#include - -#include - -#include "base/bind.h" -#include "content/public/renderer/render_frame.h" -#include "extensions/common/extension_messages.h" -#include "extensions/renderer/script_context.h" -#include "v8/include/v8.h" - -namespace extensions { - -TabsCustomBindings::TabsCustomBindings(ScriptContext* context) - : ObjectBackedNativeHandler(context) { - RouteFunction("OpenChannelToTab", - base::Bind(&TabsCustomBindings::OpenChannelToTab, - base::Unretained(this))); -} - -void TabsCustomBindings::OpenChannelToTab( - const v8::FunctionCallbackInfo& args) { - content::RenderFrame* render_frame = context()->GetRenderFrame(); - if (!render_frame) - return; - - // tabs_custom_bindings.js unwraps arguments to tabs.connect/sendMessage and - // passes them to OpenChannelToTab, in the following order: - // - |tab_id| - Positive number that specifies the destination of the channel. - // - |frame_id| - Target frame(s) in the tab where onConnect is dispatched: - // -1 for all frames, 0 for the main frame, >0 for a child frame. - // - |extension_id| - Extension ID of sender and destination. - // - |channel_name| - A user-defined channel name. - CHECK(args.Length() >= 4 && - args[0]->IsInt32() && - args[1]->IsInt32() && - args[2]->IsString() && - args[3]->IsString()); - - ExtensionMsg_TabTargetConnectionInfo info; - info.tab_id = args[0]->Int32Value(); - info.frame_id = args[1]->Int32Value(); - std::string extension_id = *v8::String::Utf8Value(args[2]); - std::string channel_name = *v8::String::Utf8Value(args[3]); - int port_id = -1; - render_frame->Send(new ExtensionHostMsg_OpenChannelToTab( - render_frame->GetRoutingID(), info, extension_id, channel_name, - &port_id)); - args.GetReturnValue().Set(static_cast(port_id)); -} - -} // namespace extensions diff --git a/chromium_src/chrome/renderer/extensions/tabs_custom_bindings.h b/chromium_src/chrome/renderer/extensions/tabs_custom_bindings.h deleted file mode 100644 index a5b4389e70..0000000000 --- a/chromium_src/chrome/renderer/extensions/tabs_custom_bindings.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_EXTENSIONS_TABS_CUSTOM_BINDINGS_H_ -#define CHROME_RENDERER_EXTENSIONS_TABS_CUSTOM_BINDINGS_H_ - -#include "extensions/renderer/object_backed_native_handler.h" - -namespace extensions { - -// Implements custom bindings for the tabs API. -class TabsCustomBindings : public ObjectBackedNativeHandler { - public: - explicit TabsCustomBindings(ScriptContext* context); - - private: - // Creates a new messaging channel to the tab with the given ID. - void OpenChannelToTab(const v8::FunctionCallbackInfo& args); -}; - -} // namespace extensions - -#endif // CHROME_RENDERER_EXTENSIONS_TABS_CUSTOM_BINDINGS_H_ diff --git a/chromium_src/chrome/renderer/media/chrome_key_systems.cc b/chromium_src/chrome/renderer/media/chrome_key_systems.cc deleted file mode 100644 index ef3ab52e59..0000000000 --- a/chromium_src/chrome/renderer/media/chrome_key_systems.cc +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2013 The Chromium 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 "chrome/renderer/media/chrome_key_systems.h" - -#include - -#include -#include - -#include "base/logging.h" -#include "base/strings/string16.h" -#include "base/strings/string_split.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/common/widevine_cdm_messages.h" -#include "components/cdm/renderer/widevine_key_system_properties.h" -#include "content/public/renderer/render_thread.h" -#include "media/base/eme_constants.h" -#include "media/base/key_system_properties.h" - -// #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. -#include "third_party/widevine/cdm/stub/widevine_cdm_version.h" - -// The following must be after widevine_cdm_version.h. - -#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) -#include -#include "base/version.h" -#endif - -using media::KeySystemProperties; -using media::SupportedCodecs; - -#if defined(ENABLE_PEPPER_CDMS) -static const char kExternalClearKeyPepperType[] = - "application/x-ppapi-clearkey-cdm"; - -static bool IsPepperCdmAvailable( - const std::string& pepper_type, - std::vector* additional_param_names, - std::vector* additional_param_values) { - bool is_available = false; - content::RenderThread::Get()->Send( - new ChromeViewHostMsg_IsInternalPluginAvailableForMimeType( - pepper_type, - &is_available, - additional_param_names, - additional_param_values)); - - return is_available; -} - -// KeySystemProperties implementation for external Clear Key systems. -class ExternalClearKeyProperties : public KeySystemProperties { - public: - explicit ExternalClearKeyProperties(const std::string& key_system_name) - : key_system_name_(key_system_name) {} - - std::string GetKeySystemName() const override { return key_system_name_; } - bool IsSupportedInitDataType( - media::EmeInitDataType init_data_type) const override { - switch (init_data_type) { - case media::EmeInitDataType::WEBM: - case media::EmeInitDataType::KEYIDS: - return true; - - case media::EmeInitDataType::CENC: -#if defined(USE_PROPRIETARY_CODECS) - return true; -#else - return false; -#endif // defined(USE_PROPRIETARY_CODECS) - - case media::EmeInitDataType::UNKNOWN: - return false; - } - NOTREACHED(); - return false; - } - - SupportedCodecs GetSupportedCodecs() const override { -#if defined(USE_PROPRIETARY_CODECS) - return media::EME_CODEC_MP4_ALL | media::EME_CODEC_WEBM_ALL; -#else - return media::EME_CODEC_WEBM_ALL; -#endif - } - - media::EmeConfigRule GetRobustnessConfigRule( - media::EmeMediaType media_type, - const std::string& requested_robustness) const override { - return requested_robustness.empty() ? media::EmeConfigRule::SUPPORTED - : media::EmeConfigRule::NOT_SUPPORTED; - } - - // Persistent license sessions are faked. - media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport() - const override { - return media::EmeSessionTypeSupport::SUPPORTED; - } - - media::EmeSessionTypeSupport GetPersistentReleaseMessageSessionSupport() - const override { - return media::EmeSessionTypeSupport::NOT_SUPPORTED; - } - - media::EmeFeatureSupport GetPersistentStateSupport() const override { - return media::EmeFeatureSupport::REQUESTABLE; - } - - media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override { - return media::EmeFeatureSupport::NOT_SUPPORTED; - } - - std::string GetPepperType() const override { - return kExternalClearKeyPepperType; - } - - private: - const std::string key_system_name_; -}; - -// External Clear Key (used for testing). -static void AddExternalClearKey( - std::vector>* concrete_key_systems) { - static const char kExternalClearKeyKeySystem[] = - "org.chromium.externalclearkey"; - static const char kExternalClearKeyDecryptOnlyKeySystem[] = - "org.chromium.externalclearkey.decryptonly"; - static const char kExternalClearKeyFileIOTestKeySystem[] = - "org.chromium.externalclearkey.fileiotest"; - static const char kExternalClearKeyInitializeFailKeySystem[] = - "org.chromium.externalclearkey.initializefail"; - static const char kExternalClearKeyCrashKeySystem[] = - "org.chromium.externalclearkey.crash"; - - std::vector additional_param_names; - std::vector additional_param_values; - if (!IsPepperCdmAvailable(kExternalClearKeyPepperType, - &additional_param_names, - &additional_param_values)) { - return; - } - - concrete_key_systems->emplace_back( - new ExternalClearKeyProperties(kExternalClearKeyKeySystem)); - - // Add support of decrypt-only mode in ClearKeyCdm. - concrete_key_systems->emplace_back( - new ExternalClearKeyProperties(kExternalClearKeyDecryptOnlyKeySystem)); - - // A key system that triggers FileIO test in ClearKeyCdm. - concrete_key_systems->emplace_back( - new ExternalClearKeyProperties(kExternalClearKeyFileIOTestKeySystem)); - - // A key system that Chrome thinks is supported by ClearKeyCdm, but actually - // will be refused by ClearKeyCdm. This is to test the CDM initialization - // failure case. - concrete_key_systems->emplace_back( - new ExternalClearKeyProperties(kExternalClearKeyInitializeFailKeySystem)); - - // A key system that triggers a crash in ClearKeyCdm. - concrete_key_systems->emplace_back( - new ExternalClearKeyProperties(kExternalClearKeyCrashKeySystem)); -} - -#if defined(WIDEVINE_CDM_AVAILABLE) -// This function finds "codecs" and parses the value into the vector |codecs|. -// Converts the codec strings to UTF-8 since we only expect ASCII strings and -// this simplifies the rest of the code in this file. -void GetSupportedCodecsForPepperCdm( - const std::vector& additional_param_names, - const std::vector& additional_param_values, - std::vector* codecs) { - DCHECK(codecs->empty()); - DCHECK_EQ(additional_param_names.size(), additional_param_values.size()); - for (size_t i = 0; i < additional_param_names.size(); ++i) { - if (additional_param_names[i] == - base::ASCIIToUTF16(kCdmSupportedCodecsParamName)) { - const base::string16& codecs_string16 = additional_param_values[i]; - std::string codecs_string; - if (!base::UTF16ToUTF8(codecs_string16.c_str(), - codecs_string16.length(), - &codecs_string)) { - DLOG(WARNING) << "Non-UTF-8 codecs string."; - // Continue using the best effort conversion. - } - *codecs = base::SplitString( - codecs_string, std::string(1, kCdmSupportedCodecsValueDelimiter), - base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - break; - } - } -} - -static void AddPepperBasedWidevine( - std::vector>* concrete_key_systems) { -#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) - Version glibc_version(gnu_get_libc_version()); - DCHECK(glibc_version.IsValid()); - if (glibc_version < base::Version(WIDEVINE_CDM_MIN_GLIBC_VERSION)) - return; -#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) - - std::vector additional_param_names; - std::vector additional_param_values; - if (!IsPepperCdmAvailable(kWidevineCdmPluginMimeType, - &additional_param_names, - &additional_param_values)) { - DVLOG(1) << "Widevine CDM is not currently available."; - return; - } - - std::vector codecs; - GetSupportedCodecsForPepperCdm(additional_param_names, - additional_param_values, - &codecs); - - SupportedCodecs supported_codecs = media::EME_CODEC_NONE; - - // Audio codecs are always supported. - // TODO(sandersd): Distinguish these from those that are directly supported, - // as those may offer a higher level of protection. - supported_codecs |= media::EME_CODEC_WEBM_OPUS; - supported_codecs |= media::EME_CODEC_WEBM_VORBIS; -#if defined(USE_PROPRIETARY_CODECS) - supported_codecs |= media::EME_CODEC_MP4_AAC; -#endif // defined(USE_PROPRIETARY_CODECS) - - for (size_t i = 0; i < codecs.size(); ++i) { - if (codecs[i] == kCdmSupportedCodecVp8) - supported_codecs |= media::EME_CODEC_WEBM_VP8; - if (codecs[i] == kCdmSupportedCodecVp9) - supported_codecs |= media::EME_CODEC_WEBM_VP9; -#if defined(USE_PROPRIETARY_CODECS) - if (codecs[i] == kCdmSupportedCodecAvc1) - supported_codecs |= media::EME_CODEC_MP4_AVC1; - if (codecs[i] == kCdmSupportedCodecVp9) - supported_codecs |= media::EME_CODEC_MP4_VP9; -#endif // defined(USE_PROPRIETARY_CODECS) - } - - using Robustness = cdm::WidevineKeySystemProperties::Robustness; - concrete_key_systems->emplace_back(new cdm::WidevineKeySystemProperties( - supported_codecs, -#if defined(OS_CHROMEOS) - media::EmeRobustness::HW_SECURE_ALL, // Maximum audio robustness. - media::EmeRobustness::HW_SECURE_ALL, // Maximim video robustness. - media::EmeSessionTypeSupport:: - SUPPORTED_WITH_IDENTIFIER, // Persistent-license. - media::EmeSessionTypeSupport:: - NOT_SUPPORTED, // Persistent-release-message. - media::EmeFeatureSupport::REQUESTABLE, // Persistent state. - media::EmeFeatureSupport::REQUESTABLE)); // Distinctive identifier. -#else // (Desktop) - Robustness::SW_SECURE_CRYPTO, // Maximum audio robustness. - Robustness::SW_SECURE_DECODE, // Maximum video robustness. - media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. - media::EmeSessionTypeSupport:: - NOT_SUPPORTED, // persistent-release-message. - media::EmeFeatureSupport::REQUESTABLE, // Persistent state. - media::EmeFeatureSupport::NOT_SUPPORTED)); // Distinctive identifier. -#endif // defined(OS_CHROMEOS) -} -#endif // defined(WIDEVINE_CDM_AVAILABLE) -#endif // defined(ENABLE_PEPPER_CDMS) - -void AddChromeKeySystems( - std::vector>* key_systems_properties) { -#if defined(ENABLE_PEPPER_CDMS) - AddExternalClearKey(key_systems_properties); - -#if defined(WIDEVINE_CDM_AVAILABLE) - AddPepperBasedWidevine(key_systems_properties); -#endif // defined(WIDEVINE_CDM_AVAILABLE) -#endif // defined(ENABLE_PEPPER_CDMS) -} diff --git a/chromium_src/chrome/renderer/media/chrome_key_systems.h b/chromium_src/chrome/renderer/media/chrome_key_systems.h deleted file mode 100644 index 3e82ee70e2..0000000000 --- a/chromium_src/chrome/renderer/media/chrome_key_systems.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Chromium 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 CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_H_ -#define CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_H_ - -#include -#include - -namespace media { -class KeySystemProperties; -} - -// Register the key systems supported by populating |key_systems_properties|. -void AddChromeKeySystems( - std::vector>* - key_systems_properties); - -#endif // CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_H_ diff --git a/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc b/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc deleted file mode 100644 index bcf6debc4c..0000000000 --- a/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h" - -#include "base/logging.h" -#include "chrome/renderer/pepper/pepper_flash_font_file_host.h" -#include "chrome/renderer/pepper/pepper_flash_fullscreen_host.h" -#include "chrome/renderer/pepper/pepper_flash_menu_host.h" -#include "chrome/renderer/pepper/pepper_flash_renderer_host.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/host/resource_host.h" -#include "ppapi/proxy/ppapi_message_utils.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/shared_impl/ppapi_permissions.h" - -using ppapi::host::ResourceHost; - -ChromeRendererPepperHostFactory::ChromeRendererPepperHostFactory( - content::RendererPpapiHost* host) - : host_(host) {} - -ChromeRendererPepperHostFactory::~ChromeRendererPepperHostFactory() {} - -std::unique_ptr ChromeRendererPepperHostFactory::CreateResourceHost( - ppapi::host::PpapiHost* host, - PP_Resource resource, - PP_Instance instance, - const IPC::Message& message) { - DCHECK_EQ(host_->GetPpapiHost(), host); - - // Make sure the plugin is giving us a valid instance for this resource. - if (!host_->IsValidInstance(instance)) - return std::unique_ptr(); - - if (host_->GetPpapiHost()->permissions().HasPermission( - ppapi::PERMISSION_FLASH)) { - switch (message.type()) { - case PpapiHostMsg_Flash_Create::ID: { - return std::unique_ptr( - new PepperFlashRendererHost(host_, instance, resource)); - } - case PpapiHostMsg_FlashFullscreen_Create::ID: { - return std::unique_ptr( - new PepperFlashFullscreenHost(host_, instance, resource)); - } - case PpapiHostMsg_FlashMenu_Create::ID: { - ppapi::proxy::SerializedFlashMenu serialized_menu; - if (ppapi::UnpackMessage( - message, &serialized_menu)) { - return std::unique_ptr(new PepperFlashMenuHost( - host_, instance, resource, serialized_menu)); - } - break; - } - } - } - - // TODO(raymes): PDF also needs access to the FlashFontFileHost currently. - // We should either rename PPB_FlashFont_File to PPB_FontFile_Private or get - // rid of its use in PDF if possible. - if (host_->GetPpapiHost()->permissions().HasPermission( - ppapi::PERMISSION_FLASH) || - host_->GetPpapiHost()->permissions().HasPermission( - ppapi::PERMISSION_PRIVATE)) { - switch (message.type()) { - case PpapiHostMsg_FlashFontFile_Create::ID: { - ppapi::proxy::SerializedFontDescription description; - PP_PrivateFontCharset charset; - if (ppapi::UnpackMessage( - message, &description, &charset)) { - return std::unique_ptr(new PepperFlashFontFileHost( - host_, instance, resource, description, charset)); - } - break; - } - } - } - - return std::unique_ptr(); -} diff --git a/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h b/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h deleted file mode 100644 index c52c73b56f..0000000000 --- a/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PEPPER_CHROME_RENDERER_PEPPER_HOST_FACTORY_H_ -#define CHROME_RENDERER_PEPPER_CHROME_RENDERER_PEPPER_HOST_FACTORY_H_ - -#include "base/macros.h" -#include "ppapi/host/host_factory.h" - -namespace content { -class RendererPpapiHost; -} - -class ChromeRendererPepperHostFactory : public ppapi::host::HostFactory { - public: - explicit ChromeRendererPepperHostFactory(content::RendererPpapiHost* host); - ~ChromeRendererPepperHostFactory() override; - - // HostFactory. - std::unique_ptr CreateResourceHost( - ppapi::host::PpapiHost* host, - PP_Resource resource, - PP_Instance instance, - const IPC::Message& message) override; - - private: - // Not owned by this object. - content::RendererPpapiHost* host_; - - DISALLOW_COPY_AND_ASSIGN(ChromeRendererPepperHostFactory); -}; - -#endif // CHROME_RENDERER_PEPPER_CHROME_RENDERER_PEPPER_HOST_FACTORY_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc deleted file mode 100644 index c109709333..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/pepper/pepper_flash_font_file_host.h" - -#include "base/sys_byteorder.h" -#include "build/build_config.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/proxy/serialized_structs.h" - -#if defined(OS_LINUX) || defined(OS_OPENBSD) -#include "content/public/common/child_process_sandbox_support_linux.h" -#elif defined(OS_WIN) -#include "third_party/skia/include/ports/SkFontMgr.h" -#endif - -PepperFlashFontFileHost::PepperFlashFontFileHost( - content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource, - const ppapi::proxy::SerializedFontDescription& description, - PP_PrivateFontCharset charset) - : ResourceHost(host->GetPpapiHost(), instance, resource) { -#if defined(OS_LINUX) || defined(OS_OPENBSD) - fd_.reset(content::MatchFontWithFallback( - description.face.c_str(), - description.weight >= PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD, - description.italic, - charset, - PP_BROWSERFONT_TRUSTED_FAMILY_DEFAULT)); -#elif defined(OS_WIN) // defined(OS_LINUX) || defined(OS_OPENBSD) - int weight = description.weight; - if (weight == FW_DONTCARE) - weight = SkFontStyle::kNormal_Weight; - SkFontStyle style(weight, SkFontStyle::kNormal_Width, - description.italic ? SkFontStyle::kItalic_Slant - : SkFontStyle::kUpright_Slant); - sk_sp font_mgr(SkFontMgr::RefDefault()); - typeface_ = sk_sp( - font_mgr->matchFamilyStyle(description.face.c_str(), style)); -#endif // defined(OS_WIN) -} - -PepperFlashFontFileHost::~PepperFlashFontFileHost() {} - -int32_t PepperFlashFontFileHost::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashFontFileHost, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFontFile_GetFontTable, - OnGetFontTable) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} -bool PepperFlashFontFileHost::GetFontData(uint32_t table, - void* buffer, - size_t* length) { - bool result = false; -#if defined(OS_LINUX) || defined(OS_OPENBSD) - int fd = fd_.get(); - if (fd != -1) - result = content::GetFontTable(fd, table, 0 /* offset */, - reinterpret_cast(buffer), length); -#elif defined(OS_WIN) - if (typeface_) { - table = base::ByteSwap(table); - if (buffer == NULL) { - *length = typeface_->getTableSize(table); - if (*length > 0) - result = true; - } else { - size_t new_length = typeface_->getTableData(table, 0, *length, buffer); - if (new_length == *length) - result = true; - } - } -#endif - return result; -} - -int32_t PepperFlashFontFileHost::OnGetFontTable( - ppapi::host::HostMessageContext* context, - uint32_t table) { - std::string contents; - int32_t result = PP_ERROR_FAILED; - size_t length = 0; - if (GetFontData(table, NULL, &length)) { - contents.resize(length); - uint8_t* contents_ptr = - reinterpret_cast(const_cast(contents.c_str())); - if (GetFontData(table, contents_ptr, &length)) { - result = PP_OK; - } else { - contents.clear(); - } - } - - context->reply_msg = PpapiPluginMsg_FlashFontFile_GetFontTableReply(contents); - return result; -} diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.h b/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.h deleted file mode 100644 index 0205a69b50..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PEPPER_PEPPER_FLASH_FONT_FILE_HOST_H_ -#define CHROME_RENDERER_PEPPER_PEPPER_FLASH_FONT_FILE_HOST_H_ - -#include "ppapi/c/private/pp_private_font_charset.h" -#include "ppapi/host/resource_host.h" - -#if defined(OS_LINUX) || defined(OS_OPENBSD) -#include "base/files/scoped_file.h" -#elif defined(OS_WIN) -#include "third_party/skia/include/core/SkRefCnt.h" -#include "third_party/skia/include/core/SkTypeface.h" -#endif - -namespace content { -class RendererPpapiHost; -} - -namespace ppapi { -namespace proxy { -struct SerializedFontDescription; -} -} - -class PepperFlashFontFileHost : public ppapi::host::ResourceHost { - public: - PepperFlashFontFileHost( - content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource, - const ppapi::proxy::SerializedFontDescription& description, - PP_PrivateFontCharset charset); - ~PepperFlashFontFileHost() override; - - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - int32_t OnGetFontTable(ppapi::host::HostMessageContext* context, - uint32_t table); - bool GetFontData(uint32_t table, void* buffer, size_t* length); - -#if defined(OS_LINUX) || defined(OS_OPENBSD) - base::ScopedFD fd_; -#elif defined(OS_WIN) - sk_sp typeface_; -#endif - - DISALLOW_COPY_AND_ASSIGN(PepperFlashFontFileHost); -}; - -#endif // CHROME_RENDERER_PEPPER_PEPPER_FLASH_FONT_FILE_HOST_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.cc deleted file mode 100644 index d590cde3a4..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/pepper/pepper_flash_fullscreen_host.h" - -#include "content/public/renderer/pepper_plugin_instance.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" - -PepperFlashFullscreenHost::PepperFlashFullscreenHost( - content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource) - : ResourceHost(host->GetPpapiHost(), instance, resource), - renderer_ppapi_host_(host) {} - -PepperFlashFullscreenHost::~PepperFlashFullscreenHost() {} - -int32_t PepperFlashFullscreenHost::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashFullscreenHost, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( - PpapiHostMsg_FlashFullscreen_SetFullscreen, - OnSetFullscreen) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperFlashFullscreenHost::OnSetFullscreen( - ppapi::host::HostMessageContext* context, - bool fullscreen) { - content::PepperPluginInstance* plugin_instance = - renderer_ppapi_host_->GetPluginInstance(pp_instance()); - if (plugin_instance && plugin_instance->FlashSetFullscreen(fullscreen, true)) - return PP_OK; - return PP_ERROR_FAILED; -} diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.h b/chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.h deleted file mode 100644 index 86d0af73ae..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_fullscreen_host.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PEPPER_PEPPER_FLASH_FULLSCREEN_HOST_H_ -#define CHROME_RENDERER_PEPPER_PEPPER_FLASH_FULLSCREEN_HOST_H_ - -#include "ppapi/host/resource_host.h" - -namespace content { -class RendererPpapiHost; -} - -class PepperFlashFullscreenHost : public ppapi::host::ResourceHost { - public: - PepperFlashFullscreenHost(content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource); - ~PepperFlashFullscreenHost() override; - - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - int32_t OnSetFullscreen(ppapi::host::HostMessageContext* context, - bool fullscreen); - - // Non-owning pointer. - content::RendererPpapiHost* renderer_ppapi_host_; - - DISALLOW_COPY_AND_ASSIGN(PepperFlashFullscreenHost); -}; - -#endif // CHROME_RENDERER_PEPPER_PEPPER_FLASH_FULLSCREEN_HOST_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.cc deleted file mode 100644 index 3b7a438f72..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/pepper/pepper_flash_menu_host.h" - -#include "base/strings/utf_string_conversions.h" -#include "content/public/common/context_menu_params.h" -#include "content/public/renderer/render_frame.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ipc/ipc_message.h" -#include "ppapi/c/private/ppb_flash_menu.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/proxy/serialized_flash_menu.h" -#include "ui/gfx/geometry/point.h" - -namespace { - -// Maximum depth of submenus allowed (e.g., 1 indicates that submenus are -// allowed, but not sub-submenus). -const size_t kMaxMenuDepth = 2; - -// Maximum number of entries in any single menu (including separators). -const size_t kMaxMenuEntries = 50; - -// Maximum total number of entries in the |menu_id_map| (see below). -// (Limit to 500 real entries; reserve the 0 action as an invalid entry.) -const size_t kMaxMenuIdMapEntries = 501; - -// Converts menu data from one form to another. -// - |depth| is the current nested depth (call it starting with 0). -// - |menu_id_map| is such that |menu_id_map[output_item.action] == -// input_item.id| (where |action| is what a |MenuItem| has, |id| is what a -// |PP_Flash_MenuItem| has). -bool ConvertMenuData(const PP_Flash_Menu* in_menu, - size_t depth, - std::vector* out_menu, - std::vector* menu_id_map) { - if (depth > kMaxMenuDepth || !in_menu) - return false; - - // Clear the output, just in case. - out_menu->clear(); - - if (!in_menu->count) - return true; // Nothing else to do. - - if (!in_menu->items || in_menu->count > kMaxMenuEntries) - return false; - for (uint32_t i = 0; i < in_menu->count; i++) { - content::MenuItem item; - - PP_Flash_MenuItem_Type type = in_menu->items[i].type; - switch (type) { - case PP_FLASH_MENUITEM_TYPE_NORMAL: - item.type = content::MenuItem::OPTION; - break; - case PP_FLASH_MENUITEM_TYPE_CHECKBOX: - item.type = content::MenuItem::CHECKABLE_OPTION; - break; - case PP_FLASH_MENUITEM_TYPE_SEPARATOR: - item.type = content::MenuItem::SEPARATOR; - break; - case PP_FLASH_MENUITEM_TYPE_SUBMENU: - item.type = content::MenuItem::SUBMENU; - break; - default: - return false; - } - if (in_menu->items[i].name) - item.label = base::UTF8ToUTF16(in_menu->items[i].name); - if (menu_id_map->size() >= kMaxMenuIdMapEntries) - return false; - item.action = static_cast(menu_id_map->size()); - // This sets |(*menu_id_map)[item.action] = in_menu->items[i].id|. - menu_id_map->push_back(in_menu->items[i].id); - item.enabled = PP_ToBool(in_menu->items[i].enabled); - item.checked = PP_ToBool(in_menu->items[i].checked); - if (type == PP_FLASH_MENUITEM_TYPE_SUBMENU) { - if (!ConvertMenuData( - in_menu->items[i].submenu, depth + 1, &item.submenu, menu_id_map)) - return false; - } - - out_menu->push_back(item); - } - - return true; -} - -} // namespace - -PepperFlashMenuHost::PepperFlashMenuHost( - content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource, - const ppapi::proxy::SerializedFlashMenu& serial_menu) - : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource), - renderer_ppapi_host_(host), - showing_context_menu_(false), - context_menu_request_id_(0), - has_saved_context_menu_action_(false), - saved_context_menu_action_(0) { - menu_id_map_.push_back(0); // Reserve |menu_id_map_[0]|. - if (!ConvertMenuData(serial_menu.pp_menu(), 0, &menu_data_, &menu_id_map_)) { - menu_data_.clear(); - menu_id_map_.clear(); - } -} - -PepperFlashMenuHost::~PepperFlashMenuHost() { - if (showing_context_menu_) { - content::RenderFrame* render_frame = - renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance()); - if (render_frame) - render_frame->CancelContextMenu(context_menu_request_id_); - } -} - -int32_t PepperFlashMenuHost::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashMenuHost, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashMenu_Show, - OnHostMsgShow) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperFlashMenuHost::OnHostMsgShow( - ppapi::host::HostMessageContext* context, - const PP_Point& location) { - // Note that all early returns must do a SendMenuReply. The sync result for - // this message isn't used, so to forward the error to the plugin, we need to - // additionally call SendMenuReply explicitly. - if (menu_data_.empty()) { - SendMenuReply(PP_ERROR_FAILED, -1); - return PP_ERROR_FAILED; - } - if (showing_context_menu_) { - SendMenuReply(PP_ERROR_INPROGRESS, -1); - return PP_ERROR_INPROGRESS; - } - - content::RenderFrame* render_frame = - renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance()); - - content::ContextMenuParams params; - params.x = location.x; - params.y = location.y; - params.custom_context.is_pepper_menu = true; - params.custom_context.render_widget_id = - renderer_ppapi_host_->GetRoutingIDForWidget(pp_instance()); - params.custom_items = menu_data_; - - // Transform the position to be in render frame's coordinates. - gfx::Point render_frame_pt = renderer_ppapi_host_->PluginPointToRenderFrame( - pp_instance(), gfx::Point(location.x, location.y)); - params.x = render_frame_pt.x(); - params.y = render_frame_pt.y(); - - showing_context_menu_ = true; - context_menu_request_id_ = render_frame->ShowContextMenu(this, params); - - // Note: the show message is sync so this OK is for the sync reply which we - // don't actually use (see the comment in the resource file for this). The - // async message containing the context menu action will be sent in the - // future. - return PP_OK; -} - -void PepperFlashMenuHost::OnMenuAction(int request_id, unsigned action) { - // Just save the action. - DCHECK(!has_saved_context_menu_action_); - has_saved_context_menu_action_ = true; - saved_context_menu_action_ = action; -} - -void PepperFlashMenuHost::OnMenuClosed(int request_id) { - if (has_saved_context_menu_action_ && - saved_context_menu_action_ < menu_id_map_.size()) { - SendMenuReply(PP_OK, menu_id_map_[saved_context_menu_action_]); - has_saved_context_menu_action_ = false; - saved_context_menu_action_ = 0; - } else { - SendMenuReply(PP_ERROR_USERCANCEL, -1); - } - - showing_context_menu_ = false; - context_menu_request_id_ = 0; -} - -void PepperFlashMenuHost::SendMenuReply(int32_t result, int action) { - ppapi::host::ReplyMessageContext reply_context( - ppapi::proxy::ResourceMessageReplyParams(pp_resource(), 0), - NULL, - MSG_ROUTING_NONE); - reply_context.params.set_result(result); - host()->SendReply(reply_context, PpapiPluginMsg_FlashMenu_ShowReply(action)); -} diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.h b/chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.h deleted file mode 100644 index 3aa730a6af..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_menu_host.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PEPPER_PEPPER_FLASH_MENU_HOST_H_ -#define CHROME_RENDERER_PEPPER_PEPPER_FLASH_MENU_HOST_H_ - -#include - -#include "base/compiler_specific.h" -#include "content/public/renderer/context_menu_client.h" -#include "ppapi/c/pp_point.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/resource_host.h" - -namespace content { -class RendererPpapiHost; -struct MenuItem; -} - -namespace ppapi { -namespace proxy { -class SerializedFlashMenu; -} -} - -class PepperFlashMenuHost : public ppapi::host::ResourceHost, - public content::ContextMenuClient { - public: - PepperFlashMenuHost(content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource, - const ppapi::proxy::SerializedFlashMenu& serial_menu); - ~PepperFlashMenuHost() override; - - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - int32_t OnHostMsgShow(ppapi::host::HostMessageContext* context, - const PP_Point& location); - - // ContextMenuClient implementation. - void OnMenuAction(int request_id, unsigned action) override; - void OnMenuClosed(int request_id) override; - - void SendMenuReply(int32_t result, int action); - - content::RendererPpapiHost* renderer_ppapi_host_; - - bool showing_context_menu_; - int context_menu_request_id_; - - std::vector menu_data_; - - // We send |MenuItem|s, which have an |unsigned| "action" field instead of - // an |int32_t| ID. (CONTENT also limits the range of valid values for - // actions.) This maps actions to IDs. - std::vector menu_id_map_; - - // Used to send a single context menu "completion" upon menu close. - bool has_saved_context_menu_action_; - unsigned saved_context_menu_action_; - - DISALLOW_COPY_AND_ASSIGN(PepperFlashMenuHost); -}; - -#endif // CHROME_RENDERER_PEPPER_PEPPER_FLASH_MENU_HOST_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc deleted file mode 100644 index 5e41c5b8a6..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/pepper/pepper_flash_renderer_host.h" - -#include -#include - -#include "base/lazy_instance.h" -#include "base/metrics/histogram.h" -#include "base/strings/string_util.h" -#include "content/public/renderer/pepper_plugin_instance.h" -#include "content/public/renderer/render_thread.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ipc/ipc_message_macros.h" -#include "net/http/http_util.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/c/trusted/ppb_browser_font_trusted.h" -#include "ppapi/host/dispatch_host_message.h" -#include "ppapi/proxy/host_dispatcher.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/proxy/resource_message_params.h" -#include "ppapi/proxy/serialized_structs.h" -#include "ppapi/thunk/enter.h" -#include "ppapi/thunk/ppb_image_data_api.h" -#include "skia/ext/platform_canvas.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkMatrix.h" -#include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkTypeface.h" -#include "ui/gfx/geometry/rect.h" -#include "url/gurl.h" - -using ppapi::thunk::EnterResourceNoLock; -using ppapi::thunk::PPB_ImageData_API; - -namespace { - -// Some non-simple HTTP request headers that Flash may set. -// (Please see http://www.w3.org/TR/cors/#simple-header for the definition of -// simple headers.) -// -// The list and the enum defined below are used to collect data about request -// headers used in PPB_Flash.Navigate() calls, in order to understand the impact -// of rejecting PPB_Flash.Navigate() requests with non-simple headers. -// -// TODO(yzshen): We should be able to remove the histogram recording code once -// we get the answer. -const char* const kRejectedHttpRequestHeaders[] = { - "authorization", // - "cache-control", // - "content-encoding", // - "content-md5", // - "content-type", // If the media type is not one of those covered by the - // simple header definition. - "expires", // - "from", // - "if-match", // - "if-none-match", // - "if-range", // - "if-unmodified-since", // - "pragma", // - "referer" // -}; - -// Please note that new entries should be added right above -// FLASH_NAVIGATE_USAGE_ENUM_COUNT, and existing entries shouldn't be re-ordered -// or removed, since this ordering is used in a histogram. -enum FlashNavigateUsage { - // This section must be in the same order as kRejectedHttpRequestHeaders. - REJECT_AUTHORIZATION = 0, - REJECT_CACHE_CONTROL, - REJECT_CONTENT_ENCODING, - REJECT_CONTENT_MD5, - REJECT_CONTENT_TYPE, - REJECT_EXPIRES, - REJECT_FROM, - REJECT_IF_MATCH, - REJECT_IF_NONE_MATCH, - REJECT_IF_RANGE, - REJECT_IF_UNMODIFIED_SINCE, - REJECT_PRAGMA, - REJECT_REFERER, - - // The navigate request is rejected because of headers not listed above - // (e.g., custom headers). - REJECT_OTHER_HEADERS, - - // Total number of rejected navigate requests. - TOTAL_REJECTED_NAVIGATE_REQUESTS, - - // Total number of navigate requests. - TOTAL_NAVIGATE_REQUESTS, - FLASH_NAVIGATE_USAGE_ENUM_COUNT -}; - -static base::LazyInstance > - g_rejected_headers = LAZY_INSTANCE_INITIALIZER; - -bool IsSimpleHeader(const std::string& lower_case_header_name, - const std::string& header_value) { - if (lower_case_header_name == "accept" || - lower_case_header_name == "accept-language" || - lower_case_header_name == "content-language") { - return true; - } - - if (lower_case_header_name == "content-type") { - std::string lower_case_mime_type; - std::string lower_case_charset; - bool had_charset = false; - net::HttpUtil::ParseContentType(header_value, - &lower_case_mime_type, - &lower_case_charset, - &had_charset, - NULL); - return lower_case_mime_type == "application/x-www-form-urlencoded" || - lower_case_mime_type == "multipart/form-data" || - lower_case_mime_type == "text/plain"; - } - - return false; -} - -void RecordFlashNavigateUsage(FlashNavigateUsage usage) { - DCHECK_NE(FLASH_NAVIGATE_USAGE_ENUM_COUNT, usage); - UMA_HISTOGRAM_ENUMERATION( - "Plugin.FlashNavigateUsage", usage, FLASH_NAVIGATE_USAGE_ENUM_COUNT); -} - -} // namespace - -PepperFlashRendererHost::PepperFlashRendererHost( - content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource) - : ResourceHost(host->GetPpapiHost(), instance, resource), - host_(host), - weak_factory_(this) {} - -PepperFlashRendererHost::~PepperFlashRendererHost() { - // This object may be destroyed in the middle of a sync message. If that is - // the case, make sure we respond to all the pending navigate calls. - std::vector::reverse_iterator it; - for (it = navigate_replies_.rbegin(); it != navigate_replies_.rend(); ++it) - SendReply(*it, IPC::Message()); -} - -int32_t PepperFlashRendererHost::OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) { - PPAPI_BEGIN_MESSAGE_MAP(PepperFlashRendererHost, msg) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_GetProxyForURL, - OnGetProxyForURL) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_SetInstanceAlwaysOnTop, - OnSetInstanceAlwaysOnTop) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_DrawGlyphs, - OnDrawGlyphs) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_Navigate, OnNavigate) - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_IsRectTopmost, - OnIsRectTopmost) - PPAPI_END_MESSAGE_MAP() - return PP_ERROR_FAILED; -} - -int32_t PepperFlashRendererHost::OnGetProxyForURL( - ppapi::host::HostMessageContext* host_context, - const std::string& url) { - GURL gurl(url); - if (!gurl.is_valid()) - return PP_ERROR_FAILED; - std::string proxy; - bool result = content::RenderThread::Get()->ResolveProxy(gurl, &proxy); - if (!result) - return PP_ERROR_FAILED; - host_context->reply_msg = PpapiPluginMsg_Flash_GetProxyForURLReply(proxy); - return PP_OK; -} - -int32_t PepperFlashRendererHost::OnSetInstanceAlwaysOnTop( - ppapi::host::HostMessageContext* host_context, - bool on_top) { - content::PepperPluginInstance* plugin_instance = - host_->GetPluginInstance(pp_instance()); - if (plugin_instance) - plugin_instance->SetAlwaysOnTop(on_top); - // Since no reply is sent for this message, it doesn't make sense to return an - // error. - return PP_OK; -} - -int32_t PepperFlashRendererHost::OnDrawGlyphs( - ppapi::host::HostMessageContext* host_context, - ppapi::proxy::PPBFlash_DrawGlyphs_Params params) { - if (params.glyph_indices.size() != params.glyph_advances.size() || - params.glyph_indices.empty()) - return PP_ERROR_FAILED; - - // Set up the typeface. - int style = SkTypeface::kNormal; - if (static_cast(params.font_desc.weight) >= - PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD) - style |= SkTypeface::kBold; - if (params.font_desc.italic) - style |= SkTypeface::kItalic; - sk_sp typeface(SkTypeface::MakeFromName( - params.font_desc.face.c_str(), SkFontStyle::FromOldStyle(style))); - if (!typeface) - return PP_ERROR_FAILED; - - EnterResourceNoLock enter( - params.image_data.host_resource(), true); - if (enter.failed()) - return PP_ERROR_FAILED; - - // Set up the canvas. - PPB_ImageData_API* image = static_cast(enter.object()); - SkCanvas* canvas = image->GetCanvas(); - bool needs_unmapping = false; - if (!canvas) { - needs_unmapping = true; - image->Map(); - canvas = image->GetCanvas(); - if (!canvas) - return PP_ERROR_FAILED; // Failure mapping. - } - - SkAutoCanvasRestore acr(canvas, true); - - // Clip is applied in pixels before the transform. - SkRect clip_rect = { - SkIntToScalar(params.clip.point.x), SkIntToScalar(params.clip.point.y), - SkIntToScalar(params.clip.point.x + params.clip.size.width), - SkIntToScalar(params.clip.point.y + params.clip.size.height)}; - canvas->clipRect(clip_rect); - - // Convert & set the matrix. - SkMatrix matrix; - matrix.set(SkMatrix::kMScaleX, SkFloatToScalar(params.transformation[0][0])); - matrix.set(SkMatrix::kMSkewX, SkFloatToScalar(params.transformation[0][1])); - matrix.set(SkMatrix::kMTransX, SkFloatToScalar(params.transformation[0][2])); - matrix.set(SkMatrix::kMSkewY, SkFloatToScalar(params.transformation[1][0])); - matrix.set(SkMatrix::kMScaleY, SkFloatToScalar(params.transformation[1][1])); - matrix.set(SkMatrix::kMTransY, SkFloatToScalar(params.transformation[1][2])); - matrix.set(SkMatrix::kMPersp0, SkFloatToScalar(params.transformation[2][0])); - matrix.set(SkMatrix::kMPersp1, SkFloatToScalar(params.transformation[2][1])); - matrix.set(SkMatrix::kMPersp2, SkFloatToScalar(params.transformation[2][2])); - canvas->concat(matrix); - - SkPaint paint; - paint.setColor(params.color); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setAntiAlias(true); - paint.setHinting(SkPaint::kFull_Hinting); - paint.setTextSize(SkIntToScalar(params.font_desc.size)); - paint.setTypeface(std::move(typeface)); - if (params.allow_subpixel_aa) { - paint.setSubpixelText(true); - paint.setLCDRenderText(true); - } - - SkScalar x = SkIntToScalar(params.position.x); - SkScalar y = SkIntToScalar(params.position.y); - - // Build up the skia advances. - size_t glyph_count = params.glyph_indices.size(); - if (glyph_count) { - std::vector storage; - storage.resize(glyph_count); - SkPoint* sk_positions = &storage[0]; - for (uint32_t i = 0; i < glyph_count; i++) { - sk_positions[i].set(x, y); - x += SkFloatToScalar(params.glyph_advances[i].x); - y += SkFloatToScalar(params.glyph_advances[i].y); - } - - canvas->drawPosText( - ¶ms.glyph_indices[0], glyph_count * 2, sk_positions, paint); - } - - if (needs_unmapping) - image->Unmap(); - - return PP_OK; -} - -// CAUTION: This code is subtle because Navigate is a sync call which may -// cause re-entrancy or cause the instance to be destroyed. If the instance -// is destroyed we need to ensure that we respond to all outstanding sync -// messages so that the plugin process does not remain blocked. -int32_t PepperFlashRendererHost::OnNavigate( - ppapi::host::HostMessageContext* host_context, - const ppapi::URLRequestInfoData& data, - const std::string& target, - bool from_user_action) { - // If our PepperPluginInstance is already destroyed, just return a failure. - content::PepperPluginInstance* plugin_instance = - host_->GetPluginInstance(pp_instance()); - if (!plugin_instance) - return PP_ERROR_FAILED; - - std::map& rejected_headers = - g_rejected_headers.Get(); - if (rejected_headers.empty()) { - for (size_t i = 0; i < arraysize(kRejectedHttpRequestHeaders); ++i) - rejected_headers[kRejectedHttpRequestHeaders[i]] = - static_cast(i); - } - - net::HttpUtil::HeadersIterator header_iter( - data.headers.begin(), data.headers.end(), "\n\r"); - bool rejected = false; - while (header_iter.GetNext()) { - std::string lower_case_header_name = - base::ToLowerASCII(header_iter.name()); - if (!IsSimpleHeader(lower_case_header_name, header_iter.values())) { - rejected = true; - - std::map::const_iterator iter = - rejected_headers.find(lower_case_header_name); - FlashNavigateUsage usage = - iter != rejected_headers.end() ? iter->second : REJECT_OTHER_HEADERS; - RecordFlashNavigateUsage(usage); - } - } - - RecordFlashNavigateUsage(TOTAL_NAVIGATE_REQUESTS); - if (rejected) { - RecordFlashNavigateUsage(TOTAL_REJECTED_NAVIGATE_REQUESTS); - return PP_ERROR_NOACCESS; - } - - // Navigate may call into Javascript (e.g. with a "javascript:" URL), - // or do things like navigate away from the page, either one of which will - // need to re-enter into the plugin. It is safe, because it is essentially - // equivalent to NPN_GetURL, where Flash would expect re-entrancy. - ppapi::proxy::HostDispatcher* host_dispatcher = - ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); - host_dispatcher->set_allow_plugin_reentrancy(); - - // Grab a weak pointer to ourselves on the stack so we can check if we are - // still alive. - base::WeakPtr weak_ptr = weak_factory_.GetWeakPtr(); - // Keep track of reply contexts in case we are destroyed during a Navigate - // call. Even if we are destroyed, we still need to send these replies to - // unblock the plugin process. - navigate_replies_.push_back(host_context->MakeReplyMessageContext()); - plugin_instance->Navigate(data, target.c_str(), from_user_action); - // This object might have been destroyed by this point. If it is destroyed - // the reply will be sent in the destructor. Otherwise send the reply here. - if (weak_ptr.get()) { - SendReply(navigate_replies_.back(), IPC::Message()); - navigate_replies_.pop_back(); - } - - // Return PP_OK_COMPLETIONPENDING so that no reply is automatically sent. - return PP_OK_COMPLETIONPENDING; -} - -int32_t PepperFlashRendererHost::OnIsRectTopmost( - ppapi::host::HostMessageContext* host_context, - const PP_Rect& rect) { - content::PepperPluginInstance* plugin_instance = - host_->GetPluginInstance(pp_instance()); - if (plugin_instance && - plugin_instance->IsRectTopmost(gfx::Rect( - rect.point.x, rect.point.y, rect.size.width, rect.size.height))) - return PP_OK; - return PP_ERROR_FAILED; -} diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.h b/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.h deleted file mode 100644 index f37907a865..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PEPPER_PEPPER_FLASH_RENDERER_HOST_H_ -#define CHROME_RENDERER_PEPPER_PEPPER_FLASH_RENDERER_HOST_H_ - -#include -#include - -#include "base/memory/weak_ptr.h" -#include "ppapi/host/host_message_context.h" -#include "ppapi/host/resource_host.h" - -struct PP_Rect; - -namespace ppapi { -struct URLRequestInfoData; -} - -namespace ppapi { -namespace proxy { -struct PPBFlash_DrawGlyphs_Params; -} -} - -namespace content { -class RendererPpapiHost; -} - -class PepperFlashRendererHost : public ppapi::host::ResourceHost { - public: - PepperFlashRendererHost(content::RendererPpapiHost* host, - PP_Instance instance, - PP_Resource resource); - ~PepperFlashRendererHost() override; - - // ppapi::host::ResourceHost override. - int32_t OnResourceMessageReceived( - const IPC::Message& msg, - ppapi::host::HostMessageContext* context) override; - - private: - int32_t OnGetProxyForURL(ppapi::host::HostMessageContext* host_context, - const std::string& url); - int32_t OnSetInstanceAlwaysOnTop( - ppapi::host::HostMessageContext* host_context, - bool on_top); - int32_t OnDrawGlyphs(ppapi::host::HostMessageContext* host_context, - ppapi::proxy::PPBFlash_DrawGlyphs_Params params); - int32_t OnNavigate(ppapi::host::HostMessageContext* host_context, - const ppapi::URLRequestInfoData& data, - const std::string& target, - bool from_user_action); - int32_t OnIsRectTopmost(ppapi::host::HostMessageContext* host_context, - const PP_Rect& rect); - - // A stack of ReplyMessageContexts to track Navigate() calls which have not - // yet been replied to. - std::vector navigate_replies_; - - content::RendererPpapiHost* host_; - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(PepperFlashRendererHost); -}; - -#endif // CHROME_RENDERER_PEPPER_PEPPER_FLASH_RENDERER_HOST_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_helper.cc b/chromium_src/chrome/renderer/pepper/pepper_helper.cc deleted file mode 100644 index eef18560f9..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_helper.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/pepper/pepper_helper.h" - -#include "chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h" -#include "chrome/renderer/pepper/pepper_shared_memory_message_filter.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ppapi/host/ppapi_host.h" - -PepperHelper::PepperHelper(content::RenderFrame* render_frame) - : RenderFrameObserver(render_frame) {} - -PepperHelper::~PepperHelper() {} - -void PepperHelper::DidCreatePepperPlugin(content::RendererPpapiHost* host) { - // TODO(brettw) figure out how to hook up the host factory. It needs some - // kind of filter-like system to allow dynamic additions. - host->GetPpapiHost()->AddHostFactoryFilter( - std::unique_ptr( - new ChromeRendererPepperHostFactory(host))); - host->GetPpapiHost()->AddInstanceMessageFilter( - std::unique_ptr( - new PepperSharedMemoryMessageFilter(host))); -} - -void PepperHelper::OnDestruct() { - delete this; -} diff --git a/chromium_src/chrome/renderer/pepper/pepper_helper.h b/chromium_src/chrome/renderer/pepper/pepper_helper.h deleted file mode 100644 index 6157d2c794..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_helper.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PEPPER_PEPPER_HELPER_H_ -#define CHROME_RENDERER_PEPPER_PEPPER_HELPER_H_ - -#include "base/compiler_specific.h" -#include "content/public/renderer/render_frame_observer.h" - -// This class listens for Pepper creation events from the RenderFrame and -// attaches the parts required for Chrome-specific plugin support. -class PepperHelper : public content::RenderFrameObserver { - public: - explicit PepperHelper(content::RenderFrame* render_frame); - ~PepperHelper() override; - - // RenderFrameObserver. - void DidCreatePepperPlugin(content::RendererPpapiHost* host) override; - void OnDestruct() override; - - private: - DISALLOW_COPY_AND_ASSIGN(PepperHelper); -}; - -#endif // CHROME_RENDERER_PEPPER_PEPPER_HELPER_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc b/chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc deleted file mode 100644 index 9919fb47b0..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/renderer/pepper/pepper_shared_memory_message_filter.h" - -#include - -#include "base/memory/shared_memory.h" -#include "base/process/process_handle.h" -#include "content/public/common/content_client.h" -#include "content/public/renderer/pepper_plugin_instance.h" -#include "content/public/renderer/render_thread.h" -#include "content/public/renderer/renderer_ppapi_host.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/shared_impl/var_tracker.h" - -PepperSharedMemoryMessageFilter::PepperSharedMemoryMessageFilter( - content::RendererPpapiHost* host) - : InstanceMessageFilter(host->GetPpapiHost()), host_(host) {} - -PepperSharedMemoryMessageFilter::~PepperSharedMemoryMessageFilter() {} - -bool PepperSharedMemoryMessageFilter::OnInstanceMessageReceived( - const IPC::Message& msg) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PepperSharedMemoryMessageFilter, msg) - IPC_MESSAGE_HANDLER(PpapiHostMsg_SharedMemory_CreateSharedMemory, - OnHostMsgCreateSharedMemory) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -bool PepperSharedMemoryMessageFilter::Send(IPC::Message* msg) { - return host_->GetPpapiHost()->Send(msg); -} - -void PepperSharedMemoryMessageFilter::OnHostMsgCreateSharedMemory( - PP_Instance instance, - uint32_t size, - int* host_handle_id, - ppapi::proxy::SerializedHandle* plugin_handle) { - plugin_handle->set_null_shmem(); - *host_handle_id = -1; - std::unique_ptr shm( - content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(size)); - if (!shm.get()) - return; - - base::SharedMemoryHandle host_shm_handle; - shm->ShareToProcess(base::GetCurrentProcessHandle(), &host_shm_handle); - *host_handle_id = - content::PepperPluginInstance::Get(instance) - ->GetVarTracker() - ->TrackSharedMemoryHandle(instance, host_shm_handle, size); - - // We set auto_close to false since we need our file descriptor to - // actually be duplicated on linux. The shared memory destructor will - // close the original handle for us. - plugin_handle->set_shmem( - host_->ShareSharedMemoryHandleWithRemote(host_shm_handle), size); -} diff --git a/chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.h b/chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.h deleted file mode 100644 index 860e1c9dbd..0000000000 --- a/chromium_src/chrome/renderer/pepper/pepper_shared_memory_message_filter.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_RENDERER_PEPPER_PEPPER_SHARED_MEMORY_MESSAGE_FILTER_H_ -#define CHROME_RENDERER_PEPPER_PEPPER_SHARED_MEMORY_MESSAGE_FILTER_H_ - -#include "ppapi/c/pp_instance.h" -#include "ppapi/host/instance_message_filter.h" - -namespace content { -class RendererPpapiHost; -} - -namespace ppapi { -namespace proxy { -class SerializedHandle; -} -} - -// Implements the backend for shared memory messages from a plugin process. -class PepperSharedMemoryMessageFilter - : public ppapi::host::InstanceMessageFilter { - public: - explicit PepperSharedMemoryMessageFilter(content::RendererPpapiHost* host); - ~PepperSharedMemoryMessageFilter() override; - - // InstanceMessageFilter: - bool OnInstanceMessageReceived(const IPC::Message& msg) override; - - bool Send(IPC::Message* msg); - - private: - // Message handlers. - void OnHostMsgCreateSharedMemory( - PP_Instance instance, - uint32_t size, - int* host_shm_handle_id, - ppapi::proxy::SerializedHandle* plugin_shm_handle); - - content::RendererPpapiHost* host_; - - DISALLOW_COPY_AND_ASSIGN(PepperSharedMemoryMessageFilter); -}; - -#endif // CHROME_RENDERER_PEPPER_PEPPER_SHARED_MEMORY_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc deleted file mode 100644 index 3c2bf680d0..0000000000 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc +++ /dev/null @@ -1,1454 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/printing/print_web_view_helper.h" - -#include - -#include "base/auto_reset.h" -#include "base/command_line.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/histogram.h" -#include "base/process/process_handle.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/common/print_messages.h" -#include "content/public/common/web_preferences.h" -#include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_thread.h" -#include "content/public/renderer/render_view.h" -#include "net/base/escape.h" -#include "printing/pdf_metafile_skia.h" -#include "printing/units.h" -#include "third_party/WebKit/public/platform/WebSize.h" -#include "third_party/WebKit/public/platform/WebURLRequest.h" -#include "third_party/WebKit/public/web/WebConsoleMessage.h" -#include "third_party/WebKit/public/web/WebDocument.h" -#include "third_party/WebKit/public/web/WebElement.h" -#include "third_party/WebKit/public/web/WebFrameClient.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/WebKit/public/web/WebPlugin.h" -#include "third_party/WebKit/public/web/WebPluginDocument.h" -#include "third_party/WebKit/public/web/WebPrintParams.h" -#include "third_party/WebKit/public/web/WebPrintScalingOption.h" -#include "third_party/WebKit/public/web/WebScriptSource.h" -#include "third_party/WebKit/public/web/WebSettings.h" -#include "third_party/WebKit/public/web/WebView.h" -#include "third_party/WebKit/public/web/WebViewClient.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "ui/base/resource/resource_bundle.h" - -using content::WebPreferences; - -namespace printing { - -namespace { - -const double kMinDpi = 1.0; - -int GetDPI(const PrintMsg_Print_Params* print_params) { -#if defined(OS_MACOSX) - // On the Mac, the printable area is in points, don't do any scaling based - // on dpi. - return kPointsPerInch; -#else - return static_cast(print_params->dpi); -#endif // defined(OS_MACOSX) -} - -bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) { - return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() && - !params.printable_area.IsEmpty() && params.document_cookie && - params.desired_dpi && params.dpi && params.margin_top >= 0 && - params.margin_left >= 0 && params.dpi > kMinDpi && - params.document_cookie != 0; -} - -PrintMsg_Print_Params GetCssPrintParams( - blink::WebFrame* frame, - int page_index, - const PrintMsg_Print_Params& page_params) { - PrintMsg_Print_Params page_css_params = page_params; - int dpi = GetDPI(&page_params); - - blink::WebSize page_size_in_pixels( - ConvertUnit(page_params.page_size.width(), dpi, kPixelsPerInch), - ConvertUnit(page_params.page_size.height(), dpi, kPixelsPerInch)); - int margin_top_in_pixels = - ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch); - int margin_right_in_pixels = ConvertUnit( - page_params.page_size.width() - - page_params.content_size.width() - page_params.margin_left, - dpi, kPixelsPerInch); - int margin_bottom_in_pixels = ConvertUnit( - page_params.page_size.height() - - page_params.content_size.height() - page_params.margin_top, - dpi, kPixelsPerInch); - int margin_left_in_pixels = ConvertUnit( - page_params.margin_left, - dpi, kPixelsPerInch); - - blink::WebSize original_page_size_in_pixels = page_size_in_pixels; - - if (frame) { - frame->pageSizeAndMarginsInPixels(page_index, - page_size_in_pixels, - margin_top_in_pixels, - margin_right_in_pixels, - margin_bottom_in_pixels, - margin_left_in_pixels); - } - - int new_content_width = page_size_in_pixels.width - - margin_left_in_pixels - margin_right_in_pixels; - int new_content_height = page_size_in_pixels.height - - margin_top_in_pixels - margin_bottom_in_pixels; - - // Invalid page size and/or margins. We just use the default setting. - if (new_content_width < 1 || new_content_height < 1) { - CHECK(frame != NULL); - page_css_params = GetCssPrintParams(NULL, page_index, page_params); - return page_css_params; - } - - page_css_params.content_size = gfx::Size( - ConvertUnit(new_content_width, kPixelsPerInch, dpi), - ConvertUnit(new_content_height, kPixelsPerInch, dpi)); - - if (original_page_size_in_pixels != page_size_in_pixels) { - page_css_params.page_size = gfx::Size( - ConvertUnit(page_size_in_pixels.width, kPixelsPerInch, dpi), - ConvertUnit(page_size_in_pixels.height, kPixelsPerInch, dpi)); - } else { - // Printing frame doesn't have any page size css. Pixels to dpi conversion - // causes rounding off errors. Therefore use the default page size values - // directly. - page_css_params.page_size = page_params.page_size; - } - - page_css_params.margin_top = - ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi); - page_css_params.margin_left = - ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi); - return page_css_params; -} - -double FitPrintParamsToPage(const PrintMsg_Print_Params& page_params, - PrintMsg_Print_Params* params_to_fit) { - double content_width = - static_cast(params_to_fit->content_size.width()); - double content_height = - static_cast(params_to_fit->content_size.height()); - int default_page_size_height = page_params.page_size.height(); - int default_page_size_width = page_params.page_size.width(); - int css_page_size_height = params_to_fit->page_size.height(); - int css_page_size_width = params_to_fit->page_size.width(); - - double scale_factor = 1.0f; - if (page_params.page_size == params_to_fit->page_size) - return scale_factor; - - if (default_page_size_width < css_page_size_width || - default_page_size_height < css_page_size_height) { - double ratio_width = - static_cast(default_page_size_width) / css_page_size_width; - double ratio_height = - static_cast(default_page_size_height) / css_page_size_height; - scale_factor = ratio_width < ratio_height ? ratio_width : ratio_height; - content_width *= scale_factor; - content_height *= scale_factor; - } - params_to_fit->margin_top = static_cast( - (default_page_size_height - css_page_size_height * scale_factor) / 2 + - (params_to_fit->margin_top * scale_factor)); - params_to_fit->margin_left = static_cast( - (default_page_size_width - css_page_size_width * scale_factor) / 2 + - (params_to_fit->margin_left * scale_factor)); - params_to_fit->content_size = gfx::Size( - static_cast(content_width), static_cast(content_height)); - params_to_fit->page_size = page_params.page_size; - return scale_factor; -} - -void CalculatePageLayoutFromPrintParams( - const PrintMsg_Print_Params& params, - PageSizeMargins* page_layout_in_points) { - int dpi = GetDPI(¶ms); - int content_width = params.content_size.width(); - int content_height = params.content_size.height(); - - int margin_bottom = params.page_size.height() - - content_height - params.margin_top; - int margin_right = params.page_size.width() - - content_width - params.margin_left; - - page_layout_in_points->content_width = - ConvertUnit(content_width, dpi, kPointsPerInch); - page_layout_in_points->content_height = - ConvertUnit(content_height, dpi, kPointsPerInch); - page_layout_in_points->margin_top = - ConvertUnit(params.margin_top, dpi, kPointsPerInch); - page_layout_in_points->margin_right = - ConvertUnit(margin_right, dpi, kPointsPerInch); - page_layout_in_points->margin_bottom = - ConvertUnit(margin_bottom, dpi, kPointsPerInch); - page_layout_in_points->margin_left = - ConvertUnit(params.margin_left, dpi, kPointsPerInch); -} - -void EnsureOrientationMatches(const PrintMsg_Print_Params& css_params, - PrintMsg_Print_Params* page_params) { - if ((page_params->page_size.width() > page_params->page_size.height()) == - (css_params.page_size.width() > css_params.page_size.height())) { - return; - } - - // Swap the |width| and |height| values. - page_params->page_size.SetSize(page_params->page_size.height(), - page_params->page_size.width()); - page_params->content_size.SetSize(page_params->content_size.height(), - page_params->content_size.width()); - page_params->printable_area.set_size( - gfx::Size(page_params->printable_area.height(), - page_params->printable_area.width())); -} - -void ComputeWebKitPrintParamsInDesiredDpi( - const PrintMsg_Print_Params& print_params, - blink::WebPrintParams* webkit_print_params) { - int dpi = GetDPI(&print_params); - webkit_print_params->printerDPI = dpi; - webkit_print_params->printScalingOption = print_params.print_scaling_option; - - webkit_print_params->printContentArea.width = - ConvertUnit(print_params.content_size.width(), dpi, - print_params.desired_dpi); - webkit_print_params->printContentArea.height = - ConvertUnit(print_params.content_size.height(), dpi, - print_params.desired_dpi); - - webkit_print_params->printableArea.x = - ConvertUnit(print_params.printable_area.x(), dpi, - print_params.desired_dpi); - webkit_print_params->printableArea.y = - ConvertUnit(print_params.printable_area.y(), dpi, - print_params.desired_dpi); - webkit_print_params->printableArea.width = - ConvertUnit(print_params.printable_area.width(), dpi, - print_params.desired_dpi); - webkit_print_params->printableArea.height = - ConvertUnit(print_params.printable_area.height(), - dpi, print_params.desired_dpi); - - webkit_print_params->paperSize.width = - ConvertUnit(print_params.page_size.width(), dpi, - print_params.desired_dpi); - webkit_print_params->paperSize.height = - ConvertUnit(print_params.page_size.height(), dpi, - print_params.desired_dpi); -} - -blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) { - return frame->document().isPluginDocument() ? - frame->document().to().plugin() : NULL; -} - -bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame, - const blink::WebNode& node) { - if (!node.isNull()) - return true; - blink::WebPlugin* plugin = GetPlugin(frame); - return plugin && plugin->supportsPaginatedPrint(); -} - -MarginType GetMarginsForPdf(blink::WebFrame* frame, - const blink::WebNode& node) { - if (frame->isPrintScalingDisabledForPlugin(node)) - return NO_MARGINS; - else - return PRINTABLE_AREA_MARGINS; -} - -PrintMsg_Print_Params CalculatePrintParamsForCss( - blink::WebFrame* frame, - int page_index, - const PrintMsg_Print_Params& page_params, - bool ignore_css_margins, - bool fit_to_page, - double* scale_factor) { - PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index, - page_params); - - PrintMsg_Print_Params params = page_params; - EnsureOrientationMatches(css_params, ¶ms); - - if (ignore_css_margins && fit_to_page) - return params; - - PrintMsg_Print_Params result_params = css_params; - if (ignore_css_margins) { - result_params.margin_top = params.margin_top; - result_params.margin_left = params.margin_left; - - DCHECK(!fit_to_page); - // Since we are ignoring the margins, the css page size is no longer - // valid. - int default_margin_right = params.page_size.width() - - params.content_size.width() - params.margin_left; - int default_margin_bottom = params.page_size.height() - - params.content_size.height() - params.margin_top; - result_params.content_size = gfx::Size( - result_params.page_size.width() - result_params.margin_left - - default_margin_right, - result_params.page_size.height() - result_params.margin_top - - default_margin_bottom); - } - - if (fit_to_page) { - double factor = FitPrintParamsToPage(params, &result_params); - if (scale_factor) - *scale_factor = factor; - } - return result_params; -} - -} // namespace - -FrameReference::FrameReference(blink::WebLocalFrame* frame) { - Reset(frame); -} - -FrameReference::FrameReference() { - Reset(NULL); -} - -FrameReference::~FrameReference() { -} - -void FrameReference::Reset(blink::WebLocalFrame* frame) { - if (frame) { - view_ = frame->view(); - frame_ = frame; - } else { - view_ = NULL; - frame_ = NULL; - } -} - -blink::WebLocalFrame* FrameReference::GetFrame() { - if (view_ == NULL || frame_ == NULL) - return NULL; - for (blink::WebFrame* frame = view_->mainFrame(); frame != NULL; - frame = frame->traverseNext(false)) { - if (frame == frame_) - return frame_; - } - return NULL; -} - -blink::WebView* FrameReference::view() { - return view_; -} - -// static - Not anonymous so that platform implementations can use it. -float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame, - int page_number, - const gfx::Rect& canvas_area, - const gfx::Rect& content_area, - double scale_factor, - blink::WebCanvas* canvas) { - SkAutoCanvasRestore auto_restore(canvas, true); - if (content_area != canvas_area) { - canvas->translate((content_area.x() - canvas_area.x()) / scale_factor, - (content_area.y() - canvas_area.y()) / scale_factor); - SkRect clip_rect( - SkRect::MakeXYWH(content_area.origin().x() / scale_factor, - content_area.origin().y() / scale_factor, - content_area.size().width() / scale_factor, - content_area.size().height() / scale_factor)); - SkIRect clip_int_rect; - clip_rect.roundOut(&clip_int_rect); - SkRegion clip_region(clip_int_rect); - canvas->setClipRegion(clip_region); - } - return frame->printPage(page_number, canvas); -} - -// Class that calls the Begin and End print functions on the frame and changes -// the size of the view temporarily to support full page printing.. -class PrepareFrameAndViewForPrint : public blink::WebViewClient, - public blink::WebFrameClient { - public: - PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params, - blink::WebLocalFrame* frame, - const blink::WebNode& node, - bool ignore_css_margins); - ~PrepareFrameAndViewForPrint() override; - - // Optional. Replaces |frame_| with selection if needed. Will call |on_ready| - // when completed. - void CopySelectionIfNeeded(const WebPreferences& preferences, - const base::Closure& on_ready); - - // Prepares frame for printing. - void StartPrinting(); - - blink::WebLocalFrame* frame() { - return frame_.GetFrame(); - } - - const blink::WebNode& node() const { - return node_to_print_; - } - - int GetExpectedPageCount() const { - return expected_pages_count_; - } - - void FinishPrinting(); - - bool IsLoadingSelection() { - // It's not selection if not |owns_web_view_|. - return owns_web_view_ && frame() && frame()->isLoading(); - } - - protected: - // blink::WebViewClient override: - void didStopLoading() override; - bool allowsBrokenNullLayerTreeView() const override; - - // blink::WebFrameClient: - blink::WebFrame* createChildFrame( - blink::WebLocalFrame* parent, - blink::WebTreeScopeType scope, - const blink::WebString& name, - const blink::WebString& unique_name, - blink::WebSandboxFlags sandbox_flags, - const blink::WebFrameOwnerProperties& frame_owner_properties) override; - void frameDetached(blink::WebFrame* frame, DetachType type) override; - - private: - void CallOnReady(); - void ResizeForPrinting(); - void RestoreSize(); - void CopySelection(const WebPreferences& preferences); - - base::WeakPtrFactory weak_ptr_factory_; - - FrameReference frame_; - blink::WebNode node_to_print_; - bool owns_web_view_; - blink::WebPrintParams web_print_params_; - gfx::Size prev_view_size_; - gfx::Size prev_scroll_offset_; - int expected_pages_count_; - base::Closure on_ready_; - bool should_print_backgrounds_; - bool should_print_selection_only_; - bool is_printing_started_; - - DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); -}; - -PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( - const PrintMsg_Print_Params& params, - blink::WebLocalFrame* frame, - const blink::WebNode& node, - bool ignore_css_margins) - : weak_ptr_factory_(this), - frame_(frame), - node_to_print_(node), - owns_web_view_(false), - expected_pages_count_(0), - should_print_backgrounds_(params.should_print_backgrounds), - should_print_selection_only_(params.selection_only), - is_printing_started_(false) { - PrintMsg_Print_Params print_params = params; - if (!should_print_selection_only_ || - !PrintingNodeOrPdfFrame(frame, node_to_print_)) { - bool fit_to_page = ignore_css_margins && - print_params.print_scaling_option == - blink::WebPrintScalingOptionFitToPrintableArea; - ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_); - frame->printBegin(web_print_params_, node_to_print_); - print_params = CalculatePrintParamsForCss(frame, 0, print_params, - ignore_css_margins, fit_to_page, - NULL); - frame->printEnd(); - } - ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_); -} - -PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() { - FinishPrinting(); -} - -void PrepareFrameAndViewForPrint::ResizeForPrinting() { - // Layout page according to printer page size. Since WebKit shrinks the - // size of the page automatically (from 125% to 200%) we trick it to - // think the page is 125% larger so the size of the page is correct for - // minimum (default) scaling. - // This is important for sites that try to fill the page. - // The 1.25 value is |printingMinimumShrinkFactor|. - gfx::Size print_layout_size(web_print_params_.printContentArea.width, - web_print_params_.printContentArea.height); - print_layout_size.set_height( - static_cast(static_cast(print_layout_size.height()) * 1.25)); - - if (!frame()) - return; - blink::WebView* web_view = frame_.view(); - // Backup size and offset. - if (blink::WebFrame* web_frame = web_view->mainFrame()) - prev_scroll_offset_ = web_frame->scrollOffset(); - prev_view_size_ = web_view->size(); - - web_view->resize(print_layout_size); -} - - -void PrepareFrameAndViewForPrint::StartPrinting() { - ResizeForPrinting(); - blink::WebView* web_view = frame_.view(); - web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_); - expected_pages_count_ = - frame()->printBegin(web_print_params_, node_to_print_); - is_printing_started_ = true; -} - -void PrepareFrameAndViewForPrint::CopySelectionIfNeeded( - const WebPreferences& preferences, - const base::Closure& on_ready) { - on_ready_ = on_ready; - if (should_print_selection_only_) { - CopySelection(preferences); - } else { - // Call immediately, async call crashes scripting printing. - CallOnReady(); - } -} - -void PrepareFrameAndViewForPrint::CopySelection( - const WebPreferences& preferences) { - ResizeForPrinting(); - std::string url_str = "data:text/html;charset=utf-8,"; - url_str.append( - net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false)); - RestoreSize(); - // Create a new WebView with the same settings as the current display one. - // Except that we disable javascript (don't want any active content running - // on the page). - WebPreferences prefs = preferences; - prefs.javascript_enabled = false; - - blink::WebView* web_view = - blink::WebView::create(this, blink::WebPageVisibilityStateVisible); - owns_web_view_ = true; - content::RenderView::ApplyWebPreferences(prefs, web_view); - web_view->setMainFrame( - blink::WebLocalFrame::create(blink::WebTreeScopeType::Document, this)); - frame_.Reset(web_view->mainFrame()->toWebLocalFrame()); - node_to_print_.reset(); - - // When loading is done this will call didStopLoading() and that will do the - // actual printing. - frame()->loadRequest(blink::WebURLRequest(GURL(url_str))); -} - -bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const { - return true; -} - -void PrepareFrameAndViewForPrint::didStopLoading() { - DCHECK(!on_ready_.is_null()); - // Don't call callback here, because it can delete |this| and WebView that is - // called didStopLoading. - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&PrepareFrameAndViewForPrint::CallOnReady, - weak_ptr_factory_.GetWeakPtr())); -} - -blink::WebFrame* PrepareFrameAndViewForPrint::createChildFrame( - blink::WebLocalFrame* parent, - blink::WebTreeScopeType scope, - const blink::WebString& name, - const blink::WebString& unique_name, - blink::WebSandboxFlags sandbox_flags, - const blink::WebFrameOwnerProperties& frame_owner_properties) { - blink::WebFrame* frame = blink::WebLocalFrame::create(scope, this); - parent->appendChild(frame); - return frame; -} - -void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame* frame, - DetachType type) { - DCHECK(type == DetachType::Remove); - if (frame->parent()) - frame->parent()->removeChild(frame); - frame->close(); -} - -void PrepareFrameAndViewForPrint::CallOnReady() { - return on_ready_.Run(); // Can delete |this|. -} - -void PrepareFrameAndViewForPrint::RestoreSize() { - if (frame()) { - blink::WebView* web_view = frame_.GetFrame()->view(); - web_view->resize(prev_view_size_); - if (blink::WebFrame* web_frame = web_view->mainFrame()) - web_frame->setScrollOffset(prev_scroll_offset_); - } -} - -void PrepareFrameAndViewForPrint::FinishPrinting() { - blink::WebLocalFrame* frame = frame_.GetFrame(); - if (frame) { - blink::WebView* web_view = frame->view(); - if (is_printing_started_) { - is_printing_started_ = false; - frame->printEnd(); - if (!owns_web_view_) { - web_view->settings()->setShouldPrintBackgrounds(false); - RestoreSize(); - } - } - if (owns_web_view_) { - DCHECK(!frame->isLoading()); - owns_web_view_ = false; - web_view->close(); - } - } - frame_.Reset(NULL); - on_ready_.Reset(); -} - -PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view) - : content::RenderViewObserver(render_view), - content::RenderViewObserverTracker(render_view), - reset_prep_frame_view_(false), - is_print_ready_metafile_sent_(false), - ignore_css_margins_(false), - is_scripted_printing_blocked_(false), - notify_browser_of_print_failure_(true), - print_for_preview_(false), - print_node_in_progress_(false), - is_loading_(false), - is_scripted_preview_delayed_(false), - weak_ptr_factory_(this) { -} - -PrintWebViewHelper::~PrintWebViewHelper() {} - -// Prints |frame| which called window.print(). -void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame, - bool user_initiated) { - DCHECK(frame); - Print(frame, blink::WebNode()); -} - -bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message) - IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages) - IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone) - IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void PrintWebViewHelper::OnDestruct() { - delete this; -} - -bool PrintWebViewHelper::GetPrintFrame(blink::WebLocalFrame** frame) { - DCHECK(frame); - blink::WebView* webView = render_view()->GetWebView(); - DCHECK(webView); - if (!webView) - return false; - - // If the user has selected text in the currently focused frame we print - // only that frame (this makes print selection work for multiple frames). - blink::WebLocalFrame* focusedFrame = - webView->focusedFrame()->toWebLocalFrame(); - *frame = focusedFrame->hasSelection() - ? focusedFrame - : webView->mainFrame()->toWebLocalFrame(); - return true; -} - -#if !defined(DISABLE_BASIC_PRINTING) -void PrintWebViewHelper::OnPrintPages(bool silent, bool print_background) { - blink::WebLocalFrame* frame; - if (GetPrintFrame(&frame)) - Print(frame, blink::WebNode(), silent, print_background); -} -#endif // !DISABLE_BASIC_PRINTING - -void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout( - const PageSizeMargins& page_layout_in_points, - gfx::Size* page_size, - gfx::Rect* content_area) { - *page_size = gfx::Size( - page_layout_in_points.content_width + - page_layout_in_points.margin_right + - page_layout_in_points.margin_left, - page_layout_in_points.content_height + - page_layout_in_points.margin_top + - page_layout_in_points.margin_bottom); - *content_area = gfx::Rect(page_layout_in_points.margin_left, - page_layout_in_points.margin_top, - page_layout_in_points.content_width, - page_layout_in_points.content_height); -} - -void PrintWebViewHelper::UpdateFrameMarginsCssInfo( - const base::DictionaryValue& settings) { - int margins_type = 0; - if (!settings.GetInteger(kSettingMarginsType, &margins_type)) - margins_type = DEFAULT_MARGINS; - ignore_css_margins_ = (margins_type != DEFAULT_MARGINS); -} - -void PrintWebViewHelper::OnPrintingDone(bool success) { - notify_browser_of_print_failure_ = false; - if (!success) - LOG(ERROR) << "Failure in OnPrintingDone"; - DidFinishPrinting(success ? OK : FAIL_PRINT); -} - -void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) { - blink::WebLocalFrame* frame; - if (GetPrintFrame(&frame)) { - print_preview_context_.InitWithFrame(frame); - if (!print_preview_context_.source_frame()) { - DidFinishPrinting(FAIL_PREVIEW); - return; - } - - if (!UpdatePrintSettings(print_preview_context_.source_frame(), - print_preview_context_.source_node(), settings)) { - DidFinishPrinting(FAIL_PREVIEW); - return; - } - is_print_ready_metafile_sent_ = false; - PrepareFrameForPreviewDocument(); - } -} - -void PrintWebViewHelper::PrepareFrameForPreviewDocument() { - reset_prep_frame_view_ = false; - - if (!print_pages_params_) { - DidFinishPrinting(FAIL_PREVIEW); - return; - } - - // Don't reset loading frame or WebKit will fail assert. Just retry when - // current selection is loaded. - if (prep_frame_view_ && prep_frame_view_->IsLoadingSelection()) { - reset_prep_frame_view_ = true; - return; - } - - const PrintMsg_Print_Params& print_params = print_pages_params_->params; - prep_frame_view_.reset(new PrepareFrameAndViewForPrint( - print_params, print_preview_context_.source_frame(), - print_preview_context_.source_node(), ignore_css_margins_)); - prep_frame_view_->CopySelectionIfNeeded( - render_view()->GetWebkitPreferences(), - base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument, - base::Unretained(this))); -} - -void PrintWebViewHelper::OnFramePreparedForPreviewDocument() { - if (reset_prep_frame_view_) { - PrepareFrameForPreviewDocument(); - return; - } - DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW); -} - -bool PrintWebViewHelper::CreatePreviewDocument() { - if (!print_pages_params_) - return false; - - const PrintMsg_Print_Params& print_params = print_pages_params_->params; - const std::vector& pages = print_pages_params_->pages; - - if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(), - pages)) { - return false; - } - - while (!print_preview_context_.IsFinalPageRendered()) { - int page_number = print_preview_context_.GetNextPageNumber(); - DCHECK_GE(page_number, 0); - if (!RenderPreviewPage(page_number, print_params)) - return false; - - // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of - // print_preview_context_.AllPagesRendered()) before calling - // FinalizePrintReadyDocument() when printing a PDF because the plugin - // code does not generate output until we call FinishPrinting(). We do not - // generate draft pages for PDFs, so IsFinalPageRendered() and - // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of - // the loop. - if (print_preview_context_.IsFinalPageRendered()) - print_preview_context_.AllPagesRendered(); - - if (print_preview_context_.IsLastPageOfPrintReadyMetafile()) { - DCHECK(print_preview_context_.IsModifiable() || - print_preview_context_.IsFinalPageRendered()); - if (!FinalizePrintReadyDocument()) - return false; - } - } - print_preview_context_.Finished(); - return true; -} - -bool PrintWebViewHelper::FinalizePrintReadyDocument() { - DCHECK(!is_print_ready_metafile_sent_); - print_preview_context_.FinalizePrintReadyDocument(); - - PdfMetafileSkia* metafile = print_preview_context_.metafile(); - - PrintHostMsg_DidPreviewDocument_Params preview_params; - - // Ask the browser to create the shared memory for us. - if (!CopyMetafileDataToSharedMem(*metafile, - &(preview_params.metafile_data_handle))) { - LOG(ERROR) << "CopyMetafileDataToSharedMem failed"; - print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED); - return false; - } - - preview_params.data_size = metafile->GetDataSize(); - preview_params.document_cookie = print_pages_params_->params.document_cookie; - preview_params.expected_pages_count = - print_preview_context_.total_page_count(); - preview_params.modifiable = print_preview_context_.IsModifiable(); - preview_params.preview_request_id = - print_pages_params_->params.preview_request_id; - - is_print_ready_metafile_sent_ = true; - - Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params)); - return true; -} - -void PrintWebViewHelper::PrintNode(const blink::WebNode& node) { - if (node.isNull() || !node.document().frame()) { - // This can occur when the context menu refers to an invalid WebNode. - // See http://crbug.com/100890#c17 for a repro case. - return; - } - - if (print_node_in_progress_) { - // This can happen as a result of processing sync messages when printing - // from ppapi plugins. It's a rare case, so its OK to just fail here. - // See http://crbug.com/159165. - return; - } - - print_node_in_progress_ = true; - blink::WebNode duplicate_node(node); - Print(duplicate_node.document().frame(), duplicate_node); - - print_node_in_progress_ = false; -} - -void PrintWebViewHelper::Print(blink::WebLocalFrame* frame, - const blink::WebNode& node, - bool silent, - bool print_background) { - // If still not finished with earlier print request simply ignore. - if (prep_frame_view_) - return; - - FrameReference frame_ref(frame); - - int expected_page_count = 0; - if (!CalculateNumberOfPages(frame, node, &expected_page_count)) { - DidFinishPrinting(FAIL_PRINT_INIT); - return; // Failed to init print page settings. - } - - // Some full screen plugins can say they don't want to print. - if (!expected_page_count) { - DidFinishPrinting(FAIL_PRINT); - return; - } - - // Ask the browser to show UI to retrieve the final print settings. - if (!silent && !GetPrintSettingsFromUser(frame_ref.GetFrame(), node, - expected_page_count)) { - DidFinishPrinting(OK); // Release resources and fail silently. - return; - } - - print_pages_params_->params.should_print_backgrounds = print_background; - - // Render Pages for printing. - if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) { - LOG(ERROR) << "RenderPagesForPrint failed"; - DidFinishPrinting(FAIL_PRINT); - } -} - -void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) { - switch (result) { - case OK: - break; - - case FAIL_PRINT_INIT: - DCHECK(!notify_browser_of_print_failure_); - break; - - case FAIL_PRINT: - if (notify_browser_of_print_failure_ && print_pages_params_) { - int cookie = print_pages_params_->params.document_cookie; - Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie)); - } - break; - - case FAIL_PREVIEW: - LOG(ERROR) << "PREVIEW FAILED."; - if (print_pages_params_) { - Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), - print_pages_params_->params.document_cookie, - print_pages_params_->params.preview_request_id)); - } - break; - } - prep_frame_view_.reset(); - print_pages_params_.reset(); - notify_browser_of_print_failure_ = true; -} - -void PrintWebViewHelper::OnFramePreparedForPrintPages() { - PrintPages(); - FinishFramePrinting(); -} - -void PrintWebViewHelper::PrintPages() { - if (!prep_frame_view_) // Printing is already canceled or failed. - return; - prep_frame_view_->StartPrinting(); - - int page_count = prep_frame_view_->GetExpectedPageCount(); - if (!page_count) { - LOG(ERROR) << "Can't print 0 pages."; - return DidFinishPrinting(FAIL_PRINT); - } - - const PrintMsg_PrintPages_Params& params = *print_pages_params_; - const PrintMsg_Print_Params& print_params = params.params; - -#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) - // TODO(vitalybuka): should be page_count or valid pages from params.pages. - // See http://crbug.com/161576 - Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(), - print_params.document_cookie, - page_count)); -#endif // !defined(OS_CHROMEOS) - - if (!PrintPagesNative(prep_frame_view_->frame(), page_count)) { - LOG(ERROR) << "Printing failed."; - return DidFinishPrinting(FAIL_PRINT); - } -} - -void PrintWebViewHelper::FinishFramePrinting() { - prep_frame_view_.reset(); -} - -#if defined(OS_MACOSX) -bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, - int page_count) { - const PrintMsg_PrintPages_Params& params = *print_pages_params_; - const PrintMsg_Print_Params& print_params = params.params; - - PrintMsg_PrintPage_Params page_params; - page_params.params = print_params; - if (params.pages.empty()) { - for (int i = 0; i < page_count; ++i) { - page_params.page_number = i; - PrintPageInternal(page_params, frame); - } - } else { - for (size_t i = 0; i < params.pages.size(); ++i) { - if (params.pages[i] >= page_count) - break; - page_params.page_number = params.pages[i]; - PrintPageInternal(page_params, frame); - } - } - return true; -} - -#endif // OS_MACOSX - -// static - Not anonymous so that platform implementations can use it. -void PrintWebViewHelper::ComputePageLayoutInPointsForCss( - blink::WebFrame* frame, - int page_index, - const PrintMsg_Print_Params& page_params, - bool ignore_css_margins, - double* scale_factor, - PageSizeMargins* page_layout_in_points) { - PrintMsg_Print_Params params = CalculatePrintParamsForCss( - frame, page_index, page_params, ignore_css_margins, - page_params.print_scaling_option == - blink::WebPrintScalingOptionFitToPrintableArea, - scale_factor); - CalculatePageLayoutFromPrintParams(params, page_layout_in_points); -} - -bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) { - PrintMsg_PrintPages_Params settings; - Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(), - &settings.params)); - // Check if the printer returned any settings, if the settings is empty, we - // can safely assume there are no printer drivers configured. So we safely - // terminate. - bool result = true; - if (!PrintMsg_Print_Params_IsValid(settings.params)) - result = false; - - // Reset to default values. - ignore_css_margins_ = false; - settings.pages.clear(); - - settings.params.print_scaling_option = - blink::WebPrintScalingOptionSourceSize; - if (fit_to_paper_size) { - settings.params.print_scaling_option = - blink::WebPrintScalingOptionFitToPrintableArea; - } - - SetPrintPagesParams(settings); - return result; -} - -bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame* frame, - const blink::WebNode& node, - int* number_of_pages) { - DCHECK(frame); - bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node)); - if (!InitPrintSettings(fit_to_paper_size)) { - notify_browser_of_print_failure_ = false; - Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id())); - return false; - } - - const PrintMsg_Print_Params& params = print_pages_params_->params; - PrepareFrameAndViewForPrint prepare(params, frame, node, ignore_css_margins_); - prepare.StartPrinting(); - - *number_of_pages = prepare.GetExpectedPageCount(); - return true; -} - -bool PrintWebViewHelper::UpdatePrintSettings( - blink::WebLocalFrame* frame, - const blink::WebNode& node, - const base::DictionaryValue& passed_job_settings) { - const base::DictionaryValue* job_settings = &passed_job_settings; - base::DictionaryValue modified_job_settings; - if (job_settings->empty()) { - if (!print_for_preview_) - print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING); - return false; - } - - bool source_is_html = true; - if (print_for_preview_) { - if (!job_settings->GetBoolean(kSettingPreviewModifiable, &source_is_html)) { - NOTREACHED(); - } - } else { - source_is_html = !PrintingNodeOrPdfFrame(frame, node); - } - - if (print_for_preview_ || !source_is_html) { - modified_job_settings.MergeDictionary(job_settings); - modified_job_settings.SetBoolean(kSettingHeaderFooterEnabled, false); - modified_job_settings.SetInteger(kSettingMarginsType, NO_MARGINS); - job_settings = &modified_job_settings; - } - - // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when - // possible. - int cookie = - print_pages_params_ ? print_pages_params_->params.document_cookie : 0; - PrintMsg_PrintPages_Params settings; - bool canceled = false; - Send(new PrintHostMsg_UpdatePrintSettings(routing_id(), cookie, *job_settings, - &settings, &canceled)); - if (canceled) { - notify_browser_of_print_failure_ = false; - return false; - } - - if (!print_for_preview_) { - job_settings->GetInteger(kPreviewRequestID, - &settings.params.preview_request_id); - settings.params.print_to_pdf = true; - UpdateFrameMarginsCssInfo(*job_settings); - settings.params.print_scaling_option = - blink::WebPrintScalingOptionSourceSize; - } - - SetPrintPagesParams(settings); - - if (!PrintMsg_Print_Params_IsValid(settings.params)) { - if (!print_for_preview_) - print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS); - return false; - } - - return true; -} - - -bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame, - const blink::WebNode& node, - int expected_pages_count) { - PrintHostMsg_ScriptedPrint_Params params; - PrintMsg_PrintPages_Params print_settings; - - params.cookie = print_pages_params_->params.document_cookie; - params.has_selection = frame->hasSelection(); - params.expected_pages_count = expected_pages_count; - MarginType margin_type = DEFAULT_MARGINS; - if (PrintingNodeOrPdfFrame(frame, node)) - margin_type = GetMarginsForPdf(frame, node); - params.margin_type = margin_type; - - // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the - // value before and restore it afterwards. - blink::WebPrintScalingOption scaling_option = - print_pages_params_->params.print_scaling_option; - - print_pages_params_.reset(); - IPC::SyncMessage* msg = - new PrintHostMsg_ScriptedPrint(routing_id(), params, &print_settings); - msg->EnableMessagePumping(); - Send(msg); - print_settings.params.print_scaling_option = scaling_option; - SetPrintPagesParams(print_settings); - return (print_settings.params.dpi && print_settings.params.document_cookie); -} - -bool PrintWebViewHelper::RenderPagesForPrint(blink::WebLocalFrame* frame, - const blink::WebNode& node) { - if (!frame || prep_frame_view_) - return false; - const PrintMsg_PrintPages_Params& params = *print_pages_params_; - const PrintMsg_Print_Params& print_params = params.params; - prep_frame_view_.reset(new PrepareFrameAndViewForPrint( - print_params, frame, node, ignore_css_margins_)); - DCHECK(!print_pages_params_->params.selection_only || - print_pages_params_->pages.empty()); - prep_frame_view_->CopySelectionIfNeeded( - render_view()->GetWebkitPreferences(), - base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages, - base::Unretained(this))); - return true; -} - -#if defined(OS_POSIX) -bool PrintWebViewHelper::CopyMetafileDataToSharedMem( - const PdfMetafileSkia& metafile, - base::SharedMemoryHandle* shared_mem_handle) { - uint32_t buf_size = metafile.GetDataSize(); - if (buf_size == 0) - return false; - - std::unique_ptr shared_buf( - content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(buf_size)); - if (!shared_buf) - return false; - - if (!shared_buf->Map(buf_size)) - return false; - - if (!metafile.GetData(shared_buf->memory(), buf_size)) - return false; - - return shared_buf->GiveToProcess(base::GetCurrentProcessHandle(), - shared_mem_handle); -} -#endif // defined(OS_POSIX) - -void PrintWebViewHelper::SetPrintPagesParams( - const PrintMsg_PrintPages_Params& settings) { - print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings)); -} - -bool PrintWebViewHelper::PreviewPageRendered(int page_number, - PdfMetafileSkia* metafile) { - DCHECK_GE(page_number, FIRST_PAGE_INDEX); - - // For non-modifiable files, |metafile| should be NULL, so do not bother - // sending a message. If we don't generate draft metafiles, |metafile| is - // NULL. - if (!print_preview_context_.IsModifiable() || - !print_preview_context_.generate_draft_pages()) { - DCHECK(!metafile); - return true; - } - - if (!metafile) { - NOTREACHED(); - print_preview_context_.set_error( - PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE); - return false; - } - - return true; -} - -PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext() - : total_page_count_(0), - current_page_index_(0), - generate_draft_pages_(true), - print_ready_metafile_page_count_(0), - error_(PREVIEW_ERROR_NONE), - state_(UNINITIALIZED) { -} - -PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() { -} - -void PrintWebViewHelper::PrintPreviewContext::InitWithFrame( - blink::WebLocalFrame* web_frame) { - DCHECK(web_frame); - DCHECK(!IsRendering()); - state_ = INITIALIZED; - source_frame_.Reset(web_frame); - source_node_.reset(); -} - -void PrintWebViewHelper::PrintPreviewContext::InitWithNode( - const blink::WebNode& web_node) { - DCHECK(!web_node.isNull()); - DCHECK(web_node.document().frame()); - DCHECK(!IsRendering()); - state_ = INITIALIZED; - source_frame_.Reset(web_node.document().frame()); - source_node_ = web_node; -} - -void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() { - DCHECK_EQ(INITIALIZED, state_); - ClearContext(); -} - -bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument( - PrepareFrameAndViewForPrint* prepared_frame, - const std::vector& pages) { - DCHECK_EQ(INITIALIZED, state_); - state_ = RENDERING; - - // Need to make sure old object gets destroyed first. - prep_frame_view_.reset(prepared_frame); - prep_frame_view_->StartPrinting(); - - total_page_count_ = prep_frame_view_->GetExpectedPageCount(); - if (total_page_count_ == 0) { - LOG(ERROR) << "CreatePreviewDocument got 0 page count"; - set_error(PREVIEW_ERROR_ZERO_PAGES); - return false; - } - - metafile_.reset(new PdfMetafileSkia(PDF_SKIA_DOCUMENT_TYPE)); - CHECK(metafile_->Init()); - - current_page_index_ = 0; - pages_to_render_ = pages; - // Sort and make unique. - std::sort(pages_to_render_.begin(), pages_to_render_.end()); - pages_to_render_.resize( - std::unique(pages_to_render_.begin(), pages_to_render_.end()) - - pages_to_render_.begin()); - // Remove invalid pages. - pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(), - pages_to_render_.end(), - total_page_count_) - - pages_to_render_.begin()); - print_ready_metafile_page_count_ = pages_to_render_.size(); - if (pages_to_render_.empty()) { - print_ready_metafile_page_count_ = total_page_count_; - // Render all pages. - for (int i = 0; i < total_page_count_; ++i) - pages_to_render_.push_back(i); - } else if (generate_draft_pages_) { - int pages_index = 0; - for (int i = 0; i < total_page_count_; ++i) { - if (pages_index < print_ready_metafile_page_count_ && - i == pages_to_render_[pages_index]) { - pages_index++; - continue; - } - pages_to_render_.push_back(i); - } - } - - document_render_time_ = base::TimeDelta(); - begin_time_ = base::TimeTicks::Now(); - - return true; -} - -void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage( - const base::TimeDelta& page_time) { - DCHECK_EQ(RENDERING, state_); - document_render_time_ += page_time; - UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time); -} - -void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() { - DCHECK_EQ(RENDERING, state_); - state_ = DONE; - prep_frame_view_->FinishPrinting(); -} - -void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() { - DCHECK(IsRendering()); - - base::TimeTicks begin_time = base::TimeTicks::Now(); - metafile_->FinishDocument(); - - if (print_ready_metafile_page_count_ <= 0) { - NOTREACHED(); - return; - } - - UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime", - document_render_time_); - base::TimeDelta total_time = - (base::TimeTicks::Now() - begin_time) + document_render_time_; - UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime", - total_time); - UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage", - total_time / pages_to_render_.size()); -} - -void PrintWebViewHelper::PrintPreviewContext::Finished() { - DCHECK_EQ(DONE, state_); - state_ = INITIALIZED; - ClearContext(); -} - -void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) { - DCHECK(state_ == INITIALIZED || state_ == RENDERING); - state_ = INITIALIZED; - if (report_error) { - DCHECK_NE(PREVIEW_ERROR_NONE, error_); - UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_, - PREVIEW_ERROR_LAST_ENUM); - } - ClearContext(); -} - -int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() { - DCHECK_EQ(RENDERING, state_); - if (IsFinalPageRendered()) - return -1; - return pages_to_render_[current_page_index_++]; -} - -bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const { - return state_ == RENDERING || state_ == DONE; -} - -bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() { - // The only kind of node we can print right now is a PDF node. - return !PrintingNodeOrPdfFrame(source_frame(), source_node_); -} - -bool PrintWebViewHelper::PrintPreviewContext::HasSelection() { - return IsModifiable() && source_frame()->hasSelection(); -} - -bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile() - const { - DCHECK(IsRendering()); - return current_page_index_ == print_ready_metafile_page_count_; -} - -bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const { - DCHECK(IsRendering()); - return static_cast(current_page_index_) == pages_to_render_.size(); -} - -void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages( - bool generate_draft_pages) { - DCHECK_EQ(INITIALIZED, state_); - generate_draft_pages_ = generate_draft_pages; -} - -void PrintWebViewHelper::PrintPreviewContext::set_error( - enum PrintPreviewErrorBuckets error) { - error_ = error; -} - -blink::WebLocalFrame* PrintWebViewHelper::PrintPreviewContext::source_frame() { - DCHECK(state_ != UNINITIALIZED); - return source_frame_.GetFrame(); -} - -const blink::WebNode& - PrintWebViewHelper::PrintPreviewContext::source_node() const { - DCHECK(state_ != UNINITIALIZED); - return source_node_; -} - -blink::WebLocalFrame* -PrintWebViewHelper::PrintPreviewContext::prepared_frame() { - DCHECK(state_ != UNINITIALIZED); - return prep_frame_view_->frame(); -} - -const blink::WebNode& - PrintWebViewHelper::PrintPreviewContext::prepared_node() const { - DCHECK(state_ != UNINITIALIZED); - return prep_frame_view_->node(); -} - -int PrintWebViewHelper::PrintPreviewContext::total_page_count() const { - DCHECK(state_ != UNINITIALIZED); - return total_page_count_; -} - -bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const { - return generate_draft_pages_; -} - -PdfMetafileSkia* PrintWebViewHelper::PrintPreviewContext::metafile() { - DCHECK(IsRendering()); - return metafile_.get(); -} - -int PrintWebViewHelper::PrintPreviewContext::last_error() const { - return error_; -} - -void PrintWebViewHelper::PrintPreviewContext::ClearContext() { - prep_frame_view_.reset(); - metafile_.reset(); - pages_to_render_.clear(); - error_ = PREVIEW_ERROR_NONE; -} - -} // namespace printing diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper.h b/chromium_src/chrome/renderer/printing/print_web_view_helper.h deleted file mode 100644 index b748a6b6fb..0000000000 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper.h +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_ -#define CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_ - -#include -#include - -#include "base/callback.h" -#include "base/gtest_prod_util.h" -#include "base/memory/shared_memory.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "content/public/renderer/render_view_observer.h" -#include "content/public/renderer/render_view_observer_tracker.h" -#include "printing/pdf_metafile_skia.h" -#include "third_party/WebKit/public/platform/WebCanvas.h" -#include "third_party/WebKit/public/web/WebNode.h" -#include "third_party/WebKit/public/web/WebPrintParams.h" -#include "ui/gfx/geometry/size.h" - -struct PrintMsg_Print_Params; -struct PrintMsg_PrintPage_Params; -struct PrintMsg_PrintPages_Params; -struct PrintHostMsg_SetOptionsFromDocument_Params; - -namespace base { -class DictionaryValue; -} - -namespace blink { -class WebFrame; -class WebView; -} - -namespace printing { - -struct PageSizeMargins; -class PrepareFrameAndViewForPrint; - -// Stores reference to frame using WebVew and unique name. -// Workaround to modal dialog issue on Linux. crbug.com/236147. -// If WebFrame someday supports WeakPtr, we should use it here. -class FrameReference { - public: - explicit FrameReference(blink::WebLocalFrame* frame); - FrameReference(); - ~FrameReference(); - - void Reset(blink::WebLocalFrame* frame); - - blink::WebLocalFrame* GetFrame(); - blink::WebView* view(); - - private: - blink::WebView* view_; - blink::WebLocalFrame* frame_; -}; - -// PrintWebViewHelper handles most of the printing grunt work for RenderView. -// We plan on making print asynchronous and that will require copying the DOM -// of the document and creating a new WebView with the contents. -class PrintWebViewHelper - : public content::RenderViewObserver, - public content::RenderViewObserverTracker { - public: - explicit PrintWebViewHelper(content::RenderView* render_view); - virtual ~PrintWebViewHelper(); - - void PrintNode(const blink::WebNode& node); - - private: - enum PrintingResult { - OK, - FAIL_PRINT_INIT, - FAIL_PRINT, - FAIL_PREVIEW, - }; - - enum PrintPreviewErrorBuckets { - PREVIEW_ERROR_NONE, // Always first. - PREVIEW_ERROR_BAD_SETTING, - PREVIEW_ERROR_METAFILE_COPY_FAILED, - PREVIEW_ERROR_METAFILE_INIT_FAILED_DEPRECATED, - PREVIEW_ERROR_ZERO_PAGES, - PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED_DEPRECATED, - PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE, - PREVIEW_ERROR_INVALID_PRINTER_SETTINGS, - PREVIEW_ERROR_LAST_ENUM // Always last. - }; - - // RenderViewObserver implementation. - bool OnMessageReceived(const IPC::Message& message) override; - void PrintPage(blink::WebLocalFrame* frame, bool user_initiated) override; - void OnDestruct() override; - - // Message handlers --------------------------------------------------------- -#if !defined(DISABLE_BASIC_PRINTING) - void OnPrintPages(bool silent, bool print_background); - void OnPrintingDone(bool success); -#endif // !DISABLE_BASIC_PRINTING - void OnPrintPreview(const base::DictionaryValue& settings); - - - // Get |page_size| and |content_area| information from - // |page_layout_in_points|. - void GetPageSizeAndContentAreaFromPageLayout( - const PageSizeMargins& page_layout_in_points, - gfx::Size* page_size, - gfx::Rect* content_area); - - // Update |ignore_css_margins_| based on settings. - void UpdateFrameMarginsCssInfo(const base::DictionaryValue& settings); - - // Prepare frame for creating preview document. - void PrepareFrameForPreviewDocument(); - - // Continue creating preview document. - void OnFramePreparedForPreviewDocument(); - - // Finalize the print ready preview document. - bool FinalizePrintReadyDocument(); - - // Renders a print preview page. |page_number| is 0-based. - // Returns true if print preview should continue, false on failure. - bool RenderPreviewPage(int page_number, - const PrintMsg_Print_Params& print_params); - - - // Initialize the print preview document. - bool CreatePreviewDocument(); - - // Main printing code ------------------------------------------------------- - - void Print(blink::WebLocalFrame* frame, - const blink::WebNode& node, - bool silent = false, - bool print_background = false); - - // Notification when printing is done - signal tear-down/free resources. - void DidFinishPrinting(PrintingResult result); - - // Print Settings ----------------------------------------------------------- - - // Initialize print page settings with default settings. - // Used only for native printing workflow. - bool InitPrintSettings(bool fit_to_paper_size); - - // Calculate number of pages in source document. - bool CalculateNumberOfPages(blink::WebLocalFrame* frame, - const blink::WebNode& node, - int* number_of_pages); - - // Update the current print settings with new |passed_job_settings|. - // |passed_job_settings| dictionary contains print job details such as printer - // name, number of copies, page range, etc. - bool UpdatePrintSettings(blink::WebLocalFrame* frame, - const blink::WebNode& node, - const base::DictionaryValue& passed_job_settings); - - - // Get final print settings from the user. - // Return false if the user cancels or on error. - bool GetPrintSettingsFromUser(blink::WebFrame* frame, - const blink::WebNode& node, - int expected_pages_count); - - // Page Printing / Rendering ------------------------------------------------ - - void OnFramePreparedForPrintPages(); - void PrintPages(); - bool PrintPagesNative(blink::WebFrame* frame, int page_count); - void FinishFramePrinting(); - - // Prints the page listed in |params|. -#if defined(OS_LINUX) || defined(OS_ANDROID) - void PrintPageInternal(const PrintMsg_PrintPage_Params& params, - blink::WebFrame* frame, - PdfMetafileSkia* metafile); -#elif defined(OS_WIN) - void PrintPageInternal(const PrintMsg_PrintPage_Params& params, - blink::WebFrame* frame, - PdfMetafileSkia* metafile, - gfx::Size* page_size_in_dpi, - gfx::Rect* content_area_in_dpi); -#else - void PrintPageInternal(const PrintMsg_PrintPage_Params& params, - blink::WebFrame* frame); -#endif - - // Render the frame for printing. - bool RenderPagesForPrint(blink::WebLocalFrame* frame, - const blink::WebNode& node); - - // Platform specific helper function for rendering page(s) to |metafile|. -#if defined(OS_MACOSX) - void RenderPage(const PrintMsg_Print_Params& params, - int page_number, - blink::WebFrame* frame, - bool is_preview, - PdfMetafileSkia* metafile, - gfx::Size* page_size, - gfx::Rect* content_rect); -#endif // defined(OS_MACOSX) - - // Renders page contents from |frame| to |content_area| of |canvas|. - // |page_number| is zero-based. - // When method is called, canvas should be setup to draw to |canvas_area| - // with |scale_factor|. - static float RenderPageContent(blink::WebFrame* frame, - int page_number, - const gfx::Rect& canvas_area, - const gfx::Rect& content_area, - double scale_factor, - blink::WebCanvas* canvas); - - // Helper methods ----------------------------------------------------------- - - bool CopyMetafileDataToSharedMem(const PdfMetafileSkia& metafile, - base::SharedMemoryHandle* shared_mem_handle); - - // Helper method to get page layout in points and fit to page if needed. - static void ComputePageLayoutInPointsForCss( - blink::WebFrame* frame, - int page_index, - const PrintMsg_Print_Params& default_params, - bool ignore_css_margins, - double* scale_factor, - PageSizeMargins* page_layout_in_points); - - bool GetPrintFrame(blink::WebLocalFrame** frame); - - // Script Initiated Printing ------------------------------------------------ - - // Notifies the browser a print preview page has been rendered. - // |page_number| is 0-based. - // For a valid |page_number| with modifiable content, - // |metafile| is the rendered page. Otherwise |metafile| is NULL. - // Returns true if print preview should continue, false on failure. - bool PreviewPageRendered(int page_number, PdfMetafileSkia* metafile); - - void SetPrintPagesParams(const PrintMsg_PrintPages_Params& settings); - - // WebView used only to print the selection. - std::unique_ptr prep_frame_view_; - bool reset_prep_frame_view_; - - std::unique_ptr print_pages_params_; - bool is_print_ready_metafile_sent_; - bool ignore_css_margins_; - - // Used for scripted initiated printing blocking. - bool is_scripted_printing_blocked_; - - // Let the browser process know of a printing failure. Only set to false when - // the failure came from the browser in the first place. - bool notify_browser_of_print_failure_; - - // True, when printing from print preview. - bool print_for_preview_; - - // Keeps track of the state of print preview between messages. - // TODO(vitalybuka): Create PrintPreviewContext when needed and delete after - // use. Now it's interaction with various messages is confusing. - class PrintPreviewContext { - public: - PrintPreviewContext(); - ~PrintPreviewContext(); - - // Initializes the print preview context. Need to be called to set - // the |web_frame| / |web_node| to generate the print preview for. - void InitWithFrame(blink::WebLocalFrame* web_frame); - void InitWithNode(const blink::WebNode& web_node); - - // Does bookkeeping at the beginning of print preview. - void OnPrintPreview(); - - // Create the print preview document. |pages| is empty to print all pages. - // Takes ownership of |prepared_frame|. - bool CreatePreviewDocument(PrepareFrameAndViewForPrint* prepared_frame, - const std::vector& pages); - - // Called after a page gets rendered. |page_time| is how long the - // rendering took. - void RenderedPreviewPage(const base::TimeDelta& page_time); - - // Updates the print preview context when the required pages are rendered. - void AllPagesRendered(); - - // Finalizes the print ready preview document. - void FinalizePrintReadyDocument(); - - // Cleanup after print preview finishes. - void Finished(); - - // Cleanup after print preview fails. - void Failed(bool report_error); - - // Helper functions - int GetNextPageNumber(); - bool IsRendering() const; - bool IsModifiable(); - bool HasSelection(); - bool IsLastPageOfPrintReadyMetafile() const; - bool IsFinalPageRendered() const; - - // Setters - void set_generate_draft_pages(bool generate_draft_pages); - void set_error(enum PrintPreviewErrorBuckets error); - - // Getters - // Original frame for which preview was requested. - blink::WebLocalFrame* source_frame(); - // Original node for which preview was requested. - const blink::WebNode& source_node() const; - - // Frame to be use to render preview. May be the same as source_frame(), or - // generated from it, e.g. copy of selected block. - blink::WebLocalFrame* prepared_frame(); - // Node to be use to render preview. May be the same as source_node(), or - // generated from it, e.g. copy of selected block. - const blink::WebNode& prepared_node() const; - - int total_page_count() const; - bool generate_draft_pages() const; - PdfMetafileSkia* metafile(); - int last_error() const; - - private: - enum State { - UNINITIALIZED, // Not ready to render. - INITIALIZED, // Ready to render. - RENDERING, // Rendering. - DONE // Finished rendering. - }; - - // Reset some of the internal rendering context. - void ClearContext(); - - // Specifies what to render for print preview. - FrameReference source_frame_; - blink::WebNode source_node_; - - std::unique_ptr prep_frame_view_; - std::unique_ptr metafile_; - - // Total page count in the renderer. - int total_page_count_; - - // The current page to render. - int current_page_index_; - - // List of page indices that need to be rendered. - std::vector pages_to_render_; - - // True, when draft pages needs to be generated. - bool generate_draft_pages_; - - // Specifies the total number of pages in the print ready metafile. - int print_ready_metafile_page_count_; - - base::TimeDelta document_render_time_; - base::TimeTicks begin_time_; - - enum PrintPreviewErrorBuckets error_; - - State state_; - }; - - - bool print_node_in_progress_; - bool is_loading_; - bool is_scripted_preview_delayed_; - - PrintPreviewContext print_preview_context_; - - // Used to fix a race condition where the source is a PDF and print preview - // hangs because RequestPrintPreview is called before DidStopLoading() is - // called. This is a store for the RequestPrintPreview() call and its - // parameters so that it can be invoked after DidStopLoading. - base::Closure on_stop_loading_closure_; - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelper); -}; - -} // namespace printing - -#endif // CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_ diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc deleted file mode 100644 index 4df052561a..0000000000 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/printing/print_web_view_helper.h" - -#include - -#include "base/logging.h" -#include "chrome/common/print_messages.h" -#include "content/public/renderer/render_thread.h" -#include "printing/metafile_skia_wrapper.h" -#include "printing/page_size_margins.h" -#include "printing/pdf_metafile_skia.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" - -#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -#include "base/process/process_handle.h" -#else -#include "base/file_descriptor_posix.h" -#endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) - -namespace printing { - -using blink::WebFrame; - -bool PrintWebViewHelper::RenderPreviewPage( - int page_number, - const PrintMsg_Print_Params& print_params) { - PrintMsg_PrintPage_Params page_params; - page_params.params = print_params; - page_params.page_number = page_number; - std::unique_ptr draft_metafile; - PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile(); - if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) { - draft_metafile.reset(new PdfMetafileSkia(PDF_SKIA_DOCUMENT_TYPE)); - initial_render_metafile = draft_metafile.get(); - } - - base::TimeTicks begin_time = base::TimeTicks::Now(); - PrintPageInternal(page_params, - print_preview_context_.prepared_frame(), - initial_render_metafile); - print_preview_context_.RenderedPreviewPage( - base::TimeTicks::Now() - begin_time); - if (draft_metafile.get()) { - draft_metafile->FinishDocument(); - } else if (print_preview_context_.IsModifiable() && - print_preview_context_.generate_draft_pages()) { - DCHECK(!draft_metafile.get()); - draft_metafile = - print_preview_context_.metafile()->GetMetafileForCurrentPage( - PDF_SKIA_DOCUMENT_TYPE); - - } - return PreviewPageRendered(page_number, draft_metafile.get()); -} - -bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, - int page_count) { - PdfMetafileSkia metafile(PDF_SKIA_DOCUMENT_TYPE); - if (!metafile.Init()) - return false; - - const PrintMsg_PrintPages_Params& params = *print_pages_params_; - std::vector printed_pages; - - if (params.pages.empty()) { - for (int i = 0; i < page_count; ++i) { - printed_pages.push_back(i); - } - } else { - // TODO(vitalybuka): redesign to make more code cross platform. - for (size_t i = 0; i < params.pages.size(); ++i) { - if (params.pages[i] >= 0 && params.pages[i] < page_count) { - printed_pages.push_back(params.pages[i]); - } - } - } - - if (printed_pages.empty()) - return false; - - PrintMsg_PrintPage_Params page_params; - page_params.params = params.params; - for (size_t i = 0; i < printed_pages.size(); ++i) { - page_params.page_number = printed_pages[i]; - PrintPageInternal(page_params, frame, &metafile); - } - - // blink::printEnd() for PDF should be called before metafile is closed. - FinishFramePrinting(); - - metafile.FinishDocument(); - - PrintHostMsg_DidPrintPage_Params printed_page_params; - if (!CopyMetafileDataToSharedMem( - metafile, &printed_page_params.metafile_data_handle)) { - return false; - } - - printed_page_params.data_size = metafile.GetDataSize(); - printed_page_params.document_cookie = params.params.document_cookie; - - for (size_t i = 0; i < printed_pages.size(); ++i) { - printed_page_params.page_number = printed_pages[i]; - Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params)); - // Send the rest of the pages with an invalid metafile handle. - printed_page_params.metafile_data_handle.fd = -1; - } - return true; -} - -void PrintWebViewHelper::PrintPageInternal( - const PrintMsg_PrintPage_Params& params, - WebFrame* frame, - PdfMetafileSkia* metafile) { - PageSizeMargins page_layout_in_points; - double scale_factor = 1.0f; - ComputePageLayoutInPointsForCss(frame, params.page_number, params.params, - ignore_css_margins_, &scale_factor, - &page_layout_in_points); - gfx::Size page_size; - gfx::Rect content_area; - GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size, - &content_area); - gfx::Rect canvas_area = content_area; - - SkCanvas* canvas = metafile->GetVectorCanvasForNewPage( - page_size, canvas_area, scale_factor); - if (!canvas) - return; - - MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); - - RenderPageContent(frame, params.page_number, canvas_area, content_area, - scale_factor, canvas); - - // Done printing. Close the device context to retrieve the compiled metafile. - if (!metafile->FinishPage()) - NOTREACHED() << "metafile failed"; -} - -} // namespace printing diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm b/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm deleted file mode 100644 index 9425bf2e4b..0000000000 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/renderer/printing/print_web_view_helper.h" - -#import - -#include "base/logging.h" -#include "base/mac/scoped_nsautorelease_pool.h" -#include "base/metrics/histogram.h" -#include "chrome/common/print_messages.h" -#include "printing/metafile_skia_wrapper.h" -#include "printing/page_size_margins.h" -#include "third_party/WebKit/public/platform/WebCanvas.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/skia/include/core/SkCanvas.h" - -namespace printing { - -using blink::WebFrame; - -void PrintWebViewHelper::PrintPageInternal( - const PrintMsg_PrintPage_Params& params, - WebFrame* frame) { - PdfMetafileSkia metafile(PDF_SKIA_DOCUMENT_TYPE); - CHECK(metafile.Init()); - - int page_number = params.page_number; - gfx::Size page_size_in_dpi; - gfx::Rect content_area_in_dpi; - RenderPage(print_pages_params_->params, page_number, frame, false, &metafile, - &page_size_in_dpi, &content_area_in_dpi); - metafile.FinishDocument(); - - PrintHostMsg_DidPrintPage_Params page_params; - page_params.data_size = metafile.GetDataSize(); - page_params.page_number = page_number; - page_params.document_cookie = params.params.document_cookie; - page_params.page_size = page_size_in_dpi; - page_params.content_area = content_area_in_dpi; - - // Ask the browser to create the shared memory for us. - if (!CopyMetafileDataToSharedMem(metafile, - &(page_params.metafile_data_handle))) { - // TODO(thestig): Fail and return false instead. - page_params.data_size = 0; - } - - Send(new PrintHostMsg_DidPrintPage(routing_id(), page_params)); -} - -bool PrintWebViewHelper::RenderPreviewPage( - int page_number, - const PrintMsg_Print_Params& print_params) { - PrintMsg_Print_Params printParams = print_params; - std::unique_ptr draft_metafile; - PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile(); - - bool render_to_draft = print_preview_context_.IsModifiable() && - is_print_ready_metafile_sent_; - - if (render_to_draft) { - draft_metafile.reset(new PdfMetafileSkia(PDF_SKIA_DOCUMENT_TYPE)); - CHECK(draft_metafile->Init()); - initial_render_metafile = draft_metafile.get(); - } - - base::TimeTicks begin_time = base::TimeTicks::Now(); - gfx::Size page_size; - RenderPage(printParams, page_number, print_preview_context_.prepared_frame(), - true, initial_render_metafile, &page_size, NULL); - print_preview_context_.RenderedPreviewPage( - base::TimeTicks::Now() - begin_time); - - if (draft_metafile.get()) { - draft_metafile->FinishDocument(); - } else { - if (print_preview_context_.IsModifiable() && - print_preview_context_.generate_draft_pages()) { - DCHECK(!draft_metafile.get()); - draft_metafile = - print_preview_context_.metafile()->GetMetafileForCurrentPage( - PDF_SKIA_DOCUMENT_TYPE); - } - } - return PreviewPageRendered(page_number, draft_metafile.get()); -} - -void PrintWebViewHelper::RenderPage(const PrintMsg_Print_Params& params, - int page_number, - WebFrame* frame, - bool is_preview, - PdfMetafileSkia* metafile, - gfx::Size* page_size, - gfx::Rect* content_rect) { - double scale_factor = 1.0f; - double webkit_shrink_factor = frame->getPrintPageShrink(page_number); - PageSizeMargins page_layout_in_points; - gfx::Rect content_area; - - ComputePageLayoutInPointsForCss(frame, page_number, params, - ignore_css_margins_, &scale_factor, - &page_layout_in_points); - GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, page_size, - &content_area); - if (content_rect) - *content_rect = content_area; - - scale_factor *= webkit_shrink_factor; - - gfx::Rect canvas_area = content_area; - - { - SkCanvas* canvas = metafile->GetVectorCanvasForNewPage( - *page_size, canvas_area, scale_factor); - if (!canvas) - return; - - MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); - skia::SetIsPreviewMetafile(*canvas, is_preview); - RenderPageContent(frame, page_number, canvas_area, content_area, - scale_factor, static_cast(canvas)); - } - - // Done printing. Close the device context to retrieve the compiled metafile. - metafile->FinishPage(); -} - -} // namespace printing diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc deleted file mode 100644 index 6c31313f0d..0000000000 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2014 The Chromium 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 "chrome/renderer/printing/print_web_view_helper.h" - -#include - -#include "base/logging.h" -#include "base/process/process_handle.h" -#include "chrome/common/print_messages.h" -#include "content/public/renderer/render_thread.h" -#include "printing/metafile_skia_wrapper.h" -#include "printing/page_size_margins.h" -#include "printing/pdf_metafile_skia.h" -#include "printing/units.h" -#include "skia/ext/platform_device.h" -#include "third_party/WebKit/public/web/WebLocalFrame.h" - - -namespace printing { - -using blink::WebFrame; - -bool PrintWebViewHelper::RenderPreviewPage( - int page_number, - const PrintMsg_Print_Params& print_params) { - PrintMsg_PrintPage_Params page_params; - page_params.params = print_params; - page_params.page_number = page_number; - std::unique_ptr draft_metafile; - PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile(); - if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) { - draft_metafile.reset(new PdfMetafileSkia(PDF_SKIA_DOCUMENT_TYPE)); - initial_render_metafile = draft_metafile.get(); - } - - base::TimeTicks begin_time = base::TimeTicks::Now(); - PrintPageInternal(page_params, - print_preview_context_.prepared_frame(), - initial_render_metafile, - NULL, - NULL); - print_preview_context_.RenderedPreviewPage( - base::TimeTicks::Now() - begin_time); - if (draft_metafile.get()) { - draft_metafile->FinishDocument(); - } else if (print_preview_context_.IsModifiable() && - print_preview_context_.generate_draft_pages()) { - DCHECK(!draft_metafile.get()); - draft_metafile = - print_preview_context_.metafile()->GetMetafileForCurrentPage( - PDF_SKIA_DOCUMENT_TYPE); - } - return PreviewPageRendered(page_number, draft_metafile.get()); -} - -bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, - int page_count) { - PdfMetafileSkia metafile(PDF_SKIA_DOCUMENT_TYPE); - if (!metafile.Init()) - return false; - - const PrintMsg_PrintPages_Params& params = *print_pages_params_; - std::vector printed_pages; - if (params.pages.empty()) { - for (int i = 0; i < page_count; ++i) { - printed_pages.push_back(i); - } - } else { - // TODO(vitalybuka): redesign to make more code cross platform. - for (size_t i = 0; i < params.pages.size(); ++i) { - if (params.pages[i] >= 0 && params.pages[i] < page_count) { - printed_pages.push_back(params.pages[i]); - } - } - } - if (printed_pages.empty()) - return false; - - std::vector page_size_in_dpi(printed_pages.size()); - std::vector content_area_in_dpi(printed_pages.size()); - - PrintMsg_PrintPage_Params page_params; - page_params.params = params.params; - for (size_t i = 0; i < printed_pages.size(); ++i) { - page_params.page_number = printed_pages[i]; - PrintPageInternal(page_params, - frame, - &metafile, - &page_size_in_dpi[i], - &content_area_in_dpi[i]); - } - - // blink::printEnd() for PDF should be called before metafile is closed. - FinishFramePrinting(); - - metafile.FinishDocument(); - - PrintHostMsg_DidPrintPage_Params printed_page_params; - if (!CopyMetafileDataToSharedMem( - metafile, &printed_page_params.metafile_data_handle)) { - return false; - } - - printed_page_params.content_area = params.params.printable_area; - printed_page_params.data_size = metafile.GetDataSize(); - printed_page_params.document_cookie = params.params.document_cookie; - printed_page_params.page_size = params.params.page_size; - - for (size_t i = 0; i < printed_pages.size(); ++i) { - printed_page_params.page_number = printed_pages[i]; - printed_page_params.page_size = page_size_in_dpi[i]; - printed_page_params.content_area = content_area_in_dpi[i]; - Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params)); - // Send the rest of the pages with an invalid metafile handle. - printed_page_params.metafile_data_handle.Close(); - printed_page_params.metafile_data_handle = base::SharedMemoryHandle(); - } - return true; -} - -void PrintWebViewHelper::PrintPageInternal( - const PrintMsg_PrintPage_Params& params, - WebFrame* frame, - PdfMetafileSkia* metafile, - gfx::Size* page_size_in_dpi, - gfx::Rect* content_area_in_dpi) { - PageSizeMargins page_layout_in_points; - double css_scale_factor = 1.0f; - ComputePageLayoutInPointsForCss(frame, params.page_number, params.params, - ignore_css_margins_, &css_scale_factor, - &page_layout_in_points); - gfx::Size page_size; - gfx::Rect content_area; - GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size, - &content_area); - int dpi = static_cast(params.params.dpi); - // Calculate the actual page size and content area in dpi. - if (page_size_in_dpi) { - *page_size_in_dpi = - gfx::Size(static_cast(ConvertUnitDouble( - page_size.width(), kPointsPerInch, dpi)), - static_cast(ConvertUnitDouble( - page_size.height(), kPointsPerInch, dpi))); - } - - if (content_area_in_dpi) { - // Output PDF matches paper size and should be printer edge to edge. - *content_area_in_dpi = - gfx::Rect(0, 0, page_size_in_dpi->width(), page_size_in_dpi->height()); - } - - gfx::Rect canvas_area = - content_area; -#if 0 - params.params.display_header_footer ? gfx::Rect(page_size) : content_area; -#endif - - float webkit_page_shrink_factor = - frame->getPrintPageShrink(params.page_number); - float scale_factor = css_scale_factor * webkit_page_shrink_factor; - - SkCanvas* canvas = metafile->GetVectorCanvasForNewPage( - page_size, canvas_area, scale_factor); - if (!canvas) - return; - - MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); - -#if 0 - if (params.params.display_header_footer) { - // |page_number| is 0-based, so 1 is added. - PrintHeaderAndFooter(canvas.get(), - params.page_number + 1, - print_preview_context_.total_page_count(), - *frame, - scale_factor, - page_layout_in_points, - params.params); - } -#endif - - float webkit_scale_factor = RenderPageContent(frame, - params.page_number, - canvas_area, - content_area, - scale_factor, - canvas); - DCHECK_GT(webkit_scale_factor, 0.0f); - // Done printing. Close the device context to retrieve the compiled metafile. - if (!metafile->FinishPage()) - NOTREACHED() << "metafile failed"; -} - -bool PrintWebViewHelper::CopyMetafileDataToSharedMem( - const PdfMetafileSkia& metafile, - base::SharedMemoryHandle* shared_mem_handle) { - uint32_t buf_size = metafile.GetDataSize(); - if (buf_size == 0) - return false; - - std::unique_ptr shared_buf( - content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(buf_size)); - if (!shared_buf) - return false; - - if (!shared_buf->Map(buf_size)) - return false; - - if (!metafile.GetData(shared_buf->memory(), buf_size)) - return false; - - *shared_mem_handle = - base::SharedMemory::DuplicateHandle(shared_buf->handle()); - return true; -} - -} // namespace printing diff --git a/chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc b/chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc deleted file mode 100644 index 46465f4dfd..0000000000 --- a/chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Implements a custom word iterator used for our spellchecker. - -#include "chrome/renderer/spellchecker/spellcheck_worditerator.h" - -#include -#include - -#include "base/i18n/break_iterator.h" -#include "base/logging.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/icu/source/common/unicode/normlzr.h" -#include "third_party/icu/source/common/unicode/schriter.h" -#include "third_party/icu/source/common/unicode/uscript.h" -#include "third_party/icu/source/i18n/unicode/ulocdata.h" - -// SpellcheckCharAttribute implementation: - -SpellcheckCharAttribute::SpellcheckCharAttribute() - : script_code_(USCRIPT_LATIN) { -} - -SpellcheckCharAttribute::~SpellcheckCharAttribute() { -} - -void SpellcheckCharAttribute::SetDefaultLanguage(const std::string& language) { - CreateRuleSets(language); -} - -base::string16 SpellcheckCharAttribute::GetRuleSet( - bool allow_contraction) const { - return allow_contraction ? - ruleset_allow_contraction_ : ruleset_disallow_contraction_; -} - -void SpellcheckCharAttribute::CreateRuleSets(const std::string& language) { - // The template for our custom rule sets, which is based on the word-break - // rules of ICU 4.0: - // . - // The major differences from the original one are listed below: - // * It discards comments in the original rules. - // * It discards characters not needed by our spellchecker (e.g. numbers, - // punctuation characters, Hiraganas, Katakanas, CJK Ideographs, and so on). - // * It allows customization of the $ALetter value (i.e. word characters). - // * It allows customization of the $ALetterPlus value (i.e. whether or not to - // use the dictionary data). - // * It allows choosing whether or not to split a text at contraction - // characters. - // This template only changes the forward-iteration rules. So, calling - // ubrk_prev() returns the same results as the original template. - static const char kRuleTemplate[] = - "!!chain;" - "$CR = [\\p{Word_Break = CR}];" - "$LF = [\\p{Word_Break = LF}];" - "$Newline = [\\p{Word_Break = Newline}];" - "$Extend = [\\p{Word_Break = Extend}];" - "$Format = [\\p{Word_Break = Format}];" - "$Katakana = [\\p{Word_Break = Katakana}];" - // Not all the characters in a given script are ALetter. - // For instance, U+05F4 is MidLetter. So, this may be - // better, but it leads to an empty set error in Thai. - // "$ALetter = [[\\p{script=%s}] & [\\p{Word_Break = ALetter}]];" - "$ALetter = [\\p{script=%s}%s];" - // U+0027 (single quote/apostrophe) is not in MidNumLet any more - // in UAX 29 rev 21 or later. For our purpose, U+0027 - // has to be treated as MidNumLet. ( http://crbug.com/364072 ) - "$MidNumLet = [\\p{Word_Break = MidNumLet} \\u0027];" - "$MidLetter = [\\p{Word_Break = MidLetter}%s];" - "$MidNum = [\\p{Word_Break = MidNum}];" - "$Numeric = [\\p{Word_Break = Numeric}];" - "$ExtendNumLet = [\\p{Word_Break = ExtendNumLet}];" - - "$Control = [\\p{Grapheme_Cluster_Break = Control}]; " - "%s" // ALetterPlus - - "$KatakanaEx = $Katakana ($Extend | $Format)*;" - "$ALetterEx = $ALetterPlus ($Extend | $Format)*;" - "$MidNumLetEx = $MidNumLet ($Extend | $Format)*;" - "$MidLetterEx = $MidLetter ($Extend | $Format)*;" - "$MidNumEx = $MidNum ($Extend | $Format)*;" - "$NumericEx = $Numeric ($Extend | $Format)*;" - "$ExtendNumLetEx = $ExtendNumLet ($Extend | $Format)*;" - - "$Hiragana = [\\p{script=Hiragana}];" - "$Ideographic = [\\p{Ideographic}];" - "$HiraganaEx = $Hiragana ($Extend | $Format)*;" - "$IdeographicEx = $Ideographic ($Extend | $Format)*;" - - "!!forward;" - "$CR $LF;" - "[^$CR $LF $Newline]? ($Extend | $Format)+;" - "$ALetterEx {200};" - "$ALetterEx $ALetterEx {200};" - "%s" // (Allow|Disallow) Contraction - - "!!reverse;" - "$BackALetterEx = ($Format | $Extend)* $ALetterPlus;" - "$BackMidNumLetEx = ($Format | $Extend)* $MidNumLet;" - "$BackNumericEx = ($Format | $Extend)* $Numeric;" - "$BackMidNumEx = ($Format | $Extend)* $MidNum;" - "$BackMidLetterEx = ($Format | $Extend)* $MidLetter;" - "$BackKatakanaEx = ($Format | $Extend)* $Katakana;" - "$BackExtendNumLetEx= ($Format | $Extend)* $ExtendNumLet;" - "$LF $CR;" - "($Format | $Extend)* [^$CR $LF $Newline]?;" - "$BackALetterEx $BackALetterEx;" - "$BackALetterEx ($BackMidLetterEx | $BackMidNumLetEx) $BackALetterEx;" - "$BackNumericEx $BackNumericEx;" - "$BackNumericEx $BackALetterEx;" - "$BackALetterEx $BackNumericEx;" - "$BackNumericEx ($BackMidNumEx | $BackMidNumLetEx) $BackNumericEx;" - "$BackKatakanaEx $BackKatakanaEx;" - "$BackExtendNumLetEx ($BackALetterEx | $BackNumericEx |" - " $BackKatakanaEx | $BackExtendNumLetEx);" - "($BackALetterEx | $BackNumericEx | $BackKatakanaEx)" - " $BackExtendNumLetEx;" - - "!!safe_reverse;" - "($Extend | $Format)+ .?;" - "($MidLetter | $MidNumLet) $BackALetterEx;" - "($MidNum | $MidNumLet) $BackNumericEx;" - - "!!safe_forward;" - "($Extend | $Format)+ .?;" - "($MidLetterEx | $MidNumLetEx) $ALetterEx;" - "($MidNumEx | $MidNumLetEx) $NumericEx;"; - - // Retrieve the script codes used by the given language from ICU. When the - // given language consists of two or more scripts, we just use the first - // script. The size of returned script codes is always < 8. Therefore, we use - // an array of size 8 so we can include all script codes without insufficient - // buffer errors. - UErrorCode error = U_ZERO_ERROR; - UScriptCode script_code[8]; - int scripts = uscript_getCode(language.c_str(), script_code, - arraysize(script_code), &error); - if (U_SUCCESS(error) && scripts >= 1) - script_code_ = script_code[0]; - - // Retrieve the values for $ALetter and $ALetterPlus. We use the dictionary - // only for the languages which need it (i.e. Korean and Thai) to prevent ICU - // from returning dictionary words (i.e. Korean or Thai words) for languages - // which don't need them. - const char* aletter = uscript_getName(script_code_); - if (!aletter) - aletter = "Latin"; - - const char kWithDictionary[] = - "$dictionary = [:LineBreak = Complex_Context:];" - "$ALetterPlus = [$ALetter [$dictionary-$Extend-$Control]];"; - const char kWithoutDictionary[] = "$ALetterPlus = $ALetter;"; - const char* aletter_plus = kWithoutDictionary; - if (script_code_ == USCRIPT_HANGUL || script_code_ == USCRIPT_THAI || - script_code_ == USCRIPT_LAO || script_code_ == USCRIPT_KHMER) - aletter_plus = kWithDictionary; - - // Treat numbers as word characters except for Arabic and Hebrew. - const char* aletter_extra = " [0123456789]"; - if (script_code_ == USCRIPT_HEBREW || script_code_ == USCRIPT_ARABIC) - aletter_extra = ""; - - const char kMidLetterExtra[] = ""; - // For Hebrew, treat single/double quoation marks as MidLetter. - const char kMidLetterExtraHebrew[] = "\"'"; - const char* midletter_extra = kMidLetterExtra; - if (script_code_ == USCRIPT_HEBREW) - midletter_extra = kMidLetterExtraHebrew; - - // Create two custom rule-sets: one allows contraction and the other does not. - // We save these strings in UTF-16 so we can use it without conversions. (ICU - // needs UTF-16 strings.) - const char kAllowContraction[] = - "$ALetterEx ($MidLetterEx | $MidNumLetEx) $ALetterEx {200};"; - const char kDisallowContraction[] = ""; - - ruleset_allow_contraction_ = base::ASCIIToUTF16( - base::StringPrintf(kRuleTemplate, - aletter, - aletter_extra, - midletter_extra, - aletter_plus, - kAllowContraction)); - ruleset_disallow_contraction_ = base::ASCIIToUTF16( - base::StringPrintf(kRuleTemplate, - aletter, - aletter_extra, - midletter_extra, - aletter_plus, - kDisallowContraction)); -} - -bool SpellcheckCharAttribute::OutputChar(UChar c, - base::string16* output) const { - // Call the language-specific function if necessary. - // Otherwise, we call the default one. - switch (script_code_) { - case USCRIPT_ARABIC: - return OutputArabic(c, output); - - case USCRIPT_HANGUL: - return OutputHangul(c, output); - - case USCRIPT_HEBREW: - return OutputHebrew(c, output); - - default: - return OutputDefault(c, output); - } -} - -bool SpellcheckCharAttribute::OutputArabic(UChar c, - base::string16* output) const { - // Discard characters not from Arabic alphabets. We also discard vowel marks - // of Arabic (Damma, Fatha, Kasra, etc.) to prevent our Arabic dictionary from - // marking an Arabic word including vowel marks as misspelled. (We need to - // check these vowel marks manually and filter them out since their script - // codes are USCRIPT_ARABIC.) - if (0x0621 <= c && c <= 0x064D) - output->push_back(c); - return true; -} - -bool SpellcheckCharAttribute::OutputHangul(UChar c, - base::string16* output) const { - // Decompose a Hangul character to a Hangul vowel and consonants used by our - // spellchecker. A Hangul character of Unicode is a ligature consisting of a - // Hangul vowel and consonants, e.g. U+AC01 "Gag" consists of U+1100 "G", - // U+1161 "a", and U+11A8 "g". That is, we can treat each Hangul character as - // a point of a cubic linear space consisting of (first consonant, vowel, last - // consonant). Therefore, we can compose a Hangul character from a vowel and - // two consonants with linear composition: - // character = 0xAC00 + - // (first consonant - 0x1100) * 28 * 21 + - // (vowel - 0x1161) * 28 + - // (last consonant - 0x11A7); - // We can also decompose a Hangul character with linear decomposition: - // first consonant = (character - 0xAC00) / 28 / 21; - // vowel = (character - 0xAC00) / 28 % 21; - // last consonant = (character - 0xAC00) % 28; - // This code is copied from Unicode Standard Annex #15 - // and added some comments. - const int kSBase = 0xAC00; // U+AC00: the top of Hangul characters. - const int kLBase = 0x1100; // U+1100: the top of Hangul first consonants. - const int kVBase = 0x1161; // U+1161: the top of Hangul vowels. - const int kTBase = 0x11A7; // U+11A7: the top of Hangul last consonants. - const int kLCount = 19; // The number of Hangul first consonants. - const int kVCount = 21; // The number of Hangul vowels. - const int kTCount = 28; // The number of Hangul last consonants. - const int kNCount = kVCount * kTCount; - const int kSCount = kLCount * kNCount; - - int index = c - kSBase; - if (index < 0 || index >= kSBase + kSCount) { - // This is not a Hangul syllable. Call the default output function since we - // should output this character when it is a Hangul syllable. - return OutputDefault(c, output); - } - - // This is a Hangul character. Decompose this characters into Hangul vowels - // and consonants. - int l = kLBase + index / kNCount; - int v = kVBase + (index % kNCount) / kTCount; - int t = kTBase + index % kTCount; - output->push_back(l); - output->push_back(v); - if (t != kTBase) - output->push_back(t); - return true; -} - -bool SpellcheckCharAttribute::OutputHebrew(UChar c, - base::string16* output) const { - // Discard characters except Hebrew alphabets. We also discard Hebrew niqquds - // to prevent our Hebrew dictionary from marking a Hebrew word including - // niqquds as misspelled. (Same as Arabic vowel marks, we need to check - // niqquds manually and filter them out since their script codes are - // USCRIPT_HEBREW.) - // Pass through ASCII single/double quotation marks and Hebrew Geresh and - // Gershayim. - if ((0x05D0 <= c && c <= 0x05EA) || c == 0x22 || c == 0x27 || - c == 0x05F4 || c == 0x05F3) - output->push_back(c); - return true; -} - -bool SpellcheckCharAttribute::OutputDefault(UChar c, - base::string16* output) const { - // Check the script code of this character and output only if it is the one - // used by the spellchecker language. - UErrorCode status = U_ZERO_ERROR; - UScriptCode script_code = uscript_getScript(c, &status); - if (script_code == script_code_ || script_code == USCRIPT_COMMON) - output->push_back(c); - return true; -} - -// SpellcheckWordIterator implementation: - -SpellcheckWordIterator::SpellcheckWordIterator() - : text_(NULL), - attribute_(NULL), - iterator_() { -} - -SpellcheckWordIterator::~SpellcheckWordIterator() { - Reset(); -} - -bool SpellcheckWordIterator::Initialize( - const SpellcheckCharAttribute* attribute, - bool allow_contraction) { - // Create a custom ICU break iterator with empty text used in this object. (We - // allow setting text later so we can re-use this iterator.) - DCHECK(attribute); - const base::string16 rule(attribute->GetRuleSet(allow_contraction)); - - // If there is no rule set, the attributes were invalid. - if (rule.empty()) - return false; - - std::unique_ptr iterator( - new base::i18n::BreakIterator(base::string16(), rule)); - if (!iterator->Init()) { - // Since we're not passing in any text, the only reason this could fail - // is if we fail to parse the rules. Since the rules are hardcoded, - // that would be a bug in this class. - NOTREACHED() << "failed to open iterator (broken rules)"; - return false; - } - iterator_ = std::move(iterator); - - // Set the character attributes so we can normalize the words extracted by - // this iterator. - attribute_ = attribute; - return true; -} - -bool SpellcheckWordIterator::IsInitialized() const { - // Return true iff we have an iterator. - return !!iterator_; -} - -bool SpellcheckWordIterator::SetText(const base::char16* text, size_t length) { - DCHECK(!!iterator_); - - // Set the text to be split by this iterator. - if (!iterator_->SetText(text, length)) { - LOG(ERROR) << "failed to set text"; - return false; - } - - text_ = text; - return true; -} - -bool SpellcheckWordIterator::GetNextWord(base::string16* word_string, - int* word_start, - int* word_length) { - DCHECK(!!text_); - - word_string->clear(); - *word_start = 0; - *word_length = 0; - - if (!text_) { - return false; - } - - // Find a word that can be checked for spelling. Our rule sets filter out - // invalid words (e.g. numbers and characters not supported by the - // spellchecker language) so this ubrk_getRuleStatus() call returns - // UBRK_WORD_NONE when this iterator finds an invalid word. So, we skip such - // words until we can find a valid word or reach the end of the input string. - while (iterator_->Advance()) { - const size_t start = iterator_->prev(); - const size_t length = iterator_->pos() - start; - if (iterator_->IsWord()) { - if (Normalize(start, length, word_string)) { - *word_start = start; - *word_length = length; - return true; - } - } - } - - // There aren't any more words in the given text. - return false; -} - -void SpellcheckWordIterator::Reset() { - iterator_.reset(); -} - -bool SpellcheckWordIterator::Normalize(int input_start, - int input_length, - base::string16* output_string) const { - // We use NFKC (Normalization Form, Compatible decomposition, followed by - // canonical Composition) defined in Unicode Standard Annex #15 to normalize - // this token because it it the most suitable normalization algorithm for our - // spellchecker. Nevertheless, it is not a perfect algorithm for our - // spellchecker and we need manual normalization as well. The normalized - // text does not have to be NUL-terminated since its characters are copied to - // string16, which adds a NUL character when we need. - icu::UnicodeString input(FALSE, &text_[input_start], input_length); - UErrorCode status = U_ZERO_ERROR; - icu::UnicodeString output; - icu::Normalizer::normalize(input, UNORM_NFKC, 0, output, status); - if (status != U_ZERO_ERROR && status != U_STRING_NOT_TERMINATED_WARNING) - return false; - - // Copy the normalized text to the output. - icu::StringCharacterIterator it(output); - for (UChar c = it.first(); c != icu::CharacterIterator::DONE; c = it.next()) - attribute_->OutputChar(c, output_string); - - return !output_string->empty(); -} diff --git a/chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h b/chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h deleted file mode 100644 index 7e07d29273..0000000000 --- a/chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Defines an iterator class that enumerates words supported by our spellchecker -// from multi-language text. This class is used for filtering out characters -// not supported by our spellchecker. - -#ifndef CHROME_RENDERER_SPELLCHECKER_SPELLCHECK_WORDITERATOR_H_ -#define CHROME_RENDERER_SPELLCHECKER_SPELLCHECK_WORDITERATOR_H_ - -#include -#include - -#include "base/macros.h" -#include "base/strings/string16.h" -#include "third_party/icu/source/common/unicode/uscript.h" - -namespace base { -namespace i18n { -class BreakIterator; -} // namespace i18n -} // namespace base - -// A class which encapsulates language-specific operations used by -// SpellcheckWordIterator. When we set the spellchecker language, this class -// creates rule sets that filter out the characters not supported by the -// spellchecker. (Please read the comment in the SpellcheckWordIterator class -// about how to use this class.) -class SpellcheckCharAttribute { - public: - SpellcheckCharAttribute(); - ~SpellcheckCharAttribute(); - - // Sets the language of the spellchecker. When this function is called with an - // ISO language code, this function creates the custom rule-sets used by - // the ICU break iterator so it can extract only words used by the language. - // GetRuleSet() returns the rule-sets created in this function. - void SetDefaultLanguage(const std::string& language); - - // Returns a custom rule-set string used by the ICU break iterator. This class - // has two rule-sets, one splits a contraction and the other does not, so we - // can split a concaticated word (e.g. "seven-year-old") into words (e.g. - // "seven", "year", and "old") and check their spellings. The result stirng is - // encoded in UTF-16 since ICU needs UTF-16 strings. - base::string16 GetRuleSet(bool allow_contraction) const; - - // Outputs a character only if it is a word character. (Please read the - // comments in CreateRuleSets() why we need this function.) - bool OutputChar(UChar c, base::string16* output) const; - - private: - // Creates the rule-sets that return words possibly used by the given - // language. Unfortunately, these rule-sets are not perfect and have some - // false-positives. For example, they return combined accent marks even though - // we need English words only. We call OutputCharacter() to filter out such - // false-positive characters. - void CreateRuleSets(const std::string& language); - - // Outputs a character only if it is one used by the given language. These - // functions are called from OutputChar(). - bool OutputArabic(UChar c, base::string16* output) const; - bool OutputHangul(UChar c, base::string16* output) const; - bool OutputHebrew(UChar c, base::string16* output) const; - bool OutputDefault(UChar c, base::string16* output) const; - - // The custom rule-set strings used by ICU break iterator. Since it is not so - // easy to create custom rule-sets from an ISO language code, this class - // saves these rule-set strings created when we set the language. - base::string16 ruleset_allow_contraction_; - base::string16 ruleset_disallow_contraction_; - - // The script code used by this language. - UScriptCode script_code_; - - DISALLOW_COPY_AND_ASSIGN(SpellcheckCharAttribute); -}; - -// A class which extracts words that can be checked for spelling from a -// multi-language string. The ICU word-break iterator does not discard some -// punctuation characters attached to a word. For example, when we set a word -// "_hello_" to a word-break iterator, it just returns "_hello_". Neither does -// it discard characters not used by the language. For example, it returns -// Russian words even though we need English words only. To extract only the -// words that our spellchecker can check their spellings, this class uses custom -// rule-sets created by the SpellcheckCharAttribute class. Also, this class -// normalizes extracted words so our spellchecker can check the spellings of -// words that include ligatures, combined characters, full-width characters, -// etc. This class uses UTF-16 strings as its input and output strings since -// UTF-16 is the native encoding of ICU and avoid unnecessary conversions -// when changing the encoding of this string for our spellchecker. (Chrome can -// use two or more spellcheckers and we cannot assume their encodings.) -// The following snippet is an example that extracts words with this class. -// -// // Creates the language-specific attributes for US English. -// SpellcheckCharAttribute attribute; -// attribute.SetDefaultLanguage("en-US"); -// -// // Set up a SpellcheckWordIterator object which extracts English words, -// // and retrieve them. -// SpellcheckWordIterator iterator; -// base::string16 text(base::UTF8ToUTF16("this is a test.")); -// iterator.Initialize(&attribute, true); -// iterator.SetText(text.c_str(), text_.length()); -// -// base::string16 word; -// int offset; -// int length; -// while (iterator.GetNextWord(&word, &offset, &length)) { -// ... -// } -// -class SpellcheckWordIterator { - public: - SpellcheckWordIterator(); - ~SpellcheckWordIterator(); - - // Initializes a word-iterator object with the language-specific attribute. If - // we need to split contractions and concatenated words, call this function - // with its 'allow_contraction' parameter false. (This function uses lots of - // temporal memory to compile a custom word-break rule into an automaton.) - bool Initialize(const SpellcheckCharAttribute* attribute, - bool allow_contraction); - - // Returns whether this word iterator is initialized. - bool IsInitialized() const; - - // Set text to be iterated. (This text does not have to be NULL-terminated.) - // This function also resets internal state so we can reuse this iterator - // without calling Initialize(). - bool SetText(const base::char16* text, size_t length); - - // Retrieves a word (or a contraction), stores its copy to 'word_string', and - // stores the position and the length for input word to 'word_start'. Since - // this function normalizes the output word, the length of 'word_string' may - // be different from the 'word_length'. Therefore, when we call functions that - // changes the input text, such as string16::replace(), we need to use - // 'word_start' and 'word_length' as listed in the following snippet. - // - // while(iterator.GetNextWord(&word, &offset, &length)) - // text.replace(offset, length, word); - // - bool GetNextWord(base::string16* word_string, - int* word_start, - int* word_length); - - // Releases all the resources attached to this object. - void Reset(); - - private: - // Normalizes a non-terminated string returned from an ICU word-break - // iterator. A word returned from an ICU break iterator may include characters - // not supported by our spellchecker, e.g. ligatures, combining/ characters, - // full-width letters, etc. This function replaces such characters with - // alternative characters supported by our spellchecker. This function also - // calls SpellcheckWordIterator::OutputChar() to filter out false-positive - // characters. - bool Normalize(int input_start, - int input_length, - base::string16* output_string) const; - - // The pointer to the input string from which we are extracting words. - const base::char16* text_; - - // The language-specific attributes used for filtering out non-word - // characters. - const SpellcheckCharAttribute* attribute_; - - // The break iterator. - std::unique_ptr iterator_; - - DISALLOW_COPY_AND_ASSIGN(SpellcheckWordIterator); -}; - -#endif // CHROME_RENDERER_SPELLCHECKER_SPELLCHECK_WORDITERATOR_H_ diff --git a/chromium_src/chrome/renderer/tts_dispatcher.cc b/chromium_src/chrome/renderer/tts_dispatcher.cc deleted file mode 100644 index 0d3b97c845..0000000000 --- a/chromium_src/chrome/renderer/tts_dispatcher.cc +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "chrome/renderer/tts_dispatcher.h" - -#include "base/strings/utf_string_conversions.h" -#include "chrome/common/tts_messages.h" -#include "chrome/common/tts_utterance_request.h" -#include "content/public/renderer/render_thread.h" -#include "third_party/WebKit/public/platform/WebCString.h" -#include "third_party/WebKit/public/platform/WebSpeechSynthesisUtterance.h" -#include "third_party/WebKit/public/platform/WebSpeechSynthesisVoice.h" -#include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/platform/WebVector.h" - -using content::RenderThread; -using blink::WebSpeechSynthesizerClient; -using blink::WebSpeechSynthesisUtterance; -using blink::WebSpeechSynthesisVoice; -using blink::WebString; -using blink::WebVector; - -int TtsDispatcher::next_utterance_id_ = 1; - -TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client) - : synthesizer_client_(client) { - RenderThread::Get()->AddObserver(this); -} - -TtsDispatcher::~TtsDispatcher() { - RenderThread::Get()->RemoveObserver(this); -} - -bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) { - IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message) - IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList) - IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary) - IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary) - IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent) - IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted) - IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled) - IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred) - IPC_END_MESSAGE_MAP() - - // Always return false because there may be multiple TtsDispatchers - // and we want them all to have a chance to handle this message. - return false; -} - -void TtsDispatcher::updateVoiceList() { - RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList()); -} - -void TtsDispatcher::speak(const WebSpeechSynthesisUtterance& web_utterance) { - int id = next_utterance_id_++; - - utterance_id_map_[id] = web_utterance; - - TtsUtteranceRequest utterance; - utterance.id = id; - utterance.text = web_utterance.text().utf8(); - utterance.lang = web_utterance.lang().utf8(); - utterance.voice = web_utterance.voice().utf8(); - utterance.volume = web_utterance.volume(); - utterance.rate = web_utterance.rate(); - utterance.pitch = web_utterance.pitch(); - RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance)); -} - -void TtsDispatcher::pause() { - RenderThread::Get()->Send(new TtsHostMsg_Pause()); -} - -void TtsDispatcher::resume() { - RenderThread::Get()->Send(new TtsHostMsg_Resume()); -} - -void TtsDispatcher::cancel() { - RenderThread::Get()->Send(new TtsHostMsg_Cancel()); -} - -WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) { - base::hash_map::const_iterator iter = - utterance_id_map_.find(utterance_id); - if (iter == utterance_id_map_.end()) - return WebSpeechSynthesisUtterance(); - return iter->second; -} - -void TtsDispatcher::OnSetVoiceList(const std::vector& voices) { - WebVector out_voices(voices.size()); - for (size_t i = 0; i < voices.size(); ++i) { - out_voices[i] = WebSpeechSynthesisVoice(); - out_voices[i].setVoiceURI(WebString::fromUTF8(voices[i].voice_uri)); - out_voices[i].setName(WebString::fromUTF8(voices[i].name)); - out_voices[i].setLanguage(WebString::fromUTF8(voices[i].lang)); - out_voices[i].setIsLocalService(voices[i].local_service); - out_voices[i].setIsDefault(voices[i].is_default); - } - synthesizer_client_->setVoiceList(out_voices); -} - -void TtsDispatcher::OnDidStartSpeaking(int utterance_id) { - if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end()) - return; - - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - synthesizer_client_->didStartSpeaking(utterance); -} - -void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - synthesizer_client_->didFinishSpeaking(utterance); - utterance_id_map_.erase(utterance_id); -} - -void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - synthesizer_client_->didPauseSpeaking(utterance); -} - -void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - synthesizer_client_->didResumeSpeaking(utterance); -} - -void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) { - CHECK(char_index >= 0); - - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - synthesizer_client_->wordBoundaryEventOccurred( - utterance, static_cast(char_index)); -} - -void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) { - CHECK(char_index >= 0); - - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - synthesizer_client_->sentenceBoundaryEventOccurred( - utterance, static_cast(char_index)); -} - -void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) { - // Not supported yet. -} - -void TtsDispatcher::OnWasInterrupted(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - // The web speech API doesn't support "interrupted". - synthesizer_client_->didFinishSpeaking(utterance); - utterance_id_map_.erase(utterance_id); -} - -void TtsDispatcher::OnWasCancelled(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - // The web speech API doesn't support "cancelled". - synthesizer_client_->didFinishSpeaking(utterance); - utterance_id_map_.erase(utterance_id); -} - -void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id, - const std::string& error_message) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.isNull()) - return; - - // The web speech API doesn't support an error message. - synthesizer_client_->speakingErrorOccurred(utterance); - utterance_id_map_.erase(utterance_id); -} diff --git a/chromium_src/chrome/renderer/tts_dispatcher.h b/chromium_src/chrome/renderer/tts_dispatcher.h deleted file mode 100644 index 45db9751c8..0000000000 --- a/chromium_src/chrome/renderer/tts_dispatcher.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 CHROME_RENDERER_TTS_DISPATCHER_H_ -#define CHROME_RENDERER_TTS_DISPATCHER_H_ - -#include - -#include "base/containers/hash_tables.h" -#include "content/public/renderer/render_thread_observer.h" -#include "third_party/WebKit/public/platform/WebSpeechSynthesizer.h" -#include "third_party/WebKit/public/platform/WebSpeechSynthesizerClient.h" - -namespace IPC { -class Message; -} - -struct TtsVoice; - -// TtsDispatcher is a delegate for methods used by Blink for speech synthesis -// APIs. It's the complement of TtsDispatcherHost (owned by RenderViewHost). -// Each TtsDispatcher is owned by the WebSpeechSynthesizerClient in Blink; -// it registers itself to listen to IPC upon construction and unregisters -// itself when deleted. There can be multiple TtsDispatchers alive at once, -// so each one routes IPC messages to its WebSpeechSynthesizerClient only if -// the utterance id (which is globally unique) matches. -class TtsDispatcher - : public blink::WebSpeechSynthesizer, - public content::RenderThreadObserver { - public: - explicit TtsDispatcher(blink::WebSpeechSynthesizerClient* client); - - private: - virtual ~TtsDispatcher(); - - // RenderProcessObserver override. - virtual bool OnControlMessageReceived(const IPC::Message& message) override; - - // blink::WebSpeechSynthesizer implementation. - virtual void updateVoiceList() override; - virtual void speak(const blink::WebSpeechSynthesisUtterance& utterance) - override; - virtual void pause() override; - virtual void resume() override; - virtual void cancel() override; - - blink::WebSpeechSynthesisUtterance FindUtterance(int utterance_id); - - void OnSetVoiceList(const std::vector& voices); - void OnDidStartSpeaking(int utterance_id); - void OnDidFinishSpeaking(int utterance_id); - void OnDidPauseSpeaking(int utterance_id); - void OnDidResumeSpeaking(int utterance_id); - void OnWordBoundary(int utterance_id, int char_index); - void OnSentenceBoundary(int utterance_id, int char_index); - void OnMarkerEvent(int utterance_id, int char_index); - void OnWasInterrupted(int utterance_id); - void OnWasCancelled(int utterance_id); - void OnSpeakingErrorOccurred(int utterance_id, - const std::string& error_message); - - // The WebKit client class that we use to send events back to the JS world. - // Weak reference, this will be valid as long as this object exists. - blink::WebSpeechSynthesizerClient* synthesizer_client_; - - // Next utterance id, used to map response IPCs to utterance objects. - static int next_utterance_id_; - - // Map from id to utterance objects. - base::hash_map utterance_id_map_; - - DISALLOW_COPY_AND_ASSIGN(TtsDispatcher); -}; - -#endif // CHROME_RENDERER_TTS_DISPATCHER_H_ diff --git a/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc b/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc index c27d2c792a..033a3fdd57 100644 --- a/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc +++ b/chromium_src/chrome/utility/importer/external_process_importer_bridge.cc @@ -34,7 +34,6 @@ const int kNumBookmarksToSend = 100; const int kNumHistoryRowsToSend = 100; const int kNumFaviconsToSend = 100; const int kNumAutofillFormDataToSend = 100; -const int kNumCookiesToSend = 100; } // namespace @@ -178,30 +177,6 @@ void ExternalProcessImporterBridge::SetAutofillFormData( DCHECK_EQ(0, autofill_form_data_entries_left); } -void ExternalProcessImporterBridge::SetCookies( - const std::vector& cookies) { - Send(new ProfileImportProcessHostMsg_NotifyCookiesImportStart( - cookies.size())); - - // |cookies_left| is required for the checks below as Windows has a - // Debug bounds-check which prevents pushing an iterator beyond its end() - // (i.e., |it + 2 < s.end()| crashes in debug mode if |i + 1 == s.end()|). - int cookies_left = cookies.end() - cookies.begin(); - for (std::vector::const_iterator it = - cookies.begin(); it < cookies.end();) { - std::vector cookies_group; - std::vector::const_iterator end_group = - it + std::min(cookies_left, kNumCookiesToSend); - cookies_group.assign(it, end_group); - - Send(new ProfileImportProcessHostMsg_NotifyCookiesImportGroup( - cookies_group)); - cookies_left -= end_group - it; - it = end_group; - } - DCHECK_EQ(0, cookies_left); -} - void ExternalProcessImporterBridge::NotifyStarted() { Send(new ProfileImportProcessHostMsg_Import_Started()); } diff --git a/chromium_src/chrome/utility/importer/external_process_importer_bridge.h b/chromium_src/chrome/utility/importer/external_process_importer_bridge.h index bd4626293a..82d6602655 100644 --- a/chromium_src/chrome/utility/importer/external_process_importer_bridge.h +++ b/chromium_src/chrome/utility/importer/external_process_importer_bridge.h @@ -83,8 +83,6 @@ class ExternalProcessImporterBridge : public ImporterBridge { void SetAutofillFormData( const std::vector& entries) override; - void SetCookies(const std::vector& cookies) override; - void NotifyStarted() override; void NotifyItemStarted(importer::ImportItem item) override; void NotifyItemEnded(importer::ImportItem item) override; diff --git a/chromium_src/chrome/utility/importer/firefox_importer.cc b/chromium_src/chrome/utility/importer/firefox_importer.cc index 73958a1e17..fbcee23bd6 100644 --- a/chromium_src/chrome/utility/importer/firefox_importer.cc +++ b/chromium_src/chrome/utility/importer/firefox_importer.cc @@ -155,11 +155,6 @@ void FirefoxImporter::StartImport(const importer::SourceProfile& source_profile, ImportAutofillFormData(); bridge_->NotifyItemEnded(importer::AUTOFILL_FORM_DATA); } - if ((items & importer::COOKIES) && !cancelled()) { - bridge_->NotifyItemStarted(importer::COOKIES); - ImportCookies(); - bridge_->NotifyItemEnded(importer::COOKIES); - } bridge_->NotifyEnded(); } @@ -346,7 +341,7 @@ void FirefoxImporter::ImportBookmarks() { } } - STLDeleteElements(&list); + base::STLDeleteElements(&list); // Write into profile. if (!bookmarks.empty() && !cancelled()) { @@ -548,50 +543,6 @@ void FirefoxImporter::GetSearchEnginesXMLData( } } -void FirefoxImporter::ImportCookies() { - base::FilePath file = source_path_.AppendASCII("cookies.sqlite"); - if (!base::PathExists(file)) - return; - - sql::Connection db; - if (!db.Open(file)) - return; - - const char query[] = - "SELECT baseDomain, name, value, host, path, expiry, isSecure, " - "isHttpOnly FROM moz_cookies"; - - sql::Statement s(db.GetUniqueStatement(query)); - - std::vector cookies; - while (s.Step() && !cancelled()) { - ImportedCookieEntry cookie; - base::string16 domain(base::UTF8ToUTF16(".")); - domain.append(s.ColumnString16(0)); - base::string16 host; - if (s.ColumnString16(3)[0] == '.') { - host.append(base::UTF8ToUTF16("*")); - host.append(s.ColumnString16(3)); - } else { - host = s.ColumnString16(3); - } - cookie.domain = domain; - cookie.name = s.ColumnString16(1); - cookie.value = s.ColumnString16(2); - cookie.host = host; - cookie.path = s.ColumnString16(4); - cookie.expiry_date = - base::Time::FromDoubleT(s.ColumnInt64(5)); - cookie.secure = s.ColumnBool(6); - cookie.httponly = s.ColumnBool(7); - - cookies.push_back(cookie); - } - - if (!cookies.empty() && !cancelled()) - bridge_->SetCookies(cookies); -} - void FirefoxImporter::GetSearchEnginesXMLDataFromJSON( std::vector* search_engine_data) { // search-metadata.json contains keywords for search engines. This diff --git a/chromium_src/chrome/utility/importer/firefox_importer.h b/chromium_src/chrome/utility/importer/firefox_importer.h index 00055f8fc1..c86b780e49 100644 --- a/chromium_src/chrome/utility/importer/firefox_importer.h +++ b/chromium_src/chrome/utility/importer/firefox_importer.h @@ -58,7 +58,6 @@ class FirefoxImporter : public Importer { // defined in browserconfig.properties. void ImportHomepage(); void ImportAutofillFormData(); - void ImportCookies(); void GetSearchEnginesXMLData(std::vector* search_engine_data); void GetSearchEnginesXMLDataFromJSON( std::vector* search_engine_data); diff --git a/chromium_src/chrome/utility/importer/nss_decryptor.cc b/chromium_src/chrome/utility/importer/nss_decryptor.cc deleted file mode 100644 index af7e8478e1..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor.cc +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) 2012 The Chromium 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 "chrome/utility/importer/nss_decryptor.h" - -#include - -#include -#include -#include - -#include "base/base64.h" -#include "base/files/file_util.h" -#include "base/json/json_reader.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "components/autofill/core/common/password_form.h" -#include "sql/connection.h" -#include "sql/statement.h" - -#if defined(USE_NSS_CERTS) -#include -#include -#endif // defined(USE_NSS_CERTS) - -// This method is based on some Firefox code in -// security/manager/ssl/src/nsSDR.cpp -// The license block is: - -/* ***** 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 the Netscape security libraries. -* -* The Initial Developer of the Original Code is -* Netscape Communications Corporation. -* Portions created by the Initial Developer are Copyright (C) 1994-2000 -* 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 ***** */ - -// Use this structure to store unprocessed information extracted from -// Firefox's password file. -struct FirefoxRawPasswordInfo { - std::string host; - std::string realm; - base::string16 username_element; - base::string16 password_element; - std::string encrypted_username; - std::string encrypted_password; - std::string form_action; -}; - -namespace { - -autofill::PasswordForm CreateBlacklistPasswordForm( - const std::string& blacklist_host) { - GURL::Replacements rep; - rep.ClearQuery(); - rep.ClearRef(); - rep.ClearUsername(); - rep.ClearPassword(); - - autofill::PasswordForm form; - form.origin = GURL(blacklist_host).ReplaceComponents(rep); - form.signon_realm = form.origin.GetOrigin().spec(); - form.blacklisted_by_user = true; - return form; -} - -} // namespace - -base::string16 NSSDecryptor::Decrypt(const std::string& crypt) const { - // Do nothing if NSS is not loaded. - if (!is_nss_initialized_) - return base::string16(); - - if (crypt.empty()) - return base::string16(); - - // The old style password is encoded in base64. They are identified - // by a leading '~'. Otherwise, we should decrypt the text. - std::string plain; - if (crypt[0] != '~') { - std::string decoded_data; - if (!base::Base64Decode(crypt, &decoded_data)) - return base::string16(); - PK11SlotInfo* slot = GetKeySlotForDB(); - SECStatus result = PK11_Authenticate(slot, PR_TRUE, NULL); - if (result != SECSuccess) { - FreeSlot(slot); - return base::string16(); - } - - SECItem request; - request.data = reinterpret_cast( - const_cast(decoded_data.data())); - request.len = static_cast(decoded_data.size()); - SECItem reply; - reply.data = NULL; - reply.len = 0; -#if defined(USE_NSS_CERTS) - result = PK11SDR_DecryptWithSlot(slot, &request, &reply, NULL); -#else - result = PK11SDR_Decrypt(&request, &reply, NULL); -#endif // defined(USE_NSS_CERTS) - if (result == SECSuccess) - plain.assign(reinterpret_cast(reply.data), reply.len); - - SECITEM_FreeItem(&reply, PR_FALSE); - FreeSlot(slot); - } else { - // Deletes the leading '~' before decoding. - if (!base::Base64Decode(crypt.substr(1), &plain)) - return base::string16(); - } - - return base::UTF8ToUTF16(plain); -} - -// There are three versions of password files. They store saved user -// names and passwords. -// References: -// http://kb.mozillazine.org/Signons.txt -// http://kb.mozillazine.org/Signons2.txt -// http://kb.mozillazine.org/Signons3.txt -void NSSDecryptor::ParseSignons(const base::FilePath& signon_file, - std::vector* forms) { - forms->clear(); - - std::string content; - base::ReadFileToString(signon_file, &content); - - // Splits the file content into lines. - std::vector lines = base::SplitString( - content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - - // The first line is the file version. We skip the unknown versions. - if (lines.empty()) - return; - int version; - if (lines[0] == "#2c") - version = 1; - else if (lines[0] == "#2d") - version = 2; - else if (lines[0] == "#2e") - version = 3; - else - return; - - // Reads never-saved list. Domains are stored one per line. - size_t i; - for (i = 1; i < lines.size() && lines[i].compare(".") != 0; ++i) - forms->push_back(CreateBlacklistPasswordForm(lines[i])); - ++i; - - // Reads saved passwords. The information is stored in blocks - // seperated by lines that only contain a dot. We find a block - // by the seperator and parse them one by one. - while (i < lines.size()) { - size_t begin = i; - size_t end = i + 1; - while (end < lines.size() && lines[end].compare(".") != 0) - ++end; - i = end + 1; - - // A block has at least five lines. - if (end - begin < 5) - continue; - - FirefoxRawPasswordInfo raw_password_info; - - // The first line is the site URL. - // For HTTP authentication logins, the URL may contain http realm, - // which will be in bracket: - // sitename:8080 (realm) - const char kRealmBracketBegin[] = " ("; - const char kRealmBracketEnd[] = ")"; - if (lines[begin].find(kRealmBracketBegin) != std::string::npos) { - size_t start = lines[begin].find(kRealmBracketBegin); - raw_password_info.host = lines[begin].substr(0, start); - start += std::string(kRealmBracketBegin).size(); - size_t end = lines[begin].rfind(kRealmBracketEnd); - raw_password_info.realm = lines[begin].substr(start, end - start); - } else { - raw_password_info.host = lines[begin]; - } - - ++begin; - - // There may be multiple username/password pairs for this site. - // In this case, they are saved in one block without a seperated - // line (contains a dot). - while (begin + 4 < end) { - // The user name. - raw_password_info.username_element = base::UTF8ToUTF16(lines[begin++]); - raw_password_info.encrypted_username = lines[begin++]; - // The element name has a leading '*'. - if (lines[begin].at(0) == '*') { - raw_password_info.password_element = - base::UTF8ToUTF16(lines[begin++].substr(1)); - raw_password_info.encrypted_password = lines[begin++]; - } else { - // Maybe the file is bad, we skip to next block. - break; - } - // The action attribute from the form element. This line exists - // in versin 2 or above. - if (version >= 2) { - if (begin < end) - raw_password_info.form_action = lines[begin]; - ++begin; - } - // Version 3 has an extra line for further use. - if (version == 3) - ++begin; - - autofill::PasswordForm form; - if (CreatePasswordFormFromRawInfo(raw_password_info, &form)) - forms->push_back(form); - } - } -} - -bool NSSDecryptor::ReadAndParseSignons( - const base::FilePath& sqlite_file, - std::vector* forms) { - sql::Connection db; - if (!db.Open(sqlite_file)) - return false; - - const char query[] = "SELECT hostname FROM moz_disabledHosts"; - sql::Statement s(db.GetUniqueStatement(query)); - if (!s.is_valid()) - return false; - - // Read domains for which passwords are never saved. - while (s.Step()) - forms->push_back(CreateBlacklistPasswordForm(s.ColumnString(0))); - - const char query2[] = "SELECT hostname, httpRealm, formSubmitURL, " - "usernameField, passwordField, encryptedUsername, " - "encryptedPassword FROM moz_logins"; - - sql::Statement s2(db.GetUniqueStatement(query2)); - if (!s2.is_valid()) - return false; - - while (s2.Step()) { - FirefoxRawPasswordInfo raw_password_info; - raw_password_info.host = s2.ColumnString(0); - raw_password_info.realm = s2.ColumnString(1); - // The user name, password and action. - raw_password_info.username_element = s2.ColumnString16(3); - raw_password_info.encrypted_username = s2.ColumnString(5); - raw_password_info.password_element = s2.ColumnString16(4); - raw_password_info.encrypted_password = s2.ColumnString(6); - raw_password_info.form_action = s2.ColumnString(2); - autofill::PasswordForm form; - if (CreatePasswordFormFromRawInfo(raw_password_info, &form)) - forms->push_back(form); - } - return true; -} - -bool NSSDecryptor::ReadAndParseLogins( - const base::FilePath& json_file, - std::vector* forms) { - std::string json_content; - base::ReadFileToString(json_file, &json_content); - std::unique_ptr parsed_json( - base::JSONReader::Read(json_content)); - const base::DictionaryValue* password_dict; - const base::ListValue* password_list; - const base::ListValue* blacklist_domains; - if (!parsed_json || !parsed_json->GetAsDictionary(&password_dict)) - return false; - - if (password_dict->GetList("disabledHosts", &blacklist_domains)) { - for (const auto& value : *blacklist_domains) { - std::string disabled_host; - if (!value->GetAsString(&disabled_host)) - continue; - forms->push_back(CreateBlacklistPasswordForm(disabled_host)); - } - } - - if (password_dict->GetList("logins", &password_list)) { - for (const auto& value : *password_list) { - const base::DictionaryValue* password_detail; - if (!value->GetAsDictionary(&password_detail)) - continue; - - FirefoxRawPasswordInfo raw_password_info; - password_detail->GetString("hostname", &raw_password_info.host); - password_detail->GetString("usernameField", - &raw_password_info.username_element); - password_detail->GetString("passwordField", - &raw_password_info.password_element); - password_detail->GetString("encryptedUsername", - &raw_password_info.encrypted_username); - password_detail->GetString("encryptedPassword", - &raw_password_info.encrypted_password); - password_detail->GetString("formSubmitURL", - &raw_password_info.form_action); - password_detail->GetString("httpRealm", &raw_password_info.realm); - - autofill::PasswordForm form; - if (CreatePasswordFormFromRawInfo(raw_password_info, &form)) - forms->push_back(form); - } - } - - return true; -} - -bool NSSDecryptor::CreatePasswordFormFromRawInfo( - const FirefoxRawPasswordInfo& raw_password_info, - autofill::PasswordForm* form) { - GURL::Replacements rep; - rep.ClearQuery(); - rep.ClearRef(); - rep.ClearUsername(); - rep.ClearPassword(); - - GURL url; - if (!raw_password_info.realm.empty() && - raw_password_info.host.find("://") == std::string::npos) { - // Assume HTTP for forms with non-empty realm and no scheme in hostname. - url = GURL("http://" + raw_password_info.host); - } else { - url = GURL(raw_password_info.host); - } - // Skip this login if the URL is not valid. - if (!url.is_valid()) - return false; - - form->origin = url.ReplaceComponents(rep); - form->signon_realm = form->origin.GetOrigin().spec(); - if (!raw_password_info.realm.empty()) { - form->signon_realm += raw_password_info.realm; - // Non-empty realm indicates that it's not html form authentication entry. - // Extracted data doesn't allow us to distinguish basic_auth entry from - // digest_auth entry, so let's assume basic_auth. - form->scheme = autofill::PasswordForm::SCHEME_BASIC; - } - form->ssl_valid = form->origin.SchemeIsCryptographic(); - form->username_element = raw_password_info.username_element; - form->username_value = Decrypt(raw_password_info.encrypted_username); - form->password_element = raw_password_info.password_element; - form->password_value = Decrypt(raw_password_info.encrypted_password); - form->action = GURL(raw_password_info.form_action).ReplaceComponents(rep); - - return true; -} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor.h b/chromium_src/chrome/utility/importer/nss_decryptor.h deleted file mode 100644 index d53e640b51..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor.h +++ /dev/null @@ -1,20 +0,0 @@ -// 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 CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_ -#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_ - -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#include "chrome/utility/importer/nss_decryptor_mac.h" -#elif defined(OS_WIN) -#include "chrome/utility/importer/nss_decryptor_win.h" -#elif defined(USE_NSS_CERTS) -#include "chrome/utility/importer/nss_decryptor_system_nss.h" -#else -#error NSSDecryptor not implemented. -#endif - -#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_mac.h b/chromium_src/chrome/utility/importer/nss_decryptor_mac.h deleted file mode 100644 index 941ce5cf58..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor_mac.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_MAC_H_ -#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_MAC_H_ - -#include -#include - -#include "base/macros.h" -#include "base/strings/string16.h" - -namespace base { -class FilePath; -} - -// The following declarations of functions and types are from Firefox -// NSS library. -// source code: -// security/nss/lib/util/seccomon.h -// security/nss/lib/nss/nss.h -// The license block is: - -/* ***** 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 the Netscape security libraries. -* -* The Initial Developer of the Original Code is -* Netscape Communications Corporation. -* Portions created by the Initial Developer are Copyright (C) 1994-2000 -* 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 ***** */ - -enum SECItemType { - siBuffer = 0, - siClearDataBuffer = 1, - siCipherDataBuffer = 2, - siDERCertBuffer = 3, - siEncodedCertBuffer = 4, - siDERNameBuffer = 5, - siEncodedNameBuffer = 6, - siAsciiNameString = 7, - siAsciiString = 8, - siDEROID = 9, - siUnsignedInteger = 10, - siUTCTime = 11, - siGeneralizedTime = 12 -}; - -struct SECItem { - SECItemType type; - unsigned char *data; - unsigned int len; -}; - -enum SECStatus { - SECWouldBlock = -2, - SECFailure = -1, - SECSuccess = 0 -}; - -typedef int PRBool; -#define PR_TRUE 1 -#define PR_FALSE 0 - -typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; - -typedef struct PK11SlotInfoStr PK11SlotInfo; - -typedef SECStatus (*NSSInitFunc)(const char *configdir); -typedef SECStatus (*NSSShutdownFunc)(void); -typedef PK11SlotInfo* (*PK11GetInternalKeySlotFunc)(void); -typedef void (*PK11FreeSlotFunc)(PK11SlotInfo *slot); -typedef SECStatus (*PK11CheckUserPasswordFunc)(PK11SlotInfo *slot, char *pw); -typedef SECStatus - (*PK11AuthenticateFunc)(PK11SlotInfo *slot, PRBool loadCerts, void *wincx); -typedef SECStatus - (*PK11SDRDecryptFunc)(SECItem *data, SECItem *result, void *cx); -typedef void (*SECITEMFreeItemFunc)(SECItem *item, PRBool free_it); -typedef void (*PLArenaFinishFunc)(void); -typedef PRStatus (*PRCleanupFunc)(void); - -struct FirefoxRawPasswordInfo; - -namespace autofill { -struct PasswordForm; -} - -// A wrapper for Firefox NSS decrypt component. -class NSSDecryptor { - public: - NSSDecryptor() - : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL), - PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL), - PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL), - is_nss_initialized_(false) {} - ~NSSDecryptor(); - - // Initializes NSS if it hasn't already been initialized. - bool Init(const base::FilePath& dll_path, const base::FilePath& db_path); - - // Decrypts Firefox stored passwords. Before using this method, - // make sure Init() returns true. - base::string16 Decrypt(const std::string& crypt) const; - - // Parses the Firefox password file content, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - void ParseSignons(const base::FilePath& signon_file, - std::vector* forms); - - // Reads and parses the Firefox password sqlite db, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - bool ReadAndParseSignons(const base::FilePath& sqlite_file, - std::vector* forms); - - // Reads and parses the Firefox password file logins.json, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - bool ReadAndParseLogins(const base::FilePath& json_file, - std::vector* forms); - - private: - PK11SlotInfo* GetKeySlotForDB() const { return PK11_GetInternalKeySlot(); } - - void FreeSlot(PK11SlotInfo* slot) const { PK11_FreeSlot(slot); } - - // Turns unprocessed information extracted from Firefox's password file - // into PasswordForm. - bool CreatePasswordFormFromRawInfo( - const FirefoxRawPasswordInfo& raw_password_info, - autofill::PasswordForm* form); - - // Methods in Firefox security components. - NSSInitFunc NSS_Init; - NSSShutdownFunc NSS_Shutdown; - PK11GetInternalKeySlotFunc PK11_GetInternalKeySlot; - PK11CheckUserPasswordFunc PK11_CheckUserPassword; - PK11FreeSlotFunc PK11_FreeSlot; - PK11AuthenticateFunc PK11_Authenticate; - PK11SDRDecryptFunc PK11SDR_Decrypt; - SECITEMFreeItemFunc SECITEM_FreeItem; - - // True if NSS_Init() has been called - bool is_nss_initialized_; - - DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); -}; - -#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_MAC_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_mac.mm b/chromium_src/chrome/utility/importer/nss_decryptor_mac.mm deleted file mode 100644 index c01e96e11b..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor_mac.mm +++ /dev/null @@ -1,71 +0,0 @@ -// 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 - -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/strings/sys_string_conversions.h" - -#include "chrome/common/importer/firefox_importer_utils.h" -#include "chrome/utility/importer/nss_decryptor_mac.h" - -// Important!! : On OS X the nss3 libraries are compiled with depedencies -// on one another, referenced using dyld's @executable_path directive. -// To make a long story short in order to get the libraries to load, dyld's -// fallback path needs to be set to the directory containing the libraries. -// To do so, the process this function runs in must have the -// DYLD_FALLBACK_LIBRARY_PATH set on startup to said directory. -bool NSSDecryptor::Init(const base::FilePath& dll_path, - const base::FilePath& db_path) { - if (getenv("DYLD_FALLBACK_LIBRARY_PATH") == NULL) { - LOG(ERROR) << "DYLD_FALLBACK_LIBRARY_PATH variable not set"; - return false; - } - base::FilePath nss3_path = dll_path.Append("libnss3.dylib"); - - void* nss_3_lib = dlopen(nss3_path.value().c_str(), RTLD_LAZY); - if (!nss_3_lib) { - LOG(ERROR) << "Failed to load nss3 lib" << dlerror(); - return false; - } - - NSS_Init = (NSSInitFunc)dlsym(nss_3_lib, "NSS_Init"); - NSS_Shutdown = (NSSShutdownFunc)dlsym(nss_3_lib, "NSS_Shutdown"); - PK11_GetInternalKeySlot = - (PK11GetInternalKeySlotFunc)dlsym(nss_3_lib, "PK11_GetInternalKeySlot"); - PK11_CheckUserPassword = - (PK11CheckUserPasswordFunc)dlsym(nss_3_lib, "PK11_CheckUserPassword"); - PK11_FreeSlot = (PK11FreeSlotFunc)dlsym(nss_3_lib, "PK11_FreeSlot"); - PK11_Authenticate = - (PK11AuthenticateFunc)dlsym(nss_3_lib, "PK11_Authenticate"); - PK11SDR_Decrypt = (PK11SDRDecryptFunc)dlsym(nss_3_lib, "PK11SDR_Decrypt"); - SECITEM_FreeItem = (SECITEMFreeItemFunc)dlsym(nss_3_lib, "SECITEM_FreeItem"); - - if (!NSS_Init || !NSS_Shutdown || !PK11_GetInternalKeySlot || - !PK11_CheckUserPassword || !PK11_FreeSlot || !PK11_Authenticate || - !PK11SDR_Decrypt || !SECITEM_FreeItem) { - LOG(ERROR) << "NSS3 importer couldn't find entry points"; - return false; - } - - SECStatus result = NSS_Init(db_path.value().c_str()); - - if (result != SECSuccess) { - LOG(ERROR) << "NSS_Init Failed returned: " << result; - return false; - } - - is_nss_initialized_ = true; - return true; -} - -NSSDecryptor::~NSSDecryptor() { - if (NSS_Shutdown && is_nss_initialized_) { - NSS_Shutdown(); - is_nss_initialized_ = false; - } -} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc b/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc deleted file mode 100644 index 9d5d2486fb..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.cc +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 "chrome/utility/importer/nss_decryptor_system_nss.h" - -#include -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/strings/stringprintf.h" -#include "base/strings/sys_string_conversions.h" -#include "crypto/nss_util.h" - -NSSDecryptor::NSSDecryptor() : is_nss_initialized_(false), db_slot_(NULL) {} -NSSDecryptor::~NSSDecryptor() { - if (db_slot_) { - // Deliberately leave the user db open, just in case we need to open more - // than one, because there's an NSS bug with reopening user dbs. - // https://bugzilla.mozilla.org/show_bug.cgi?id=506140 - // SECMOD_CloseUserDB(db_slot_); - PK11_FreeSlot(db_slot_); - } -} - -bool NSSDecryptor::Init(const base::FilePath& dll_path, - const base::FilePath& db_path) { - crypto::EnsureNSSInit(); - is_nss_initialized_ = true; - const std::string modspec = - base::StringPrintf( - "configDir='%s' tokenDescription='Firefox NSS database' " - "flags=readOnly", - db_path.value().c_str()); - db_slot_ = SECMOD_OpenUserDB(modspec.c_str()); - return db_slot_ != NULL; -} - -// This method is based on some NSS code in -// security/nss/lib/pk11wrap/pk11sdr.c, CVS revision 1.22 -// This code is copied because the implementation assumes the use of the -// internal key slot for decryption, but we need to use another slot. -// The license block is: -/* ***** 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 the Netscape security libraries. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1994-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * thayes@netscape.com - * - * 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 ***** */ - -/* - * Data structure and template for encoding the result of an SDR operation - * This is temporary. It should include the algorithm ID of the encryption - * mechanism - */ -struct SDRResult -{ - SECItem keyid; - SECAlgorithmID alg; - SECItem data; -}; -typedef struct SDRResult SDRResult; - -static SEC_ASN1Template g_template[] = { - { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, - { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, - { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), - SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, - { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, - { 0 } -}; - -static SECStatus -unpadBlock(SECItem *data, int blockSize, SECItem *result) -{ - SECStatus rv = SECSuccess; - int padLength; - int i; - - result->data = 0; - result->len = 0; - - /* Remove the padding from the end if the input data */ - if (data->len == 0 || data->len % blockSize != 0) { - rv = SECFailure; - goto loser; - } - - padLength = data->data[data->len-1]; - if (padLength > blockSize) { rv = SECFailure; goto loser; } - - /* verify padding */ - for (i = data->len - padLength; static_cast(i) < data->len; i++) { - if (data->data[i] != padLength) { - rv = SECFailure; - goto loser; - } - } - - result->len = data->len - padLength; - result->data = (unsigned char *)PORT_Alloc(result->len); - if (!result->data) { rv = SECFailure; goto loser; } - - PORT_Memcpy(result->data, data->data, result->len); - - if (padLength < 2) { - return SECWouldBlock; - } - -loser: - return rv; -} - -/* decrypt a block */ -static SECStatus -pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, - CK_MECHANISM_TYPE type, PK11SymKey *key, - SECItem *params, SECItem *in, SECItem *result) -{ - PK11Context *ctx = 0; - SECItem paddedResult; - SECStatus rv; - - paddedResult.len = 0; - paddedResult.data = 0; - - ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); - if (!ctx) { rv = SECFailure; goto loser; } - - paddedResult.len = in->len; - paddedResult.data = static_cast( - PORT_ArenaAlloc(arena, paddedResult.len)); - - rv = PK11_CipherOp(ctx, paddedResult.data, - (int*)&paddedResult.len, paddedResult.len, - in->data, in->len); - if (rv != SECSuccess) goto loser; - - PK11_Finalize(ctx); - - /* Remove the padding */ - rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); - if (rv) goto loser; - -loser: - if (ctx) PK11_DestroyContext(ctx, PR_TRUE); - return rv; -} - -SECStatus NSSDecryptor::PK11SDR_DecryptWithSlot( - PK11SlotInfo* slot, SECItem* data, SECItem* result, void* cx) const { - SECStatus rv = SECSuccess; - PK11SymKey *key = 0; - CK_MECHANISM_TYPE type; - SDRResult sdrResult; - SECItem *params = 0; - SECItem possibleResult = { siBuffer, NULL, 0 }; - PLArenaPool *arena = 0; - - arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); - if (!arena) { rv = SECFailure; goto loser; } - - /* Decode the incoming data */ - memset(&sdrResult, 0, sizeof sdrResult); - rv = SEC_QuickDERDecodeItem(arena, &sdrResult, g_template, data); - if (rv != SECSuccess) goto loser; /* Invalid format */ - - /* Get the parameter values from the data */ - params = PK11_ParamFromAlgid(&sdrResult.alg); - if (!params) { rv = SECFailure; goto loser; } - - /* Use triple-DES (Should look up the algorithm) */ - type = CKM_DES3_CBC; - key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); - if (!key) { - rv = SECFailure; - } else { - rv = pk11Decrypt(slot, arena, type, key, params, - &sdrResult.data, result); - } - - /* - * if the pad value was too small (1 or 2), then it's statistically - * 'likely' that (1 in 256) that we may not have the correct key. - * Check the other keys for a better match. If we find none, use - * this result. - */ - if (rv == SECWouldBlock) - possibleResult = *result; - - /* - * handle the case where your key indicies may have been broken - */ - if (rv != SECSuccess) { - PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); - PK11SymKey *testKey = NULL; - PK11SymKey *nextKey = NULL; - - for (testKey = keyList; testKey; - testKey = PK11_GetNextSymKey(testKey)) { - rv = pk11Decrypt(slot, arena, type, testKey, params, - &sdrResult.data, result); - if (rv == SECSuccess) - break; - - /* found a close match. If it's our first remember it */ - if (rv == SECWouldBlock) { - if (possibleResult.data) { - /* this is unlikely but possible. If we hit this condition, - * we have no way of knowing which possibility to prefer. - * in this case we just match the key the application - * thought was the right one */ - SECITEM_ZfreeItem(result, PR_FALSE); - } else { - possibleResult = *result; - } - } - } - - /* free the list */ - for (testKey = keyList; testKey; testKey = nextKey) { - nextKey = PK11_GetNextSymKey(testKey); - PK11_FreeSymKey(testKey); - } - } - - /* we didn't find a better key, use the one with a small pad value */ - if ((rv != SECSuccess) && (possibleResult.data)) { - *result = possibleResult; - possibleResult.data = NULL; - rv = SECSuccess; - } - - loser: - if (arena) PORT_FreeArena(arena, PR_TRUE); - if (key) PK11_FreeSymKey(key); - if (params) SECITEM_ZfreeItem(params, PR_TRUE); - if (possibleResult.data) SECITEM_ZfreeItem(&possibleResult, PR_FALSE); - - return rv; -} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h b/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h deleted file mode 100644 index 8edd95f0f2..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor_system_nss.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_SYSTEM_NSS_H_ -#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_SYSTEM_NSS_H_ - -#include -#include -#include - -#include "base/macros.h" -#include "base/strings/string16.h" - -struct FirefoxRawPasswordInfo; - -namespace autofill { -struct PasswordForm; -} - -namespace base { -class FilePath; -} - -// A wrapper for Firefox NSS decrypt component. -class NSSDecryptor { - public: - NSSDecryptor(); - ~NSSDecryptor(); - - // Initializes NSS if it hasn't already been initialized. - bool Init(const base::FilePath& dll_path, const base::FilePath& db_path); - - // Decrypts Firefox stored passwords. Before using this method, - // make sure Init() returns true. - base::string16 Decrypt(const std::string& crypt) const; - - // Parses the Firefox password file content, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - void ParseSignons(const base::FilePath& signon_file, - std::vector* forms); - - // Reads and parses the Firefox password sqlite db, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - bool ReadAndParseSignons(const base::FilePath& sqlite_file, - std::vector* forms); - - // Reads and parses the Firefox password file logins.json, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - bool ReadAndParseLogins(const base::FilePath& json_file, - std::vector* forms); - - private: - // Does not actually free the slot, since we'll free it when NSSDecryptor is - // destroyed. - void FreeSlot(PK11SlotInfo* slot) const {} - - // Turns unprocessed information extracted from Firefox's password file - // into PasswordForm. - bool CreatePasswordFormFromRawInfo( - const FirefoxRawPasswordInfo& raw_password_info, - autofill::PasswordForm* form); - - PK11SlotInfo* GetKeySlotForDB() const { return db_slot_; } - - SECStatus PK11SDR_DecryptWithSlot( - PK11SlotInfo* slot, SECItem* data, SECItem* result, void* cx) const; - - bool is_nss_initialized_; - PK11SlotInfo* db_slot_; - - DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); -}; - -#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_SYSTEM_NSS_H_ diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_win.cc b/chromium_src/chrome/utility/importer/nss_decryptor_win.cc deleted file mode 100644 index 587d3fe929..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor_win.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 "chrome/utility/importer/nss_decryptor_win.h" - -#include "base/files/file_path.h" -#include "base/strings/sys_string_conversions.h" - -namespace { - -typedef BOOL (WINAPI* SetDllDirectoryFunc)(LPCTSTR lpPathName); - -// A helper class whose destructor calls SetDllDirectory(NULL) to undo the -// effects of a previous SetDllDirectory call. -class SetDllDirectoryCaller { - public: - explicit SetDllDirectoryCaller() : func_(NULL) { } - - ~SetDllDirectoryCaller() { - if (func_) - func_(NULL); - } - - // Sets the SetDllDirectory function pointer to activates this object. - void set_func(SetDllDirectoryFunc func) { func_ = func; } - - private: - SetDllDirectoryFunc func_; -}; - -} // namespace - -// static -const wchar_t NSSDecryptor::kNSS3Library[] = L"nss3.dll"; -const wchar_t NSSDecryptor::kSoftokn3Library[] = L"softokn3.dll"; -const wchar_t NSSDecryptor::kPLDS4Library[] = L"plds4.dll"; -const wchar_t NSSDecryptor::kNSPR4Library[] = L"nspr4.dll"; - -bool NSSDecryptor::Init(const base::FilePath& dll_path, - const base::FilePath& db_path) { - // We call SetDllDirectory to work around a Purify bug (GetModuleHandle - // fails inside Purify under certain conditions). SetDllDirectory only - // exists on Windows XP SP1 or later, so we look up its address at run time. - HMODULE kernel32_dll = GetModuleHandle(L"kernel32.dll"); - if (kernel32_dll == NULL) - return false; - SetDllDirectoryFunc set_dll_directory = - (SetDllDirectoryFunc)GetProcAddress(kernel32_dll, "SetDllDirectoryW"); - SetDllDirectoryCaller caller; - - if (set_dll_directory != NULL) { - if (!set_dll_directory(dll_path.value().c_str())) - return false; - caller.set_func(set_dll_directory); - nss3_dll_ = LoadLibrary(kNSS3Library); - if (nss3_dll_ == NULL) - return false; - } else { - // Fall back on LoadLibraryEx if SetDllDirectory isn't available. We - // actually prefer this method because it doesn't change the DLL search - // path, which is a process-wide property. - base::FilePath path = dll_path.Append(kNSS3Library); - nss3_dll_ = LoadLibraryEx(path.value().c_str(), NULL, - LOAD_WITH_ALTERED_SEARCH_PATH); - if (nss3_dll_ == NULL) - return false; - - // Firefox 2 uses NSS 3.11. Firefox 3 uses NSS 3.12. NSS 3.12 has two - // changes in its DLLs: - // 1. nss3.dll is not linked with softokn3.dll at build time, but rather - // loads softokn3.dll using LoadLibrary in NSS_Init. - // 2. softokn3.dll has a new dependency sqlite3.dll. - // NSS_Init's LoadLibrary call has trouble finding sqlite3.dll. To help - // it out, we preload softokn3.dll using LoadLibraryEx with the - // LOAD_WITH_ALTERED_SEARCH_PATH flag. This helps because LoadLibrary - // doesn't load a DLL again if it's already loaded. This workaround is - // harmless for NSS 3.11. - path = base::FilePath(dll_path).Append(kSoftokn3Library); - softokn3_dll_ = LoadLibraryEx(path.value().c_str(), NULL, - LOAD_WITH_ALTERED_SEARCH_PATH); - if (softokn3_dll_ == NULL) { - Free(); - return false; - } - } - HMODULE plds4_dll = GetModuleHandle(kPLDS4Library); - HMODULE nspr4_dll = GetModuleHandle(kNSPR4Library); - - // On Firefox 22 and higher, NSPR is part of nss3.dll rather than separate - // DLLs. - if (plds4_dll == NULL) { - plds4_dll = nss3_dll_; - nspr4_dll = nss3_dll_; - } - return InitNSS(db_path, plds4_dll, nspr4_dll); -} - -NSSDecryptor::NSSDecryptor() - : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL), - PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL), - PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL), - PL_ArenaFinish(NULL), PR_Cleanup(NULL), - nss3_dll_(NULL), softokn3_dll_(NULL), - is_nss_initialized_(false) { -} - -NSSDecryptor::~NSSDecryptor() { - Free(); -} - -bool NSSDecryptor::InitNSS(const base::FilePath& db_path, - base::NativeLibrary plds4_dll, - base::NativeLibrary nspr4_dll) { - // Gets the function address. - NSS_Init = (NSSInitFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Init"); - NSS_Shutdown = (NSSShutdownFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "NSS_Shutdown"); - PK11_GetInternalKeySlot = (PK11GetInternalKeySlotFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, - "PK11_GetInternalKeySlot"); - PK11_FreeSlot = (PK11FreeSlotFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_FreeSlot"); - PK11_Authenticate = (PK11AuthenticateFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11_Authenticate"); - PK11SDR_Decrypt = (PK11SDRDecryptFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "PK11SDR_Decrypt"); - SECITEM_FreeItem = (SECITEMFreeItemFunc) - base::GetFunctionPointerFromNativeLibrary(nss3_dll_, "SECITEM_FreeItem"); - PL_ArenaFinish = (PLArenaFinishFunc) - base::GetFunctionPointerFromNativeLibrary(plds4_dll, "PL_ArenaFinish"); - PR_Cleanup = (PRCleanupFunc) - base::GetFunctionPointerFromNativeLibrary(nspr4_dll, "PR_Cleanup"); - - if (NSS_Init == NULL || NSS_Shutdown == NULL || - PK11_GetInternalKeySlot == NULL || PK11_FreeSlot == NULL || - PK11_Authenticate == NULL || PK11SDR_Decrypt == NULL || - SECITEM_FreeItem == NULL || PL_ArenaFinish == NULL || - PR_Cleanup == NULL) { - Free(); - return false; - } - - SECStatus result = NSS_Init(base::SysWideToNativeMB(db_path.value()).c_str()); - if (result != SECSuccess) { - Free(); - return false; - } - - is_nss_initialized_ = true; - return true; -} - -void NSSDecryptor::Free() { - if (is_nss_initialized_) { - NSS_Shutdown(); - PL_ArenaFinish(); - PR_Cleanup(); - is_nss_initialized_ = false; - } - if (softokn3_dll_ != NULL) - base::UnloadNativeLibrary(softokn3_dll_); - if (nss3_dll_ != NULL) - base::UnloadNativeLibrary(nss3_dll_); - NSS_Init = NULL; - NSS_Shutdown = NULL; - PK11_GetInternalKeySlot = NULL; - PK11_FreeSlot = NULL; - PK11_Authenticate = NULL; - PK11SDR_Decrypt = NULL; - SECITEM_FreeItem = NULL; - PL_ArenaFinish = NULL; - PR_Cleanup = NULL; - nss3_dll_ = NULL; - softokn3_dll_ = NULL; -} diff --git a/chromium_src/chrome/utility/importer/nss_decryptor_win.h b/chromium_src/chrome/utility/importer/nss_decryptor_win.h deleted file mode 100644 index edcf46c2f7..0000000000 --- a/chromium_src/chrome/utility/importer/nss_decryptor_win.h +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2011 The Chromium 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 CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_WIN_H_ -#define CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_WIN_H_ - -#include -#include - -#include "base/macros.h" -#include "base/native_library.h" -#include "base/strings/string16.h" - -// The following declarations of functions and types are from Firefox -// NSS library. -// source code: -// security/nss/lib/util/seccomon.h -// security/nss/lib/nss/nss.h -// The license block is: - -/* ***** 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 the Netscape security libraries. -* -* The Initial Developer of the Original Code is -* Netscape Communications Corporation. -* Portions created by the Initial Developer are Copyright (C) 1994-2000 -* 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 ***** */ - -enum SECItemType { - siBuffer = 0, - siClearDataBuffer = 1, - siCipherDataBuffer = 2, - siDERCertBuffer = 3, - siEncodedCertBuffer = 4, - siDERNameBuffer = 5, - siEncodedNameBuffer = 6, - siAsciiNameString = 7, - siAsciiString = 8, - siDEROID = 9, - siUnsignedInteger = 10, - siUTCTime = 11, - siGeneralizedTime = 12 -}; - -struct SECItem { - SECItemType type; - unsigned char *data; - unsigned int len; -}; - -enum SECStatus { - SECWouldBlock = -2, - SECFailure = -1, - SECSuccess = 0 -}; - -typedef int PRBool; -#define PR_TRUE 1 -#define PR_FALSE 0 - -typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; - -typedef struct PK11SlotInfoStr PK11SlotInfo; - -typedef SECStatus (*NSSInitFunc)(const char *configdir); -typedef SECStatus (*NSSShutdownFunc)(void); -typedef PK11SlotInfo* (*PK11GetInternalKeySlotFunc)(void); -typedef void (*PK11FreeSlotFunc)(PK11SlotInfo *slot); -typedef SECStatus (*PK11CheckUserPasswordFunc)(PK11SlotInfo *slot, char *pw); -typedef SECStatus - (*PK11AuthenticateFunc)(PK11SlotInfo *slot, PRBool loadCerts, void *wincx); -typedef SECStatus - (*PK11SDRDecryptFunc)(SECItem *data, SECItem *result, void *cx); -typedef void (*SECITEMFreeItemFunc)(SECItem *item, PRBool free_it); -typedef void (*PLArenaFinishFunc)(void); -typedef PRStatus (*PRCleanupFunc)(void); - -struct FirefoxRawPasswordInfo; - -namespace autofill { -struct PasswordForm; -} - -// A wrapper for Firefox NSS decrypt component. -class NSSDecryptor { - public: - NSSDecryptor(); - ~NSSDecryptor(); - - // Loads NSS3 library and returns true if successful. - // |dll_path| indicates the location of NSS3 DLL files, and |db_path| - // is the location of the database file that stores the keys. - bool Init(const base::FilePath& dll_path, const base::FilePath& db_path); - - // Frees the libraries. - void Free(); - - // Decrypts Firefox stored passwords. Before using this method, - // make sure Init() returns true. - base::string16 Decrypt(const std::string& crypt) const; - - // Parses the Firefox password file content, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - void ParseSignons(const base::FilePath& signon_file, - std::vector* forms); - - // Reads and parses the Firefox password sqlite db, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - bool ReadAndParseSignons(const base::FilePath& sqlite_file, - std::vector* forms); - - // Reads and parses the Firefox password file logins.json, decrypts the - // username/password and reads other related information. - // The result will be stored in |forms|. - bool ReadAndParseLogins(const base::FilePath& json_file, - std::vector* forms); - - private: - // Call NSS initialization funcs. - bool InitNSS(const base::FilePath& db_path, - base::NativeLibrary plds4_dll, - base::NativeLibrary nspr4_dll); - - PK11SlotInfo* GetKeySlotForDB() const { return PK11_GetInternalKeySlot(); } - - void FreeSlot(PK11SlotInfo* slot) const { PK11_FreeSlot(slot); } - - // Turns unprocessed information extracted from Firefox's password file - // into PasswordForm. - bool CreatePasswordFormFromRawInfo( - const FirefoxRawPasswordInfo& raw_password_info, - autofill::PasswordForm* form); - - // Methods in Firefox security components. - NSSInitFunc NSS_Init; - NSSShutdownFunc NSS_Shutdown; - PK11GetInternalKeySlotFunc PK11_GetInternalKeySlot; - PK11CheckUserPasswordFunc PK11_CheckUserPassword; - PK11FreeSlotFunc PK11_FreeSlot; - PK11AuthenticateFunc PK11_Authenticate; - PK11SDRDecryptFunc PK11SDR_Decrypt; - SECITEMFreeItemFunc SECITEM_FreeItem; - PLArenaFinishFunc PL_ArenaFinish; - PRCleanupFunc PR_Cleanup; - - // Libraries necessary for decrypting the passwords. - static const wchar_t kNSS3Library[]; - static const wchar_t kSoftokn3Library[]; - static const wchar_t kPLDS4Library[]; - static const wchar_t kNSPR4Library[]; - - // NSS3 module handles. - base::NativeLibrary nss3_dll_; - base::NativeLibrary softokn3_dll_; - - // True if NSS_Init() has been called - bool is_nss_initialized_; - - DISALLOW_COPY_AND_ASSIGN(NSSDecryptor); -}; - -#endif // CHROME_UTILITY_IMPORTER_NSS_DECRYPTOR_WIN_H_ diff --git a/chromium_src/components/sessions/core/session_id.cc b/chromium_src/components/sessions/core/session_id.cc deleted file mode 100644 index 1ab9753a83..0000000000 --- a/chromium_src/components/sessions/core/session_id.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 The Chromium 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 "components/sessions/core/session_id.h" - -static SessionID::id_type next_id = 1; - -SessionID::SessionID() { - id_ = next_id++; -} diff --git a/chromium_src/components/sessions/core/session_id.h b/chromium_src/components/sessions/core/session_id.h deleted file mode 100644 index d4c2645f1e..0000000000 --- a/chromium_src/components/sessions/core/session_id.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 The Chromium 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 COMPONENTS_SESSIONS_CORE_SESSION_ID_H_ -#define COMPONENTS_SESSIONS_CORE_SESSION_ID_H_ - -#include - -#include "components/sessions/core/sessions_export.h" - -// Uniquely identifies a tab or window for the duration of a session. -class SESSIONS_EXPORT SessionID { - public: - typedef int32_t id_type; - - SessionID(); - ~SessionID() {} - - // Returns the underlying id. - void set_id(id_type id) { id_ = id; } - id_type id() const { return id_; } - - private: - id_type id_; -}; - -#endif // COMPONENTS_SESSIONS_CORE_SESSION_ID_H_ diff --git a/chromium_src/components/sessions/core/sessions_export.h b/chromium_src/components/sessions/core/sessions_export.h deleted file mode 100644 index cd5ade48c3..0000000000 --- a/chromium_src/components/sessions/core/sessions_export.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 The Chromium 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 COMPONENTS_SESSIONS_CORE_SESSIONS_EXPORT_H_ -#define COMPONENTS_SESSIONS_CORE_SESSIONS_EXPORT_H_ - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(SESSIONS_IMPLEMENTATION) -#define SESSIONS_EXPORT __declspec(dllexport) -#else -#define SESSIONS_EXPORT __declspec(dllimport) -#endif // defined(SESSIONS_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(SESSIONS_IMPLEMENTATION) -#define SESSIONS_EXPORT __attribute__((visibility("default"))) -#else -#define SESSIONS_EXPORT -#endif -#endif - -#else // defined(COMPONENT_BUILD) -#define SESSIONS_EXPORT -#endif - -#endif // COMPONENTS_SESSIONS_CORE_SESSIONS_EXPORT_H_ diff --git a/chromium_src/grit/generated_resources.h b/chromium_src/grit/generated_resources.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/component_updater.gypi b/component_updater.gypi deleted file mode 100644 index cedb28bc7f..0000000000 --- a/component_updater.gypi +++ /dev/null @@ -1,49 +0,0 @@ -{ - 'variables': { - 'component_updater_sources': [ - 'brave/browser/api/brave_api_component_updater.cc', - 'brave/browser/api/brave_api_component_updater.h', - 'brave/browser/component_updater/brave_component_updater_configurator.cc', - 'brave/browser/component_updater/brave_component_updater_configurator.h', - 'brave/browser/component_updater/default_extensions.h', - 'brave/browser/component_updater/extension_installer_traits.cc', - 'brave/browser/component_updater/extension_installer_traits.h', - 'brave/browser/component_updater/widevine_cdm_component_installer.cc', - 'brave/browser/component_updater/widevine_cdm_component_installer.h', - ], - 'component_updater_js_sources': [ - 'lib/browser/api/component-updater.js', - ], - }, - 'conditions': [ - ['OS=="mac" or OS=="linux"', { - 'variables': { - 'component_updater_libraries': [ - '<(libchromiumcontent_dir)/libupdate_client.a', - '<(libchromiumcontent_dir)/libclient_update_protocol.a', - '<(libchromiumcontent_dir)/libcomponent_updater.a', - '<(libchromiumcontent_dir)/libcourgette_lib.a', - '<(libchromiumcontent_dir)/libversion_info.a', - '<(libchromiumcontent_dir)/liblzma_sdk.a', - '<(libchromiumcontent_dir)/libminizip.a', - '<(libchromiumcontent_dir)/libzip.a', - ] - } - }], - ['OS=="win"', { - 'variables': { - 'component_updater_libraries': [ - '<(libchromiumcontent_dir)/client_update_protocol.lib', - '<(libchromiumcontent_dir)/component_updater.lib', - '<(libchromiumcontent_dir)/courgette_lib.lib', - '<(libchromiumcontent_dir)/lzma_sdk.lib', - '<(libchromiumcontent_dir)/minizip.lib', - '<(libchromiumcontent_dir)/update_client.lib', - '<(libchromiumcontent_dir)/version_info.lib', - '<(libchromiumcontent_dir)/zip.lib', - '<(libchromiumcontent_dir)/zlib.lib', - ], - }, - }], - ] -} diff --git a/default_app/BUILD.gn b/default_app/BUILD.gn new file mode 100644 index 0000000000..181c4b07da --- /dev/null +++ b/default_app/BUILD.gn @@ -0,0 +1,23 @@ +action("default_app") { + script = "//electron/tools/js2asar.py" + inputs = [ + "default_app.js", + "icon.png", + "index.html", + "main.js", + "package.json", + ] + + outputs = [ + "$root_out_dir/default_app.asar" + ] + + args = [ + rebase_path("$root_out_dir/default_app.asar"), + rebase_path("//electron/default_app"), + ] + + foreach(filename, inputs) { + args += [ rebase_path(filename, root_build_dir) ] + } +} diff --git a/default_app/default_app.js b/default_app/default_app.js index bfb97a9ab0..b2d51038ef 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -1,6 +1,9 @@ -const {app, BrowserWindow} = require('electron') +console.log('1') +const {app, BrowserWindow, ipcMain} = require('electron') const path = require('path') +// app.disableHardwareAcceleration() + let mainWindow = null // Quit when all windows are closed. @@ -8,21 +11,99 @@ app.on('window-all-closed', () => { app.quit() }) -exports.load = (appUrl) => { +// exports.load = (appUrl) => { app.on('ready', () => { + + + // { + // "minWidth":480, + // "minHeight":300, + // "titleBarStyle":"hidden-inset", + // "autoHideMenuBar":true, + // "title":"Brave", + // "webPreferences": + // { + // "sharedWorker":true, + // "nodeIntegration":false, + // "partition":"default", + // "allowFileAccessFromFileUrls":true, + // "allowUniversalAccessFromFileUrls":true + // }, + // "frame":true, + // "width":721,"height":575,"x":418,"y":512,"center":false} + + const options = { + minWidth: 480, + minHeight: 300, + title: 'Brave', + titleBarStyle: 'hidden-inset', + frame: true, width: 800, height: 600, autoHideMenuBar: true, backgroundColor: '#FFFFFF', - useContentSize: true + useContentSize: true, + webPreferences: { + sharedWorker: true, + partition: 'default', + allowFileAccessFromFileUrls: true, + allowUniversalAccessFromFileUrls: true + }, + x: 418, + y: 512, + center: false, + show: false } + if (process.platform === 'linux') { options.icon = path.join(__dirname, 'icon.png') } mainWindow = new BrowserWindow(options) - mainWindow.loadURL(appUrl) - mainWindow.focus() + + mainWindow.webContents.on('load-start', (e) => { + console.log('load-start') + }) + mainWindow.webContents.on('did-get-redirect-request', (e) => { + console.log('did-get-redirect-request') + }) + mainWindow.webContents.on('close', (e) => { + console.log('close') + }) + mainWindow.webContents.on('unresponive', (e) => { + console.log('unresponive') + }) + mainWindow.webContents.on('responsive', (e) => { + console.log('responsive') + }) + mainWindow.webContents.on('did-navigate', (e) => { + console.log('did-navigate') + // mainWindow.webContents.loadURL('http://test.com') + }) + mainWindow.webContents.on('will-navigate', (e) => { + console.log('will-navigate') + }) + mainWindow.webContents.on('did-start-load', (e) => { + console.log('did-start-load') + }) + mainWindow.webContents.on('render-view-deleted', (e) => { + console.log('render-view-deleted') + }) + mainWindow.webContents.on('crashed', (e) => { + console.log('crashed') + }) + mainWindow.webContents.on('did-finish-load', (e) => { + console.log('did-finish-load') + mainWindow.webContents.openDevTools() + mainWindow.webContents.send('test2', 'blah2') + }) + mainWindow.show() + // mainWindow.loadURL('http://test.com') + // mainWindow.loadURL('file:///Users/bjohnson/Documents/brave/browser-laptop/app/extensions/brave/index-dev.html') + mainWindow.loadURL('chrome://brave/Users/bjohnson/Documents/chromium/src/electron/default_app/index.html') + // mainWindow.loadURL('chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/@e24af55b17c3554262658d03395bc48ca05deb4a/&can_dock=true&toolbarColor=rgba(223,223,223,1)&textColor=rgba(0,0,0,1)&experiments=true') + // mainWindow.focus() + }) -} +// } diff --git a/default_app/index.html b/default_app/index.html index 292fd119b2..a3b5d1cf57 100644 --- a/default_app/index.html +++ b/default_app/index.html @@ -1,219 +1,10 @@ Electron - - - - -
- - - - Electron - - - - - -
- Docs - Repository - Blog -
-
- -
- -

- To run your app with Electron, execute the following command in your - Console (or Terminal): -

- - - -

- The path-to-your-app should be the path to your own Electron - app. -

- -

You can read the - - guide in Electron's - - to learn how to write one. -

- -

- Or you can just drag your app here to run it: -

- -
- Drag your app here to run it -
- -
- - + +
+ +
diff --git a/default_app/main.js b/default_app/main.js index a3b8c9bc77..512ba5033a 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -272,8 +272,6 @@ function loadApplicationPackage (packagePath) { } else if (packageJson.name) { app.setName(packageJson.name) } - app.setPath('userData', path.join(app.getPath('appData'), app.getName())) - app.setPath('userCache', path.join(app.getPath('cache'), app.getName())) app.setAppPath(packagePath) } diff --git a/electron.gyp b/electron.gyp deleted file mode 100644 index 7c9deaacb4..0000000000 --- a/electron.gyp +++ /dev/null @@ -1,691 +0,0 @@ -{ - 'variables': { - 'project_name%': 'brave', - 'product_name%': 'Brave', - 'company_name%': 'Brave Software', - 'company_abbr%': 'brave', - 'version%': '1.4.25', - }, - 'includes': [ - 'filenames.gypi', - 'vendor/native_mate/native_mate_files.gypi', - 'extensions.gypi', - 'autofill.gypi', - 'importer.gypi', - 'component_updater.gypi', - ], - 'target_defaults': { - 'msvs_disabled_warnings': [ - 4456, - ], - 'defines': [ - 'ATOM_PRODUCT_NAME="<(product_name)"', - 'ATOM_PROJECT_NAME="<(project_name)"', - ], - 'conditions': [ - ['OS=="mac"', { - 'mac_framework_dirs': [ - '<(source_root)/external_binaries', - ], - }], - ], - }, - 'targets': [ - { - 'target_name': '<(project_name)', - 'type': 'executable', - 'dependencies': [ - 'js2asar', - 'app2asar', - '<(project_name)_lib', - ], - 'sources': [ - '<@(app_sources)', - ], - 'include_dirs': [ - '.', - ], - 'conditions': [ - ['OS=="mac"', { - 'product_name': '<(product_name)', - 'mac_bundle': 1, - 'dependencies!': [ - '<(project_name)_lib', - ], - 'dependencies': [ - '<(project_name)_framework', - '<(project_name)_helper', - ], - 'xcode_settings': { - 'ATOM_BUNDLE_ID': 'com.<(company_abbr).<(project_name)', - 'INFOPLIST_FILE': 'atom/browser/resources/mac/Info.plist', - 'LD_RUNPATH_SEARCH_PATHS': [ - '@executable_path/../Frameworks', - ], - }, - 'mac_bundle_resources': [ - '<@(bundle_sources)', - ], - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Frameworks', - 'files': [ - '<(PRODUCT_DIR)/<(product_name) Helper.app', - '<(PRODUCT_DIR)/<(product_name) Framework.framework', - ], - }, - ], - 'postbuilds': [ - { - # This postbuid step is responsible for creating the following - # helpers: - # - # <(product_name) EH.app and <(product_name) NP.app are created - # from <(product_name).app. - # - # The EH helper is marked for an executable heap. The NP helper - # is marked for no PIE (ASLR). - 'postbuild_name': 'Make More Helpers', - 'action': [ - 'vendor/brightray/tools/mac/make_more_helpers.sh', - 'Frameworks', - '<(product_name)', - ], - }, - # The application doesn't have real localizations, it just has - # empty .lproj directories, which is enough to convince Cocoa - # that Electron supports those languages. - { - 'postbuild_name': 'Make Empty Localizations', - 'variables': { - 'apply_locales_cmd': ['python', 'tools/mac/apply_locales.py'], - 'locale_dirs': [ - '>!@(<(apply_locales_cmd) -d ZZLOCALE.lproj <(locales))', - ], - }, - 'action': [ - 'tools/mac/make_locale_dirs.sh', - '<@(locale_dirs)', - ], - }, - ], - 'conditions': [ - ['mas_build==0', { - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Frameworks', - 'files': [ - 'external_binaries/Squirrel.framework', - 'external_binaries/ReactiveCocoa.framework', - 'external_binaries/Mantle.framework', - ], - }, - ], - }], - ], - }], # OS!="mac" - ['OS=="win"', { - 'include_dirs': [ - '<(libchromiumcontent_dir)/gen/ui/resources', - ], - 'msvs_settings': { - 'VCManifestTool': { - 'EmbedManifest': 'true', - 'AdditionalManifestFiles': 'atom/browser/resources/win/atom.manifest', - } - }, - 'copies': [ - { - 'variables': { - 'conditions': [ - ['libchromiumcontent_component', { - 'copied_libraries': [ - '<@(libchromiumcontent_shared_libraries)', - '<@(libchromiumcontent_shared_v8_libraries)', - ], - }, { - 'copied_libraries': [ - '<(libchromiumcontent_dir)/ffmpeg.dll', - ], - }], - ], - }, - 'destination': '<(PRODUCT_DIR)', - 'files': [ - '<@(copied_libraries)', - '<(libchromiumcontent_dir)/locales', - '<(libchromiumcontent_dir)/libEGL.dll', - '<(libchromiumcontent_dir)/libGLESv2.dll', - '<(libchromiumcontent_dir)/icudtl.dat', - '<(libchromiumcontent_dir)/blink_image_resources_200_percent.pak', - '<(libchromiumcontent_dir)/content_resources_200_percent.pak', - '<(libchromiumcontent_dir)/content_shell.pak', - '<(libchromiumcontent_dir)/ui_resources_200_percent.pak', - '<(libchromiumcontent_dir)/views_resources_200_percent.pak', - '<(libchromiumcontent_dir)/natives_blob.bin', - '<(libchromiumcontent_dir)/snapshot_blob.bin', - '<(libchromiumcontent_dir)/widevinecdmadapter.dll', - 'external_binaries/d3dcompiler_47.dll', - 'external_binaries/xinput1_3.dll', - ], - }, - ], - }, { - 'dependencies': [ - 'vendor/breakpad/breakpad.gyp:dump_syms#host', - ], - }], # OS=="win" - ['OS=="linux"', { - 'copies': [ - { - 'variables': { - 'conditions': [ - ['libchromiumcontent_component', { - 'copied_libraries': [ - '<(PRODUCT_DIR)/lib/libnode.so', - '<@(libchromiumcontent_shared_libraries)', - '<@(libchromiumcontent_shared_v8_libraries)', - ], - }, { - 'copied_libraries': [ - '<(PRODUCT_DIR)/lib/libnode.so', - '<(libchromiumcontent_dir)/libffmpeg.so', - ], - }], - ], - }, - 'destination': '<(PRODUCT_DIR)', - 'files': [ - '<@(copied_libraries)', - '<(libchromiumcontent_dir)/locales', - '<(libchromiumcontent_dir)/icudtl.dat', - '<(libchromiumcontent_dir)/blink_image_resources_200_percent.pak', - '<(libchromiumcontent_dir)/content_resources_200_percent.pak', - '<(libchromiumcontent_dir)/content_shell.pak', - '<(libchromiumcontent_dir)/ui_resources_200_percent.pak', - '<(libchromiumcontent_dir)/views_resources_200_percent.pak', - '<(libchromiumcontent_dir)/natives_blob.bin', - '<(libchromiumcontent_dir)/snapshot_blob.bin', - '<(libchromiumcontent_dir)/libwidevinecdmadapter.so', - ], - }, - ], - }], # OS=="linux" - ], - }, # target <(project_name) - { - 'target_name': '<(project_name)_lib', - 'type': 'static_library', - 'dependencies': [ - 'atom_js2c', - 'vendor/brightray/brightray.gyp:brightray', - 'vendor/node/node.gyp:node', - ], - 'defines': [ - # We need to access internal implementations of Node. - 'NODE_WANT_INTERNALS=1', - 'NODE_SHARED_MODE', - # This is defined in skia/skia_common.gypi. - 'SK_SUPPORT_LEGACY_GETTOPDEVICE', - # Disable warnings for g_settings_list_schemas. - 'GLIB_DISABLE_DEPRECATION_WARNINGS', - # Defined in Chromium but not exposed in its gyp file. - 'V8_USE_EXTERNAL_STARTUP_DATA', - 'ENABLE_PLUGINS', - 'ENABLE_PEPPER_CDMS', - 'USE_PROPRIETARY_CODECS', - '__STDC_FORMAT_MACROS', - ], - 'sources': [ - '<@(lib_sources)', - '<@(autofill_sources)', - '<@(importer_sources)', - '<@(component_updater_sources)', - ], - 'include_dirs': [ - '.', - 'chromium_src', - # autofill - TODO(bridiver) - move to autofill.gypi - '<(libchromiumcontent_dir)/gen/protoc_out', - '<(libchromiumcontent_src_dir)/third_party/protobuf/src', - # end autofill - '<@(importer_include_dir)', - 'vendor/brightray', - 'vendor/native_mate', - # Include atom_natives.h. - '<(SHARED_INTERMEDIATE_DIR)', - # Include directories for uv and node. - 'vendor/node/src', - 'vendor/node/deps/http_parser', - 'vendor/node/deps/uv/include', - # The `node.h` is using `#include"v8.h"`. - '<(libchromiumcontent_src_dir)/v8/include', - # The `node.h` is using `#include"ares.h"`. - 'vendor/node/deps/cares/include', - # The `third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h` is using `platform/PlatformExport.h`. - '<(libchromiumcontent_src_dir)/third_party/WebKit/Source', - # The 'third_party/libyuv/include/libyuv/scale_argb.h' is using 'libyuv/basic_types.h'. - '<(libchromiumcontent_src_dir)/third_party/libyuv/include', - # The 'third_party/webrtc/modules/desktop_capture/desktop_frame.h' is using 'webrtc/base/scoped_ptr.h'. - '<(libchromiumcontent_src_dir)/third_party/', - '<(libchromiumcontent_src_dir)/components/cdm', - '<(libchromiumcontent_src_dir)/third_party/widevine', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '.', - ], - }, - 'export_dependent_settings': [ - 'vendor/brightray/brightray.gyp:brightray', - ], - 'conditions': [ - ['enable_extensions==1', { - 'dependencies': [ 'extensions.gyp:atom_resources' ], - 'sources': [ '<@(extension_sources)' ], - 'include_dirs': [ - '<(libchromiumcontent_dir)/gen/extensions', - ], - 'conditions': [ - ['OS=="linux"', { - 'dependencies': [ - 'extensions.gyp:xscrnsaver', - ], - }], - ] - }], - ['OS!="linux" and libchromiumcontent_component', { - 'link_settings': { - # Following libraries are always linked statically. - 'libraries': [ '<@(autofill_libraries)', - '<@(importer_libraries)', - '<@(component_updater_libraries)', - ], - }, - }], - ['OS=="linux" and libchromiumcontent_component', { - 'link_settings': { - 'libraries': [ - # hack to handle cyclic dependencies - '-Wl,--start-group', - '<@(autofill_libraries)', - '<@(importer_libraries)', - '<@(component_updater_libraries)', - '-Wl,--end-group', - ], - } - }], - ['OS!="linux" and enable_extensions==1 and libchromiumcontent_component', { - 'link_settings': { - # Following libraries are always linked statically. - 'libraries': [ '<@(extension_libraries)' ], - }, - }], - ['OS=="linux" and enable_extensions==1 and libchromiumcontent_component', { - 'link_settings': { - 'libraries': [ - # hack to handle cyclic dependencies - '-Wl,--start-group', - '<@(extension_libraries)', - '-Wl,--end-group', - ], - } - }], - ['libchromiumcontent_component', { - 'link_settings': { - 'libraries': [ '<@(libchromiumcontent_v8_libraries)' ], - }, - }], - ['OS=="win"', { - 'sources': [ - '<@(lib_sources_win)', - ], - 'link_settings': { - 'libraries': [ - '-limm32.lib', - '-loleacc.lib', - '-lcomctl32.lib', - '-lcomdlg32.lib', - '-lwininet.lib', - '-lwinmm.lib', - # TODO(Anthony): doesn't work in importer.gypi for release build - '-lesent.lib', - ], - }, - 'dependencies': [ - # Node is built as static_library on Windows, so we also need to - # include its dependencies here. - 'vendor/node/deps/cares/cares.gyp:cares', - 'vendor/node/deps/http_parser/http_parser.gyp:http_parser', - 'vendor/node/deps/uv/uv.gyp:libuv', - 'vendor/node/deps/zlib/zlib.gyp:zlib', - # Build with breakpad support. - 'vendor/breakpad/breakpad.gyp:breakpad_handler', - 'vendor/breakpad/breakpad.gyp:breakpad_sender', - ], - }], # OS=="win" - ['OS=="mac" and mas_build==0', { - 'dependencies': [ - 'vendor/crashpad/client/client.gyp:crashpad_client', - 'vendor/crashpad/handler/handler.gyp:crashpad_handler', - ], - 'link_settings': { - # Do not link with QTKit for mas build. - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/QTKit.framework', - ], - }, - 'xcode_settings': { - # ReactiveCocoa which is used by Squirrel requires using __weak. - 'CLANG_ENABLE_OBJC_WEAK': 'YES', - }, - }], # OS=="mac" and mas_build==0 - ['OS=="mac" and mas_build==1', { - 'defines': [ - 'MAS_BUILD', - ], - 'sources!': [ - 'atom/browser/auto_updater_mac.mm', - 'atom/common/crash_reporter/crash_reporter_mac.h', - 'atom/common/crash_reporter/crash_reporter_mac.mm', - ], - }], # OS=="mac" and mas_build==1 - ['OS=="linux"', { - 'sources': [ - '<@(lib_sources_nss)', - ], - 'link_settings': { - 'ldflags': [ - # Make binary search for libraries under current directory, so we - # don't have to manually set $LD_LIBRARY_PATH: - # http://serverfault.com/questions/279068/cant-find-so-in-the-same-directory-as-the-executable - '-rpath \$$ORIGIN', - # Make native module dynamic loading work. - '-rdynamic', - ], - }, - # Required settings of using breakpad. - 'cflags_cc': [ - '-Wno-empty-body', - '-Wno-reserved-user-defined-literal', - ], - 'include_dirs': [ - 'vendor/breakpad/src', - ], - 'dependencies': [ - 'vendor/breakpad/breakpad.gyp:breakpad_client', - ], - }], # OS=="linux" - ], - }, # target <(product_name)_lib - { - 'target_name': 'js2asar', - 'type': 'none', - 'actions': [ - { - 'action_name': 'js2asar', - 'variables': { - 'conditions': [ - ['OS=="mac"', { - 'resources_path': '<(PRODUCT_DIR)/<(product_name).app/Contents/Resources', - },{ - 'resources_path': '<(PRODUCT_DIR)/resources', - }], - ], - }, - 'inputs': [ - '<@(js_sources)', - '<@(extension_js_sources)', - '<@(importer_js_sources)', - '<@(component_updater_js_sources)', - ], - 'outputs': [ - '<(resources_path)/electron.asar', - ], - 'action': [ - 'python', - 'tools/js2asar.py', - '<@(_outputs)', - 'lib', - '<@(_inputs)', - ], - } - ], - }, # target js2asar - { - 'target_name': 'app2asar', - 'type': 'none', - 'actions': [ - { - 'action_name': 'app2asar', - 'variables': { - 'conditions': [ - ['OS=="mac"', { - 'resources_path': '<(PRODUCT_DIR)/<(product_name).app/Contents/Resources', - },{ - 'resources_path': '<(PRODUCT_DIR)/resources', - }], - ], - }, - 'inputs': [ - '<@(default_app_sources)', - ], - 'outputs': [ - '<(resources_path)/default_app.asar', - ], - 'action': [ - 'python', - 'tools/js2asar.py', - '<@(_outputs)', - 'default_app', - '<@(_inputs)', - ], - } - ], - }, # target app2asar - { - 'target_name': 'atom_js2c', - 'type': 'none', - 'actions': [ - { - 'action_name': 'atom_js2c', - 'inputs': [ - '<@(js2c_sources)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', - ], - 'action': [ - 'python', - 'tools/js2c.py', - '<@(_outputs)', - '<@(_inputs)', - ], - } - ], - }, # target atom_js2c - ], - 'conditions': [ - ['OS=="mac"', { - 'targets': [ - { - 'target_name': '<(project_name)_framework', - 'product_name': '<(product_name) Framework', - 'type': 'shared_library', - 'dependencies': [ - '<(project_name)_lib', - 'extensions.gyp:atom_resources', - ], - 'sources': [ - '<@(framework_sources)', - ], - 'include_dirs': [ - '.', - 'vendor', - '<(libchromiumcontent_src_dir)', - ], - 'export_dependent_settings': [ - '<(project_name)_lib', - ], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/Carbon.framework', - '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework', - ], - }, - 'mac_bundle': 1, - 'mac_bundle_resources': [ - 'atom/common/resources/mac/MainMenu.xib', - '<(libchromiumcontent_dir)/content_shell.pak', - '<(libchromiumcontent_dir)/icudtl.dat', - '<(libchromiumcontent_dir)/natives_blob.bin', - '<(libchromiumcontent_dir)/snapshot_blob.bin', - ], - 'xcode_settings': { - 'ATOM_BUNDLE_ID': 'com.<(company_abbr).<(project_name).framework', - 'INFOPLIST_FILE': 'atom/common/resources/mac/Info.plist', - 'LD_DYLIB_INSTALL_NAME': '@rpath/<(product_name) Framework.framework/<(product_name) Framework', - 'LD_RUNPATH_SEARCH_PATHS': [ - '@loader_path/Libraries', - ], - 'OTHER_LDFLAGS': [ - '-ObjC', - ], - }, - 'copies': [ - { - 'variables': { - 'conditions': [ - ['libchromiumcontent_component', { - 'copied_libraries': [ - '<(PRODUCT_DIR)/libnode.dylib', - '<@(libchromiumcontent_shared_libraries)', - '<@(libchromiumcontent_shared_v8_libraries)', - ], - }, { - 'copied_libraries': [ - '<(PRODUCT_DIR)/libnode.dylib', - '<(libchromiumcontent_dir)/libffmpeg.dylib', - ], - }], - ], - }, - 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Libraries', - 'files': [ - '<@(copied_libraries)', - ], - }, - { - 'variables': { - 'conditions': [ - ['libchromiumcontent_component', { - 'copied_libraries': [ - '<(libchromiumcontent_dir)/widevinecdmadapter.plugin', - ] - }, { - 'copied_libraries': [ - '<(libchromiumcontent_dir)/widevinecdmadapter.plugin', - ], - }], - ] - }, - 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Internet Plug-Ins', - 'files': [ - '<@(copied_libraries)', - ], - }, - ], - 'postbuilds': [ - { - 'postbuild_name': 'Fix path of libnode', - 'action': [ - 'install_name_tool', - '-change', - '/usr/local/lib/libnode.dylib', - '@rpath/libnode.dylib', - '${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Versions/A/<(product_name) Framework', - ], - }, - { - 'postbuild_name': 'Fix path of ffmpeg', - 'action': [ - 'install_name_tool', - '-change', - '/usr/local/lib/libffmpeg.dylib', - '@rpath/libffmpeg.dylib', - '${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Versions/A/<(product_name) Framework', - ], - }, - { - 'postbuild_name': 'Add symlinks for framework subdirectories', - 'action': [ - 'tools/mac/create-framework-subdir-symlinks.sh', - '<(product_name) Framework', - 'Libraries', - ], - }, - { - 'postbuild_name': 'Add symlinks for framework subdirectories', - 'action': [ - 'tools/mac/create-framework-subdir-symlinks.sh', - '<(product_name) Framework', - 'Internet Plug-Ins', - ], - }, - { - 'postbuild_name': 'Copy locales', - 'action': [ - 'tools/mac/copy-locales.py', - '-d', - '<(libchromiumcontent_dir)/locales', - '${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Resources', - '<@(locales)', - ], - }, - ], - 'conditions': [ - ['mas_build==0', { - 'link_settings': { - 'libraries': [ - 'external_binaries/Squirrel.framework', - 'external_binaries/ReactiveCocoa.framework', - 'external_binaries/Mantle.framework', - ], - }, - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources', - 'files': [ - '<(PRODUCT_DIR)/crashpad_handler', - ], - }, - ], - }], - ], - }, # target framework - { - 'target_name': '<(project_name)_helper', - 'product_name': '<(product_name) Helper', - 'type': 'executable', - 'dependencies': [ - '<(project_name)_framework', - ], - 'sources': [ - '<@(app_sources)', - ], - 'include_dirs': [ - '.', - ], - 'mac_bundle': 1, - 'xcode_settings': { - 'ATOM_BUNDLE_ID': 'com.<(company_abbr).<(project_name).helper', - 'INFOPLIST_FILE': 'atom/renderer/resources/mac/Info.plist', - 'LD_RUNPATH_SEARCH_PATHS': [ - '@executable_path/../../..', - ], - }, - }, # target helper - ], - }], # OS!="mac" - ], -} diff --git a/extensions.gyp b/extensions.gyp deleted file mode 100644 index 76716fcee6..0000000000 --- a/extensions.gyp +++ /dev/null @@ -1,120 +0,0 @@ -{ - 'includes': [ - 'extensions.gypi', - ], - 'conditions': [ - ['OS=="linux"', { - 'targets': [ - { - 'target_name': 'xscrnsaver', - 'type': 'none', - 'direct_dependent_settings': { - 'cflags': [ - ' { - const options = { - show: true, - width: 800, - height: 600 - } - ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options) - }) - // window.resizeTo(...) // window.moveTo(...) this.webContents.on('move', (event, size) => { @@ -50,6 +40,9 @@ BrowserWindow.prototype._init = function () { if (!event.defaultPrevented) this.setTitle(title) }) + this.webContents.once('render-view-ready', () => { + this.notifyReady() + }) // Sometimes the webContents doesn't get focus when window is shown, so we // have to force focusing on webContents in this case. The safest way is to // focus it when we first start to load URL, if we do it earlier it won't diff --git a/lib/browser/api/exports/electron.js b/lib/browser/api/exports/electron.js index 03766e62d9..def7a1e9d0 100644 --- a/lib/browser/api/exports/electron.js +++ b/lib/browser/api/exports/electron.js @@ -11,12 +11,12 @@ Object.defineProperties(exports, { return require('../app') } }, - autoUpdater: { - enumerable: true, - get: function () { - return require('../auto-updater') - } - }, + // autoUpdater: { + // enumerable: true, + // get: function () { + // return require('../auto-updater') + // } + // }, BrowserWindow: { enumerable: true, get: function () { diff --git a/lib/browser/api/extensions.js b/lib/browser/api/extensions.js index b833d5704c..2f265cc8ad 100644 --- a/lib/browser/api/extensions.js +++ b/lib/browser/api/extensions.js @@ -167,8 +167,7 @@ var updateWindow = function (windowId, updateInfo) { var createGuest = function (opener, url) { var payload = {} - process.emit('ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID', payload) - var guestInstanceId = payload.returnValue + var guestInstanceId = -1 // TODO(bridiver) - GuestViewManager->GetNextInstanceID() let embedder = opener.hostWebContents || opener var options = { diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index 29ca8557fd..4122746290 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -97,63 +97,6 @@ WebContents.prototype.sendToAll = function (channel, ...args) { return this._send(true, channel, args) } -// Following methods are mapped to webFrame. -const webFrameMethods = [ - 'insertText', - 'setZoomFactor', - 'setZoomLevel', - 'setZoomLevelLimits' -] -const webFrameMethodsWithResult = [ - 'getZoomFactor', - 'getZoomLevel' -] - -const asyncWebFrameMethods = function (requestId, method, callback, ...args) { - this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args) - ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) { - if (callback) callback(result) - }) -} - -const syncWebFrameMethods = function (requestId, method, callback, ...args) { - this.send('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', requestId, method, args) - ipcMain.once(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) { - if (callback) callback(result) - }) -} - -for (const method of webFrameMethods) { - WebContents.prototype[method] = function (...args) { - this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args) - } -} - -for (const method of webFrameMethodsWithResult) { - WebContents.prototype[method] = function (...args) { - const callback = args[args.length - 1] - const actualArgs = args.slice(0, args.length - 2) - syncWebFrameMethods.call(this, getNextId(), method, callback, ...actualArgs) - } -} - -// Make sure WebContents::executeJavaScript would run the code only when the -// WebContents has been loaded. -WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) { - const requestId = getNextId() - if (typeof hasUserGesture === 'function') { - callback = hasUserGesture - hasUserGesture = false - } - if (this.getURL() && !this.isLoadingMainFrame()) { - asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) - } else { - this.once('did-finish-load', () => { - asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) - }) - } -} - // Translate the options of printToPDF. WebContents.prototype.printToPDF = function (options, callback) { const printingSetting = Object.assign({}, defaultPrintingSetting) @@ -256,6 +199,12 @@ module.exports = { return binding.fromId(id) }, + fromTabID (tabID) { + if (!tabID) + return + return binding.fromTabID(tabID) + }, + getFocusedWebContents () { let focused = null for (let contents of binding.getAllWebContents()) { diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js deleted file mode 100644 index aace5f7612..0000000000 --- a/lib/browser/chrome-extension.js +++ /dev/null @@ -1,428 +0,0 @@ -const {app, ipcMain, session, webContents, BrowserWindow} = require('electron') -require('./api/extensions') -return // we do won't anything here -const {getAllWebContents} = process.atomBinding('web_contents') -const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllWebContents() - -const {Buffer} = require('buffer') -const fs = require('fs') -const path = require('path') -const url = require('url') - -// TODO(zcbenz): Remove this when we have Object.values(). -const objectValues = function (object) { - return Object.keys(object).map(function (key) { return object[key] }) -} - -// Mapping between extensionId(hostname) and manifest. -const manifestMap = {} // extensionId => manifest -const manifestNameMap = {} // name => manifest - -const generateExtensionIdFromName = function (name) { - return name.replace(/[\W_]+/g, '-').toLowerCase() -} - -const isWindowOrWebView = function (webContents) { - const type = webContents.getType() - return type === 'window' || type === 'webview' -} - -// Create or get manifest object from |srcDirectory|. -const getManifestFromPath = function (srcDirectory) { - let manifest - let manifestContent - - try { - manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')) - } catch (readError) { - console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`) - console.warn(readError.stack || readError) - throw readError - } - - try { - manifest = JSON.parse(manifestContent) - } catch (parseError) { - console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`) - console.warn(parseError.stack || parseError) - throw parseError - } - - if (!manifestNameMap[manifest.name]) { - const extensionId = generateExtensionIdFromName(manifest.name) - manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest - Object.assign(manifest, { - srcDirectory: srcDirectory, - extensionId: extensionId, - // We can not use 'file://' directly because all resources in the extension - // will be treated as relative to the root in Chrome. - startPage: url.format({ - protocol: 'chrome-extension', - slashes: true, - hostname: extensionId, - pathname: manifest.devtools_page - }) - }) - return manifest - } else if (manifest && manifest.name) { - console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`) - } -} - -// Manage the background pages. -const backgroundPages = {} - -const startBackgroundPages = function (manifest) { - if (backgroundPages[manifest.extensionId] || !manifest.background) return - - let html - let name - if (manifest.background.page) { - name = manifest.background.page - html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)) - } else { - name = '_generated_background_page.html' - const scripts = manifest.background.scripts.map((name) => { - return `` - }).join('') - html = new Buffer(`${scripts}`) - } - - const contents = webContents.create({ - partition: 'persist:__chrome_extension', - isBackgroundPage: true, - commandLineSwitches: ['--background-page'] - }) - backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name } - contents.loadURL(url.format({ - protocol: 'chrome-extension', - slashes: true, - hostname: manifest.extensionId, - pathname: name - })) -} - -const removeBackgroundPages = function (manifest) { - if (!backgroundPages[manifest.extensionId]) return - - backgroundPages[manifest.extensionId].webContents.destroy() - delete backgroundPages[manifest.extensionId] -} - -const sendToBackgroundPages = function (...args) { - for (const page of objectValues(backgroundPages)) { - page.webContents.sendToAll(...args) - } -} - -// Dispatch web contents events to Chrome APIs -const hookWebContentsEvents = function (webContents) { - const tabId = webContents.id - - sendToBackgroundPages('CHROME_TABS_ONCREATED') - - webContents.on('will-navigate', (event, url) => { - sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', { - frameId: 0, - parentFrameId: -1, - processId: webContents.getId(), - tabId: tabId, - timeStamp: Date.now(), - url: url - }) - }) - - webContents.on('did-navigate', (event, url) => { - sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', { - frameId: 0, - parentFrameId: -1, - processId: webContents.getId(), - tabId: tabId, - timeStamp: Date.now(), - url: url - }) - }) - - webContents.once('destroyed', () => { - sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId) - }) -} - -// Handle the chrome.* API messages. -let nextId = 0 - -ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) { - const page = backgroundPages[extensionId] - if (!page) { - console.error(`Connect to unknown extension ${extensionId}`) - return - } - - const portId = ++nextId - event.returnValue = {tabId: page.webContents.id, portId: portId} - - event.sender.once('will-destroy', () => { - if (page.webContents.isDestroyed()) return - page.webContents.sendToAll(`CHROME_PORT_DISCONNECT_${portId}`) - }) - page.webContents.sendToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo) -}) - -ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) { - event.returnValue = manifestMap[extensionId] -}) - -ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message) { - const page = backgroundPages[extensionId] - if (!page) { - console.error(`Connect to unknown extension ${extensionId}`) - return - } - - page.webContents.sendToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message) -}) - -ipcMain.on('CHROME_TABS_SEND_MESSAGE', function (event, tabId, extensionId, isBackgroundPage, message) { - const contents = webContents.fromId(tabId) - if (!contents) { - console.error(`Sending message to unknown tab ${tabId}`) - return - } - - const senderTabId = isBackgroundPage ? null : event.sender.id - - contents.sendToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message) -}) - -ipcMain.on('CHROME_TABS_EXECUTESCRIPT', function (event, requestId, tabId, extensionId, details) { - const contents = webContents.fromId(tabId) - if (!contents) { - console.error(`Sending message to unknown tab ${tabId}`) - return - } - - let code, url - if (details.file) { - const manifest = manifestMap[extensionId] - code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file))) - url = `chrome-extension://${extensionId}${details.file}` - } else { - code = details.code - url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js` - } - - contents.send('CHROME_TABS_EXECUTESCRIPT', event.sender.id, requestId, extensionId, url, code) -}) - -// Transfer the content scripts to renderer. -const contentScripts = {} - -const injectContentScripts = function (manifest) { - if (contentScripts[manifest.name] || !manifest.content_scripts) return - - const readArrayOfFiles = function (relativePath) { - return { - url: `chrome-extension://${manifest.extensionId}/${relativePath}`, - code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath))) - } - } - - const contentScriptToEntry = function (script) { - return { - matches: script.matches, - js: script.js.map(readArrayOfFiles), - runAt: script.run_at || 'document_idle' - } - } - - try { - const entry = { - extensionId: manifest.extensionId, - contentScripts: manifest.content_scripts.map(contentScriptToEntry) - } - contentScripts[manifest.name] = renderProcessPreferences.addEntry(entry) - } catch (e) { - console.error('Failed to read content scripts', e) - } -} - -const removeContentScripts = function (manifest) { - if (!contentScripts[manifest.name]) return - - renderProcessPreferences.removeEntry(contentScripts[manifest.name]) - delete contentScripts[manifest.name] -} - -// Transfer the |manifest| to a format that can be recognized by the -// |DevToolsAPI.addExtensions|. -const manifestToExtensionInfo = function (manifest) { - return { - startPage: manifest.startPage, - srcDirectory: manifest.srcDirectory, - name: manifest.name, - exposeExperimentalAPIs: true - } -} - -// Load the extensions for the window. -const loadExtension = function (manifest) { - startBackgroundPages(manifest) - injectContentScripts(manifest) -} - -const loadDevToolsExtensions = function (win, manifests) { - if (!win.devToolsWebContents) return - - manifests.forEach(loadExtension) - - const extensionInfoArray = manifests.map(manifestToExtensionInfo) - win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`) -} - -app.on('web-contents-created', function (event, webContents) { - if (!isWindowOrWebView(webContents)) return - - hookWebContentsEvents(webContents) - webContents.on('devtools-opened', function () { - loadDevToolsExtensions(webContents, objectValues(manifestMap)) - }) -}) - -// The chrome-extension: can map a extension URL request to real file path. -const chromeExtensionHandler = function (request, callback) { - const parsed = url.parse(request.url) - if (!parsed.hostname || !parsed.path) return callback() - - const manifest = manifestMap[parsed.hostname] - if (!manifest) return callback() - - const page = backgroundPages[parsed.hostname] - if (page && parsed.path === `/${page.name}`) { - return callback({ - mimeType: 'text/html', - data: page.html - }) - } - - fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) { - if (err) { - return callback(-6) // FILE_NOT_FOUND - } else { - return callback(content) - } - }) -} - -app.on('session-created', function (ses) { - ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) { - if (error) { - console.error(`Unable to register chrome-extension protocol: ${error}`) - } - }) -}) - -// The persistent path of "DevTools Extensions" preference file. -let loadedExtensionsPath = null - -app.on('will-quit', function () { - try { - const loadedExtensions = objectValues(manifestMap).map(function (manifest) { - return manifest.srcDirectory - }) - if (loadedExtensions.length > 0) { - try { - fs.mkdirSync(path.dirname(loadedExtensionsPath)) - } catch (error) { - // Ignore error - } - fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions)) - } else { - fs.unlinkSync(loadedExtensionsPath) - } - } catch (error) { - // Ignore error - } -}) - -// We can not use protocol or BrowserWindow until app is ready. -app.once('ready', function () { - // The chrome-extension: can map a extension URL request to real file path. - const chromeExtensionHandler = function (request, callback) { - const parsed = url.parse(request.url) - if (!parsed.hostname || !parsed.path) return callback() - - const manifest = manifestMap[parsed.hostname] - if (!manifest) return callback() - - const page = backgroundPages[parsed.hostname] - if (page && parsed.path === `/${page.name}`) { - return callback({ - mimeType: 'text/html', - data: page.html - }) - } - - fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) { - if (err) { - return callback(-6) // FILE_NOT_FOUND - } else { - return callback(content) - } - }) - } - session.on('session-created', function (ses) { - ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) { - if (error) { - console.error(`Unable to register chrome-extension protocol: ${error}`) - } - }) - }) - - // Load persisted extensions. - loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions') - try { - const loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath)) - if (Array.isArray(loadedExtensions)) { - for (const srcDirectory of loadedExtensions) { - // Start background pages and set content scripts. - const manifest = getManifestFromPath(srcDirectory) - loadExtension(manifest) - } - } - } catch (error) { - // Ignore error - } - - // The public API to add/remove extensions. - BrowserWindow.addDevToolsExtension = function (srcDirectory) { - const manifest = getManifestFromPath(srcDirectory) - if (manifest) { - loadExtension(manifest) - for (const webContents of getAllWebContents()) { - if (isWindowOrWebView(webContents)) { - loadDevToolsExtensions(webContents, [manifest]) - } - } - return manifest.name - } - } - - BrowserWindow.removeDevToolsExtension = function (name) { - const manifest = manifestNameMap[name] - if (!manifest) return - - removeBackgroundPages(manifest) - removeContentScripts(manifest) - delete manifestMap[manifest.extensionId] - delete manifestNameMap[name] - } - - BrowserWindow.getDevToolsExtensions = function () { - const extensions = {} - Object.keys(manifestNameMap).forEach(function (name) { - const manifest = manifestNameMap[name] - extensions[name] = {name: manifest.name, version: manifest.version} - }) - return extensions - } -}) diff --git a/lib/browser/desktop-capturer.js b/lib/browser/desktop-capturer.js deleted file mode 100644 index da1d481c60..0000000000 --- a/lib/browser/desktop-capturer.js +++ /dev/null @@ -1,77 +0,0 @@ -'use strict' - -const ipcMain = require('electron').ipcMain -const desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer - -var deepEqual = function (opt1, opt2) { - return JSON.stringify(opt1) === JSON.stringify(opt2) -} - -// A queue for holding all requests from renderer process. -var requestsQueue = [] - -ipcMain.on('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, captureWindow, captureScreen, thumbnailSize, id) { - var request - request = { - id: id, - options: { - captureWindow: captureWindow, - captureScreen: captureScreen, - thumbnailSize: thumbnailSize - }, - webContents: event.sender - } - requestsQueue.push(request) - if (requestsQueue.length === 1) { - desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize) - } - - // If the WebContents is destroyed before receiving result, just remove the - // reference from requestsQueue to make the module not send the result to it. - event.sender.once('destroyed', function () { - request.webContents = null - }) -}) - -desktopCapturer.emit = function (event, name, sources) { - // Receiving sources result from main process, now send them back to renderer. - var handledRequest, i, len, ref, ref1, request, result, source, unhandledRequestsQueue - handledRequest = requestsQueue.shift(0) - result = (function () { - var i, len, results - results = [] - for (i = 0, len = sources.length; i < len; i++) { - source = sources[i] - results.push({ - id: source.id, - name: source.name, - thumbnail: source.thumbnail.toDataURL() - }) - } - return results - })() - if ((ref = handledRequest.webContents) != null) { - ref.send('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + handledRequest.id, result) - } - - // Check the queue to see whether there is other same request. If has, handle - // it for reducing redunplicated `desktopCaptuer.startHandling` calls. - unhandledRequestsQueue = [] - for (i = 0, len = requestsQueue.length; i < len; i++) { - request = requestsQueue[i] - if (deepEqual(handledRequest.options, request.options)) { - if ((ref1 = request.webContents) != null) { - ref1.send('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + request.id, result) - } - } else { - unhandledRequestsQueue.push(request) - } - } - requestsQueue = unhandledRequestsQueue - - // If the requestsQueue is not empty, start a new request handling. - if (requestsQueue.length > 0) { - const {captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options - return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize) - } -} diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index b156564b34..713d6ec64f 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -2,11 +2,6 @@ const ipcMain = require('electron').ipcMain const webContents = require('electron').webContents -const BrowserWindow = require('electron').BrowserWindow -const dialog = require('electron').dialog - -// Doesn't exist in early initialization. -let webViewManager = null let supportedWebViewEvents = [ 'load-start', @@ -65,103 +60,36 @@ let supportedWebViewEvents = [ 'did-block-run-insecure-content' ] -let nextInstanceId = 0 -const guestInstances = {} -const embedderElementsMap = {} -const reverseEmbedderElementsMap = {} - -// Moves the last element of array to the first one. -const moveLastToFirst = function (list) { - return list.unshift(list.pop()) -} +let guests = {} +const registerGuest = function (guest, tabId) { + if (guests[tabId]) + return -// Generate guestInstanceId. -const getNextInstanceId = function () { - return ++nextInstanceId -} + guests[tabId] = true -var createWebContents = function (embedder, params) { - return webContents.create({ - isGuest: true, - partition: params.partition, - embedder: embedder + // Dispatch events to embedder. + guest.once('will-destroy', function () { + delete guests[tabId] }) -} - -// Create a new guest instance. -const addGuest = function (embedder, webContents, guestInstanceId) { - if (webViewManager == null) { - webViewManager = process.atomBinding('web_view_manager') - } - const id = guestInstanceId || getNextInstanceId() - const guest = webContents - guestInstances[id] = { - guest: guest, - embedder: embedder - } - - // Destroy guest when the embedder is gone or navigated. - const destroyEvents = ['will-destroy', 'crashed', 'did-navigate'] - const destroy = function () { - if (guestInstances[id] != null) { - destroyGuest(embedder, id) - } - } - for (const event of destroyEvents) { - embedder.once(event, destroy) - - // Users might also listen to the crashed event, so we must ensure the guest - // is destroyed before users' listener gets called. It is done by moving our - // listener to the first one in queue. - const listeners = embedder._events[event] - if (Array.isArray(listeners)) { - moveLastToFirst(listeners) - } - } guest.once('destroyed', function () { - for (const event of destroyEvents) { - embedder.removeListener(event, destroy) - } + delete guests[tabId] }) - - // Init guest web view after attached. - guest.once('did-attach', function () { - let opts, params - params = this.attachParams - delete this.attachParams - this.viewInstanceId = params.instanceId - this.setSize({ - normal: { - width: params.elementWidth, - height: params.elementHeight - }, - enableAutoSize: params.autosize, - min: { - width: params.minwidth, - height: params.minheight - }, - max: { - width: params.maxwidth, - height: params.maxheight - } - }) - if (params.src) { - opts = {} - if (params.httpreferrer) { - opts.httpReferrer = params.httpreferrer - } - if (params.useragent) { - opts.userAgent = params.useragent - } - this.loadURL(params.src, opts) - } - guest.allowPopups = params.allowpopups + guest.once('close', function () { + delete guests[tabId] + }) + guest.once('crashed', function () { + delete guests[tabId] }) - // Dispatch events to embedder. const fn = function (event) { guest.on(event, function (_, ...args) { - embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(args)) + if (guest.isDestroyed()) + return + + const embedder = guest.hostWebContents + if (embedder) { + embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + tabId, event].concat(args)) + } }) } for (const event of supportedWebViewEvents) { @@ -170,157 +98,14 @@ const addGuest = function (embedder, webContents, guestInstanceId) { // Dispatch guest's IPC messages to embedder. guest.on('ipc-message-host', function (_, [channel, ...args]) { - embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + guest.viewInstanceId, channel].concat(args)) - }) - - // Autosize. - guest.on('size-changed', function (_, ...args) { - embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args)) - }) - - return id -} - -// Attach the guest to an element of embedder. -const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) { - let guest, key, oldGuestInstanceId, ref1, webPreferences - guest = guestInstances[guestInstanceId].guest - - // Destroy the old guest when attaching. - key = (embedder.getId()) + '-' + elementInstanceId - oldGuestInstanceId = embedderElementsMap[key] - if (oldGuestInstanceId != null) { - // Reattachment to the same guest is not currently supported. - if (oldGuestInstanceId === guestInstanceId) { - return - } - if (guestInstances[oldGuestInstanceId] == null) { - return + const embedder = guest.hostWebContents + if (embedder) { + embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + tabId, channel].concat(args)) } - destroyGuest(embedder, oldGuestInstanceId) - } - webPreferences = { - guestInstanceId: guestInstanceId, - nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false, - plugins: params.plugins, - zoomFactor: params.zoomFactor, - allowDisplayingInsecureContent: (ref1 = params.allowDisplayingInsecureContent) != null ? ref1 : false, - allowRunningInsecureContent: (ref1 = params.allowRunningInsecureContent) != null ? ref1 : false, - webSecurity: !params.disablewebsecurity, - blinkFeatures: params.blinkfeatures, - disableBlinkFeatures: params.disableblinkfeatures - } - - if (params.preload) { - webPreferences.preloadURL = params.preload - } - webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences) - guest.attachParams = params - embedderElementsMap[key] = guestInstanceId - reverseEmbedderElementsMap[guestInstanceId] = key -} - -// Destroy an existing guest instance. -var destroyGuest = function (embedder, id) { - webViewManager.removeGuest(embedder, id) - const guest = guestInstances[id].guest - if (!guest) { - return - } - guest.destroy() - delete guestInstances[id] - - const key = reverseEmbedderElementsMap[id] - if (key != null) { - delete reverseEmbedderElementsMap[id] - return delete embedderElementsMap[key] - } -} - -process.on('ELECTRON_GUEST_VIEW_MANAGER_TAB_OPEN', function() { - var args, event, frameName, options, url, disposition - event = arguments[0], args = 2 <= arguments.length ? [].slice.call(arguments, 1) : [] - url = args[0], frameName = args[1], disposition = args[2], options = args[3] - event.sender.emit('new-window', event, url, frameName, disposition, options) - if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) { - return event.returnValue = null - } else { - return event.returnValue = addGuest(event.sender, createWebContents(event.sender, options)) - } -}) - -process.on('ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID', function (event) { - return event.returnValue = getNextInstanceId() -}) - -process.on('ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST', function (event, webContents, id) { - guestInstances[id] = { guest: webContents } -}) - -ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ADD_GUEST', function (event, id, requestId) { - return event.sender.send('ELECTRON_RESPONSE_' + requestId, addGuest(event.sender, guestInstances[id].guest, id)) -}) - -ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) { - return event.sender.send('ELECTRON_RESPONSE_' + requestId, addGuest(event.sender, createWebContents(event.sender, params))) -}) - -ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementInstanceId, guestInstanceId, params) { - attachGuest(event.sender, elementInstanceId, guestInstanceId, params) -}) - -ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, id) { - destroyGuest(event.sender, id) -}) - -ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, id, params) { - const guestInstance = guestInstances[id] - return guestInstance != null ? guestInstance.guest.setSize(params) : void 0 -}) - -// Returns WebContents from its guest id. -exports.getGuest = function (id) { - const guestInstance = guestInstances[id] - return guestInstance != null ? guestInstance.guest : void 0 -} - -// Returns the embedder of the guest. -exports.getEmbedder = function (id) { - const guestInstance = guestInstances[id] - return guestInstance != null ? guestInstance.embedder : void 0 -} - -ipcMain.on('window-alert', function(event, message, title) { - var buttons - if (title == null) { - title = '' - } - buttons = ['OK']; - message = message.toString() - dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { - message: message, - title: title, - buttons: buttons }) - // Alert should always return undefined. -}) +} -ipcMain.on('window-confirm', function(event, message, title) { - var buttons, cancelId; - if (title == null) { - title = '' - } - buttons = ['OK', 'Cancel'] - cancelId = 1 - return event.returnValue = !dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { - message: message, - title: title, - buttons: buttons, - cancelId: cancelId - }) +process.on('ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST', function (event, webContents, tabId) { + registerGuest(webContents, tabId) }) -ipcMain.on('window-prompt', function(event, text, defaultText) { - console.warn('window.prompt is not supported yet') - return event.returnValue = null -}) diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js deleted file mode 100644 index 9ab8fd9f29..0000000000 --- a/lib/browser/guest-window-manager.js +++ /dev/null @@ -1,148 +0,0 @@ -'use strict' - -const {BrowserWindow, ipcMain, webContents} = require('electron') - -const hasProp = {}.hasOwnProperty -const frameToGuest = {} - -// Copy attribute of |parent| to |child| if it is not defined in |child|. -const mergeOptions = function (child, parent) { - let key, value - for (key in parent) { - if (!hasProp.call(parent, key)) continue - value = parent[key] - if (!(key in child)) { - if (typeof value === 'object') { - child[key] = mergeOptions({}, value) - } else { - child[key] = value - } - } - } - return child -} - -// Merge |options| with the |embedder|'s window's options. -const mergeBrowserWindowOptions = function (embedder, options) { - if (embedder.browserWindowOptions != null) { - // Inherit the original options if it is a BrowserWindow. - mergeOptions(options, embedder.browserWindowOptions) - } else { - // Or only inherit web-preferences if it is a webview. - if (options.webPreferences == null) { - options.webPreferences = {} - } - mergeOptions(options.webPreferences, embedder.getWebPreferences()) - } - - // Disable node integration on child window if disabled on parent window - if (embedder.getWebPreferences().nodeIntegration === false) { - options.webPreferences.nodeIntegration = false - } - - return options -} - -// Create a new guest created by |embedder| with |options|. -const createGuest = function (embedder, url, frameName, options) { - let guest = frameToGuest[frameName] - if (frameName && (guest != null)) { - guest.loadURL(url) - return guest.id - } - - // Remember the embedder window's id. - if (options.webPreferences == null) { - options.webPreferences = {} - } - options.webPreferences.openerId = embedder.id - guest = new BrowserWindow(options) - guest.loadURL(url) - - // When |embedder| is destroyed we should also destroy attached guest, and if - // guest is closed by user then we should prevent |embedder| from double - // closing guest. - const guestId = guest.webContents.id - - const closedByEmbedder = function () { - guest.removeListener('closed', closedByUser) - guest.destroy() - } - const closedByUser = function () { - embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId) - embedder.removeListener('will-destroy', closedByEmbedder) - } - embedder.once('will-destroy', closedByEmbedder) - guest.once('closed', closedByUser) - - if (frameName) { - frameToGuest[frameName] = guest - guest.frameName = frameName - guest.once('closed', function () { - delete frameToGuest[frameName] - }) - } - - return guestId -} - -const getGuestWindow = function (guestId) { - const guestContents = webContents.fromId(guestId) - if (guestContents == null) return - - let guestWindow = BrowserWindow.fromWebContents(guestContents) - if (guestWindow == null) { - const hostContents = guestContents.hostWebContents - if (hostContents != null) { - guestWindow = BrowserWindow.fromWebContents(hostContents) - } - } - return guestWindow -} - -var guestWindowOpen = function () { - var args, event, frameName, disposition, options, url - event = arguments[0], args = 2 <= arguments.length ? [].slice.call(arguments, 1) : [] - url = args[0], frameName = args[1], disposition = args[2], options = args[3] - options = mergeBrowserWindowOptions(event.sender, options) - event.sender.emit('new-window', event, url, frameName, disposition, options) - if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) { - event.returnValue = null - } else { - event.returnValue = createGuest(event.sender, url, frameName, options) - } -}; - -process.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function () { - return guestWindowOpen.apply(null, arguments) -}) - -// Routed window.open messages. -ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function () { - return guestWindowOpen.apply(null, arguments) -}) - -ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) { - const guestWindow = getGuestWindow(guestId) - if (guestWindow != null) guestWindow.destroy() -}) - -ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) { - const guestWindow = getGuestWindow(guestId) - event.returnValue = guestWindow != null ? guestWindow[method].apply(guestWindow, args) : null -}) - -ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) { - const guestContents = webContents.fromId(guestId) - if (guestContents == null) return - - if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') { - const sourceId = event.sender.id - guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) - } -}) - -ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) { - const guestContents = webContents.fromId(guestId) - if (guestContents != null) guestContents[method].apply(guestContents, args) -}) diff --git a/lib/browser/init.js b/lib/browser/init.js index fb1688d0cc..c76343144d 100644 --- a/lib/browser/init.js +++ b/lib/browser/init.js @@ -98,7 +98,6 @@ require('./rpc-server') // Load the guest view manager. require('./guest-view-manager') -require('./guest-window-manager') // Now we try to load app's package.json. let packagePath = null @@ -146,16 +145,8 @@ if (packageJson.v8Flags != null) { } // Set the user path according to application's name. -app.setPath('userData', path.join(app.getPath('appData'), app.getName())) -app.setPath('userCache', path.join(app.getPath('cache'), app.getName())) app.setAppPath(packagePath) -// Load the chrome extension support. -require('./chrome-extension') - -// Load internal desktop-capturer module. -require('./desktop-capturer') - // Load protocol module to ensure it is populated on app ready require('./api/protocol') diff --git a/lib/browser/objects-registry.js b/lib/browser/objects-registry.js index f21676420f..9850498802 100644 --- a/lib/browser/objects-registry.js +++ b/lib/browser/objects-registry.js @@ -46,6 +46,10 @@ class ObjectsRegistry { // Dereference an object according to its ID. remove (webContentsId, id) { + // don't let an owner remove itself + if (webContentsId === id) + return + // Dereference from the storage. this.dereference(id) diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index d1ea1920f7..36642aaa6a 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -263,6 +263,14 @@ ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) { event.returnValue = valueToMeta(event.sender, event.sender) }) +ipcMain.on('ELECTRON_BROWSER_GET_WEB_CONTENTS', function (event, tabID) { + try { + event.returnValue = valueToMeta(event.sender, webContents.fromTabID(tabID)) + } catch (error) { + event.returnValue = null + } +}) + ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) { try { args = unwrapArgs(event.sender, args) @@ -314,6 +322,14 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) { } }) +ipcMain.on('ELECTRON_BROWSER_ASYNC_MEMBER_CALL', function (event, tabID, method, args) { + args = unwrapArgs(event.sender, args) + let obj = webContents.fromTabID(tabID) + if (obj) { + callFunction(event, obj[method], obj, args) + } +}) + ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, value) { try { let obj = objectsRegistry.get(id) @@ -337,31 +353,6 @@ ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) { objectsRegistry.remove(event.sender.getId(), id) }) -ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) { - try { - let guestViewManager = require('./guest-view-manager') - event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId)) - } catch (error) { - event.returnValue = exceptionToMeta(error) - } -}) - -ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) { - try { - let guestViewManager = require('./guest-view-manager') - let guest = guestViewManager.getGuest(guestInstanceId) - if (requestId) { - const responseCallback = function (result) { - event.sender.send(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, result) - } - args.push(responseCallback) - } - guest[method].apply(guest, args) - } catch (error) { - event.returnValue = exceptionToMeta(error) - } -}) - ipcMain.on('ELECTRON_BROWSER_SEND_TO', function (event, sendToAll, webContentsId, channel, ...args) { let contents = webContents.fromId(webContentsId) if (sendToAll) { diff --git a/lib/common/api/callbacks-registry.js b/lib/common/api/callbacks-registry.js index 459c392bc1..37c227daf1 100644 --- a/lib/common/api/callbacks-registry.js +++ b/lib/common/api/callbacks-registry.js @@ -1,6 +1,13 @@ 'use strict' -const v8Util = process.atomBinding('v8_util') +const v8Util = typeof requireNative !== 'undefined' ? requireNative('atom').GetBinding().v8 : process.atomBinding('v8_util') + +if (v8Util.getHiddenValueOnObject) { + v8Util.getHiddenValue = v8Util.getHiddenValueOnObject + v8Util.setHiddenValue = v8Util.setHiddenValueOnObject +} + +const context = typeof global !== 'undefined' ? global : this class CallbacksRegistry { constructor () { @@ -46,12 +53,12 @@ class CallbacksRegistry { call (id, ...args) { var ref - return (ref = this.get(id)).call.apply(ref, [global].concat(args)) + return (ref = this.get(id)).call.apply(ref, [context].concat(args)) } apply (id, ...args) { var ref - return (ref = this.get(id)).apply.apply(ref, [global].concat(args)) + return (ref = this.get(id)).apply.apply(ref, [context].concat(args)) } remove (id) { @@ -63,4 +70,8 @@ class CallbacksRegistry { } } -module.exports = CallbacksRegistry +if (typeof module !== 'undefined') { + module.exports = CallbacksRegistry +} else { + exports.$set('CallbacksRegistry', CallbacksRegistry) +} diff --git a/lib/common/api/exports/electron.js b/lib/common/api/exports/electron.js index efa44d452c..d702b163cb 100644 --- a/lib/common/api/exports/electron.js +++ b/lib/common/api/exports/electron.js @@ -9,12 +9,12 @@ exports.defineProperties = function (exports) { return require('../clipboard') } }, - crashReporter: { - enumerable: true, - get: function () { - return require('../crash-reporter') - } - }, + // crashReporter: { + // enumerable: true, + // get: function () { + // return require('../crash-reporter') + // } + // }, nativeImage: { enumerable: true, get: function () { diff --git a/lib/common/api/is-promise.js b/lib/common/api/is-promise.js index d6115a1455..38e47b7430 100644 --- a/lib/common/api/is-promise.js +++ b/lib/common/api/is-promise.js @@ -1,6 +1,6 @@ 'use strict' -module.exports = function isPromise (val) { +function isPromise (val) { return ( val && val.then && @@ -12,3 +12,9 @@ module.exports = function isPromise (val) { val.constructor.resolve instanceof Function ) } + +if (typeof module !== 'undefined') { + module.exports = isPromise +} else { + exports.$set('isPromise', isPromise) +} diff --git a/lib/electron_api_resources.grd b/lib/electron_api_resources.grd new file mode 100644 index 0000000000..7db971627c --- /dev/null +++ b/lib/electron_api_resources.grd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/renderer/api/desktop-capturer.js b/lib/renderer/api/desktop-capturer.js deleted file mode 100644 index e998c786a5..0000000000 --- a/lib/renderer/api/desktop-capturer.js +++ /dev/null @@ -1,47 +0,0 @@ -const ipcRenderer = require('electron').ipcRenderer -const nativeImage = require('electron').nativeImage - -var nextId = 0 -var includes = [].includes - -var getNextId = function () { - return ++nextId -} - -// |options.type| can not be empty and has to include 'window' or 'screen'. -var isValid = function (options) { - return ((options != null ? options.types : void 0) != null) && Array.isArray(options.types) -} - -exports.getSources = function (options, callback) { - var captureScreen, captureWindow, id - if (!isValid(options)) { - return callback(new Error('Invalid options')) - } - captureWindow = includes.call(options.types, 'window') - captureScreen = includes.call(options.types, 'screen') - if (options.thumbnailSize == null) { - options.thumbnailSize = { - width: 150, - height: 150 - } - } - id = getNextId() - ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id) - return ipcRenderer.once('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + id, function (event, sources) { - var source - callback(null, (function () { - var i, len, results - results = [] - for (i = 0, len = sources.length; i < len; i++) { - source = sources[i] - results.push({ - id: source.id, - name: source.name, - thumbnail: nativeImage.createFromDataURL(source.thumbnail) - }) - } - return results - })()) - }) -} diff --git a/lib/renderer/api/exports/electron.js b/lib/renderer/api/exports/electron.js deleted file mode 100644 index 8e05adc980..0000000000 --- a/lib/renderer/api/exports/electron.js +++ /dev/null @@ -1,38 +0,0 @@ -const common = require('../../../common/api/exports/electron') - -// Import common modules. -common.defineProperties(exports) - -Object.defineProperties(exports, { - // Renderer side modules, please sort with alphabet order. - desktopCapturer: { - enumerable: true, - get: function () { - return require('../desktop-capturer') - } - }, - ipcRenderer: { - enumerable: true, - get: function () { - return require('../ipc-renderer') - } - }, - remote: { - enumerable: true, - get: function () { - return require('../remote') - } - }, - screen: { - enumerable: true, - get: function () { - return require('../screen') - } - }, - webFrame: { - enumerable: true, - get: function () { - return require('../web-frame') - } - } -}) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 3cb99e5b9d..73b73d1799 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -1,8 +1,10 @@ 'use strict' -const {Buffer} = require('buffer') -const v8Util = process.atomBinding('v8_util') -const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron') +const Buffer = require('buffer').Buffer +const v8Util = requireNative('atom').GetBinding().v8 +const ipcRenderer = require('ipc_utils') +const isPromise = require('is-promise').isPromise +const CallbacksRegistry = require('callbacks-registry').CallbacksRegistry const callbacksRegistry = new CallbacksRegistry() @@ -49,10 +51,10 @@ const wrapArgs = function (args, visited) { value.then(onFulfilled, onRejected) }) } - } else if (v8Util.getHiddenValue(value, 'atomId')) { + } else if (privates(value).atomId) { return { type: 'remote-object', - id: v8Util.getHiddenValue(value, 'atomId') + id: privates(value).atomId } } @@ -70,7 +72,7 @@ const wrapArgs = function (args, visited) { } visited.delete(value) return meta - } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) { + } else if (typeof value === 'function' && privates(value).returnValue) { return { type: 'function-with-return-value', value: valueToMeta(value()) @@ -79,7 +81,7 @@ const wrapArgs = function (args, visited) { return { type: 'function', id: callbacksRegistry.add(value), - location: v8Util.getHiddenValue(value, 'location') + location: privates(value).location } } else { return { @@ -252,7 +254,7 @@ const metaToValue = function (meta) { v8Util.setRemoteObjectFreer(ret, meta.id) // Remember object's id. - v8Util.setHiddenValue(ret, 'atomId', meta.id) + privates(ret).atomId = meta.id remoteObjectCache.set(meta.id, ret) return ret } @@ -282,64 +284,52 @@ ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', function (event, id, args) { callbacksRegistry.apply(id, metaToValue(args)) }) -// A callback in browser is released. +// // A callback in browser is released. ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) { callbacksRegistry.remove(id) }) -// List all built-in modules in browser process. -const browserModules = require('../../browser/api/exports/electron') +var binding = {} -// And add a helper receiver for each one. -for (let name of Object.getOwnPropertyNames(browserModules)) { - Object.defineProperty(exports, name, { - get: function () { - return exports.getBuiltin(name) - } - }) -} - -// Get remote module. -exports.require = function (module) { +binding.require = function (module) { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module)) } // Alias to remote.require('electron').xxx. -exports.getBuiltin = function (module) { +binding.getBuiltin = function (module) { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module)) } // Get current BrowserWindow. -exports.getCurrentWindow = function () { +binding.getCurrentWindow = function () { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW')) } // Get current WebContents object. -exports.getCurrentWebContents = function () { +binding.getCurrentWebContents = function () { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS')) } -// Get a global object in browser. -exports.getGlobal = function (name) { - return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name)) +binding.getWebContents = function (tabId) { + const res = ipcRenderer.sendSync('ELECTRON_BROWSER_GET_WEB_CONTENTS', tabId) + return res && metaToValue(res) } -// Get the process object in browser. -exports.__defineGetter__('process', function () { - return exports.getGlobal('process') -}) - -// Create a funtion that will return the specifed value when called in browser. -exports.createFunctionWithReturnValue = function (returnValue) { - const func = function () { - return returnValue - } - v8Util.setHiddenValue(func, 'returnValue', true) - return func +binding.callAsyncWebContentsFunction = function (tabId, name, ...args) { + ipcRenderer.send('ELECTRON_BROWSER_ASYNC_MEMBER_CALL', tabId, name, wrapArgs(...args)) } -// Get the guest WebContents from guestInstanceId. -exports.getGuestWebContents = function (guestInstanceId) { - const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId) - return metaToValue(meta) +const deprecatedRemoteAPIs = ['app', 'Menu', 'shell', 'clipboard', 'session', 'systemPreferences', 'BrowserWindow'] +for (var i = 0, len = deprecatedRemoteAPIs.length; i < len; i++) { + const name = deprecatedRemoteAPIs[i] + Object.defineProperty(binding, name, { + get: function () { + return binding.getBuiltin(name) + } + }) } + +exports.$set('callAsyncWebContentsFunction', binding.callAsyncWebContentsFunction) +exports.$set('getWebContents', binding.getWebContents) +exports.$set('getCurrentWebContents', binding.getCurrentWebContents) +exports.$set('binding', binding) diff --git a/lib/renderer/chrome-api.js b/lib/renderer/chrome-api.js deleted file mode 100644 index b49d7382c0..0000000000 --- a/lib/renderer/chrome-api.js +++ /dev/null @@ -1,184 +0,0 @@ -const {ipcRenderer} = require('electron') -const Event = require('./extensions/event') -const url = require('url') - -let nextId = 0 - -class Tab { - constructor (tabId) { - this.id = tabId - } -} - -class MessageSender { - constructor (tabId, extensionId) { - this.tab = tabId ? new Tab(tabId) : null - this.id = extensionId - this.url = `chrome-extension://${extensionId}` - } -} - -class Port { - constructor (tabId, portId, extensionId, name) { - this.tabId = tabId - this.portId = portId - this.disconnected = false - - this.name = name - this.onDisconnect = new Event() - this.onMessage = new Event() - this.sender = new MessageSender(tabId, extensionId) - - ipcRenderer.once(`CHROME_PORT_DISCONNECT_${portId}`, () => { - this._onDisconnect() - }) - ipcRenderer.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (event, message) => { - const sendResponse = function () { console.error('sendResponse is not implemented') } - this.onMessage.emit(message, this.sender, sendResponse) - }) - } - - disconnect () { - if (this.disconnected) return - - ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`) - this._onDisconnect() - } - - postMessage (message) { - ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message) - } - - _onDisconnect () { - this.disconnected = true - ipcRenderer.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`) - this.onDisconnect.emit() - } -} - -// Inject chrome API to the |context| -exports.injectTo = function (extensionId, isBackgroundPage, context) { - const chrome = context.chrome = context.chrome || {} - - ipcRenderer.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (event, tabId, portId, connectInfo) => { - chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name)) - }) - - ipcRenderer.on(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (event, tabId, message) => { - chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId)) - }) - - ipcRenderer.on('CHROME_TABS_ONCREATED', (event, tabId) => { - chrome.tabs.onCreated.emit(new Tab(tabId)) - }) - - ipcRenderer.on('CHROME_TABS_ONREMOVED', (event, tabId) => { - chrome.tabs.onRemoved.emit(tabId) - }) - - chrome.runtime = { - id: extensionId, - - getURL: function (path) { - return url.format({ - protocol: 'chrome-extension', - slashes: true, - hostname: extensionId, - pathname: path - }) - }, - - connect (...args) { - if (isBackgroundPage) { - console.error('chrome.runtime.connect is not supported in background page') - return - } - - // Parse the optional args. - let targetExtensionId = extensionId - let connectInfo = {name: ''} - if (args.length === 1) { - connectInfo = args[0] - } else if (args.length === 2) { - [targetExtensionId, connectInfo] = args - } - - const {tabId, portId} = ipcRenderer.sendSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo) - return new Port(tabId, portId, extensionId, connectInfo.name) - }, - - sendMessage (...args) { - if (isBackgroundPage) { - console.error('chrome.runtime.sendMessage is not supported in background page') - return - } - - // Parse the optional args. - let targetExtensionId = extensionId - let message - if (args.length === 1) { - message = args[0] - } else if (args.length === 2) { - // A case of not provide extension-id: (message, responseCallback) - if (typeof args[1] === 'function') { - console.error('responseCallback is not supported') - message = args[0] - } else { - [targetExtensionId, message] = args - } - } else { - console.error('options and responseCallback are not supported') - } - - ipcRenderer.send('CHROME_RUNTIME_SENDMESSAGE', targetExtensionId, message) - }, - - onConnect: new Event(), - onMessage: new Event(), - onInstalled: new Event() - } - - chrome.tabs = { - executeScript (tabId, details, callback) { - const requestId = ++nextId - ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, (event, result) => { - callback([event.result]) - }) - ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', requestId, tabId, extensionId, details) - }, - - sendMessage (tabId, message, options, responseCallback) { - if (responseCallback) { - console.error('responseCallback is not supported') - } - ipcRenderer.send('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, isBackgroundPage, message) - }, - - onUpdated: new Event(), - onCreated: new Event(), - onRemoved: new Event() - } - - chrome.extension = { - getURL: chrome.runtime.getURL, - connect: chrome.runtime.connect, - onConnect: chrome.runtime.onConnect, - sendMessage: chrome.runtime.sendMessage, - onMessage: chrome.runtime.onMessage - } - - chrome.storage = require('./extensions/storage') - - chrome.pageAction = { - show () {}, - hide () {}, - setTitle () {}, - getTitle () {}, - setIcon () {}, - setPopup () {}, - getPopup () {} - } - - chrome.i18n = require('./extensions/i18n').setup(extensionId) - chrome.webNavigation = require('./extensions/web-navigation').setup() -} diff --git a/lib/renderer/content-scripts-injector.js b/lib/renderer/content-scripts-injector.js deleted file mode 100644 index e4a801110f..0000000000 --- a/lib/renderer/content-scripts-injector.js +++ /dev/null @@ -1,61 +0,0 @@ -const {ipcRenderer} = require('electron') -const {runInThisContext} = require('vm') - -// Check whether pattern matches. -// https://developer.chrome.com/extensions/match_patterns -const matchesPattern = function (pattern) { - if (pattern === '') return true - - const regexp = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$') - return location.href.match(regexp) -} - -// Run the code with chrome API integrated. -const runContentScript = function (extensionId, url, code) { - const context = {} - require('./chrome-api').injectTo(extensionId, false, context) - const wrapper = `(function (chrome) {\n ${code}\n })` - const compiledWrapper = runInThisContext(wrapper, { - filename: url, - lineOffset: 1, - displayErrors: true - }) - return compiledWrapper.call(this, context.chrome) -} - -// Run injected scripts. -// https://developer.chrome.com/extensions/content_scripts -const injectContentScript = function (extensionId, script) { - for (const match of script.matches) { - if (!matchesPattern(match)) return - } - - for (const {url, code} of script.js) { - const fire = runContentScript.bind(window, extensionId, url, code) - if (script.runAt === 'document_start') { - process.once('document-start', fire) - } else if (script.runAt === 'document_end') { - process.once('document-end', fire) - } else if (script.runAt === 'document_idle') { - document.addEventListener('DOMContentLoaded', fire) - } - } -} - -// Handle the request of chrome.tabs.executeJavaScript. -ipcRenderer.on('CHROME_TABS_EXECUTESCRIPT', function (event, senderWebContentsId, requestId, extensionId, url, code) { - const result = runContentScript.call(window, extensionId, url, code) - ipcRenderer.sendToAll(senderWebContentsId, `CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, result) -}) - -// Read the renderer process preferences. -const preferences = process.getRenderProcessPreferences() -if (preferences) { - for (const pref of preferences) { - if (pref.contentScripts) { - for (const script of pref.contentScripts) { - injectContentScript(pref.extensionId, script) - } - } - } -} diff --git a/lib/renderer/extensions/event.js b/lib/renderer/extensions/event.js deleted file mode 100644 index 4a951407f5..0000000000 --- a/lib/renderer/extensions/event.js +++ /dev/null @@ -1,24 +0,0 @@ -class Event { - constructor () { - this.listeners = [] - } - - addListener (callback) { - this.listeners.push(callback) - } - - removeListener (callback) { - const index = this.listeners.indexOf(callback) - if (index !== -1) { - this.listeners.splice(index, 1) - } - } - - emit (...args) { - for (const listener of this.listeners) { - listener(...args) - } - } -} - -module.exports = Event diff --git a/lib/renderer/extensions/i18n.js b/lib/renderer/extensions/i18n.js deleted file mode 100644 index d0279601f4..0000000000 --- a/lib/renderer/extensions/i18n.js +++ /dev/null @@ -1,84 +0,0 @@ -// Implementation of chrome.i18n.getMessage -// https://developer.chrome.com/extensions/i18n#method-getMessage -// -// Does not implement predefined messages: -// https://developer.chrome.com/extensions/i18n#overview-predefined - -const {ipcRenderer} = require('electron') -const fs = require('fs') -const path = require('path') - -let metadata - -const getExtensionMetadata = (extensionId) => { - if (!metadata) { - metadata = ipcRenderer.sendSync('CHROME_I18N_MANIFEST', extensionId) - } - return metadata -} - -const getMessagesPath = (extensionId, language) => { - const metadata = getExtensionMetadata(extensionId) - const defaultLocale = metadata.default_locale || 'en' - const localesDirectory = path.join(metadata.srcDirectory, '_locales') - let messagesPath = path.join(localesDirectory, language, 'messages.json') - if (!fs.statSyncNoException(messagesPath)) { - messagesPath = path.join(localesDirectory, defaultLocale, 'messages.json') - } - return messagesPath -} - -const getMessages = (extensionId, language) => { - try { - const messagesPath = getMessagesPath(extensionId, language) - return JSON.parse(fs.readFileSync(messagesPath)) || {} - } catch (error) { - return {} - } -} - -const getLanguage = () => { - return navigator.language.replace(/-.*$/, '').toLowerCase() -} - -const replaceNumberedSubstitutions = (message, substitutions) => { - return message.replace(/\$(\d+)/, (_, number) => { - const index = parseInt(number, 10) - 1 - return substitutions[index] || '' - }) -} - -const replacePlaceholders = (message, placeholders, substitutions) => { - if (typeof substitutions === 'string') { - substitutions = [substitutions] - } - if (!Array.isArray(substitutions)) { - substitutions = [] - } - - if (placeholders) { - Object.keys(placeholders).forEach((name) => { - let {content} = placeholders[name] - content = replaceNumberedSubstitutions(content, substitutions) - message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content) - }) - } - - return replaceNumberedSubstitutions(message, substitutions) -} - -const getMessage = (extensionId, messageName, substitutions) => { - const messages = getMessages(extensionId, getLanguage()) - if (messages.hasOwnProperty(messageName)) { - const {message, placeholders} = messages[messageName] - return replacePlaceholders(message, placeholders, substitutions) - } -} - -exports.setup = (extensionId) => { - return { - getMessage (messageName, substitutions) { - return getMessage(extensionId, messageName, substitutions) - } - } -} diff --git a/lib/renderer/extensions/storage.js b/lib/renderer/extensions/storage.js deleted file mode 100644 index af3978e648..0000000000 --- a/lib/renderer/extensions/storage.js +++ /dev/null @@ -1,67 +0,0 @@ -const getStorage = (storageType) => { - const data = window.localStorage.getItem(`__chrome.storage.${storageType}__`) - if (data != null) { - return JSON.parse(data) - } else { - return {} - } -} - -const setStorage = (storageType, storage) => { - const json = JSON.stringify(storage) - window.localStorage.setItem(`__chrome.storage.${storageType}__`, json) -} - -const scheduleCallback = (items, callback) => { - setTimeout(function () { - callback(items) - }) -} - -const getStorageManager = (storageType) => { - return { - get (keys, callback) { - const storage = getStorage(storageType) - if (keys == null) return scheduleCallback(storage, callback) - - let defaults = {} - switch (typeof keys) { - case 'string': - keys = [keys] - break - case 'object': - if (!Array.isArray(keys)) { - defaults = keys - keys = Object.keys(keys) - } - break - } - if (keys.length === 0) return scheduleCallback({}, callback) - - let items = {} - keys.forEach(function (key) { - var value = storage[key] - if (value == null) value = defaults[key] - items[key] = value - }) - scheduleCallback(items, callback) - }, - - set (items, callback) { - const storage = getStorage(storageType) - - Object.keys(items).forEach(function (name) { - storage[name] = items[name] - }) - - setStorage(storageType, storage) - - setTimeout(callback) - } - } -} - -module.exports = { - sync: getStorageManager('sync'), - local: getStorageManager('local') -} diff --git a/lib/renderer/extensions/web-navigation.js b/lib/renderer/extensions/web-navigation.js deleted file mode 100644 index 19faa80019..0000000000 --- a/lib/renderer/extensions/web-navigation.js +++ /dev/null @@ -1,21 +0,0 @@ -const Event = require('./event') -const {ipcRenderer} = require('electron') - -class WebNavigation { - constructor () { - this.onBeforeNavigate = new Event() - this.onCompleted = new Event() - - ipcRenderer.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event, details) => { - this.onBeforeNavigate.emit(details) - }) - - ipcRenderer.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event, details) => { - this.onCompleted.emit(details) - }) - } -} - -exports.setup = () => { - return new WebNavigation() -} diff --git a/lib/renderer/init.js b/lib/renderer/init.js deleted file mode 100644 index 12878ddc6e..0000000000 --- a/lib/renderer/init.js +++ /dev/null @@ -1,138 +0,0 @@ -'use strict' - -const events = require('events') -const path = require('path') -const Module = require('module') - -// We modified the original process.argv to let node.js load the -// atom-renderer.js, we need to restore it here. -process.argv.splice(1, 1) - -// Clear search paths. -require('../common/reset-search-paths') - -// Import common settings. -require('../common/init') - -var globalPaths = Module.globalPaths - -// Expose public APIs. -globalPaths.push(path.join(__dirname, 'api', 'exports')) - -// The global variable will be used by ipc for event dispatching -var v8Util = process.atomBinding('v8_util') - -v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter()) - -// Use electron module after everything is ready. -const electron = require('electron') - -// Call webFrame method. -electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => { - electron.webFrame[method].apply(electron.webFrame, args) -}) - -electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { - const result = electron.webFrame[method].apply(electron.webFrame, args) - event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result) -}) - -electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { - const responseCallback = function (result) { - event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, result) - } - args.push(responseCallback) - electron.webFrame[method].apply(electron.webFrame, args) -}) - -// Process command line arguments. -let nodeIntegration = 'false' -let preloadScript = null -let isBackgroundPage = false -for (let arg of process.argv) { - if (arg.indexOf('--guest-instance-id=') === 0) { - // This is a guest web view. - process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1)) - } else if (arg.indexOf('--opener-id=') === 0) { - // This is a guest BrowserWindow. - process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1)) - } else if (arg.indexOf('--node-integration=') === 0) { - nodeIntegration = arg.substr(arg.indexOf('=') + 1) - } else if (arg.indexOf('--preload=') === 0) { - preloadScript = arg.substr(arg.indexOf('=') + 1) - } else if (arg === '--background-page') { - isBackgroundPage = true - } -} - -if (window.location.protocol === 'chrome-devtools:') { - // Override some inspector APIs. - require('./inspector') - nodeIntegration = 'true' -} else if (window.location.protocol === 'chrome-extension:') { - // Add implementations of chrome API. - // require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window) - nodeIntegration = 'false' -} else { - // Override default web functions. - require('./override') - - // Inject content scripts. - require('./content-scripts-injector') - - // Load webview tag implementation. - if (nodeIntegration === 'true' && process.guestInstanceId == null) { - require('./web-view/web-view') - require('./web-view/web-view-attributes') - } -} - -if (nodeIntegration === 'true') { - // Export node bindings to global. - global.require = require - global.module = module - - // Set the __filename to the path of html file if it is file: protocol. - if (window.location.protocol === 'file:') { - var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname - global.__filename = path.normalize(decodeURIComponent(pathname)) - global.__dirname = path.dirname(global.__filename) - - // Set module's filename so relative require can work as expected. - module.filename = global.__filename - - // Also search for module under the html file. - module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname)) - } else { - global.__filename = __filename - global.__dirname = __dirname - } - - // Redirect window.onerror to uncaughtException. - window.onerror = function (message, filename, lineno, colno, error) { - if (global.process.listeners('uncaughtException').length > 0) { - global.process.emit('uncaughtException', error) - return true - } else { - return false - } - } -} else { - // Delete Node's symbols after the Environment has been loaded. - process.once('loaded', function () { - delete global.process - delete global.setImmediate - delete global.clearImmediate - delete global.global - }) -} - -// Load the script specfied by the "preload" attribute. -if (preloadScript) { - try { - require(preloadScript) - } catch (error) { - console.error('Unable to load preload script: ' + preloadScript) - console.error(error.stack || error.message) - } -} diff --git a/lib/renderer/inspector.js b/lib/renderer/inspector.js deleted file mode 100644 index 3192aa160e..0000000000 --- a/lib/renderer/inspector.js +++ /dev/null @@ -1,114 +0,0 @@ -window.onload = function () { - // Use menu API to show context menu. - window.InspectorFrontendHost.showContextMenuAtPoint = createMenu - - // Use dialog API to override file chooser dialog. - window.WebInspector.createFileSelectorElement = createFileSelectorElement -} - -const convertToMenuTemplate = function (items) { - return items.map(function (item) { - const transformed = item.type === 'subMenu' ? { - type: 'submenu', - label: item.label, - enabled: item.enabled, - submenu: convertToMenuTemplate(item.subItems) - } : item.type === 'separator' ? { - type: 'separator' - } : item.type === 'checkbox' ? { - type: 'checkbox', - label: item.label, - enabled: item.enabled, - checked: item.checked - } : { - type: 'normal', - label: item.label, - enabled: item.enabled - } - - if (item.id != null) { - transformed.click = function () { - window.DevToolsAPI.contextMenuItemSelected(item.id) - return window.DevToolsAPI.contextMenuCleared() - } - } - - return transformed - }) -} - -const createMenu = function (x, y, items) { - const {remote} = require('electron') - const {Menu} = remote - - let template = convertToMenuTemplate(items) - if (useEditMenuItems(x, y, template)) { - template = getEditMenuItems() - } - const menu = Menu.buildFromTemplate(template) - - // The menu is expected to show asynchronously. - setTimeout(function () { - menu.popup(remote.getCurrentWindow()) - }) -} - -const useEditMenuItems = function (x, y, items) { - return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) { - return element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA' || element.isContentEditable - }) -} - -const getEditMenuItems = function () { - return [ - { - role: 'undo' - }, - { - role: 'redo' - }, - { - type: 'separator' - }, - { - role: 'cut' - }, - { - role: 'copy' - }, - { - role: 'paste' - }, - { - role: 'pasteandmatchstyle' - }, - { - role: 'delete' - }, - { - role: 'selectall' - } - ] -} - -const showFileChooserDialog = function (callback) { - const {dialog} = require('electron').remote - const files = dialog.showOpenDialog({}) - if (files != null) { - callback(pathToHtml5FileObject(files[0])) - } -} - -const pathToHtml5FileObject = function (path) { - const fs = require('fs') - const blob = new Blob([fs.readFileSync(path)]) - blob.name = path - return blob -} - -const createFileSelectorElement = function (callback) { - const fileSelectorElement = document.createElement('span') - fileSelectorElement.style.display = 'none' - fileSelectorElement.click = showFileChooserDialog.bind(this, callback) - return fileSelectorElement -} diff --git a/lib/renderer/override.js b/lib/renderer/override.js deleted file mode 100644 index 502f6cb44c..0000000000 --- a/lib/renderer/override.js +++ /dev/null @@ -1,228 +0,0 @@ -'use strict' - -const ipcRenderer = require('electron').ipcRenderer -const remote = require('electron').remote - -// Helper function to resolve relative url. -var a = window.top.document.createElement('a') -var resolveURL = function (url) { - a.href = url - return a.href -} - -// Window object returned by "window.open". -var BrowserWindowProxy = (function () { - BrowserWindowProxy.proxies = {} - - BrowserWindowProxy.getOrCreate = function (guestId) { - var base = this.proxies - base[guestId] != null ? base[guestId] : base[guestId] = new BrowserWindowProxy(guestId) - return base[guestId] - } - - BrowserWindowProxy.remove = function (guestId) { - return delete this.proxies[guestId] - } - - function BrowserWindowProxy (guestId1) { - Object.defineProperty(this, 'guestId', { - configurable: false, - enumerable: true, - writeable: false, - value: guestId1 - }) - - this.closed = false - ipcRenderer.once('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + this.guestId, () => { - BrowserWindowProxy.remove(this.guestId) - this.closed = true - }) - } - - BrowserWindowProxy.prototype.close = function () { - return ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId) - } - - BrowserWindowProxy.prototype.focus = function () { - return ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus') - } - - BrowserWindowProxy.prototype.blur = function () { - return ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur') - } - - BrowserWindowProxy.prototype.print = function () { - return ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'print') - } - - Object.defineProperty(BrowserWindowProxy.prototype, 'location', { - get: function () { - return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'getURL') - }, - set: function (url) { - url = resolveURL(url) - return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'loadURL', url) - } - }) - - BrowserWindowProxy.prototype.postMessage = function (message, targetOrigin) { - if (targetOrigin == null) { - targetOrigin = '*' - } - return ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, targetOrigin, window.location.origin) - } - - BrowserWindowProxy.prototype['eval'] = function (...args) { - return ipcRenderer.send.apply(ipcRenderer, ['ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript'].concat(args)) - } - - return BrowserWindowProxy -})() - -if (process.guestInstanceId == null) { - // Override default window.close. - window.close = function () { - return remote.getCurrentWindow().close() - } -} - -// Make the browser window or guest view emit "new-window" event. -window.open = function (url, frameName, features) { - var feature, guestId, i, j, len, len1, name, options, ref1, ref2, value - if (frameName == null) { - frameName = '' - } - if (features == null) { - features = '' - } - options = {} - - const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'] - const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload'] - const disposition = 'new-window' - - // Make sure to get rid of excessive whitespace in the property name - ref1 = features.split(/,\s*/) - for (i = 0, len = ref1.length; i < len; i++) { - feature = ref1[i] - ref2 = feature.split(/\s*=/) - name = ref2[0] - value = ref2[1] - value = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : value - if (webPreferences.includes(name)) { - if (options.webPreferences == null) { - options.webPreferences = {} - } - options.webPreferences[name] = value - } else { - options[name] = value - } - } - if (options.left) { - if (options.x == null) { - options.x = options.left - } - } - if (options.top) { - if (options.y == null) { - options.y = options.top - } - } - if (options.title == null) { - options.title = frameName - } - if (options.width == null) { - options.width = 800 - } - if (options.height == null) { - options.height = 600 - } - - // Resolve relative urls. - if (url == null || url === '') { - url = 'about:blank' - } else { - url = resolveURL(url) - } - for (j = 0, len1 = ints.length; j < len1; j++) { - name = ints[j] - if (options[name] != null) { - options[name] = parseInt(options[name], 10) - } - } - guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, disposition, options) - if (guestId) { - return BrowserWindowProxy.getOrCreate(guestId) - } else { - return null - } -} - -// Use the dialog API to implement alert(). -window.alert = function (message = '', title = '') { - remote.dialog.showMessageBox(remote.getCurrentWindow(), { - message: String(message), - title: String(title), - buttons: ['OK'] - }) -} - -// And the confirm(). -window.confirm = function (message, title) { - var buttons, cancelId - if (title == null) { - title = '' - } - buttons = ['OK', 'Cancel'] - cancelId = 1 - return !remote.dialog.showMessageBox(remote.getCurrentWindow(), { - message: message, - title: title, - buttons: buttons, - cancelId: cancelId - }) -} - -// But we do not support prompt(). -window.prompt = function () { - throw new Error('prompt() is and will not be supported.') -} - -if (process.openerId != null) { - window.opener = BrowserWindowProxy.getOrCreate(process.openerId) -} - -ipcRenderer.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (event, sourceId, message, sourceOrigin) { - // Manually dispatch event instead of using postMessage because we also need to - // set event.source. - event = document.createEvent('Event') - event.initEvent('message', false, false) - event.data = message - event.origin = sourceOrigin - event.source = BrowserWindowProxy.getOrCreate(sourceId) - window.dispatchEvent(event) -}) - -// The initial visibilityState. -let cachedVisibilityState = process.argv.includes('--hidden-page') ? 'hidden' : 'visible' - -// Subscribe to visibilityState changes. -ipcRenderer.on('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', function (event, visibilityState) { - if (cachedVisibilityState !== visibilityState) { - cachedVisibilityState = visibilityState - document.dispatchEvent(new Event('visibilitychange')) - } -}) - -// Make document.hidden and document.visibilityState return the correct value. -Object.defineProperty(document, 'hidden', { - get: function () { - return cachedVisibilityState !== 'visible' - } -}) - -Object.defineProperty(document, 'visibilityState', { - get: function () { - return cachedVisibilityState - } -}) diff --git a/lib/renderer/web-view/guest-view-internal.js b/lib/renderer/web-view/guest-view-internal.js index 99b62a3dd4..7e1aa160cf 100644 --- a/lib/renderer/web-view/guest-view-internal.js +++ b/lib/renderer/web-view/guest-view-internal.js @@ -1,14 +1,11 @@ 'use strict' -const ipcRenderer = require('electron').ipcRenderer -const webFrame = require('electron').webFrame - -var requestId = 0 +const ipcRenderer = require('ipc_utils') var WEB_VIEW_EVENTS = { 'load-start': ['url', 'isMainFrame', 'isErrorPage', 'isFrameSrcDoc'], 'load-commit': ['url', 'isMainFrame'], - 'did-attach': ['guestProxyRoutingId'], + 'did-attach': [], 'did-detach': [], 'did-finish-load': [], 'did-fail-provisional-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame'], @@ -77,58 +74,26 @@ var dispatchEvent = function (webView, eventName, eventKey, ...args) { domEvent[f] = args[i] } webView.dispatchEvent(domEvent) - if (eventName === 'load-commit') { - return webView.onLoadCommit(domEvent) - } } -module.exports = { - registerEvents: function (webView, viewInstanceId) { - ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + viewInstanceId, function (event, eventName, ...args) { +const GuestViewInternal = { + registerEvents: function (webView, tabId) { + ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + tabId, function (event, eventName, ...args) { dispatchEvent.apply(null, [webView, eventName, eventName].concat(args)) }) - ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + viewInstanceId, function (event, channel, ...args) { + ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + tabId, function (event, channel, ...args) { var domEvent = new Event('ipc-message') domEvent.channel = channel domEvent.args = args webView.dispatchEvent(domEvent) }) - return ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + viewInstanceId, function (event, ...args) { - var domEvent, f, i, j, len, ref1 - domEvent = new Event('size-changed') - ref1 = ['oldWidth', 'oldHeight', 'newWidth', 'newHeight'] - for (i = j = 0, len = ref1.length; j < len; i = ++j) { - f = ref1[i] - domEvent[f] = args[i] - } - webView.onSizeChanged(domEvent) - }) - }, - deregisterEvents: function (viewInstanceId) { - ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + viewInstanceId) - ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + viewInstanceId) - return ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + viewInstanceId) - }, - addGuest: function (params, callback) { - requestId++ - ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ADD_GUEST', params, requestId) - return ipcRenderer.once('ELECTRON_RESPONSE_' + requestId, callback) }, - createGuest: function (params, callback) { - requestId++ - ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId) - return ipcRenderer.once('ELECTRON_RESPONSE_' + requestId, callback) - }, - attachGuest: function (elementInstanceId, guestInstanceId, params) { - ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params) - return webFrame.attachGuest(elementInstanceId) - }, - destroyGuest: function (guestInstanceId) { - return ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId) - }, - setSize: function (guestInstanceId, params) { - return ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', guestInstanceId, params) + deregisterEvents: function (tabId) { + ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + tabId) + ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + tabId) } } + +exports.$set('GuestViewInternal', GuestViewInternal); diff --git a/lib/renderer/web-view/web-view-attributes.js b/lib/renderer/web-view/web-view-attributes.js deleted file mode 100644 index d42caee50d..0000000000 --- a/lib/renderer/web-view/web-view-attributes.js +++ /dev/null @@ -1,319 +0,0 @@ -'use strict' - -const WebViewImpl = require('./web-view') -const guestViewInternal = require('./guest-view-internal') -const webViewConstants = require('./web-view-constants') -const remote = require('electron').remote - -// Helper function to resolve url set in attribute. -var a = document.createElement('a') - -var resolveURL = function (url) { - if (url === '') return '' - a.href = url - return a.href -} - -// Attribute objects. -// Default implementation of a WebView attribute. -class WebViewAttribute { - constructor (name, webViewImpl) { - this.name = name - this.value = webViewImpl.webviewNode[name] || '' - this.webViewImpl = webViewImpl - this.ignoreMutation = false - this.defineProperty() - } - - // Retrieves and returns the attribute's value. - getValue () { - return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value - } - - // Sets the attribute's value. - setValue (value) { - return this.webViewImpl.webviewNode.setAttribute(this.name, value || '') - } - - // Changes the attribute's value without triggering its mutation handler. - setValueIgnoreMutation (value) { - this.ignoreMutation = true - this.setValue(value) - this.ignoreMutation = false - } - - // Defines this attribute as a property on the webview node. - defineProperty () { - return Object.defineProperty(this.webViewImpl.webviewNode, this.name, { - get: () => { - return this.getValue() - }, - set: (value) => { - return this.setValue(value) - }, - enumerable: true - }) - } - - // Called when the attribute's value changes. - handleMutation () {} -} - -// An attribute that is treated as a Boolean. -class BooleanAttribute extends WebViewAttribute { - getValue () { - return this.webViewImpl.webviewNode.hasAttribute(this.name) - } - - setValue (value) { - if (!value) { - return this.webViewImpl.webviewNode.removeAttribute(this.name) - } else { - return this.webViewImpl.webviewNode.setAttribute(this.name, '') - } - } -} - -// Attribute used to define the demension limits of autosizing. -class AutosizeDimensionAttribute extends WebViewAttribute { - getValue () { - return parseInt(this.webViewImpl.webviewNode.getAttribute(this.name)) || 0 - } - - handleMutation () { - if (!this.webViewImpl.guestInstanceId) { - return - } - return guestViewInternal.setSize(this.webViewImpl.guestInstanceId, { - enableAutoSize: this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue(), - min: { - width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0), - height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0) - }, - max: { - width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0), - height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0) - } - }) - } -} - -// Attribute that specifies whether the webview should be autosized. -class AutosizeAttribute extends BooleanAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl) - } -} - -AutosizeAttribute.prototype.handleMutation = AutosizeDimensionAttribute.prototype.handleMutation - -// Attribute representing the state of the storage partition. -class PartitionAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_PARTITION, webViewImpl) - this.validPartitionId = true - } - - handleMutation (oldValue, newValue) { - newValue = newValue || '' - - // The partition cannot change if the webview has already navigated. - if (!this.webViewImpl.beforeFirstNavigation) { - window.console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED) - this.setValueIgnoreMutation(oldValue) - return - } - if (newValue === 'persist:') { - this.validPartitionId = false - return window.console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE) - } - } -} - -// Attribute that handles the location and navigation of the webview. -class SrcAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_SRC, webViewImpl) - this.setupMutationObserver() - } - - getValue () { - if (this.webViewImpl.webviewNode.hasAttribute(this.name)) { - return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) - } else { - return this.value - } - } - - setValueIgnoreMutation (value) { - super.setValueIgnoreMutation(value) - - // takeRecords() is needed to clear queued up src mutations. Without it, it - // is possible for this change to get picked up asyncronously by src's - // mutation observer |observer|, and then get handled even though we do not - // want to handle this mutation. - return this.observer.takeRecords() - } - - handleMutation (oldValue, newValue) { - // Once we have navigated, we don't allow clearing the src attribute. - // Once enters a navigated state, it cannot return to a - // placeholder state. - if (!newValue && oldValue) { - // src attribute changes normally initiate a navigation. We suppress - // the next src attribute handler call to avoid reloading the page - // on every guest-initiated navigation. - this.setValueIgnoreMutation(oldValue) - return - } - return this.parse() - } - - // The purpose of this mutation observer is to catch assignment to the src - // attribute without any changes to its value. This is useful in the case - // where the webview guest has crashed and navigating to the same address - // spawns off a new process. - setupMutationObserver () { - var params - this.observer = new MutationObserver((mutations) => { - var i, len, mutation, newValue, oldValue - for (i = 0, len = mutations.length; i < len; i++) { - mutation = mutations[i] - oldValue = mutation.oldValue - newValue = this.getValue() - if (oldValue !== newValue) { - return - } - this.handleMutation(oldValue, newValue) - } - }) - params = { - attributes: true, - attributeOldValue: true, - attributeFilter: [this.name] - } - return this.observer.observe(this.webViewImpl.webviewNode, params) - } - - parse () { - var guestContents, httpreferrer, opts, useragent - if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) { - return - } - if (this.webViewImpl.guestInstanceId == null) { - if (this.webViewImpl.beforeFirstNavigation) { - this.webViewImpl.beforeFirstNavigation = false - this.webViewImpl.createGuest() - } - return - } - - // Navigate to |this.src|. - opts = {} - httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue() - if (httpreferrer) { - opts.httpReferrer = httpreferrer - } - useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue() - if (useragent) { - opts.userAgent = useragent - } - guestContents = remote.getGuestWebContents(this.webViewImpl.guestInstanceId) - return guestContents.loadURL(this.getValue(), opts) - } -} - -// Attribute specifies HTTP referrer. -class HttpReferrerAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl) - } -} - -// Attribute specifies user agent -class UserAgentAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl) - } -} - -// Attribute that set preload script. -class PreloadAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl) - } - - getValue () { - var preload, protocol - if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) { - return this.value - } - preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) - protocol = preload.substr(0, 5) - if (protocol !== 'file:') { - console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE) - preload = '' - } - return preload - } -} - -// Attribute that specifies the blink features to be enabled. -class BlinkFeaturesAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_BLINKFEATURES, webViewImpl) - } -} - -// Attribute that specifies the blink features to be disabled. -class DisableBlinkFeaturesAttribute extends WebViewAttribute { - constructor (webViewImpl) { - super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl) - } -} - -// Attribute specifies guest instance id -// Attribute specifies guest instance id that should be attached -// to this webview -class GuestInstanceIdAttribute extends WebViewAttribute { - constructor(webViewImpl) { - super(webViewConstants.ATTRIBUTE_GUEST_INSTANCE_ID, webViewImpl); - } - - handleMutation (oldValue, newValue) { - if (!oldValue) { - this.webViewImpl.guestInstanceId = parseInt(newValue) - this.webViewImpl.addGuest(this.webViewImpl.guestInstanceId) - this.webViewImpl.beforeFirstNavigation = false - } else { - this.setValueIgnoreMutation(oldValue) - } - } -} - -// Sets up all of the webview attributes. -WebViewImpl.prototype.setupWebViewAttributes = function () { - this.attributes = {} - this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = new AutosizeAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_GUEST_INSTANCE_ID] = new GuestInstanceIdAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this) - this.attributes[webViewConstants.ATTRIBUTE_ALLOWDISPLAYINGINSECURECONTENT] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWDISPLAYINGINSECURECONTENT, this) - this.attributes[webViewConstants.ATTRIBUTE_ALLOWRUNNINGINSECURECONTENT] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWRUNNINGINSECURECONTENT, this) - this.attributes[webViewConstants.ATTRIBUTE_ALLOWFILEACCESSFROMFILEURLS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWFILEACCESSFROMFILEURLS, this) - this.attributes[webViewConstants.ATTRIBUTE_ALLOWUNIVERSALACCESSFROMFILEURLS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWUNIVERSALACCESSFROMFILEURLS, this) - this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this) - this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this) - this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this) - this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) - this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) - - const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH] - autosizeAttributes.forEach((attribute) => { - this.attributes[attribute] = new AutosizeDimensionAttribute(attribute, this) - }) -} diff --git a/lib/renderer/web-view/web-view-constants.js b/lib/renderer/web-view/web-view-constants.js deleted file mode 100644 index 6305817a24..0000000000 --- a/lib/renderer/web-view/web-view-constants.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = { - // Attributes. - ATTRIBUTE_AUTOSIZE: 'autosize', - ATTRIBUTE_MAXHEIGHT: 'maxheight', - ATTRIBUTE_MAXWIDTH: 'maxwidth', - ATTRIBUTE_MINHEIGHT: 'minheight', - ATTRIBUTE_MINWIDTH: 'minwidth', - ATTRIBUTE_NAME: 'name', - ATTRIBUTE_PARTITION: 'partition', - ATTRIBUTE_SRC: 'src', - ATTRIBUTE_HTTPREFERRER: 'httpreferrer', - ATTRIBUTE_NODEINTEGRATION: 'nodeintegration', - ATTRIBUTE_ALLOWDISPLAYINGINSECURECONTENT: 'allowDisplayingInsecureContent', - ATTRIBUTE_ALLOWRUNNINGINSECURECONTENT: 'allowRunningInsecureContent', - ATTRIBUTE_ALLOWFILEACCESSFROMFILEURLS: 'allowFileAccessFromFileUrls', - ATTRIBUTE_ALLOWUNIVERSALACCESSFROMFILEURLS: 'allowUniversalAccessFromFileUrls', - ATTRIBUTE_PLUGINS: 'plugins', - ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity', - ATTRIBUTE_ALLOWPOPUPS: 'allowpopups', - ATTRIBUTE_PRELOAD: 'preload', - ATTRIBUTE_USERAGENT: 'useragent', - ATTRIBUTE_BLINKFEATURES: 'blinkfeatures', - ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures', - ATTRIBUTE_GUEST_INSTANCE_ID: 'data-guest-instance-id', - - // Internal attribute. - ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid', - - // Error messages. - ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.', - ERROR_MSG_CANNOT_INJECT_SCRIPT: ': ' + 'Script cannot be injected into content until the page has loaded.', - ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.', - ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.' -} diff --git a/lib/renderer/web-view/web-view.js b/lib/renderer/web-view/web-view.js deleted file mode 100644 index 02e3c0063a..0000000000 --- a/lib/renderer/web-view/web-view.js +++ /dev/null @@ -1,533 +0,0 @@ -'use strict' - -const webFrame = require('electron').webFrame -const remote = require('electron').remote -const ipcRenderer = require('electron').ipcRenderer - -const v8Util = process.atomBinding('v8_util') -const guestViewInternal = require('./guest-view-internal') -const webViewConstants = require('./web-view-constants') - -var hasProp = {}.hasOwnProperty - -// ID generator. -var nextId = 0 - -var getNextId = function () { - return ++nextId -} - -// Represents the internal state of the WebView node. -var WebViewImpl = (function () { - function WebViewImpl (webviewNode) { - var shadowRoot - this.webviewNode = webviewNode - v8Util.setHiddenValue(this.webviewNode, 'internal', this) - this.attached = false - this.elementAttached = false - this.beforeFirstNavigation = true - - // on* Event handlers. - this.on = {} - this.browserPluginNode = this.createBrowserPluginNode() - shadowRoot = this.webviewNode.createShadowRoot() - shadowRoot.innerHTML = '' - this.setupWebViewAttributes() - this.setupFocusPropagation() - this.viewInstanceId = getNextId() - shadowRoot.appendChild(this.browserPluginNode) - - // Subscribe to host's zoom level changes. - this.onZoomLevelChanged = (zoomLevel) => { - this.webviewNode.setZoomLevel(zoomLevel) - } - webFrame.on('zoom-level-changed', this.onZoomLevelChanged) - - this.onVisibilityChanged = (event, visibilityState) => { - this.webviewNode.send('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', visibilityState) - } - ipcRenderer.on('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', this.onVisibilityChanged) - - Object.defineProperty(this.webviewNode, 'contentWindow', { - enumerable: true, - configurable: false, - get: function() { - var guestInstanceId = v8Util.getHiddenValue(this, 'internal').guestInstanceId - var guest = remote.getGuestWebContents(guestInstanceId) - if (guest) { - return webFrame.getContentWindow(guest.getContentWindowId()) - } else { - return null - } - } - }) - } - - WebViewImpl.prototype.createBrowserPluginNode = function () { - // We create BrowserPlugin as a custom element in order to observe changes - // to attributes synchronously. - var browserPluginNode = new WebViewImpl.BrowserPlugin() - v8Util.setHiddenValue(browserPluginNode, 'internal', this) - return browserPluginNode - } - - // Resets some state upon reattaching element to the DOM. - WebViewImpl.prototype.reset = function (destroyGuest = true) { - // Unlisten the zoom-level-changed event. - webFrame.removeListener('zoom-level-changed', this.onZoomLevelChanged) - ipcRenderer.removeListener('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', this.onVisibilityChanged) - - // If guestInstanceId is defined then the has navigated and has - // already picked up a partition ID. Thus, we need to reset the initialization - // state. However, it may be the case that beforeFirstNavigation is false BUT - // guestInstanceId has yet to be initialized. This means that we have not - // heard back from createGuest yet. We will not reset the flag in this case so - // that we don't end up allocating a second guest. - if (this.guestInstanceId) { - if (destroyGuest) { - guestViewInternal.destroyGuest(this.guestInstanceId) - } - this.webContents = null - this.guestInstanceId = void 0 - this.beforeFirstNavigation = true - this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true - } - this.internalInstanceId = 0 - } - - // Sets the .request property. - WebViewImpl.prototype.setRequestPropertyOnWebViewNode = function (request) { - return Object.defineProperty(this.webviewNode, 'request', { - value: request, - enumerable: true - }) - } - - WebViewImpl.prototype.setupFocusPropagation = function () { - if (!this.webviewNode.hasAttribute('tabIndex')) { - // needs a tabIndex in order to be focusable. - // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute - // to allow to be focusable. - // See http://crbug.com/231664. - this.webviewNode.setAttribute('tabIndex', -1) - } - - // Focus the BrowserPlugin when the takes focus. - this.webviewNode.addEventListener('focus', () => { - this.browserPluginNode.focus() - }) - - // Blur the BrowserPlugin when the loses focus. - this.webviewNode.addEventListener('blur', () => { - this.browserPluginNode.blur() - }) - } - - // This observer monitors mutations to attributes of the and - // updates the BrowserPlugin properties accordingly. In turn, updating - // a BrowserPlugin property will update the corresponding BrowserPlugin - // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more - // details. - WebViewImpl.prototype.handleWebviewAttributeMutation = function (attributeName, oldValue, newValue) { - if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) { - return - } - - // Let the changed attribute handle its own mutation - return this.attributes[attributeName].handleMutation(oldValue, newValue) - } - - WebViewImpl.prototype.handleBrowserPluginAttributeMutation = function (attributeName, oldValue, newValue) { - if (attributeName === webViewConstants.ATTRIBUTE_INTERNALINSTANCEID && !oldValue && !!newValue) { - this.browserPluginNode.removeAttribute(webViewConstants.ATTRIBUTE_INTERNALINSTANCEID) - this.internalInstanceId = parseInt(newValue) - - // Track when the element resizes using the element resize callback. - webFrame.registerElementResizeCallback(this.internalInstanceId, this.onElementResize.bind(this)) - if (!this.guestInstanceId) { - return - } - return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams()) - } - } - - WebViewImpl.prototype.onSizeChanged = function (webViewEvent) { - var maxHeight, maxWidth, minHeight, minWidth, newHeight, newWidth, node, width - newWidth = webViewEvent.newWidth - newHeight = webViewEvent.newHeight - node = this.webviewNode - width = node.offsetWidth - - // Check the current bounds to make sure we do not resize - // outside of current constraints. - maxWidth = this.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() | width - maxHeight = this.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | width - minWidth = this.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() | width - minHeight = this.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | width - minWidth = Math.min(minWidth, maxWidth) - minHeight = Math.min(minHeight, maxHeight) - if (!this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || (newWidth >= minWidth && newWidth <= maxWidth && newHeight >= minHeight && newHeight <= maxHeight)) { - node.style.width = newWidth + 'px' - node.style.height = newHeight + 'px' - - // Only fire the DOM event if the size of the has actually - // changed. - return this.dispatchEvent(webViewEvent) - } - } - - WebViewImpl.prototype.onElementResize = function (newSize) { - // Dispatch the 'resize' event. - var resizeEvent - resizeEvent = new Event('resize', { - bubbles: true - }) - - // Using client size values, because when a webview is transformed `newSize` - // is incorrect - newSize.width = this.webviewNode.clientWidth - newSize.height = this.webviewNode.clientHeight - - resizeEvent.newWidth = newSize.width - resizeEvent.newHeight = newSize.height - this.dispatchEvent(resizeEvent) - if (this.guestInstanceId) { - return guestViewInternal.setSize(this.guestInstanceId, { - normal: newSize - }) - } - } - - WebViewImpl.prototype.addGuest = function (guestInstanceId) { - return guestViewInternal.addGuest(guestInstanceId, (function(_this) { - return function(event, guestInstanceId) { - return _this.attachWindow(guestInstanceId) - } - })(this)) - } - - WebViewImpl.prototype.createGuest = function () { - return guestViewInternal.createGuest(this.buildParams(), (event, guestInstanceId) => { - this.attachWindow(guestInstanceId) - }) - } - - WebViewImpl.prototype.dispatchEvent = function (webViewEvent) { - return this.webviewNode.dispatchEvent(webViewEvent) - } - - // Adds an 'on' property on the webview, which can be used to set/unset - // an event handler. - WebViewImpl.prototype.setupEventProperty = function (eventName) { - var propertyName - propertyName = 'on' + eventName.toLowerCase() - return Object.defineProperty(this.webviewNode, propertyName, { - get: () => { - return this.on[propertyName] - }, - set: (value) => { - if (this.on[propertyName]) { - this.webviewNode.removeEventListener(eventName, this.on[propertyName]) - } - this.on[propertyName] = value - if (value) { - return this.webviewNode.addEventListener(eventName, value) - } - }, - enumerable: true - }) - } - - // Updates state upon loadcommit. - WebViewImpl.prototype.onLoadCommit = function (webViewEvent) { - var newValue, oldValue - oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC) - newValue = webViewEvent.url - if (webViewEvent.isMainFrame && (oldValue !== newValue)) { - // Touching the src attribute triggers a navigation. To avoid - // triggering a page reload on every guest-initiated navigation, - // we do not handle this mutation. - return this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue) - } - } - - WebViewImpl.prototype.onAttach = function (storagePartitionId) { - return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId) - } - - WebViewImpl.prototype.buildParams = function () { - var attribute, attributeName, css, elementRect, params, ref1 - params = { - instanceId: this.viewInstanceId, - userAgentOverride: this.userAgentOverride, - zoomFactor: webFrame.getZoomFactor() - } - ref1 = this.attributes - for (attributeName in ref1) { - if (!hasProp.call(ref1, attributeName)) continue - attribute = ref1[attributeName] - params[attributeName] = attribute.getValue() - } - - // When the WebView is not participating in layout (display:none) - // then getBoundingClientRect() would report a width and height of 0. - // However, in the case where the WebView has a fixed size we can - // use that value to initially size the guest so as to avoid a relayout of - // the on display:block. - css = window.getComputedStyle(this.webviewNode, null) - elementRect = this.webviewNode.getBoundingClientRect() - params.elementWidth = parseInt(elementRect.width) || parseInt(css.getPropertyValue('width')) - params.elementHeight = parseInt(elementRect.height) || parseInt(css.getPropertyValue('height')) - return params - } - - WebViewImpl.prototype.attachWindow = function (guestInstanceId) { - this.guestInstanceId = guestInstanceId - this.webContents = remote.getGuestWebContents(this.guestInstanceId) - if (!this.internalInstanceId) { - return true - } - return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams()) - } - - return WebViewImpl -})() - -// Registers browser plugin custom element. -var registerBrowserPluginElement = function () { - var proto = Object.create(HTMLObjectElement.prototype) - proto.createdCallback = function () { - this.setAttribute('type', 'application/browser-plugin') - this.setAttribute('id', 'browser-plugin-' + getNextId()) - - // The node fills in the container. - this.style.flex = '1 1 auto' - } - proto.attributeChangedCallback = function (name, oldValue, newValue) { - var internal - internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - return internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue) - } - proto.attachedCallback = function () { - // Load the plugin immediately. - return this.nonExistentAttribute - } - WebViewImpl.BrowserPlugin = webFrame.registerEmbedderCustomElement('browserplugin', { - 'extends': 'object', - prototype: proto - }) - delete proto.createdCallback - delete proto.attachedCallback - delete proto.detachedCallback - return delete proto.attributeChangedCallback -} - -// Registers custom element. -var registerWebViewElement = function () { - var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, proto - proto = Object.create(HTMLObjectElement.prototype) - proto.createdCallback = function () { - return new WebViewImpl(this) - } - proto.attributeChangedCallback = function (name, oldValue, newValue) { - var internal - internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - return internal.handleWebviewAttributeMutation(name, oldValue, newValue) - } - proto.__defineGetter__('guestInstanceId', function () { - var internal - internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - return internal.guestInstanceId - }) - proto.detach = function (destroyGuest = false) { - var internal - internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - if (internal.elementAttached) { - guestViewInternal.deregisterEvents(internal.viewInstanceId) - internal.elementAttached = false - let guestInstanceId = internal.guestInstanceId - internal.reset(destroyGuest) - return guestInstanceId - } - } - proto.detachedCallback = function () { - this.detach(true) - } - proto.attachedCallback = function () { - var internal - internal = v8Util.getHiddenValue(this, 'internal') - if (!internal) { - return - } - if (!internal.elementAttached) { - guestViewInternal.registerEvents(internal, internal.viewInstanceId) - internal.elementAttached = true - return internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse() - } - } - proto.clone = function (options) { - const internal = v8Util.getHiddenValue(this, 'internal') - if (internal.webContents) { - const options = Object.assign({}, internal.webContents.getWebPreferences()) - return internal.webContents.clone(options) - } else { - throw new Error(`Cannot call clone because the webContents is unavailable. The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.`) - } - } - - // Public-facing API methods. - methods = [ - 'getURL', - 'loadURL', - 'getTitle', - 'isInitialBlankNavigation', - 'isLoading', - 'isLoadingMainFrame', - 'isWaitingForResponse', - 'stop', - 'reload', - 'reloadIgnoringCache', - 'canGoBack', - 'canGoForward', - 'canGoToOffset', - 'clearHistory', - 'goBack', - 'goForward', - 'goToIndex', - 'goToOffset', - 'isCrashed', - 'setUserAgent', - 'getUserAgent', - 'openDevTools', - 'closeDevTools', - 'isDevToolsOpened', - 'isDevToolsFocused', - 'inspectElement', - 'setAudioMuted', - 'isAudioMuted', - 'undo', - 'redo', - 'cut', - 'copy', - 'paste', - 'pasteAndMatchStyle', - 'delete', - 'selectAll', - 'unselect', - 'replace', - 'replaceMisspelling', - 'findInPage', - 'stopFindInPage', - 'getId', - 'downloadURL', - 'inspectServiceWorker', - 'print', - 'printToPDF', - 'showDefinitionForSelection', - 'capturePage', - 'setActive', - 'setWebRTCIPHandlingPolicy', - 'getWebRTCIPHandlingPolicy', - 'executeScriptInTab', - 'setZoomLevel', - 'zoomIn', - 'zoomOut', - 'zoomReset', - 'getZoomPercent', - 'enablePreferredSizeMode', - 'getPreferredSize', - 'close' - ] - nonblockMethods = [ - 'insertCSS', - 'insertText', - 'send', - 'sendInputEvent', - 'setZoomFactor', - 'setZoomLevel', - 'setZoomLevelLimits' - ] - - // Forward proto.foo* method calls to WebViewImpl.foo*. - createBlockHandler = function (m) { - return function (...args) { - const internal = v8Util.getHiddenValue(this, 'internal') - if (internal.webContents) { - return internal.webContents[m].apply(internal.webContents, args) - } else { - throw new Error(`Cannot call ${m} because the webContents is unavailable. The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.`) - } - } - } - for (i = 0, len = methods.length; i < len; i++) { - m = methods[i] - proto[m] = createBlockHandler(m) - } - createNonBlockHandler = function (m) { - return function (...args) { - const internal = v8Util.getHiddenValue(this, 'internal') - return ipcRenderer.send.apply(ipcRenderer, ['ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', null, internal.guestInstanceId, m].concat(args)) - } - } - for (j = 0, len1 = nonblockMethods.length; j < len1; j++) { - m = nonblockMethods[j] - proto[m] = createNonBlockHandler(m) - } - - proto.executeJavaScript = function (code, hasUserGesture, callback) { - var internal = v8Util.getHiddenValue(this, 'internal') - if (typeof hasUserGesture === 'function') { - callback = hasUserGesture - hasUserGesture = false - } - let requestId = getNextId() - ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture) - ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, result) { - if (callback) callback(result) - }) - } - - // WebContents associated with this webview. - proto.getWebContents = function () { - var internal = v8Util.getHiddenValue(this, 'internal') - return internal.webContents - } - - window.WebView = webFrame.registerEmbedderCustomElement('webview', { - prototype: proto - }) - - // Delete the callbacks so developers cannot call them and produce unexpected - // behavior. - delete proto.createdCallback - delete proto.attachedCallback - delete proto.detachedCallback - return delete proto.attributeChangedCallback -} - -var useCapture = true - -var listener = function (event) { - if (document.readyState === 'loading') { - return - } - registerBrowserPluginElement() - registerWebViewElement() - return window.removeEventListener(event.type, listener, useCapture) -} - -window.addEventListener('readystatechange', listener, true) - -module.exports = WebViewImpl diff --git a/lib/util/buffer.js b/lib/util/buffer.js new file mode 100644 index 0000000000..7e7c34acbe --- /dev/null +++ b/lib/util/buffer.js @@ -0,0 +1,1774 @@ +// from https://github.com/feross/buffer +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.buffer=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + var len = b64.length + placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(b64.length * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length + + var L = 0 + + function push (v) { + arr[L++] = v + } + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) + push((tmp & 0xFF0000) >> 16) + push((tmp & 0xFF00) >> 8) + push(tmp & 0xFF) + } + + if (placeHolders === 2) { + tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) + push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) + push((tmp >> 8) & 0xFF) + push(tmp & 0xFF) + } + + return arr + } + + function uint8ToBase64 (uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length + + function encode (num) { + return lookup.charAt(num) + } + + function tripletToBase64 (num) { + return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) + } + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output += tripletToBase64(temp) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1] + output += encode(temp >> 2) + output += encode((temp << 4) & 0x3F) + output += '==' + break + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) + output += encode(temp >> 10) + output += encode((temp >> 4) & 0x3F) + output += encode((temp << 2) & 0x3F) + output += '=' + break + } + + return output + } + + exports.toByteArray = b64ToByteArray + exports.fromByteArray = uint8ToBase64 +}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) + +},{}],2:[function(require,module,exports){ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],3:[function(require,module,exports){ +var toString = {}.toString; + +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; + +},{}],"buffer":[function(require,module,exports){ +(function (global){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +var base64 = require('base64-js') +var ieee754 = require('ieee754') +var isArray = require('isarray') + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 +Buffer.poolSize = 8192 // not used by this implementation + +var rootParent = {} + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Use Object implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * Due to various browser bugs, sometimes the Object implementation will be used even + * when the browser supports typed arrays. + * + * Note: + * + * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, + * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. + * + * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property + * on objects. + * + * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. + * + * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of + * incorrect length in some situations. + + * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they + * get the Object implementation, which is slower but behaves correctly. + */ +Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined + ? global.TYPED_ARRAY_SUPPORT + : typedArraySupport() + +function typedArraySupport () { + function Bar () {} + try { + var arr = new Uint8Array(1) + arr.foo = function () { return 42 } + arr.constructor = Bar + return arr.foo() === 42 && // typed array instances can be augmented + arr.constructor === Bar && // constructor can be set + typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` + arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + } catch (e) { + return false + } +} + +function kMaxLength () { + return Buffer.TYPED_ARRAY_SUPPORT + ? 0x7fffffff + : 0x3fffffff +} + +/** + * Class: Buffer + * ============= + * + * The Buffer constructor returns instances of `Uint8Array` that are augmented + * with function properties for all the node `Buffer` API functions. We use + * `Uint8Array` so that square bracket notation works as expected -- it returns + * a single octet. + * + * By augmenting the instances, we can avoid modifying the `Uint8Array` + * prototype. + */ +function Buffer (arg) { + if (!(this instanceof Buffer)) { + // Avoid going through an ArgumentsAdaptorTrampoline in the common case. + if (arguments.length > 1) return new Buffer(arg, arguments[1]) + return new Buffer(arg) + } + + if (!Buffer.TYPED_ARRAY_SUPPORT) { + this.length = 0 + this.parent = undefined + } + + // Common case. + if (typeof arg === 'number') { + return fromNumber(this, arg) + } + + // Slightly less common case. + if (typeof arg === 'string') { + return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') + } + + // Unusual. + return fromObject(this, arg) +} + +function fromNumber (that, length) { + that = allocate(that, length < 0 ? 0 : checked(length) | 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) { + for (var i = 0; i < length; i++) { + that[i] = 0 + } + } + return that +} + +function fromString (that, string, encoding) { + if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' + + // Assumption: byteLength() return value is always < kMaxLength. + var length = byteLength(string, encoding) | 0 + that = allocate(that, length) + + that.write(string, encoding) + return that +} + +function fromObject (that, object) { + if (Buffer.isBuffer(object)) return fromBuffer(that, object) + + if (isArray(object)) return fromArray(that, object) + + if (object == null) { + throw new TypeError('must start with number, buffer, array or string') + } + + if (typeof ArrayBuffer !== 'undefined') { + if (object.buffer instanceof ArrayBuffer) { + return fromTypedArray(that, object) + } + if (object instanceof ArrayBuffer) { + return fromArrayBuffer(that, object) + } + } + + if (object.length) return fromArrayLike(that, object) + + return fromJsonObject(that, object) +} + +function fromBuffer (that, buffer) { + var length = checked(buffer.length) | 0 + that = allocate(that, length) + buffer.copy(that, 0, 0, length) + return that +} + +function fromArray (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +// Duplicate of fromArray() to keep fromArray() monomorphic. +function fromTypedArray (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + // Truncating the elements is probably not what people expect from typed + // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior + // of the old Buffer constructor. + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +function fromArrayBuffer (that, array) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + array.byteLength + that = Buffer._augment(new Uint8Array(array)) + } else { + // Fallback: Return an object instance of the Buffer class + that = fromTypedArray(that, new Uint8Array(array)) + } + return that +} + +function fromArrayLike (that, array) { + var length = checked(array.length) | 0 + that = allocate(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +// Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. +// Returns a zero-length buffer for inputs that don't conform to the spec. +function fromJsonObject (that, object) { + var array + var length = 0 + + if (object.type === 'Buffer' && isArray(object.data)) { + array = object.data + length = checked(array.length) | 0 + } + that = allocate(that, length) + + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +if (Buffer.TYPED_ARRAY_SUPPORT) { + Buffer.prototype.__proto__ = Uint8Array.prototype + Buffer.__proto__ = Uint8Array +} else { + // pre-set for values that may exist in the future + Buffer.prototype.length = undefined + Buffer.prototype.parent = undefined +} + +function allocate (that, length) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + that = Buffer._augment(new Uint8Array(length)) + that.__proto__ = Buffer.prototype + } else { + // Fallback: Return an object instance of the Buffer class + that.length = length + that._isBuffer = true + } + + var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 + if (fromPool) that.parent = rootParent + + return that +} + +function checked (length) { + // Note: cannot use `length < kMaxLength` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= kMaxLength()) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + kMaxLength().toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (subject, encoding) { + if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) + + var buf = new Buffer(subject, encoding) + delete buf.parent + return buf +} + +Buffer.isBuffer = function isBuffer (b) { + return !!(b != null && b._isBuffer) +} + +Buffer.compare = function compare (a, b) { + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError('Arguments must be Buffers') + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + var i = 0 + var len = Math.min(x, y) + while (i < len) { + if (a[i] !== b[i]) break + + ++i + } + + if (i !== len) { + x = a[i] + y = b[i] + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'raw': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') + + if (list.length === 0) { + return new Buffer(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; i++) { + length += list[i].length + } + } + + var buf = new Buffer(length) + var pos = 0 + for (i = 0; i < list.length; i++) { + var item = list[i] + item.copy(buf, pos) + pos += item.length + } + return buf +} + +function byteLength (string, encoding) { + if (typeof string !== 'string') string = '' + string + + var len = string.length + if (len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'binary': + // Deprecated + case 'raw': + case 'raws': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) return utf8ToBytes(string).length // assume utf8 + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + var loweredCase = false + + start = start | 0 + end = end === undefined || end === Infinity ? this.length : end | 0 + + if (!encoding) encoding = 'utf8' + if (start < 0) start = 0 + if (end > this.length) end = this.length + if (end <= start) return '' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'binary': + return binarySlice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toString = function toString () { + var length = this.length | 0 + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + if (this.length > 0) { + str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') + if (this.length > max) str += ' ... ' + } + return '' +} + +Buffer.prototype.compare = function compare (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return 0 + return Buffer.compare(this, b) +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset) { + if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff + else if (byteOffset < -0x80000000) byteOffset = -0x80000000 + byteOffset >>= 0 + + if (this.length === 0) return -1 + if (byteOffset >= this.length) return -1 + + // Negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) + + if (typeof val === 'string') { + if (val.length === 0) return -1 // special case: looking for empty string always fails + return String.prototype.indexOf.call(this, val, byteOffset) + } + if (Buffer.isBuffer(val)) { + return arrayIndexOf(this, val, byteOffset) + } + if (typeof val === 'number') { + if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { + return Uint8Array.prototype.indexOf.call(this, val, byteOffset) + } + return arrayIndexOf(this, [ val ], byteOffset) + } + + function arrayIndexOf (arr, val, byteOffset) { + var foundIndex = -1 + for (var i = 0; byteOffset + i < arr.length; i++) { + if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex + } else { + foundIndex = -1 + } + } + return -1 + } + + throw new TypeError('val must be string, number or Buffer') +} + +// `get` is deprecated +Buffer.prototype.get = function get (offset) { + console.log('.get() is deprecated. Access using array indexes instead.') + return this.readUInt8(offset) +} + +// `set` is deprecated +Buffer.prototype.set = function set (v, offset) { + console.log('.set() is deprecated. Access using array indexes instead.') + return this.writeUInt8(v, offset) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + // must be an even number of digits + var strLen = string.length + if (strLen % 2 !== 0) throw new Error('Invalid hex string') + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; i++) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (isNaN(parsed)) throw new Error('Invalid hex string') + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function binaryWrite (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset | 0 + if (isFinite(length)) { + length = length | 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + // legacy write(string, encoding, offset, length) - remove in v0.13 + } else { + var swap = encoding + encoding = offset + offset = length | 0 + length = swap + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'binary': + return binaryWrite(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function binarySlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; i++) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf + if (Buffer.TYPED_ARRAY_SUPPORT) { + newBuf = Buffer._augment(this.subarray(start, end)) + } else { + var sliceLen = end - start + newBuf = new Buffer(sliceLen, undefined) + for (var i = 0; i < sliceLen; i++) { + newBuf[i] = this[i + start] + } + } + + if (newBuf.length) newBuf.parent = this.parent || this + + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') + if (value > max || value < min) throw new RangeError('value is out of bounds') + if (offset + ext > buf.length) throw new RangeError('index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) + this[offset] = (value & 0xff) + return offset + 1 +} + +function objectWriteUInt16 (buf, value, offset, littleEndian) { + if (value < 0) value = 0xffff + value + 1 + for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { + buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> + (littleEndian ? i : 1 - i) * 8 + } +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + } else { + objectWriteUInt16(this, value, offset, true) + } + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + } else { + objectWriteUInt16(this, value, offset, false) + } + return offset + 2 +} + +function objectWriteUInt32 (buf, value, offset, littleEndian) { + if (value < 0) value = 0xffffffff + value + 1 + for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { + buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff + } +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + } else { + objectWriteUInt32(this, value, offset, true) + } + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + } else { + objectWriteUInt32(this, value, offset, false) + } + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) { + var limit = Math.pow(2, 8 * byteLength - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = value < 0 ? 1 : 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) { + var limit = Math.pow(2, 8 * byteLength - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = value < 0 ? 1 : 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + } else { + objectWriteUInt16(this, value, offset, true) + } + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + } else { + objectWriteUInt16(this, value, offset, false) + } + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + } else { + objectWriteUInt32(this, value, offset, true) + } + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + } else { + objectWriteUInt32(this, value, offset, false) + } + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (value > max || value < min) throw new RangeError('value is out of bounds') + if (offset + ext > buf.length) throw new RangeError('index out of range') + if (offset < 0) throw new RangeError('index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + var i + + if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (i = len - 1; i >= 0; i--) { + target[i + targetStart] = this[i + start] + } + } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { + // ascending copy from start + for (i = 0; i < len; i++) { + target[i + targetStart] = this[i + start] + } + } else { + target._set(this.subarray(start, start + len), targetStart) + } + + return len +} + +// fill(value, start=0, end=buffer.length) +Buffer.prototype.fill = function fill (value, start, end) { + if (!value) value = 0 + if (!start) start = 0 + if (!end) end = this.length + + if (end < start) throw new RangeError('end < start') + + // Fill 0 bytes; we're done + if (end === start) return + if (this.length === 0) return + + if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') + if (end < 0 || end > this.length) throw new RangeError('end out of bounds') + + var i + if (typeof value === 'number') { + for (i = start; i < end; i++) { + this[i] = value + } + } else { + var bytes = utf8ToBytes(value.toString()) + var len = bytes.length + for (i = start; i < end; i++) { + this[i] = bytes[i % len] + } + } + + return this +} + +/** + * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. + * Added in Node 0.12. Only available in browsers that support ArrayBuffer. + */ +Buffer.prototype.toArrayBuffer = function toArrayBuffer () { + if (typeof Uint8Array !== 'undefined') { + if (Buffer.TYPED_ARRAY_SUPPORT) { + return (new Buffer(this)).buffer + } else { + var buf = new Uint8Array(this.length) + for (var i = 0, len = buf.length; i < len; i += 1) { + buf[i] = this[i] + } + return buf.buffer + } + } else { + throw new TypeError('Buffer.toArrayBuffer not supported in this browser') + } +} + +// HELPER FUNCTIONS +// ================ + +var BP = Buffer.prototype + +/** + * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods + */ +Buffer._augment = function _augment (arr) { + arr.constructor = Buffer + arr._isBuffer = true + + // save reference to original Uint8Array set method before overwriting + arr._set = arr.set + + // deprecated + arr.get = BP.get + arr.set = BP.set + + arr.write = BP.write + arr.toString = BP.toString + arr.toLocaleString = BP.toString + arr.toJSON = BP.toJSON + arr.equals = BP.equals + arr.compare = BP.compare + arr.indexOf = BP.indexOf + arr.copy = BP.copy + arr.slice = BP.slice + arr.readUIntLE = BP.readUIntLE + arr.readUIntBE = BP.readUIntBE + arr.readUInt8 = BP.readUInt8 + arr.readUInt16LE = BP.readUInt16LE + arr.readUInt16BE = BP.readUInt16BE + arr.readUInt32LE = BP.readUInt32LE + arr.readUInt32BE = BP.readUInt32BE + arr.readIntLE = BP.readIntLE + arr.readIntBE = BP.readIntBE + arr.readInt8 = BP.readInt8 + arr.readInt16LE = BP.readInt16LE + arr.readInt16BE = BP.readInt16BE + arr.readInt32LE = BP.readInt32LE + arr.readInt32BE = BP.readInt32BE + arr.readFloatLE = BP.readFloatLE + arr.readFloatBE = BP.readFloatBE + arr.readDoubleLE = BP.readDoubleLE + arr.readDoubleBE = BP.readDoubleBE + arr.writeUInt8 = BP.writeUInt8 + arr.writeUIntLE = BP.writeUIntLE + arr.writeUIntBE = BP.writeUIntBE + arr.writeUInt16LE = BP.writeUInt16LE + arr.writeUInt16BE = BP.writeUInt16BE + arr.writeUInt32LE = BP.writeUInt32LE + arr.writeUInt32BE = BP.writeUInt32BE + arr.writeIntLE = BP.writeIntLE + arr.writeIntBE = BP.writeIntBE + arr.writeInt8 = BP.writeInt8 + arr.writeInt16LE = BP.writeInt16LE + arr.writeInt16BE = BP.writeInt16BE + arr.writeInt32LE = BP.writeInt32LE + arr.writeInt32BE = BP.writeInt32BE + arr.writeFloatLE = BP.writeFloatLE + arr.writeFloatBE = BP.writeFloatBE + arr.writeDoubleLE = BP.writeDoubleLE + arr.writeDoubleBE = BP.writeDoubleBE + arr.fill = BP.fill + arr.inspect = BP.inspect + arr.toArrayBuffer = BP.toArrayBuffer + + return arr +} + +var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = stringtrim(str).replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function stringtrim (str) { + if (str.trim) return str.trim() + return str.replace(/^\s+|\s+$/g, '') +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; i++) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; i++) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; i++) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; i++) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"base64-js":1,"ieee754":2,"isarray":3}]},{},[])("buffer") +}); diff --git a/resource_ids b/resource_ids index 2f4f4afaa9..115c27d89a 100644 --- a/resource_ids +++ b/resource_ids @@ -2,5 +2,14 @@ "SRCDIR": ".", "atom/atom_resources.grd": { "includes": [60000], - } + }, + "brave/brave_resources.grd": { + "includes": [61000], + }, + "lib/electron_api_resources.grd": { + "includes": [62000], + }, + "brave/brave_strings.grd": { + "messages": [11200], + }, } diff --git a/script/bootstrap.py b/script/bootstrap.py index 37f4ec21de..05364e883c 100755 --- a/script/bootstrap.py +++ b/script/bootstrap.py @@ -34,39 +34,39 @@ def main(): if sys.platform == 'cygwin': update_win32_python() - update_submodules() - - libcc_source_path = args.libcc_source_path - libcc_shared_library_path = args.libcc_shared_library_path - libcc_static_library_path = args.libcc_static_library_path - - # Redirect to use local libchromiumcontent build. - if args.build_libchromiumcontent: - build_libchromiumcontent(args.verbose, args.target_arch, defines) - dist_dir = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'libchromiumcontent', 'dist', 'main') - libcc_source_path = os.path.join(dist_dir, 'src') - libcc_shared_library_path = os.path.join(dist_dir, 'shared_library') - libcc_static_library_path = os.path.join(dist_dir, 'static_library') - - if PLATFORM != 'win32': - if not args.disable_clang and args.clang_dir == '': - # Download prebuilt clang binaries. - update_clang() - - setup_python_libs() + # update_submodules() + + # libcc_source_path = args.libcc_source_path + # libcc_shared_library_path = args.libcc_shared_library_path + # libcc_static_library_path = args.libcc_static_library_path + + # # Redirect to use local libchromiumcontent build. + # if args.build_libchromiumcontent: + # build_libchromiumcontent(args.verbose, args.target_arch, defines) + # dist_dir = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', + # 'libchromiumcontent', 'dist', 'main') + # libcc_source_path = os.path.join(dist_dir, 'src') + # libcc_shared_library_path = os.path.join(dist_dir, 'shared_library') + # libcc_static_library_path = os.path.join(dist_dir, 'static_library') + + # if PLATFORM != 'win32': + # if not args.disable_clang and args.clang_dir == '': + # # Download prebuilt clang binaries. + # update_clang() + + # setup_python_libs() update_node_modules('.') - bootstrap_brightray(args.dev, args.url, args.target_arch, - libcc_source_path, libcc_shared_library_path, - libcc_static_library_path) + # bootstrap_brightray(args.dev, args.url, args.target_arch, + # libcc_source_path, libcc_shared_library_path, + # libcc_static_library_path) - if PLATFORM == 'linux': - download_sysroot(args.target_arch) + # if PLATFORM == 'linux': + # download_sysroot(args.target_arch) - create_chrome_version_h() - touch_config_gypi() - run_update(defines, args.msvs) - update_electron_modules('spec', args.target_arch) + # create_chrome_version_h() + # touch_config_gypi() + # run_update(defines, args.msvs) + # update_electron_modules('spec', args.target_arch) def parse_args(): diff --git a/script/create-dist.py b/script/create-dist.py index fbf44e685b..b129f3c999 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -42,9 +42,7 @@ 'content_resources_200_percent.pak', 'ui_resources_200_percent.pak', 'views_resources_200_percent.pak', - 'extensions_resources.pak', - 'extensions_renderer_resources.pak', - 'extensions_api_resources.pak', + 'libchromiumcontent_resources.pak', 'atom_resources.pak', 'xinput1_3.dll', 'natives_blob.bin', @@ -63,9 +61,7 @@ 'views_resources_200_percent.pak', 'natives_blob.bin', 'snapshot_blob.bin', - 'extensions_resources.pak', - 'extensions_renderer_resources.pak', - 'extensions_api_resources.pak', + 'libchromiumcontent_resources.pak', 'atom_resources.pak', 'libwidevinecdmadapter.so', ], diff --git a/toolchain.gypi b/toolchain.gypi deleted file mode 100644 index 1c5f8a7135..0000000000 --- a/toolchain.gypi +++ /dev/null @@ -1,264 +0,0 @@ -{ - 'variables': { - # Clang stuff. - 'make_clang_dir%': 'vendor/llvm-build/Release+Asserts', - # Set this to true when building with Clang. - 'clang%': 1, - - 'variables': { - # The minimum macOS SDK version to use. - 'mac_sdk_min%': '10.10', - - # Set ARM architecture version. - 'arm_version%': 7, - - # Set NEON compilation flags. - 'arm_neon%': 1, - - # Abosulte path to source root. - 'source_root%': ' + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/brightray/LICENSE-CHROMIUM b/vendor/brightray/LICENSE-CHROMIUM new file mode 100644 index 0000000000..3d0f7d3edf --- /dev/null +++ b/vendor/brightray/LICENSE-CHROMIUM @@ -0,0 +1,27 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +// OWNER 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. diff --git a/vendor/brightray/README.md b/vendor/brightray/README.md new file mode 100644 index 0000000000..eba0f515c9 --- /dev/null +++ b/vendor/brightray/README.md @@ -0,0 +1,44 @@ +# Brightray + +Brightray is a static library that makes +[libchromiumcontent](https://github.com/electron/libchromiumcontent) easier to +use in applications. + +## Using it in your app + +See [brightray_example](https://github.com/electron/brightray_example) for a +sample application written using Brightray. + +## Development + +### Prerequisites + +* Python 2.7 +* Linux: + * Clang 3.0 +* Mac: + * Xcode +* Windows: + * Visual Studio 2010 SP1 + +### One-time setup + +You must previously have built and uploaded libchromiumcontent using its +`script/upload` script. + + $ script/bootstrap http://base.url.com/used/by/script/upload + +### Building + + $ script/build + +Building Brightray on its own isn’t all that interesting, since it’s just a +static library. Building it into an application (like +[brightray_example](https://github.com/electron/brightray_example)) is the only +way to test it. + +## License + +In general, everything is covered by the [`LICENSE`](LICENSE) file. Some files +specify at the top that they are covered by the +[`LICENSE-CHROMIUM`](LICENSE-CHROMIUM) file instead. diff --git a/vendor/brightray/browser/brightray_paths.h b/vendor/brightray/browser/brightray_paths.h new file mode 100644 index 0000000000..14c51ca8f2 --- /dev/null +++ b/vendor/brightray/browser/brightray_paths.h @@ -0,0 +1,45 @@ +// Copyright 2015 The Chromium 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 BROWSER_BRIGHTRAY_PATHS_H_ +#define BROWSER_BRIGHTRAY_PATHS_H_ + +#include "base/compiler_specific.h" + +#if defined(OS_WIN) +#include "base/base_paths_win.h" +#elif defined(OS_MACOSX) +#include "base/base_paths_mac.h" +#endif + +#if defined(OS_POSIX) +#include "base/base_paths_posix.h" +#endif + +namespace brightray { + +enum { + PATH_START = 11000, + + DIR_USER_DATA = PATH_START, // Directory where user data can be written. + DIR_USER_CACHE, // Directory where user cache can be written. + +#if defined(OS_LINUX) + DIR_APP_DATA, // Application Data directory under the user profile. +#else + DIR_APP_DATA = base::DIR_APP_DATA, +#endif + +#if defined(OS_POSIX) + DIR_CACHE = base::DIR_CACHE, // Directory where to put cache data. +#else + DIR_CACHE = base::DIR_APP_DATA, +#endif + + PATH_END +}; + +} // namespace brightray + +#endif // BROWSER_BRIGHTRAY_PATHS_H_ diff --git a/vendor/brightray/browser/browser_client.cc b/vendor/brightray/browser/browser_client.cc new file mode 100644 index 0000000000..c42cf05423 --- /dev/null +++ b/vendor/brightray/browser/browser_client.cc @@ -0,0 +1,98 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/browser_client.h" + +#include "base/path_service.h" +#include "browser/browser_context.h" +#include "browser/browser_main_parts.h" +#include "browser/devtools_manager_delegate.h" +#include "browser/media/media_capture_devices_dispatcher.h" +#include "browser/notification_presenter.h" +#include "browser/platform_notification_service.h" +#include "content/public/common/url_constants.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +namespace brightray { + +namespace { + +BrowserClient* g_browser_client; + +} + +BrowserClient* BrowserClient::Get() { + return g_browser_client; +} + +BrowserClient::BrowserClient() + : browser_main_parts_(nullptr) { + DCHECK(!g_browser_client); + g_browser_client = this; +} + +BrowserClient::~BrowserClient() { +} + +NotificationPresenter* BrowserClient::GetNotificationPresenter() { + #if defined(OS_WIN) + // Bail out if on Windows 7 or even lower, no operating will follow + if (base::win::GetVersion() < base::win::VERSION_WIN8) + return nullptr; + #endif + + if (!notification_presenter_) { + // Create a new presenter if on OS X, Linux, or Windows 8+ + notification_presenter_.reset(NotificationPresenter::Create()); + } + return notification_presenter_.get(); +} + +BrowserMainParts* BrowserClient::OverrideCreateBrowserMainParts( + const content::MainFunctionParams&) { + return new BrowserMainParts; +} + +content::BrowserMainParts* BrowserClient::CreateBrowserMainParts( + const content::MainFunctionParams& parameters) { + DCHECK(!browser_main_parts_); + browser_main_parts_ = OverrideCreateBrowserMainParts(parameters); + return browser_main_parts_; +} + +content::MediaObserver* BrowserClient::GetMediaObserver() { + return MediaCaptureDevicesDispatcher::GetInstance(); +} + +content::PlatformNotificationService* BrowserClient::GetPlatformNotificationService() { + if (!notification_service_) + notification_service_.reset(new PlatformNotificationService(this)); + return notification_service_.get(); +} + +void BrowserClient::GetAdditionalAllowedSchemesForFileSystem( + std::vector* additional_schemes) { +} + +net::NetLog* BrowserClient::GetNetLog() { + return &net_log_; +} + +base::FilePath BrowserClient::GetDefaultDownloadDirectory() { + // ~/Downloads + base::FilePath path; + if (PathService::Get(base::DIR_HOME, &path)) + path = path.Append(FILE_PATH_LITERAL("Downloads")); + + return path; +} + +content::DevToolsManagerDelegate* BrowserClient::GetDevToolsManagerDelegate() { + return new DevToolsManagerDelegate; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/browser_client.h b/vendor/brightray/browser/browser_client.h new file mode 100644 index 0000000000..e810a44fad --- /dev/null +++ b/vendor/brightray/browser/browser_client.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_BROWSER_CLIENT_H_ +#define BRIGHTRAY_BROWSER_BROWSER_CLIENT_H_ + +#include "browser/net_log.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/content_browser_client.h" + +namespace brightray { + +class BrowserContext; +class BrowserMainParts; +class NotificationPresenter; +class PlatformNotificationService; + +class BrowserClient : public content::ContentBrowserClient { + public: + static BrowserClient* Get(); + + BrowserClient(); + ~BrowserClient(); + + BrowserMainParts* browser_main_parts() { return browser_main_parts_; } + + NotificationPresenter* GetNotificationPresenter(); + + // Subclasses should override this to enable or disable WebNotification. + virtual void WebNotificationAllowed( + int render_process_id, + const base::Callback& callback) { + callback.Run(false, true); + } + + // Subclasses that override this (e.g., to provide their own protocol + // handlers) should call this implementation after doing their own work. + content::BrowserMainParts* CreateBrowserMainParts( + const content::MainFunctionParams&) override; + content::MediaObserver* GetMediaObserver() override; + content::PlatformNotificationService* GetPlatformNotificationService() override; + void GetAdditionalAllowedSchemesForFileSystem( + std::vector* additional_schemes) override; + net::NetLog* GetNetLog() override; + base::FilePath GetDefaultDownloadDirectory() override; + content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; + + protected: + // Subclasses should override this to provide their own BrowserMainParts + // implementation. The lifetime of the returned instance is managed by the + // caller. + virtual BrowserMainParts* OverrideCreateBrowserMainParts( + const content::MainFunctionParams&); + + private: + BrowserMainParts* browser_main_parts_; + NetLog net_log_; + + std::unique_ptr notification_service_; + std::unique_ptr notification_presenter_; + + DISALLOW_COPY_AND_ASSIGN(BrowserClient); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/browser_context.cc b/vendor/brightray/browser/browser_context.cc new file mode 100644 index 0000000000..c8ddfc4d2a --- /dev/null +++ b/vendor/brightray/browser/browser_context.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/browser_context.h" + +#include "browser/brightray_paths.h" +#include "browser/browser_client.h" +#include "browser/inspectable_web_contents_impl.h" +#include "browser/network_delegate.h" +#include "browser/permission_manager.h" +#include "browser/special_storage_policy.h" +#include "common/application_info.h" + +#include "base/files/file_path.h" +#include "base/path_service.h" + +#include "components/prefs/json_pref_store.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_service_factory.h" + +#include "base/strings/string_util.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_context.h" +#include "content/public/browser/storage_partition.h" +#include "net/base/escape.h" + +using content::BrowserThread; + +namespace brightray { + +namespace { + +// Convert string to lower case and escape it. +std::string MakePartitionName(const std::string& input) { + return net::EscapePath(base::ToLowerASCII(input)); +} + +} // namespace + +class BrowserContext::ResourceContext : public content::ResourceContext { + public: + ResourceContext() : getter_(nullptr) {} + + void set_url_request_context_getter(URLRequestContextGetter* getter) { + getter_ = getter; + } + + private: + net::HostResolver* GetHostResolver() override { + return getter_->host_resolver(); + } + + net::URLRequestContext* GetRequestContext() override { + return getter_->GetURLRequestContext(); + } + + URLRequestContextGetter* getter_; +}; + +// static +BrowserContext::BrowserContextMap BrowserContext::browser_context_map_; + +// static +scoped_refptr BrowserContext::Get( + const std::string& partition, bool in_memory) { + PartitionKey key(partition, in_memory); + if (browser_context_map_[key].get()) + return make_scoped_refptr(browser_context_map_[key].get()); + + return nullptr; +} + +BrowserContext::BrowserContext(const std::string& partition, bool in_memory) + : in_memory_(in_memory), + resource_context_(new ResourceContext), + storage_policy_(new SpecialStoragePolicy), + weak_factory_(this) { + PathService::Get(DIR_USER_DATA, &path_); + + if (!in_memory_ && !partition.empty()) + path_ = path_.Append(FILE_PATH_LITERAL("Partitions")) + .Append(base::FilePath::FromUTF8Unsafe(MakePartitionName(partition))); + + content::BrowserContext::Initialize(this, path_); + + browser_context_map_[PartitionKey(partition, in_memory)] = GetWeakPtr(); +} + +BrowserContext::~BrowserContext() { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&URLRequestContextGetter::NotifyContextShuttingDown, + base::RetainedRef(url_request_getter_))); + + BrowserThread::DeleteSoon(BrowserThread::IO, + FROM_HERE, + resource_context_.release()); +} + +void BrowserContext::InitPrefs( + scoped_refptr task_runner) { + auto prefs_path = GetPath().Append(FILE_PATH_LITERAL("Preferences")); + PrefServiceFactory prefs_factory; + prefs_factory.set_async(true); + prefs_factory.SetUserPrefsFile(prefs_path, task_runner.get()); + + auto registry = make_scoped_refptr(new PrefRegistrySimple); + RegisterInternalPrefs(registry.get()); + RegisterPrefs(registry.get()); + + prefs_ = prefs_factory.Create(registry.get()); +} + +void BrowserContext::RegisterInternalPrefs(PrefRegistrySimple* registry) { + InspectableWebContentsImpl::RegisterPrefs(registry); +} + +URLRequestContextGetter* BrowserContext::GetRequestContext() { + return static_cast( + GetDefaultStoragePartition(this)->GetURLRequestContext()); +} + +net::URLRequestContextGetter* BrowserContext::CreateRequestContext( + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector protocol_interceptors) { + DCHECK(!url_request_getter_.get()); + url_request_getter_ = new URLRequestContextGetter( + this, + network_controller_handle(), + static_cast(BrowserClient::Get()->GetNetLog()), + GetPath(), + in_memory_, + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO), + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::FILE), + protocol_handlers, + std::move(protocol_interceptors)); + resource_context_->set_url_request_context_getter(url_request_getter_.get()); + return url_request_getter_.get(); +} + +net::NetworkDelegate* BrowserContext::CreateNetworkDelegate() { + return new NetworkDelegate; +} + +base::FilePath BrowserContext::GetPath() const { + return path_; +} + +std::unique_ptr BrowserContext::CreateZoomLevelDelegate( + const base::FilePath& partition_path) { + return std::unique_ptr(); +} + +bool BrowserContext::IsOffTheRecord() const { + return in_memory_; +} + +content::ResourceContext* BrowserContext::GetResourceContext() { + return resource_context_.get(); +} + +content::DownloadManagerDelegate* BrowserContext::GetDownloadManagerDelegate() { + return nullptr; +} + +content::BrowserPluginGuestManager* BrowserContext::GetGuestManager() { + return nullptr; +} + +storage::SpecialStoragePolicy* BrowserContext::GetSpecialStoragePolicy() { + return storage_policy_.get(); +} + +content::PushMessagingService* BrowserContext::GetPushMessagingService() { + return nullptr; +} + +content::SSLHostStateDelegate* BrowserContext::GetSSLHostStateDelegate() { + return nullptr; +} + +content::PermissionManager* BrowserContext::GetPermissionManager() { + if (!permission_manager_.get()) + permission_manager_.reset(new PermissionManager); + return permission_manager_.get(); +} + +content::BackgroundSyncController* BrowserContext::GetBackgroundSyncController() { + return nullptr; +} + +net::URLRequestContextGetter* +BrowserContext::CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector request_interceptors) { + return nullptr; +} + +net::URLRequestContextGetter* +BrowserContext::CreateMediaRequestContext() { + return url_request_getter_.get(); +} + +net::URLRequestContextGetter* +BrowserContext::CreateMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) { + return nullptr; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/browser_context.h b/vendor/brightray/browser/browser_context.h new file mode 100644 index 0000000000..779ff54b68 --- /dev/null +++ b/vendor/brightray/browser/browser_context.h @@ -0,0 +1,138 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_BROWSER_CONTEXT_H_ +#define BRIGHTRAY_BROWSER_BROWSER_CONTEXT_H_ + +#include + +#include "browser/net/devtools_network_controller_handle.h" +#include "browser/permission_manager.h" +#include "browser/url_request_context_getter.h" + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/browser_context.h" + +class PrefRegistrySimple; +class PrefService; + +namespace storage { +class SpecialStoragePolicy; +} + +namespace brightray { + +class PermissionManager; + +class BrowserContext : public base::RefCounted, + public content::BrowserContext, + public brightray::URLRequestContextGetter::Delegate { + public: + // Get the BrowserContext according to its |partition| and |in_memory|, + // empty pointer when be returned when there is no matching BrowserContext. + static scoped_refptr Get( + const std::string& partition, bool in_memory); + + base::WeakPtr GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + // Get the request context, if there is no one, create it. + URLRequestContextGetter* GetRequestContext(); + + // content::BrowserContext: + std::unique_ptr CreateZoomLevelDelegate( + const base::FilePath& partition_path) override; + bool IsOffTheRecord() const override; + content::ResourceContext* GetResourceContext() override; + content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; + content::BrowserPluginGuestManager* GetGuestManager() override; + storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override; + content::PushMessagingService* GetPushMessagingService() override; + content::SSLHostStateDelegate* GetSSLHostStateDelegate() override; + content::PermissionManager* GetPermissionManager() override; + content::BackgroundSyncController* GetBackgroundSyncController() override; + net::URLRequestContextGetter* CreateRequestContext( + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector request_interceptors) override; + net::URLRequestContextGetter* CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector request_interceptors) override; + net::URLRequestContextGetter* CreateMediaRequestContext() override; + net::URLRequestContextGetter* CreateMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) override; + + URLRequestContextGetter* url_request_context_getter() const { + return url_request_getter_.get(); + } + + DevToolsNetworkControllerHandle* network_controller_handle() { + return &network_controller_handle_; + } + + void InitPrefs(scoped_refptr task_runner); + PrefService* prefs() { return prefs_.get(); } + + base::FilePath GetPath() const override; + protected: + BrowserContext(const std::string& partition, bool in_memory); + ~BrowserContext() override; + + // Subclasses should override this to register custom preferences. + virtual void RegisterPrefs(PrefRegistrySimple* pref_registry) {} + + // URLRequestContextGetter::Delegate: + net::NetworkDelegate* CreateNetworkDelegate() override; + + private: + friend class base::RefCounted; + class ResourceContext; + + void RegisterInternalPrefs(PrefRegistrySimple* pref_registry); + + // partition_id => browser_context + struct PartitionKey { + std::string partition; + bool in_memory; + + PartitionKey(const std::string& partition, bool in_memory) + : partition(partition), in_memory(in_memory) {} + + bool operator<(const PartitionKey& other) const { + if (partition == other.partition) + return in_memory < other.in_memory; + return partition < other.partition; + } + + bool operator==(const PartitionKey& other) const { + return (partition == other.partition) && (in_memory == other.in_memory); + } + }; + using BrowserContextMap = + std::map>; + static BrowserContextMap browser_context_map_; + + base::FilePath path_; + bool in_memory_; + + DevToolsNetworkControllerHandle network_controller_handle_; + + std::unique_ptr resource_context_; + scoped_refptr url_request_getter_; + scoped_refptr storage_policy_; + std::unique_ptr prefs_; + std::unique_ptr permission_manager_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(BrowserContext); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/browser_main_parts.cc b/vendor/brightray/browser/browser_main_parts.cc new file mode 100644 index 0000000000..13bc9f2a15 --- /dev/null +++ b/vendor/brightray/browser/browser_main_parts.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/browser_main_parts.h" + +#include "browser/browser_context.h" +#include "browser/devtools_manager_delegate.h" +#include "browser/web_ui_controller_factory.h" +#include "common/main_delegate.h" + +#include "base/command_line.h" +#include "base/feature_list.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "components/devtools_http_handler/devtools_http_handler.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/content_switches.h" +#include "media/base/media_resources.h" +#include "net/proxy/proxy_resolver_v8.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" + +#if defined(USE_AURA) +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/views/widget/desktop_aura/desktop_screen.h" +#endif + +#if defined(TOOLKIT_VIEWS) +#include "browser/views/views_delegate.h" +#endif + +#if defined(USE_X11) +#include "base/environment.h" +#include "base/path_service.h" +#include "base/nix/xdg_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "brave/common/brave_paths.h" +#include "browser/brightray_paths.h" +#include "chrome/browser/ui/libgtk2ui/gtk2_ui.h" +#include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_util_internal.h" +#include "ui/views/linux_ui/linux_ui.h" +#include "ui/wm/core/wm_state.h" +#endif + +#if defined(OS_WIN) +#include "ui/base/cursor/cursor_loader_win.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/gfx/platform_font_win.h" +#endif + +#if defined(OS_LINUX) +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/dbus/dbus_bluez_manager_wrapper_linux.h" +#endif + +using content::BrowserThread; + +namespace brightray { + +namespace { + +#if defined(OS_WIN) +// gfx::Font callbacks +void AdjustUIFont(LOGFONT* logfont) { + l10n_util::AdjustUIFont(logfont); +} + +int GetMinimumFontSize() { + return 10; +} +#endif + +#if defined(USE_X11) +// Indicates that we're currently responding to an IO error (by shutting down). +bool g_in_x11_io_error_handler = false; + +// Number of seconds to wait for UI thread to get an IO error if we get it on +// the background thread. +const int kWaitForUIThreadSeconds = 10; + +const base::FilePath& OverrideLinuxAppDataPath() { + base::FilePath path; + if (PathService::Get(DIR_APP_DATA, &path)) + return path; + + if (brave::GetDefaultUserDataDirectory(&path)) { + PathService::Override(DIR_APP_DATA, path); + } + return path; +} + +int BrowserX11ErrorHandler(Display* d, XErrorEvent* error) { + if (!g_in_x11_io_error_handler) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&ui::LogErrorEventDescription, d, *error)); + } + return 0; +} + +// This function is used to help us diagnose crash dumps that happen +// during the shutdown process. +NOINLINE void WaitingForUIThreadToHandleIOError() { + // Ensure function isn't optimized away. + asm(""); + sleep(kWaitForUIThreadSeconds); +} + +int BrowserX11IOErrorHandler(Display* d) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + // Wait for the UI thread (which has a different connection to the X server) + // to get the error. We can't call shutdown from this thread without + // tripping an error. Doing it through a function so that we'll be able + // to see it in any crash dumps. + WaitingForUIThreadToHandleIOError(); + return 0; + } + + // If there's an IO error it likely means the X server has gone away. + // If this CHECK fails, then that means SessionEnding() below triggered some + // code that tried to talk to the X server, resulting in yet another error. + CHECK(!g_in_x11_io_error_handler); + + g_in_x11_io_error_handler = true; + LOG(ERROR) << "X IO error received (X server probably went away)"; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + + return 0; +} + +int X11EmptyErrorHandler(Display* d, XErrorEvent* error) { + return 0; +} + +int X11EmptyIOErrorHandler(Display* d) { + return 0; +} +#endif + +base::string16 MediaStringProvider(media::MessageId id) { + switch (id) { + case media::DEFAULT_AUDIO_DEVICE_NAME: + return base::ASCIIToUTF16("Default"); +#if defined(OS_WIN) + case media::COMMUNICATIONS_AUDIO_DEVICE_NAME: + return base::ASCIIToUTF16("Communications"); +#endif + default: + return base::string16(); + } +} + +} // namespace + +BrowserMainParts::BrowserMainParts() { +} + +BrowserMainParts::~BrowserMainParts() { +} + +void BrowserMainParts::PreEarlyInitialization() { + std::unique_ptr feature_list(new base::FeatureList); + feature_list->InitializeFromCommandLine("", ""); + base::FeatureList::SetInstance(std::move(feature_list)); + +#if defined(USE_X11) + views::LinuxUI::SetInstance(BuildGtk2UI()); + const base::FilePath path = OverrideLinuxAppDataPath(); + if (path.empty()) { + LOG(ERROR) << "Could not set linux app data path"; + } + + // Installs the X11 error handlers for the browser process used during + // startup. They simply print error messages and exit because + // we can't shutdown properly while creating and initializing services. + ui::SetX11ErrorHandlers(nullptr, nullptr); +#endif +} + +void BrowserMainParts::ToolkitInitialized() { + ui::MaterialDesignController::Initialize(); + +#if defined(USE_AURA) && defined(USE_X11) + views::LinuxUI::instance()->Initialize(); + wm_state_.reset(new wm::WMState); +#endif + +#if defined(TOOLKIT_VIEWS) + views_delegate_.reset(new ViewsDelegate); +#endif + +#if defined(OS_WIN) + gfx::PlatformFontWin::adjust_font_callback = &AdjustUIFont; + gfx::PlatformFontWin::get_minimum_font_size_callback = &GetMinimumFontSize; + + wchar_t module_name[MAX_PATH] = { 0 }; + if (GetModuleFileName(NULL, module_name, MAX_PATH)) + ui::CursorLoaderWin::SetCursorResourceModule(module_name); +#endif +} + +void BrowserMainParts::PreMainMessageLoopStart() { +#if defined(OS_MACOSX) + l10n_util::OverrideLocaleWithCocoaLocale(); +#endif + InitializeResourceBundle(""); +#if defined(OS_MACOSX) + InitializeMainNib(); +#endif + media::SetLocalizedStringProvider(MediaStringProvider); +} + +void BrowserMainParts::PreMainMessageLoopRun() { + content::WebUIControllerFactory::RegisterFactory( + WebUIControllerFactory::GetInstance()); + + // --remote-debugging-port + // auto command_line = base::CommandLine::ForCurrentProcess(); + // if (command_line->HasSwitch(switches::kRemoteDebuggingPort)) + // devtools_http_handler_.reset(DevToolsManagerDelegate::CreateHttpHandler()); +} + +void BrowserMainParts::PostMainMessageLoopStart() { +#if defined(USE_X11) + // Installs the X11 error handlers for the browser process after the + // main message loop has started. This will allow us to exit cleanly + // if X exits before us. + ui::SetX11ErrorHandlers(BrowserX11ErrorHandler, BrowserX11IOErrorHandler); +#endif +#if defined(OS_LINUX) + bluez::DBusBluezManagerWrapperLinux::Initialize(); +#endif +} + +void BrowserMainParts::PostMainMessageLoopRun() { +#if defined(USE_X11) + // Unset the X11 error handlers. The X11 error handlers log the errors using a + // |PostTask()| on the message-loop. But since the message-loop is in the + // process of terminating, this can cause errors. + ui::SetX11ErrorHandlers(X11EmptyErrorHandler, X11EmptyIOErrorHandler); +#endif +} + +int BrowserMainParts::PreCreateThreads() { +#if defined(USE_AURA) + display::Screen* screen = views::CreateDesktopScreen(); + display::Screen::SetScreenInstance(screen); +#if defined(USE_X11) + views::LinuxUI::instance()->UpdateDeviceScaleFactor( + screen->GetPrimaryDisplay().device_scale_factor()); +#endif +#endif + return 0; +} + +void BrowserMainParts::PostDestroyThreads() { +#if defined(OS_LINUX) + device::BluetoothAdapterFactory::Shutdown(); + bluez::DBusBluezManagerWrapperLinux::Shutdown(); +#endif +} + +} // namespace brightray diff --git a/vendor/brightray/browser/browser_main_parts.h b/vendor/brightray/browser/browser_main_parts.h new file mode 100644 index 0000000000..a0d58e7609 --- /dev/null +++ b/vendor/brightray/browser/browser_main_parts.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_BROWSER_MAIN_PARTS_H_ +#define BRIGHTRAY_BROWSER_BROWSER_MAIN_PARTS_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "content/public/browser/browser_main_parts.h" + +namespace devtools_http_handler { +class DevToolsHttpHandler; +} + +#if defined(TOOLKIT_VIEWS) +namespace brightray { +class ViewsDelegate; +} +#endif + +#if defined(USE_AURA) && defined(USE_X11) +namespace wm { +class WMState; +} +#endif + +namespace brightray { + +class BrowserMainParts : public content::BrowserMainParts { + public: + BrowserMainParts(); + ~BrowserMainParts(); + + protected: + // content::BrowserMainParts: + void PreEarlyInitialization() override; + void ToolkitInitialized() override; + void PreMainMessageLoopStart() override; + void PreMainMessageLoopRun() override; + void PostMainMessageLoopStart() override; + void PostMainMessageLoopRun() override; + int PreCreateThreads() override; + void PostDestroyThreads() override; + + private: +#if defined(OS_MACOSX) + void InitializeMainNib(); +#endif + + std::unique_ptr devtools_http_handler_; + +#if defined(TOOLKIT_VIEWS) + std::unique_ptr views_delegate_; +#endif + +#if defined(USE_AURA) && defined(USE_X11) + std::unique_ptr wm_state_; +#endif + + DISALLOW_COPY_AND_ASSIGN(BrowserMainParts); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/browser_main_parts_mac.mm b/vendor/brightray/browser/browser_main_parts_mac.mm new file mode 100644 index 0000000000..19d0262a10 --- /dev/null +++ b/vendor/brightray/browser/browser_main_parts_mac.mm @@ -0,0 +1,22 @@ +#import "browser_main_parts.h" + +#import "base/logging.h" +#import "base/mac/bundle_locations.h" +#import + +namespace brightray { + +// Replicates NSApplicationMain, but doesn't start a run loop. +void BrowserMainParts::InitializeMainNib() { + auto infoDictionary = base::mac::OuterBundle().infoDictionary; + + auto principalClass = NSClassFromString([infoDictionary objectForKey:@"NSPrincipalClass"]); + auto application = [principalClass sharedApplication]; + + NSString *mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"]; + auto mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:base::mac::FrameworkBundle()]; + [mainNib instantiateWithOwner:application topLevelObjects:nil]; + [mainNib release]; +} + +} diff --git a/vendor/brightray/browser/devtools_contents_resizing_strategy.cc b/vendor/brightray/browser/devtools_contents_resizing_strategy.cc new file mode 100644 index 0000000000..30ee028669 --- /dev/null +++ b/vendor/brightray/browser/devtools_contents_resizing_strategy.cc @@ -0,0 +1,53 @@ +// Copyright 2014 The Chromium 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 "browser/devtools_contents_resizing_strategy.h" + +#include + +DevToolsContentsResizingStrategy::DevToolsContentsResizingStrategy() + : hide_inspected_contents_(false) { +} + +DevToolsContentsResizingStrategy::DevToolsContentsResizingStrategy( + const gfx::Rect& bounds) + : bounds_(bounds), + hide_inspected_contents_(bounds_.IsEmpty() && !bounds_.x() && + !bounds_.y()) { +} + + +void DevToolsContentsResizingStrategy::CopyFrom( + const DevToolsContentsResizingStrategy& strategy) { + bounds_ = strategy.bounds(); + hide_inspected_contents_ = strategy.hide_inspected_contents(); +} + +bool DevToolsContentsResizingStrategy::Equals( + const DevToolsContentsResizingStrategy& strategy) { + return bounds_ == strategy.bounds() && + hide_inspected_contents_ == strategy.hide_inspected_contents(); +} + +void ApplyDevToolsContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy, + const gfx::Size& container_size, + gfx::Rect* new_devtools_bounds, + gfx::Rect* new_contents_bounds) { + new_devtools_bounds->SetRect( + 0, 0, container_size.width(), container_size.height()); + + const gfx::Rect& bounds = strategy.bounds(); + if (bounds.size().IsEmpty() && !strategy.hide_inspected_contents()) { + new_contents_bounds->SetRect( + 0, 0, container_size.width(), container_size.height()); + return; + } + + int left = std::min(bounds.x(), container_size.width()); + int top = std::min(bounds.y(), container_size.height()); + int width = std::min(bounds.width(), container_size.width() - left); + int height = std::min(bounds.height(), container_size.height() - top); + new_contents_bounds->SetRect(left, top, width, height); +} diff --git a/vendor/brightray/browser/devtools_contents_resizing_strategy.h b/vendor/brightray/browser/devtools_contents_resizing_strategy.h new file mode 100644 index 0000000000..c48272f797 --- /dev/null +++ b/vendor/brightray/browser/devtools_contents_resizing_strategy.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium 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 BRIGHTRAY_BROWSER_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_ +#define BRIGHTRAY_BROWSER_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_ + +#include "base/macros.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +// This class knows how to resize both DevTools and inspected WebContents +// inside a browser window hierarchy. +class DevToolsContentsResizingStrategy { + public: + DevToolsContentsResizingStrategy(); + explicit DevToolsContentsResizingStrategy( + const gfx::Rect& bounds); + + void CopyFrom(const DevToolsContentsResizingStrategy& strategy); + bool Equals(const DevToolsContentsResizingStrategy& strategy); + + const gfx::Rect& bounds() const { return bounds_; } + bool hide_inspected_contents() const { return hide_inspected_contents_; } + + private: + // Contents bounds. When non-empty, used instead of insets. + gfx::Rect bounds_; + + // Determines whether inspected contents is visible. + bool hide_inspected_contents_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsContentsResizingStrategy); +}; + +// Applies contents resizing strategy, producing bounds for devtools and +// page contents views. Generally, page contents view is placed atop of devtools +// inside a common parent view, which size should be passed in |container_size|. +// When unknown, providing empty rect as previous devtools and contents bounds +// is allowed. +void ApplyDevToolsContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy, + const gfx::Size& container_size, + gfx::Rect* new_devtools_bounds, + gfx::Rect* new_contents_bounds); + +#endif // BRIGHTRAY_BROWSER_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_ diff --git a/vendor/brightray/browser/devtools_embedder_message_dispatcher.cc b/vendor/brightray/browser/devtools_embedder_message_dispatcher.cc new file mode 100644 index 0000000000..ffba3268ae --- /dev/null +++ b/vendor/brightray/browser/devtools_embedder_message_dispatcher.cc @@ -0,0 +1,208 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/devtools_embedder_message_dispatcher.h" + +#include "base/bind.h" +#include "base/values.h" + +namespace brightray { + +namespace { + +using DispatchCallback = DevToolsEmbedderMessageDispatcher::DispatchCallback; + +bool GetValue(const base::Value& value, std::string* result) { + return value.GetAsString(result); +} + +bool GetValue(const base::Value& value, int* result) { + return value.GetAsInteger(result); +} + +bool GetValue(const base::Value& value, bool* result) { + return value.GetAsBoolean(result); +} + +bool GetValue(const base::Value& value, gfx::Rect* rect) { + const base::DictionaryValue* dict; + if (!value.GetAsDictionary(&dict)) + return false; + int x = 0; + int y = 0; + int width = 0; + int height = 0; + if (!dict->GetInteger("x", &x) || + !dict->GetInteger("y", &y) || + !dict->GetInteger("width", &width) || + !dict->GetInteger("height", &height)) + return false; + rect->SetRect(x, y, width, height); + return true; +} + +template +struct StorageTraits { + using StorageType = T; +}; + +template +struct StorageTraits { + using StorageType = T; +}; + +template +struct ParamTuple { + bool Parse(const base::ListValue& list, + const base::ListValue::const_iterator& it) { + return it == list.end(); + } + + template + void Apply(const H& handler, As... args) { + handler.Run(args...); + } +}; + +template +struct ParamTuple { + bool Parse(const base::ListValue& list, + const base::ListValue::const_iterator& it) { + return it != list.end() && GetValue(**it, &head) && + tail.Parse(list, it + 1); + } + + template + void Apply(const H& handler, As... args) { + tail.template Apply(handler, args..., head); + } + + typename StorageTraits::StorageType head; + ParamTuple tail; +}; + +template +bool ParseAndHandle(const base::Callback& handler, + const DispatchCallback& callback, + const base::ListValue& list) { + ParamTuple tuple; + if (!tuple.Parse(list, list.begin())) + return false; + tuple.Apply(handler); + return true; +} + +template +bool ParseAndHandleWithCallback( + const base::Callback& handler, + const DispatchCallback& callback, + const base::ListValue& list) { + ParamTuple tuple; + if (!tuple.Parse(list, list.begin())) + return false; + tuple.Apply(handler, callback); + return true; +} + +} // namespace + +/** + * Dispatcher for messages sent from the frontend running in an + * isolated renderer (chrome-devtools:// or chrome://inspect) to the embedder + * in the browser. + * + * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder or + * chrome.send method accordingly. + */ +class DispatcherImpl : public DevToolsEmbedderMessageDispatcher { + public: + ~DispatcherImpl() override {} + + bool Dispatch(const DispatchCallback& callback, + const std::string& method, + const base::ListValue* params) override { + auto it = handlers_.find(method); + return it != handlers_.end() && it->second.Run(callback, *params); + } + + template + void RegisterHandler(const std::string& method, + void (Delegate::*handler)(As...), + Delegate* delegate) { + handlers_[method] = base::Bind(&ParseAndHandle, + base::Bind(handler, + base::Unretained(delegate))); + } + + template + void RegisterHandlerWithCallback( + const std::string& method, + void (Delegate::*handler)(const DispatchCallback&, As...), + Delegate* delegate) { + handlers_[method] = base::Bind(&ParseAndHandleWithCallback, + base::Bind(handler, + base::Unretained(delegate))); + } + + + private: + using Handler = base::Callback; + using HandlerMap = std::map; + HandlerMap handlers_; +}; + +// static +DevToolsEmbedderMessageDispatcher* +DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend( + Delegate* delegate) { + auto* d = new DispatcherImpl(); + + d->RegisterHandler("bringToFront", &Delegate::ActivateWindow, delegate); + d->RegisterHandler("closeWindow", &Delegate::CloseWindow, delegate); + d->RegisterHandler("loadCompleted", &Delegate::LoadCompleted, delegate); + d->RegisterHandler("setInspectedPageBounds", + &Delegate::SetInspectedPageBounds, delegate); + d->RegisterHandler("inspectElementCompleted", + &Delegate::InspectElementCompleted, delegate); + d->RegisterHandler("inspectedURLChanged", + &Delegate::InspectedURLChanged, delegate); + d->RegisterHandlerWithCallback("setIsDocked", + &Delegate::SetIsDocked, delegate); + d->RegisterHandler("openInNewTab", &Delegate::OpenInNewTab, delegate); + d->RegisterHandler("save", &Delegate::SaveToFile, delegate); + d->RegisterHandler("append", &Delegate::AppendToFile, delegate); + d->RegisterHandler("requestFileSystems", + &Delegate::RequestFileSystems, delegate); + d->RegisterHandler("addFileSystem", &Delegate::AddFileSystem, delegate); + d->RegisterHandler("removeFileSystem", &Delegate::RemoveFileSystem, delegate); + d->RegisterHandler("upgradeDraggedFileSystemPermissions", + &Delegate::UpgradeDraggedFileSystemPermissions, delegate); + d->RegisterHandler("indexPath", &Delegate::IndexPath, delegate); + d->RegisterHandlerWithCallback("loadNetworkResource", + &Delegate::LoadNetworkResource, delegate); + d->RegisterHandler("stopIndexing", &Delegate::StopIndexing, delegate); + d->RegisterHandler("searchInPath", &Delegate::SearchInPath, delegate); + d->RegisterHandler("setWhitelistedShortcuts", + &Delegate::SetWhitelistedShortcuts, delegate); + d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate); + d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate); + d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate); + d->RegisterHandler("setDevicesUpdatesEnabled", + &Delegate::SetDevicesUpdatesEnabled, delegate); + d->RegisterHandler("dispatchProtocolMessage", + &Delegate::DispatchProtocolMessageFromDevToolsFrontend, + delegate); + d->RegisterHandler("recordActionUMA", &Delegate::RecordActionUMA, delegate); + d->RegisterHandlerWithCallback("sendJsonRequest", + &Delegate::SendJsonRequest, delegate); + d->RegisterHandlerWithCallback("getPreferences", + &Delegate::GetPreferences, delegate); + d->RegisterHandler("setPreference", &Delegate::SetPreference, delegate); + d->RegisterHandler("removePreference", &Delegate::RemovePreference, delegate); + d->RegisterHandler("clearPreferences", &Delegate::ClearPreferences, delegate); + return d; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/devtools_embedder_message_dispatcher.h b/vendor/brightray/browser/devtools_embedder_message_dispatcher.h new file mode 100644 index 0000000000..4401bd5ab8 --- /dev/null +++ b/vendor/brightray/browser/devtools_embedder_message_dispatcher.h @@ -0,0 +1,97 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_ +#define BRIGHTRAY_BROWSER_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_ + +#include +#include + +#include "base/callback.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace base { +class ListValue; +class Value; +} + +namespace brightray { + +/** + * Dispatcher for messages sent from the DevTools frontend running in an + * isolated renderer (on chrome-devtools://) to the embedder in the browser. + * + * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder method. + */ +class DevToolsEmbedderMessageDispatcher { + public: + class Delegate { + public: + using DispatchCallback = base::Callback; + + virtual ~Delegate() {} + + virtual void ActivateWindow() = 0; + virtual void CloseWindow() = 0; + virtual void LoadCompleted() = 0; + virtual void SetInspectedPageBounds(const gfx::Rect& rect) = 0; + virtual void InspectElementCompleted() = 0; + virtual void InspectedURLChanged(const std::string& url) = 0; + virtual void SetIsDocked(const DispatchCallback& callback, + bool is_docked) = 0; + virtual void OpenInNewTab(const std::string& url) = 0; + virtual void SaveToFile(const std::string& url, + const std::string& content, + bool save_as) = 0; + virtual void AppendToFile(const std::string& url, + const std::string& content) = 0; + virtual void RequestFileSystems() = 0; + virtual void AddFileSystem(const std::string& file_system_path) = 0; + virtual void RemoveFileSystem(const std::string& file_system_path) = 0; + virtual void UpgradeDraggedFileSystemPermissions( + const std::string& file_system_url) = 0; + virtual void IndexPath(int index_request_id, + const std::string& file_system_path) = 0; + virtual void StopIndexing(int index_request_id) = 0; + virtual void LoadNetworkResource(const DispatchCallback& callback, + const std::string& url, + const std::string& headers, + int stream_id) = 0; + virtual void SearchInPath(int search_request_id, + const std::string& file_system_path, + const std::string& query) = 0; + virtual void SetWhitelistedShortcuts(const std::string& message) = 0; + virtual void ZoomIn() = 0; + virtual void ZoomOut() = 0; + virtual void ResetZoom() = 0; + virtual void SetDevicesUpdatesEnabled(bool enabled) = 0; + virtual void DispatchProtocolMessageFromDevToolsFrontend( + const std::string& message) = 0; + virtual void RecordActionUMA(const std::string& name, int action) = 0; + virtual void SendJsonRequest(const DispatchCallback& callback, + const std::string& browser_id, + const std::string& url) = 0; + virtual void GetPreferences(const DispatchCallback& callback) = 0; + virtual void SetPreference(const std::string& name, + const std::string& value) = 0; + virtual void RemovePreference(const std::string& name) = 0; + virtual void ClearPreferences() = 0; + }; + + using DispatchCallback = Delegate::DispatchCallback; + + virtual ~DevToolsEmbedderMessageDispatcher() {} + virtual bool Dispatch(const DispatchCallback& callback, + const std::string& method, + const base::ListValue* params) = 0; + + static DevToolsEmbedderMessageDispatcher* CreateForDevToolsFrontend( + Delegate* delegate); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_ diff --git a/vendor/brightray/browser/devtools_file_system_indexer.cc b/vendor/brightray/browser/devtools_file_system_indexer.cc new file mode 100644 index 0000000000..32f5feae30 --- /dev/null +++ b/vendor/brightray/browser/devtools_file_system_indexer.cc @@ -0,0 +1,490 @@ +// Copyright 2013 The Chromium 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 "browser/devtools_file_system_indexer.h" + +#include + +#include + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "base/files/file_util_proxy.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" + +using base::Bind; +using base::Callback; +using base::FileEnumerator; +using base::FilePath; +using base::Time; +using base::TimeDelta; +using base::TimeTicks; +using content::BrowserThread; +using std::map; +using std::set; +using std::string; +using std::vector; + +namespace brightray { + +namespace { + +typedef int32_t Trigram; +typedef char TrigramChar; +typedef uint16_t FileId; + +const int kMinTimeoutBetweenWorkedNitification = 200; +// Trigram characters include all ASCII printable characters (32-126) except for +// the capital letters, because the index is case insensitive. +const size_t kTrigramCharacterCount = 126 - 'Z' - 1 + 'A' - ' ' + 1; +const size_t kTrigramCount = + kTrigramCharacterCount * kTrigramCharacterCount * kTrigramCharacterCount; +const int kMaxReadLength = 10 * 1024; +const TrigramChar kUndefinedTrigramChar = -1; +const TrigramChar kBinaryTrigramChar = -2; +const Trigram kUndefinedTrigram = -1; + +class Index { + public: + Index(); + Time LastModifiedTimeForFile(const FilePath& file_path); + void SetTrigramsForFile(const FilePath& file_path, + const vector& index, + const Time& time); + vector Search(string query); + void PrintStats(); + void NormalizeVectors(); + + private: + ~Index(); + + FileId GetFileId(const FilePath& file_path); + + typedef map FileIdsMap; + FileIdsMap file_ids_; + FileId last_file_id_; + // The index in this vector is the trigram id. + vector > index_; + typedef map IndexedFilesMap; + IndexedFilesMap index_times_; + vector is_normalized_; + + DISALLOW_COPY_AND_ASSIGN(Index); +}; + +base::LazyInstance::Leaky g_trigram_index = LAZY_INSTANCE_INITIALIZER; + +TrigramChar TrigramCharForChar(char c) { + static TrigramChar* trigram_chars = nullptr; + if (!trigram_chars) { + trigram_chars = new TrigramChar[256]; + for (size_t i = 0; i < 256; ++i) { + if (i > 127) { + trigram_chars[i] = kUndefinedTrigramChar; + continue; + } + char ch = static_cast(i); + if (ch == '\t') + ch = ' '; + if (base::IsAsciiUpper(ch)) + ch = ch - 'A' + 'a'; + + bool is_binary_char = ch < 9 || (ch >= 14 && ch < 32) || ch == 127; + if (is_binary_char) { + trigram_chars[i] = kBinaryTrigramChar; + continue; + } + + if (ch < ' ') { + trigram_chars[i] = kUndefinedTrigramChar; + continue; + } + + if (ch >= 'Z') + ch = ch - 'Z' - 1 + 'A'; + ch -= ' '; + char signed_trigram_count = static_cast(kTrigramCharacterCount); + CHECK(ch >= 0 && ch < signed_trigram_count); + trigram_chars[i] = ch; + } + } + unsigned char uc = static_cast(c); + return trigram_chars[uc]; +} + +Trigram TrigramAtIndex(const vector& trigram_chars, size_t index) { + static int kTrigramCharacterCountSquared = + kTrigramCharacterCount * kTrigramCharacterCount; + if (trigram_chars[index] == kUndefinedTrigramChar || + trigram_chars[index + 1] == kUndefinedTrigramChar || + trigram_chars[index + 2] == kUndefinedTrigramChar) + return kUndefinedTrigram; + Trigram trigram = kTrigramCharacterCountSquared * trigram_chars[index] + + kTrigramCharacterCount * trigram_chars[index + 1] + + trigram_chars[index + 2]; + return trigram; +} + +Index::Index() : last_file_id_(0) { + index_.resize(kTrigramCount); + is_normalized_.resize(kTrigramCount); + std::fill(is_normalized_.begin(), is_normalized_.end(), true); +} + +Index::~Index() {} + +Time Index::LastModifiedTimeForFile(const FilePath& file_path) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + Time last_modified_time; + if (index_times_.find(file_path) != index_times_.end()) + last_modified_time = index_times_[file_path]; + return last_modified_time; +} + +void Index::SetTrigramsForFile(const FilePath& file_path, + const vector& index, + const Time& time) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + FileId file_id = GetFileId(file_path); + vector::const_iterator it = index.begin(); + for (; it != index.end(); ++it) { + Trigram trigram = *it; + index_[trigram].push_back(file_id); + is_normalized_[trigram] = false; + } + index_times_[file_path] = time; +} + +vector Index::Search(string query) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + const char* data = query.c_str(); + vector trigram_chars; + trigram_chars.reserve(query.size()); + for (size_t i = 0; i < query.size(); ++i) { + TrigramChar trigram_char = TrigramCharForChar(data[i]); + if (trigram_char == kBinaryTrigramChar) + trigram_char = kUndefinedTrigramChar; + trigram_chars.push_back(trigram_char); + } + vector trigrams; + for (size_t i = 0; i + 2 < query.size(); ++i) { + Trigram trigram = TrigramAtIndex(trigram_chars, i); + if (trigram != kUndefinedTrigram) + trigrams.push_back(trigram); + } + set file_ids; + bool first = true; + vector::const_iterator it = trigrams.begin(); + for (; it != trigrams.end(); ++it) { + Trigram trigram = *it; + if (first) { + std::copy(index_[trigram].begin(), + index_[trigram].end(), + std::inserter(file_ids, file_ids.begin())); + first = false; + continue; + } + set intersection = base::STLSetIntersection >( + file_ids, index_[trigram]); + file_ids.swap(intersection); + } + vector result; + FileIdsMap::const_iterator ids_it = file_ids_.begin(); + for (; ids_it != file_ids_.end(); ++ids_it) { + if (trigrams.size() == 0 || + file_ids.find(ids_it->second) != file_ids.end()) { + result.push_back(ids_it->first); + } + } + return result; +} + +FileId Index::GetFileId(const FilePath& file_path) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + string file_path_str = file_path.AsUTF8Unsafe(); + if (file_ids_.find(file_path) != file_ids_.end()) + return file_ids_[file_path]; + file_ids_[file_path] = ++last_file_id_; + return last_file_id_; +} + +void Index::NormalizeVectors() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + for (size_t i = 0; i < kTrigramCount; ++i) { + if (!is_normalized_[i]) { + std::sort(index_[i].begin(), index_[i].end()); + if (index_[i].capacity() > index_[i].size()) + vector(index_[i]).swap(index_[i]); + is_normalized_[i] = true; + } + } +} + +void Index::PrintStats() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + LOG(ERROR) << "Index stats:"; + size_t size = 0; + size_t maxSize = 0; + size_t capacity = 0; + for (size_t i = 0; i < kTrigramCount; ++i) { + if (index_[i].size() > maxSize) + maxSize = index_[i].size(); + size += index_[i].size(); + capacity += index_[i].capacity(); + } + LOG(ERROR) << " - total trigram count: " << size; + LOG(ERROR) << " - max file count per trigram: " << maxSize; + LOG(ERROR) << " - total vectors capacity " << capacity; + size_t total_index_size = + capacity * sizeof(FileId) + sizeof(vector) * kTrigramCount; + LOG(ERROR) << " - estimated total index size " << total_index_size; +} + +typedef Callback&)> IndexerCallback; + +} // namespace + +DevToolsFileSystemIndexer::FileSystemIndexingJob::FileSystemIndexingJob( + const FilePath& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback) + : file_system_path_(file_system_path), + total_work_callback_(total_work_callback), + worked_callback_(worked_callback), + done_callback_(done_callback), + current_file_( + BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get()), + files_indexed_(0), + stopped_(false) { + current_trigrams_set_.resize(kTrigramCount); + current_trigrams_.reserve(kTrigramCount); +} + +DevToolsFileSystemIndexer::FileSystemIndexingJob::~FileSystemIndexingJob() {} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::Start() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + Bind(&FileSystemIndexingJob::CollectFilesToIndex, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::Stop() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask(BrowserThread::FILE, + FROM_HERE, + Bind(&FileSystemIndexingJob::StopOnFileThread, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::StopOnFileThread() { + stopped_ = true; +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::CollectFilesToIndex() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + if (stopped_) + return; + if (!file_enumerator_) { + file_enumerator_.reset( + new FileEnumerator(file_system_path_, true, FileEnumerator::FILES)); + } + FilePath file_path = file_enumerator_->Next(); + if (file_path.empty()) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + Bind(total_work_callback_, file_path_times_.size())); + indexing_it_ = file_path_times_.begin(); + IndexFiles(); + return; + } + Time saved_last_modified_time = + g_trigram_index.Get().LastModifiedTimeForFile(file_path); + FileEnumerator::FileInfo file_info = file_enumerator_->GetInfo(); + Time current_last_modified_time = file_info.GetLastModifiedTime(); + if (current_last_modified_time > saved_last_modified_time) { + file_path_times_[file_path] = current_last_modified_time; + } + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + Bind(&FileSystemIndexingJob::CollectFilesToIndex, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::IndexFiles() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + if (stopped_) + return; + if (indexing_it_ == file_path_times_.end()) { + g_trigram_index.Get().NormalizeVectors(); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_callback_); + return; + } + FilePath file_path = indexing_it_->first; + current_file_.CreateOrOpen( + file_path, + base::File::FLAG_OPEN | base::File::FLAG_READ, + Bind(&FileSystemIndexingJob::StartFileIndexing, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::StartFileIndexing( + base::File::Error error) { + if (!current_file_.IsValid()) { + FinishFileIndexing(false); + return; + } + current_file_offset_ = 0; + current_trigrams_.clear(); + std::fill(current_trigrams_set_.begin(), current_trigrams_set_.end(), false); + ReadFromFile(); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::ReadFromFile() { + if (stopped_) { + CloseFile(); + return; + } + current_file_.Read(current_file_offset_, kMaxReadLength, + Bind(&FileSystemIndexingJob::OnRead, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::OnRead( + base::File::Error error, + const char* data, + int bytes_read) { + if (error != base::File::FILE_OK) { + FinishFileIndexing(false); + return; + } + + if (!bytes_read || bytes_read < 3) { + FinishFileIndexing(true); + return; + } + + size_t size = static_cast(bytes_read); + vector trigram_chars; + trigram_chars.reserve(size); + for (size_t i = 0; i < size; ++i) { + TrigramChar trigram_char = TrigramCharForChar(data[i]); + if (trigram_char == kBinaryTrigramChar) { + current_trigrams_.clear(); + FinishFileIndexing(true); + return; + } + trigram_chars.push_back(trigram_char); + } + + for (size_t i = 0; i + 2 < size; ++i) { + Trigram trigram = TrigramAtIndex(trigram_chars, i); + if ((trigram != kUndefinedTrigram) && !current_trigrams_set_[trigram]) { + current_trigrams_set_[trigram] = true; + current_trigrams_.push_back(trigram); + } + } + current_file_offset_ += bytes_read - 2; + ReadFromFile(); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::FinishFileIndexing( + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + CloseFile(); + if (success) { + FilePath file_path = indexing_it_->first; + g_trigram_index.Get().SetTrigramsForFile( + file_path, current_trigrams_, file_path_times_[file_path]); + } + ReportWorked(); + ++indexing_it_; + IndexFiles(); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::CloseFile() { + if (current_file_.IsValid()) + current_file_.Close(Bind(&FileSystemIndexingJob::CloseCallback, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::CloseCallback( + base::File::Error error) {} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::ReportWorked() { + TimeTicks current_time = TimeTicks::Now(); + bool should_send_worked_nitification = true; + if (!last_worked_notification_time_.is_null()) { + TimeDelta delta = current_time - last_worked_notification_time_; + if (delta.InMilliseconds() < kMinTimeoutBetweenWorkedNitification) + should_send_worked_nitification = false; + } + ++files_indexed_; + if (should_send_worked_nitification) { + last_worked_notification_time_ = current_time; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, Bind(worked_callback_, files_indexed_)); + files_indexed_ = 0; + } +} + +DevToolsFileSystemIndexer::DevToolsFileSystemIndexer() { +} + +DevToolsFileSystemIndexer::~DevToolsFileSystemIndexer() {} + +scoped_refptr +DevToolsFileSystemIndexer::IndexPath( + const string& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + scoped_refptr indexing_job = + new FileSystemIndexingJob(FilePath::FromUTF8Unsafe(file_system_path), + total_work_callback, + worked_callback, + done_callback); + indexing_job->Start(); + return indexing_job; +} + +void DevToolsFileSystemIndexer::SearchInPath(const string& file_system_path, + const string& query, + const SearchCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + Bind(&DevToolsFileSystemIndexer::SearchInPathOnFileThread, + this, + file_system_path, + query, + callback)); +} + +void DevToolsFileSystemIndexer::SearchInPathOnFileThread( + const string& file_system_path, + const string& query, + const SearchCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + vector file_paths = g_trigram_index.Get().Search(query); + vector result; + FilePath path = FilePath::FromUTF8Unsafe(file_system_path); + vector::const_iterator it = file_paths.begin(); + for (; it != file_paths.end(); ++it) { + if (path.IsParent(*it)) + result.push_back(it->AsUTF8Unsafe()); + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, Bind(callback, result)); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/devtools_file_system_indexer.h b/vendor/brightray/browser/devtools_file_system_indexer.h new file mode 100644 index 0000000000..bb1acefab5 --- /dev/null +++ b/vendor/brightray/browser/devtools_file_system_indexer.h @@ -0,0 +1,115 @@ +// Copyright 2013 The Chromium 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 BROWSER_DEVTOOLS_FILE_SYSTEM_INDEXER_H_ +#define BROWSER_DEVTOOLS_FILE_SYSTEM_INDEXER_H_ + +#include + +#include +#include +#include +#include + +#include "base/callback.h" +#include "base/files/file_proxy.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { +class FilePath; +class FileEnumerator; +class Time; +} + +namespace content { +class WebContents; +} + +namespace brightray { + +class DevToolsFileSystemIndexer + : public base::RefCountedThreadSafe { + public: + typedef base::Callback TotalWorkCallback; + typedef base::Callback WorkedCallback; + typedef base::Callback DoneCallback; + typedef base::Callback&)> SearchCallback; + + class FileSystemIndexingJob : public base::RefCounted { + public: + void Stop(); + + private: + friend class base::RefCounted; + friend class DevToolsFileSystemIndexer; + FileSystemIndexingJob(const base::FilePath& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback); + virtual ~FileSystemIndexingJob(); + + void Start(); + void StopOnFileThread(); + void CollectFilesToIndex(); + void IndexFiles(); + void StartFileIndexing(base::File::Error error); + void ReadFromFile(); + void OnRead(base::File::Error error, + const char* data, + int bytes_read); + void FinishFileIndexing(bool success); + void CloseFile(); + void CloseCallback(base::File::Error error); + void ReportWorked(); + + base::FilePath file_system_path_; + TotalWorkCallback total_work_callback_; + WorkedCallback worked_callback_; + DoneCallback done_callback_; + std::unique_ptr file_enumerator_; + typedef std::map FilePathTimesMap; + FilePathTimesMap file_path_times_; + FilePathTimesMap::const_iterator indexing_it_; + base::FileProxy current_file_; + int64_t current_file_offset_; + typedef int32_t Trigram; + std::vector current_trigrams_; + // The index in this vector is the trigram id. + std::vector current_trigrams_set_; + base::TimeTicks last_worked_notification_time_; + int files_indexed_; + bool stopped_; + }; + + DevToolsFileSystemIndexer(); + + // Performs file system indexing for given |file_system_path| and sends + // progress callbacks. + scoped_refptr IndexPath( + const std::string& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback); + + // Performs trigram search for given |query| in |file_system_path|. + void SearchInPath(const std::string& file_system_path, + const std::string& query, + const SearchCallback& callback); + + private: + friend class base::RefCountedThreadSafe; + + virtual ~DevToolsFileSystemIndexer(); + + void SearchInPathOnFileThread(const std::string& file_system_path, + const std::string& query, + const SearchCallback& callback); + + DISALLOW_COPY_AND_ASSIGN(DevToolsFileSystemIndexer); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_FILE_SYSTEM_INDEXER_H_ diff --git a/vendor/brightray/browser/devtools_manager_delegate.cc b/vendor/brightray/browser/devtools_manager_delegate.cc new file mode 100644 index 0000000000..7a7a97a037 --- /dev/null +++ b/vendor/brightray/browser/devtools_manager_delegate.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/devtools_manager_delegate.h" + +#include + +#include "browser/net/devtools_network_protocol_handler.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "common/content_client.h" +#include "components/devtools_discovery/basic_target_descriptor.h" +#include "components/devtools_discovery/devtools_discovery_manager.h" +#include "components/devtools_http_handler/devtools_http_handler.h" +#include "content/shell/grit/shell_resources.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_frontend_host.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/public/common/user_agent.h" +#include "net/base/net_errors.h" +#include "net/socket/tcp_server_socket.h" +#include "net/socket/stream_socket.h" +#include "ui/base/resource/resource_bundle.h" + + +namespace brightray { + +namespace { + +class TCPServerSocketFactory + : public devtools_http_handler::DevToolsHttpHandler::ServerSocketFactory { + public: + TCPServerSocketFactory(const std::string& address, int port) + : address_(address), port_(port) { + } + + private: + // content::DevToolsHttpHandler::ServerSocketFactory. + std::unique_ptr CreateForHttpServer() override { + std::unique_ptr socket( + new net::TCPServerSocket(nullptr, net::NetLog::Source())); + if (socket->ListenWithAddressAndPort(address_, port_, 10) != net::OK) + return std::unique_ptr(); + + return socket; + } + + std::string address_; + uint16_t port_; + + DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory); +}; + +std::unique_ptr +CreateSocketFactory() { + auto& command_line = *base::CommandLine::ForCurrentProcess(); + // See if the user specified a port on the command line (useful for + // automation). If not, use an ephemeral port by specifying 0. + int port = 0; + if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { + int temp_port; + std::string port_str = + command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); + if (base::StringToInt(port_str, &temp_port) && + temp_port > 0 && temp_port < 65535) { + port = temp_port; + } else { + DLOG(WARNING) << "Invalid http debugger port number " << temp_port; + } + } + return std::unique_ptr< + devtools_http_handler::DevToolsHttpHandler::ServerSocketFactory>( + new TCPServerSocketFactory("127.0.0.1", port)); +} + + +// DevToolsDelegate -------------------------------------------------------- + +class DevToolsDelegate : + public devtools_http_handler::DevToolsHttpHandlerDelegate { + public: + DevToolsDelegate(); + ~DevToolsDelegate() override; + + // devtools_http_handler::DevToolsHttpHandlerDelegate. + std::string GetDiscoveryPageHTML() override; + std::string GetFrontendResource(const std::string& path) override; + std::string GetPageThumbnailData(const GURL& url) override; + content::DevToolsExternalAgentProxyDelegate* HandleWebSocketConnection( + const std::string& path) override; + + private: + DISALLOW_COPY_AND_ASSIGN(DevToolsDelegate); +}; + +DevToolsDelegate::DevToolsDelegate() { +} + +DevToolsDelegate::~DevToolsDelegate() { +} + +std::string DevToolsDelegate::GetDiscoveryPageHTML() { + return ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string(); +} + + +std::string DevToolsDelegate::GetFrontendResource( + const std::string& path) { + return content::DevToolsFrontendHost::GetFrontendResource(path).as_string(); +} + +std::string DevToolsDelegate::GetPageThumbnailData(const GURL& url) { + return std::string(); +} + +content::DevToolsExternalAgentProxyDelegate* +DevToolsDelegate::HandleWebSocketConnection(const std::string& path) { + return nullptr; +} + +} // namespace + +// DevToolsManagerDelegate --------------------------------------------------- + +// static +devtools_http_handler::DevToolsHttpHandler* +DevToolsManagerDelegate::CreateHttpHandler() { + return new devtools_http_handler::DevToolsHttpHandler( + CreateSocketFactory(), + std::string(), + new DevToolsDelegate, + base::FilePath(), + base::FilePath(), + std::string(), + GetBrightrayUserAgent()); +} + +DevToolsManagerDelegate::DevToolsManagerDelegate() + : handler_(new DevToolsNetworkProtocolHandler) { + // NB(zcbenz): This call does nothing, the only purpose is to make sure the + // devtools_discovery module is linked into the final executable on Linux. + // Though it is possible to achieve this by modifying the gyp settings, it + // would greatly increase gyp file's complexity, so I chose to instead do + // this hack. + devtools_discovery::DevToolsDiscoveryManager::GetInstance(); +} + +DevToolsManagerDelegate::~DevToolsManagerDelegate() { +} + +void DevToolsManagerDelegate::DevToolsAgentStateChanged( + content::DevToolsAgentHost* agent_host, + bool attached) { + handler_->DevToolsAgentStateChanged(agent_host, attached); +} + +base::DictionaryValue* DevToolsManagerDelegate::HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command) { + return handler_->HandleCommand(agent_host, command); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/devtools_manager_delegate.h b/vendor/brightray/browser/devtools_manager_delegate.h new file mode 100644 index 0000000000..23d28c74db --- /dev/null +++ b/vendor/brightray/browser/devtools_manager_delegate.h @@ -0,0 +1,44 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_MANAGER_DELEGATE_H_ +#define BROWSER_DEVTOOLS_MANAGER_DELEGATE_H_ + +#include "base/macros.h" +#include "base/compiler_specific.h" +#include "components/devtools_http_handler/devtools_http_handler_delegate.h" +#include "content/public/browser/devtools_manager_delegate.h" + +namespace devtools_http_handler { +class DevToolsHttpHandler; +} + +namespace brightray { + +class DevToolsNetworkProtocolHandler; + +class DevToolsManagerDelegate : public content::DevToolsManagerDelegate { + public: + static devtools_http_handler::DevToolsHttpHandler* CreateHttpHandler(); + + DevToolsManagerDelegate(); + virtual ~DevToolsManagerDelegate(); + + // DevToolsManagerDelegate implementation. + void Inspect(content::BrowserContext* browser_context, + content::DevToolsAgentHost* agent_host) override {} + void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host, + bool attached) override; + base::DictionaryValue* HandleCommand(content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command) override; + + private: + std::unique_ptr handler_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsManagerDelegate); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_MANAGER_DELEGATE_H_ diff --git a/vendor/brightray/browser/devtools_ui.cc b/vendor/brightray/browser/devtools_ui.cc new file mode 100644 index 0000000000..24d7d4737c --- /dev/null +++ b/vendor/brightray/browser/devtools_ui.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/devtools_ui.h" + +#include + +#include "base/memory/ref_counted_memory.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "content/public/browser/devtools_frontend_host.h" +#include "content/public/browser/url_data_source.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" + + +namespace brightray { + +namespace { + +const char kChromeUIDevToolsHost[] = "devtools"; +const char kChromeUIDevToolsBundledPath[] = "bundled"; + +std::string PathWithoutParams(const std::string& path) { + return GURL(std::string("chrome-devtools://devtools/") + path) + .path().substr(1); +} + +std::string GetMimeTypeForPath(const std::string& path) { + std::string filename = PathWithoutParams(path); + if (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) { + return "text/html"; + } else if (base::EndsWith(filename, ".css", + base::CompareCase::INSENSITIVE_ASCII)) { + return "text/css"; + } else if (base::EndsWith(filename, ".js", + base::CompareCase::INSENSITIVE_ASCII)) { + return "application/javascript"; + } else if (base::EndsWith(filename, ".png", + base::CompareCase::INSENSITIVE_ASCII)) { + return "image/png"; + } else if (base::EndsWith(filename, ".gif", + base::CompareCase::INSENSITIVE_ASCII)) { + return "image/gif"; + } else if (base::EndsWith(filename, ".svg", + base::CompareCase::INSENSITIVE_ASCII)) { + return "image/svg+xml"; + } else if (base::EndsWith(filename, ".manifest", + base::CompareCase::INSENSITIVE_ASCII)) { + return "text/cache-manifest"; + } + return "text/html"; +} + +class BundledDataSource : public content::URLDataSource { + public: + BundledDataSource() {} + + // content::URLDataSource implementation. + std::string GetSource() const override { + return kChromeUIDevToolsHost; + } + + void StartDataRequest(const std::string& path, + int render_process_id, + int render_frame_id, + const GotDataCallback& callback) override { + // Serve request from local bundle. + std::string bundled_path_prefix(kChromeUIDevToolsBundledPath); + bundled_path_prefix += "/"; + if (base::StartsWith(path, bundled_path_prefix, + base::CompareCase::INSENSITIVE_ASCII)) { + StartBundledDataRequest(path.substr(bundled_path_prefix.length()), + render_process_id, render_frame_id, callback); + return; + } + callback.Run(nullptr); + } + + std::string GetMimeType(const std::string& path) const override { + return GetMimeTypeForPath(path); + } + + bool ShouldAddContentSecurityPolicy() const override { + return false; + } + + bool ShouldDenyXFrameOptions() const override { + return false; + } + + bool ShouldServeMimeTypeAsContentTypeHeader() const override { + return true; + } + + void StartBundledDataRequest( + const std::string& path, + int render_process_id, + int render_frame_id, + const content::URLDataSource::GotDataCallback& callback) { + std::string filename = PathWithoutParams(path); + base::StringPiece resource = + content::DevToolsFrontendHost::GetFrontendResource(filename); + + DLOG_IF(WARNING, resource.empty()) + << "Unable to find dev tool resource: " << filename + << ". If you compiled with debug_devtools=1, try running with " + "--debug-devtools."; + scoped_refptr bytes( + new base::RefCountedStaticMemory(resource.data(), resource.length())); + callback.Run(bytes.get()); + } + + private: + ~BundledDataSource() override {} + DISALLOW_COPY_AND_ASSIGN(BundledDataSource); +}; + +} // namespace + +DevToolsUI::DevToolsUI(content::BrowserContext* browser_context, + content::WebUI* web_ui) + : WebUIController(web_ui) { + web_ui->SetBindings(0); + content::URLDataSource::Add(browser_context, new BundledDataSource()); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/devtools_ui.h b/vendor/brightray/browser/devtools_ui.h new file mode 100644 index 0000000000..176de9b71b --- /dev/null +++ b/vendor/brightray/browser/devtools_ui.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_DEVTOOLS_UI_H_ +#define BRIGHTRAY_BROWSER_DEVTOOLS_UI_H_ + +#include "base/compiler_specific.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/web_ui_controller.h" + +namespace brightray { + +class BrowserContext; + +class DevToolsUI : public content::WebUIController { + public: + explicit DevToolsUI(content::BrowserContext* browser_context, + content::WebUI* web_ui); + + private: + DISALLOW_COPY_AND_ASSIGN(DevToolsUI); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/inspectable_web_contents.cc b/vendor/brightray/browser/inspectable_web_contents.cc new file mode 100644 index 0000000000..74aeac6711 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents.cc @@ -0,0 +1,18 @@ +#include "browser/inspectable_web_contents.h" + +#include "browser/inspectable_web_contents_impl.h" + +namespace brightray { + +InspectableWebContents* InspectableWebContents::Create( + const content::WebContents::CreateParams& create_params) { + auto contents = content::WebContents::Create(create_params); + return Create(contents); +} + +InspectableWebContents* InspectableWebContents::Create( + content::WebContents* web_contents) { + return new InspectableWebContentsImpl(web_contents); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/inspectable_web_contents.h b/vendor/brightray/browser/inspectable_web_contents.h new file mode 100644 index 0000000000..01e9c6d274 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents.h @@ -0,0 +1,51 @@ +#ifndef BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_H_ +#define BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_H_ + +#include "content/public/browser/web_contents.h" + +namespace base { +class Value; +} + +namespace content { +class DevToolsAgentHost; +} + +namespace brightray { + +class InspectableWebContentsDelegate; +class InspectableWebContentsView; + +class InspectableWebContents { + public: + static InspectableWebContents* Create(const content::WebContents::CreateParams&); + + // The returned InspectableWebContents takes ownership of the passed-in + // WebContents. + static InspectableWebContents* Create(content::WebContents*); + + virtual ~InspectableWebContents() {} + + virtual InspectableWebContentsView* GetView() const = 0; + virtual content::WebContents* GetWebContents() const = 0; + virtual content::WebContents* GetDevToolsWebContents() const = 0; + + // The delegate manages its own life. + virtual void SetDelegate(InspectableWebContentsDelegate* delegate) = 0; + virtual InspectableWebContentsDelegate* GetDelegate() const = 0; + + virtual void SetDockState(const std::string& state) = 0; + virtual void ShowDevTools() = 0; + virtual void CloseDevTools() = 0; + virtual bool IsDevToolsViewShowing() = 0; + virtual void AttachTo(const scoped_refptr&) = 0; + virtual void Detach() = 0; + virtual void CallClientFunction(const std::string& function_name, + const base::Value* arg1 = nullptr, + const base::Value* arg2 = nullptr, + const base::Value* arg3 = nullptr) = 0; +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/inspectable_web_contents_delegate.h b/vendor/brightray/browser/inspectable_web_contents_delegate.h new file mode 100644 index 0000000000..32bf5e0acb --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_delegate.h @@ -0,0 +1,35 @@ +#ifndef BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_DELEGATE_H_ +#define BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_DELEGATE_H_ + +#include +#include "base/files/file_path.h" + +namespace brightray { + +class InspectableWebContentsDelegate { + public: + virtual ~InspectableWebContentsDelegate() {} + + // Requested by WebContents of devtools. + virtual void DevToolsReloadPage() {} + virtual void DevToolsSaveToFile( + const std::string& url, const std::string& content, bool save_as) {} + virtual void DevToolsAppendToFile( + const std::string& url, const std::string& content) {} + virtual void DevToolsRequestFileSystems() {} + virtual void DevToolsAddFileSystem( + const base::FilePath& file_system_path) {} + virtual void DevToolsRemoveFileSystem( + const base::FilePath& file_system_path) {} + virtual void DevToolsIndexPath( + int request_id, const std::string& file_system_path) {} + virtual void DevToolsStopIndexing(int request_id) {} + virtual void DevToolsSearchInPath( + int request_id, + const std::string& file_system_path, + const std::string& query) {} +}; + +} // namespace brightray + +#endif // BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_DELEGATE_H_ diff --git a/vendor/brightray/browser/inspectable_web_contents_impl.cc b/vendor/brightray/browser/inspectable_web_contents_impl.cc new file mode 100644 index 0000000000..bdc6579242 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_impl.cc @@ -0,0 +1,769 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/inspectable_web_contents_impl.h" + +#include "base/base64.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/metrics/histogram.h" +#include "base/strings/pattern.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "browser/browser_client.h" +#include "browser/browser_context.h" +#include "browser/browser_main_parts.h" +#include "browser/inspectable_web_contents_delegate.h" +#include "browser/inspectable_web_contents_view.h" +#include "browser/inspectable_web_contents_view_delegate.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/prefs/pref_registry_simple.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/host_zoom_map.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/user_agent.h" +#include "ipc/ipc_channel.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_response_writer.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" + +namespace brightray { + +namespace { + +const double kPresetZoomFactors[] = { 0.25, 0.333, 0.5, 0.666, 0.75, 0.9, 1.0, + 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, + 5.0 }; + +const char kChromeUIDevToolsURL[] = + "chrome-devtools://devtools/bundled/inspector.html?" + "remoteBase=%s&" + "can_dock=%s&" + "toolbarColor=rgba(223,223,223,1)&" + "textColor=rgba(0,0,0,1)&" + "experiments=true"; +const char kChromeUIDevToolsRemoteFrontendBase[] = + "https://chrome-devtools-frontend.appspot.com/"; +const char kChromeUIDevToolsRemoteFrontendPath[] = "serve_file"; + +const char kDevToolsBoundsPref[] = "brightray.devtools.bounds"; +const char kDevToolsZoomPref[] = "brightray.devtools.zoom"; +const char kDevToolsPreferences[] = "brightray.devtools.preferences"; + +const char kFrontendHostId[] = "id"; +const char kFrontendHostMethod[] = "method"; +const char kFrontendHostParams[] = "params"; +const char kTitleFormat[] = "Developer Tools - %s"; + +const char kDevToolsActionTakenHistogram[] = "DevTools.ActionTaken"; +const int kDevToolsActionTakenBoundary = 100; +const char kDevToolsPanelShownHistogram[] = "DevTools.PanelShown"; +const int kDevToolsPanelShownBoundary = 20; + +const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4; + +void RectToDictionary(const gfx::Rect& bounds, base::DictionaryValue* dict) { + dict->SetInteger("x", bounds.x()); + dict->SetInteger("y", bounds.y()); + dict->SetInteger("width", bounds.width()); + dict->SetInteger("height", bounds.height()); +} + +void DictionaryToRect(const base::DictionaryValue& dict, gfx::Rect* bounds) { + int x = 0, y = 0, width = 800, height = 600; + dict.GetInteger("x", &x); + dict.GetInteger("y", &y); + dict.GetInteger("width", &width); + dict.GetInteger("height", &height); + *bounds = gfx::Rect(x, y, width, height); +} + +bool IsPointInRect(const gfx::Point& point, const gfx::Rect& rect) { + return point.x() > rect.x() && point.x() < (rect.width() + rect.x()) && + point.y() > rect.y() && point.y() < (rect.height() + rect.y()); +} + +bool IsPointInScreen(const gfx::Point& point) { + for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) { + if (IsPointInRect(point, display.bounds())) + return true; + } + return false; +} + +void SetZoomLevelForWebContents(content::WebContents* web_contents, + double level) { + content::HostZoomMap::SetZoomLevel(web_contents, level); +} + +double GetNextZoomLevel(double level, bool out) { + double factor = content::ZoomLevelToZoomFactor(level); + size_t size = arraysize(kPresetZoomFactors); + for (size_t i = 0; i < size; ++i) { + if (!content::ZoomValuesEqual(kPresetZoomFactors[i], factor)) + continue; + if (out && i > 0) + return content::ZoomFactorToZoomLevel(kPresetZoomFactors[i - 1]); + if (!out && i != size - 1) + return content::ZoomFactorToZoomLevel(kPresetZoomFactors[i + 1]); + } + return level; +} + +GURL GetRemoteBaseURL() { + return GURL(base::StringPrintf( + "%s%s/%s/", + kChromeUIDevToolsRemoteFrontendBase, + kChromeUIDevToolsRemoteFrontendPath, + content::GetWebKitRevision().c_str())); +} + +GURL GetDevToolsURL( + bool can_dock, + const std::string& dock_state) { + auto url_string = + base::StringPrintf(kChromeUIDevToolsURL, + GetRemoteBaseURL().spec().c_str(), + can_dock ? "true" : ""); + if (!dock_state.empty()) + url_string += "&settings={\"currentDockState\":\"\\\"" + dock_state + "\\\"\"}&"; + return GURL(url_string); +} + +// ResponseWriter ------------------------------------------------------------- + +class ResponseWriter : public net::URLFetcherResponseWriter { + public: + ResponseWriter(base::WeakPtr bindings, int stream_id); + ~ResponseWriter() override; + + // URLFetcherResponseWriter overrides: + int Initialize(const net::CompletionCallback& callback) override; + int Write(net::IOBuffer* buffer, + int num_bytes, + const net::CompletionCallback& callback) override; + int Finish(const net::CompletionCallback& callback) override; + + private: + base::WeakPtr bindings_; + int stream_id_; + + DISALLOW_COPY_AND_ASSIGN(ResponseWriter); +}; + +ResponseWriter::ResponseWriter(base::WeakPtr bindings, + int stream_id) + : bindings_(bindings), + stream_id_(stream_id) { +} + +ResponseWriter::~ResponseWriter() { +} + +int ResponseWriter::Initialize(const net::CompletionCallback& callback) { + return net::OK; +} + +int ResponseWriter::Write(net::IOBuffer* buffer, + int num_bytes, + const net::CompletionCallback& callback) { + std::string chunk = std::string(buffer->data(), num_bytes); + bool encoded = false; + if (!base::IsStringUTF8(chunk)) { + encoded = true; + base::Base64Encode(chunk, &chunk); + } + + base::FundamentalValue* id = new base::FundamentalValue(stream_id_); + base::StringValue* chunkValue = new base::StringValue(chunk); + base::FundamentalValue* encodedValue = new base::FundamentalValue(encoded); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&InspectableWebContentsImpl::CallClientFunction, bindings_, + "DevToolsAPI.streamWrite", base::Owned(id), + base::Owned(chunkValue), base::Owned(encodedValue))); + return num_bytes; +} + +int ResponseWriter::Finish(const net::CompletionCallback& callback) { + return net::OK; +} + +} // namespace + +// Implemented separately on each platform. +InspectableWebContentsView* CreateInspectableContentsView( + InspectableWebContentsImpl* inspectable_web_contents_impl); + +void InspectableWebContentsImpl::RegisterPrefs(PrefRegistrySimple* registry) { + std::unique_ptr bounds_dict(new base::DictionaryValue); + RectToDictionary(gfx::Rect(0, 0, 800, 600), bounds_dict.get()); + registry->RegisterDictionaryPref(kDevToolsBoundsPref, bounds_dict.release()); + registry->RegisterDoublePref(kDevToolsZoomPref, 0.); + registry->RegisterDictionaryPref(kDevToolsPreferences); +} + +InspectableWebContentsImpl::InspectableWebContentsImpl( + content::WebContents* web_contents) + : frontend_loaded_(false), + can_dock_(true), + delegate_(nullptr), + web_contents_(web_contents), + weak_factory_(this) { + auto context = static_cast(web_contents_->GetBrowserContext()); + pref_service_ = context->prefs(); + auto bounds_dict = pref_service_->GetDictionary(kDevToolsBoundsPref); + if (bounds_dict) { + DictionaryToRect(*bounds_dict, &devtools_bounds_); + // Sometimes the devtools window is out of screen or has too small size. + if (devtools_bounds_.height() < 100 || devtools_bounds_.width() < 100) { + devtools_bounds_.set_height(600); + devtools_bounds_.set_width(800); + } + if (!IsPointInScreen(devtools_bounds_.origin())) { + gfx::Rect display; + if (web_contents->GetNativeView()) { + display = display::Screen::GetScreen()-> + GetDisplayNearestWindow(web_contents->GetNativeView()).bounds(); + } else { + display = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); + } + + devtools_bounds_.set_x(display.x() + (display.width() - devtools_bounds_.width()) / 2); + devtools_bounds_.set_y(display.y() + (display.height() - devtools_bounds_.height()) / 2); + } + } + + view_.reset(CreateInspectableContentsView(this)); +} + +InspectableWebContentsImpl::~InspectableWebContentsImpl() { + if (web_contents_) { + CloseContents(web_contents_); + delete web_contents_; + } +} + +InspectableWebContentsView* InspectableWebContentsImpl::GetView() const { + return view_.get(); +} + +content::WebContents* InspectableWebContentsImpl::GetWebContents() const { + return web_contents_; +} + +content::WebContents* InspectableWebContentsImpl::GetDevToolsWebContents() const { + return devtools_web_contents_.get(); +} + +void InspectableWebContentsImpl::SetDelegate(InspectableWebContentsDelegate* delegate) { + delegate_ = delegate; +} + +InspectableWebContentsDelegate* InspectableWebContentsImpl::GetDelegate() const { + return delegate_; +} + +void InspectableWebContentsImpl::SetDockState(const std::string& state) { + if (state == "detach") { + can_dock_ = false; + } else { + can_dock_ = true; + dock_state_ = state; + } +} + +void InspectableWebContentsImpl::ShowDevTools() { + if (!view_) + return; + + // Show devtools only after it has done loading, this is to make sure the + // SetIsDocked is called *BEFORE* ShowDevTools. + if (!devtools_web_contents_) { + embedder_message_dispatcher_.reset( + DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this)); + + content::WebContents::CreateParams create_params(web_contents_->GetBrowserContext()); + devtools_web_contents_.reset(content::WebContents::Create(create_params)); + + Observe(devtools_web_contents_.get()); + devtools_web_contents_->SetDelegate(this); + + agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents_); + agent_host_->AttachClient(this); + + devtools_web_contents_->GetController().LoadURL( + GetDevToolsURL(can_dock_, dock_state_), + content::Referrer(), + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, + std::string()); + } else { + view_->ShowDevTools(); + } +} + +void InspectableWebContentsImpl::CloseDevTools() { + if (devtools_web_contents_) { + view_->CloseDevTools(); + devtools_web_contents_.reset(); + if (web_contents_) + web_contents_->Focus(); + } +} + +bool InspectableWebContentsImpl::IsDevToolsViewShowing() { + return devtools_web_contents_ && view_->IsDevToolsViewShowing(); +} + +void InspectableWebContentsImpl::AttachTo(const scoped_refptr& host) { + if (agent_host_.get()) + Detach(); + agent_host_ = host; + agent_host_->AttachClient(this); +} + +void InspectableWebContentsImpl::Detach() { + if (agent_host_.get()) + agent_host_->DetachClient(this); + agent_host_ = nullptr; +} + +void InspectableWebContentsImpl::CallClientFunction(const std::string& function_name, + const base::Value* arg1, + const base::Value* arg2, + const base::Value* arg3) { + if (!devtools_web_contents_) + return; + + std::string javascript = function_name + "("; + if (arg1) { + std::string json; + base::JSONWriter::Write(*arg1, &json); + javascript.append(json); + if (arg2) { + base::JSONWriter::Write(*arg2, &json); + javascript.append(", ").append(json); + if (arg3) { + base::JSONWriter::Write(*arg3, &json); + javascript.append(", ").append(json); + } + } + } + javascript.append(");"); + devtools_web_contents_->GetMainFrame()->ExecuteJavaScript( + base::UTF8ToUTF16(javascript)); +} + +gfx::Rect InspectableWebContentsImpl::GetDevToolsBounds() const { + return devtools_bounds_; +} + +void InspectableWebContentsImpl::SaveDevToolsBounds(const gfx::Rect& bounds) { + base::DictionaryValue bounds_dict; + RectToDictionary(bounds, &bounds_dict); + pref_service_->Set(kDevToolsBoundsPref, bounds_dict); + devtools_bounds_ = bounds; +} + +double InspectableWebContentsImpl::GetDevToolsZoomLevel() const { + return pref_service_->GetDouble(kDevToolsZoomPref); +} + +void InspectableWebContentsImpl::UpdateDevToolsZoomLevel(double level) { + pref_service_->SetDouble(kDevToolsZoomPref, level); +} + +void InspectableWebContentsImpl::ActivateWindow() { + // Set the zoom level. + SetZoomLevelForWebContents(GetDevToolsWebContents(), + GetDevToolsZoomLevel()); +} + +void InspectableWebContentsImpl::CloseWindow() { + GetDevToolsWebContents()->DispatchBeforeUnload(); +} + +void InspectableWebContentsImpl::LoadCompleted() { + frontend_loaded_ = true; + view_->ShowDevTools(); + + // If the devtools can dock, "SetIsDocked" will be called by devtools itself. + if (!can_dock_) + SetIsDocked(DispatchCallback(), false); + + if (view_->GetDelegate()) + view_->GetDelegate()->DevToolsOpened(); +} + +void InspectableWebContentsImpl::SetInspectedPageBounds(const gfx::Rect& rect) { + DevToolsContentsResizingStrategy strategy(rect); + if (contents_resizing_strategy_.Equals(strategy)) + return; + + contents_resizing_strategy_.CopyFrom(strategy); + view_->SetContentsResizingStrategy(contents_resizing_strategy_); +} + +void InspectableWebContentsImpl::InspectElementCompleted() { +} + +void InspectableWebContentsImpl::InspectedURLChanged(const std::string& url) { + view_->SetTitle(base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, + url.c_str()))); +} + +void InspectableWebContentsImpl::LoadNetworkResource( + const DispatchCallback& callback, + const std::string& url, + const std::string& headers, + int stream_id) { + GURL gurl(url); + if (!gurl.is_valid()) { + base::DictionaryValue response; + response.SetInteger("statusCode", 404); + callback.Run(&response); + return; + } + + auto browser_context = static_cast(devtools_web_contents_->GetBrowserContext()); + + net::URLFetcher* fetcher = + (net::URLFetcher::Create(gurl, net::URLFetcher::GET, this)).release(); + pending_requests_[fetcher] = callback; + fetcher->SetRequestContext(browser_context->url_request_context_getter()); + fetcher->SetExtraRequestHeaders(headers); + fetcher->SaveResponseWithWriter(std::unique_ptr( + new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id))); + fetcher->Start(); +} + +void InspectableWebContentsImpl::SetIsDocked(const DispatchCallback& callback, + bool docked) { + view_->SetIsDocked(docked); + if (!callback.is_null()) + callback.Run(nullptr); +} + +void InspectableWebContentsImpl::OpenInNewTab(const std::string& url) { +} + +void InspectableWebContentsImpl::SaveToFile( + const std::string& url, const std::string& content, bool save_as) { + if (delegate_) + delegate_->DevToolsSaveToFile(url, content, save_as); +} + +void InspectableWebContentsImpl::AppendToFile( + const std::string& url, const std::string& content) { + if (delegate_) + delegate_->DevToolsAppendToFile(url, content); +} + +void InspectableWebContentsImpl::RequestFileSystems() { + if (delegate_) + delegate_->DevToolsRequestFileSystems(); +} + +void InspectableWebContentsImpl::AddFileSystem( + const std::string& file_system_path) { + if (delegate_) + delegate_->DevToolsAddFileSystem( + base::FilePath::FromUTF8Unsafe(file_system_path)); +} + +void InspectableWebContentsImpl::RemoveFileSystem( + const std::string& file_system_path) { + if (delegate_) + delegate_->DevToolsRemoveFileSystem( + base::FilePath::FromUTF8Unsafe(file_system_path)); +} + +void InspectableWebContentsImpl::UpgradeDraggedFileSystemPermissions( + const std::string& file_system_url) { +} + +void InspectableWebContentsImpl::IndexPath( + int request_id, const std::string& file_system_path) { + if (delegate_) + delegate_->DevToolsIndexPath(request_id, file_system_path); +} + +void InspectableWebContentsImpl::StopIndexing(int request_id) { + if (delegate_) + delegate_->DevToolsStopIndexing(request_id); +} + +void InspectableWebContentsImpl::SearchInPath( + int request_id, + const std::string& file_system_path, + const std::string& query) { + if (delegate_) + delegate_->DevToolsSearchInPath(request_id, file_system_path, query); +} + +void InspectableWebContentsImpl::SetWhitelistedShortcuts(const std::string& message) { +} + +void InspectableWebContentsImpl::ZoomIn() { + double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), false); + SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level); + UpdateDevToolsZoomLevel(new_level); +} + +void InspectableWebContentsImpl::ZoomOut() { + double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), true); + SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level); + UpdateDevToolsZoomLevel(new_level); +} + +void InspectableWebContentsImpl::ResetZoom() { + SetZoomLevelForWebContents(GetDevToolsWebContents(), 0.); + UpdateDevToolsZoomLevel(0.); +} + +void InspectableWebContentsImpl::SetDevicesUpdatesEnabled(bool enabled) { +} + +void InspectableWebContentsImpl::DispatchProtocolMessageFromDevToolsFrontend( + const std::string& message) { + // If the devtools wants to reload the page, hijack the message and handle it + // to the delegate. + if (base::MatchPattern(message, "{\"id\":*," + "\"method\":\"Page.reload\"," + "\"params\":*}")) { + if (delegate_) + delegate_->DevToolsReloadPage(); + return; + } + + if (agent_host_.get()) + agent_host_->DispatchProtocolMessage(this, message); +} + +void InspectableWebContentsImpl::RecordActionUMA(const std::string& name, int action) { + if (name == kDevToolsActionTakenHistogram) + UMA_HISTOGRAM_ENUMERATION(name, action, kDevToolsActionTakenBoundary); + else if (name == kDevToolsPanelShownHistogram) + UMA_HISTOGRAM_ENUMERATION(name, action, kDevToolsPanelShownBoundary); +} + +void InspectableWebContentsImpl::SendJsonRequest(const DispatchCallback& callback, + const std::string& browser_id, + const std::string& url) { + callback.Run(nullptr); +} + +void InspectableWebContentsImpl::GetPreferences( + const DispatchCallback& callback) { + const base::DictionaryValue* prefs = pref_service_->GetDictionary( + kDevToolsPreferences); + callback.Run(prefs); +} + +void InspectableWebContentsImpl::SetPreference(const std::string& name, + const std::string& value) { + DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences); + update.Get()->SetStringWithoutPathExpansion(name, value); +} + +void InspectableWebContentsImpl::RemovePreference(const std::string& name) { + DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences); + update.Get()->RemoveWithoutPathExpansion(name, nullptr); +} + +void InspectableWebContentsImpl::ClearPreferences() { + DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences); + update.Get()->Clear(); +} + +void InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend(const std::string& message) { + std::string method; + base::ListValue empty_params; + base::ListValue* params = &empty_params; + + base::DictionaryValue* dict = nullptr; + std::unique_ptr parsed_message(base::JSONReader::Read(message)); + if (!parsed_message || + !parsed_message->GetAsDictionary(&dict) || + !dict->GetString(kFrontendHostMethod, &method) || + (dict->HasKey(kFrontendHostParams) && + !dict->GetList(kFrontendHostParams, ¶ms))) { + LOG(ERROR) << "Invalid message was sent to embedder: " << message; + return; + } + int id = 0; + dict->GetInteger(kFrontendHostId, &id); + embedder_message_dispatcher_->Dispatch( + base::Bind(&InspectableWebContentsImpl::SendMessageAck, + weak_factory_.GetWeakPtr(), + id), + method, + params); +} + +void InspectableWebContentsImpl::DispatchProtocolMessage( + content::DevToolsAgentHost* agent_host, const std::string& message) { + if (!frontend_loaded_) + return; + + if (message.length() < kMaxMessageChunkSize) { + base::string16 javascript = base::UTF8ToUTF16( + "DevToolsAPI.dispatchMessage(" + message + ");"); + devtools_web_contents_->GetMainFrame()->ExecuteJavaScript(javascript); + return; + } + + base::FundamentalValue total_size(static_cast(message.length())); + for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) { + base::StringValue message_value(message.substr(pos, kMaxMessageChunkSize)); + CallClientFunction("DevToolsAPI.dispatchMessageChunk", + &message_value, pos ? nullptr : &total_size, nullptr); + } +} + +void InspectableWebContentsImpl::AgentHostClosed( + content::DevToolsAgentHost* agent_host, bool replaced) { +} + +void InspectableWebContentsImpl::RenderFrameHostChanged( + content::RenderFrameHost* old_host, + content::RenderFrameHost* new_host) { + if (new_host->GetParent()) + return; + frontend_host_.reset(content::DevToolsFrontendHost::Create( + new_host, + base::Bind(&InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend, + base::Unretained(this)))); +} + +void InspectableWebContentsImpl::WebContentsDestroyed() { + CloseContents(web_contents_); + web_contents_ = nullptr; +} + +bool InspectableWebContentsImpl::AddMessageToConsole( + content::WebContents* source, + int32_t level, + const base::string16& message, + int32_t line_no, + const base::string16& source_id) { + logging::LogMessage("CONSOLE", line_no, level).stream() << "\"" << + message << "\", source: " << source_id << " (" << line_no << ")"; + return true; +} + +bool InspectableWebContentsImpl::ShouldCreateWebContents( + content::WebContents* web_contents, + int32_t route_id, + int32_t main_frame_route_id, + int32_t main_frame_widget_route_id, + WindowContainerType window_container_type, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) { + return false; +} + +void InspectableWebContentsImpl::HandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + delegate->HandleKeyboardEvent(source, event); +} + +void InspectableWebContentsImpl::CloseContents(content::WebContents* source) { + Observe(nullptr); + frontend_loaded_ = false; + Detach(); + + for (const auto& pair : pending_requests_) + delete pair.first; + + CloseDevTools(); + view_.reset(); +} + +content::ColorChooser* InspectableWebContentsImpl::OpenColorChooser( + content::WebContents* source, + SkColor color, + const std::vector& suggestions) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + return delegate->OpenColorChooser(source, color, suggestions); + return nullptr; +} + +void InspectableWebContentsImpl::RunFileChooser( + content::RenderFrameHost* render_frame_host, + const content::FileChooserParams& params) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + delegate->RunFileChooser(render_frame_host, params); +} + +void InspectableWebContentsImpl::EnumerateDirectory( + content::WebContents* source, + int request_id, + const base::FilePath& path) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + delegate->EnumerateDirectory(source, request_id, path); +} + +void InspectableWebContentsImpl::OnWebContentsFocused() { +#if defined(TOOLKIT_VIEWS) + if (view_->GetDelegate()) + view_->GetDelegate()->DevToolsFocused(); +#endif +} + +void InspectableWebContentsImpl::DidStartNavigationToPendingEntry( + const GURL& url, + content::NavigationController::ReloadType reload_type) { + frontend_host_.reset( + content::DevToolsFrontendHost::Create( + web_contents()->GetMainFrame(), + base::Bind(&InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend, + base::Unretained(this)))); +} + +void InspectableWebContentsImpl::OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK(source); + auto it = pending_requests_.find(source); + DCHECK(it != pending_requests_.end()); + + base::DictionaryValue response; + auto* headers = new base::DictionaryValue(); + net::HttpResponseHeaders* rh = source->GetResponseHeaders(); + response.SetInteger("statusCode", rh ? rh->response_code() : 200); + response.Set("headers", headers); + + size_t iterator = 0; + std::string name; + std::string value; + while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value)) + headers->SetString(name, value); + + it->second.Run(&response); + pending_requests_.erase(it); + delete source; +} + +void InspectableWebContentsImpl::SendMessageAck(int request_id, + const base::Value* arg) { + base::FundamentalValue id_value(request_id); + CallClientFunction("DevToolsAPI.embedderMessageAck", + &id_value, arg, nullptr); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/inspectable_web_contents_impl.h b/vendor/brightray/browser/inspectable_web_contents_impl.h new file mode 100644 index 0000000000..58bc066502 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_impl.h @@ -0,0 +1,200 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_IMPL_H_ +#define BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_IMPL_H_ + +#include "browser/inspectable_web_contents.h" + +#include "browser/devtools_contents_resizing_strategy.h" +#include "browser/devtools_embedder_message_dispatcher.h" + +#include "base/memory/weak_ptr.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_frontend_host.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_observer.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "ui/gfx/geometry/rect.h" + +class PrefService; +class PrefRegistrySimple; + +namespace content { +class DevToolsAgentHost; +} + +namespace brightray { + +class InspectableWebContentsDelegate; +class InspectableWebContentsView; + +class InspectableWebContentsImpl : + public InspectableWebContents, + public content::DevToolsAgentHostClient, + public content::WebContentsObserver, + public content::WebContentsDelegate, + public DevToolsEmbedderMessageDispatcher::Delegate, + public net::URLFetcherDelegate { + public: + static void RegisterPrefs(PrefRegistrySimple* pref_registry); + + explicit InspectableWebContentsImpl(content::WebContents*); + virtual ~InspectableWebContentsImpl(); + + InspectableWebContentsView* GetView() const override; + content::WebContents* GetWebContents() const override; + content::WebContents* GetDevToolsWebContents() const override; + + void SetDelegate(InspectableWebContentsDelegate* delegate) override; + InspectableWebContentsDelegate* GetDelegate() const override; + void SetDockState(const std::string& state) override; + void ShowDevTools() override; + void CloseDevTools() override; + bool IsDevToolsViewShowing() override; + void AttachTo(const scoped_refptr&) override; + void Detach() override; + void CallClientFunction(const std::string& function_name, + const base::Value* arg1, + const base::Value* arg2, + const base::Value* arg3) override; + + // Return the last position and size of devtools window. + gfx::Rect GetDevToolsBounds() const; + void SaveDevToolsBounds(const gfx::Rect& bounds); + + // Return the last set zoom level of devtools window. + double GetDevToolsZoomLevel() const; + void UpdateDevToolsZoomLevel(double level); + + void WebContentsDestroyed() override; + private: + // DevToolsEmbedderMessageDispacher::Delegate + void ActivateWindow() override; + void CloseWindow() override; + void LoadCompleted() override; + void SetInspectedPageBounds(const gfx::Rect& rect) override; + void InspectElementCompleted() override; + void InspectedURLChanged(const std::string& url) override; + void LoadNetworkResource(const DispatchCallback& callback, + const std::string& url, + const std::string& headers, + int stream_id) override; + void SetIsDocked(const DispatchCallback& callback, bool is_docked) override; + void OpenInNewTab(const std::string& url) override; + void SaveToFile(const std::string& url, + const std::string& content, + bool save_as) override; + void AppendToFile(const std::string& url, + const std::string& content) override; + void RequestFileSystems() override; + void AddFileSystem(const std::string& file_system_path) override; + void RemoveFileSystem(const std::string& file_system_path) override; + void UpgradeDraggedFileSystemPermissions( + const std::string& file_system_url) override; + void IndexPath(int index_request_id, + const std::string& file_system_path) override; + void StopIndexing(int index_request_id) override; + void SearchInPath(int search_request_id, + const std::string& file_system_path, + const std::string& query) override; + void SetWhitelistedShortcuts(const std::string& message) override; + void ZoomIn() override; + void ZoomOut() override; + void ResetZoom() override; + void SetDevicesUpdatesEnabled(bool enabled) override; + void DispatchProtocolMessageFromDevToolsFrontend( + const std::string& message) override; + void RecordActionUMA(const std::string& name, int action) override; + void SendJsonRequest(const DispatchCallback& callback, + const std::string& browser_id, + const std::string& url) override; + void GetPreferences(const DispatchCallback& callback) override; + void SetPreference(const std::string& name, + const std::string& value) override; + void RemovePreference(const std::string& name) override; + void ClearPreferences() override; + + // content::DevToolsFrontendHostDelegate: + void HandleMessageFromDevToolsFrontend(const std::string& message); + + // content::DevToolsAgentHostClient: + void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host, + const std::string& message) override; + void AgentHostClosed(content::DevToolsAgentHost* agent_host, + bool replaced) override; + + // content::WebContentsObserver: + void RenderFrameHostChanged(content::RenderFrameHost* old_host, + content::RenderFrameHost* new_host) override; + void OnWebContentsFocused() override; + void DidStartNavigationToPendingEntry( + const GURL& url, + content::NavigationController::ReloadType reload_type) override; + + // content::WebContentsDelegate: + bool AddMessageToConsole(content::WebContents* source, + int32_t level, + const base::string16& message, + int32_t line_no, + const base::string16& source_id) override; + bool ShouldCreateWebContents( + content::WebContents* web_contents, + int32_t route_id, + int32_t main_frame_route_id, + int32_t main_frame_widget_route_id, + WindowContainerType window_container_type, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) override; + void HandleKeyboardEvent( + content::WebContents*, const content::NativeWebKeyboardEvent&) override; + void CloseContents(content::WebContents* source) override; + content::ColorChooser* OpenColorChooser( + content::WebContents* source, + SkColor color, + const std::vector& suggestions) override; + void RunFileChooser(content::RenderFrameHost* render_frame_host, + const content::FileChooserParams& params) override; + void EnumerateDirectory(content::WebContents* source, + int request_id, + const base::FilePath& path) override; + + // net::URLFetcherDelegate: + void OnURLFetchComplete(const net::URLFetcher* source) override; + + void SendMessageAck(int request_id, + const base::Value* arg1); + + bool frontend_loaded_; + scoped_refptr agent_host_; + std::unique_ptr frontend_host_; + std::unique_ptr embedder_message_dispatcher_; + + DevToolsContentsResizingStrategy contents_resizing_strategy_; + gfx::Rect devtools_bounds_; + bool can_dock_; + std::string dock_state_; + + using PendingRequestsMap = std::map; + PendingRequestsMap pending_requests_; + InspectableWebContentsDelegate* delegate_; // weak references. + + PrefService* pref_service_; // weak reference. + + content::WebContents* web_contents_; // not owned + + std::unique_ptr devtools_web_contents_; + std::unique_ptr view_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsImpl); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/inspectable_web_contents_view.h b/vendor/brightray/browser/inspectable_web_contents_view.h new file mode 100644 index 0000000000..1b8f0930f8 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_view.h @@ -0,0 +1,59 @@ +#ifndef BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_H_ +#define BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_H_ + +#include "base/strings/string16.h" +#include "ui/gfx/native_widget_types.h" + +class DevToolsContentsResizingStrategy; + +#if defined(USE_ASH) +namespace views { +class View; +} +#endif + +namespace brightray { + +class InspectableWebContentsViewDelegate; + +class InspectableWebContentsView { + public: + InspectableWebContentsView() : delegate_(nullptr) {} + virtual ~InspectableWebContentsView() {} + + // The delegate manages its own life. + void SetDelegate(InspectableWebContentsViewDelegate* delegate) { + delegate_ = delegate; + } + InspectableWebContentsViewDelegate* GetDelegate() const { + return delegate_; + } + +#if defined(USE_ASH) + // Returns the container control, which has devtools view attached. + virtual views::View* GetView() = 0; + + // Returns the web view control, which can be used by the + // GetInitiallyFocusedView() to set initial focus to web view. + virtual views::View* GetWebView() = 0; +#else + virtual gfx::NativeView GetNativeView() const = 0; +#endif + + virtual void ShowDevTools() = 0; + // Hide the DevTools view. + virtual void CloseDevTools() = 0; + virtual bool IsDevToolsViewShowing() = 0; + virtual bool IsDevToolsViewFocused() = 0; + virtual void SetIsDocked(bool docked) = 0; + virtual void SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) = 0; + virtual void SetTitle(const base::string16& title) = 0; + + private: + InspectableWebContentsViewDelegate* delegate_; // weak references. +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/inspectable_web_contents_view_delegate.cc b/vendor/brightray/browser/inspectable_web_contents_view_delegate.cc new file mode 100644 index 0000000000..5906499da8 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_view_delegate.cc @@ -0,0 +1,10 @@ +#include "browser/inspectable_web_contents_view_delegate.h" + +namespace brightray { + +gfx::ImageSkia InspectableWebContentsViewDelegate::GetDevToolsWindowIcon() { + return gfx::ImageSkia(); +} + +} // namespace brightray + diff --git a/vendor/brightray/browser/inspectable_web_contents_view_delegate.h b/vendor/brightray/browser/inspectable_web_contents_view_delegate.h new file mode 100644 index 0000000000..900857c7a1 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_view_delegate.h @@ -0,0 +1,28 @@ +#ifndef BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_DELEGATE_H_ +#define BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_DELEGATE_H_ + +#include "ui/gfx/image/image_skia.h" + +namespace brightray { + +class InspectableWebContentsViewDelegate { + public: + virtual ~InspectableWebContentsViewDelegate() {} + + virtual void DevToolsFocused() {} + virtual void DevToolsOpened() {} + virtual void DevToolsClosed() {} + + // Returns the icon of devtools window. + virtual gfx::ImageSkia GetDevToolsWindowIcon(); + +#if defined(USE_X11) + // Called when creating devtools window. + virtual void GetDevToolsWindowWMClass( + std::string* name, std::string* class_name) {} +#endif +}; + +} // namespace brightray + +#endif // BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_DELEGATE_H_ diff --git a/vendor/brightray/browser/inspectable_web_contents_view_mac.h b/vendor/brightray/browser/inspectable_web_contents_view_mac.h new file mode 100644 index 0000000000..e7ad57fbee --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_view_mac.h @@ -0,0 +1,45 @@ +#ifndef BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_ +#define BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_ + +#include "browser/inspectable_web_contents_view.h" + +#include "base/mac/scoped_nsobject.h" + +@class BRYInspectableWebContentsView; + +namespace brightray { + +class InspectableWebContentsImpl; + +class InspectableWebContentsViewMac : public InspectableWebContentsView { + public: + explicit InspectableWebContentsViewMac( + InspectableWebContentsImpl* inspectable_web_contents_impl); + virtual ~InspectableWebContentsViewMac(); + + gfx::NativeView GetNativeView() const override; + void ShowDevTools() override; + void CloseDevTools() override; + bool IsDevToolsViewShowing() override; + bool IsDevToolsViewFocused() override; + void SetIsDocked(bool docked) override; + void SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) override; + void SetTitle(const base::string16& title) override; + + InspectableWebContentsImpl* inspectable_web_contents() { + return inspectable_web_contents_; + } + + private: + // Owns us. + InspectableWebContentsImpl* inspectable_web_contents_; + + base::scoped_nsobject view_; + + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsViewMac); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/inspectable_web_contents_view_mac.mm b/vendor/brightray/browser/inspectable_web_contents_view_mac.mm new file mode 100644 index 0000000000..3da91048d8 --- /dev/null +++ b/vendor/brightray/browser/inspectable_web_contents_view_mac.mm @@ -0,0 +1,61 @@ +#include "browser/inspectable_web_contents_view_mac.h" + +#import + +#include "base/strings/sys_string_conversions.h" +#include "browser/inspectable_web_contents.h" +#include "browser/inspectable_web_contents_view_delegate.h" +#import "browser/mac/bry_inspectable_web_contents_view.h" +#include "browser/inspectable_web_contents_impl.h" + +namespace brightray { + +InspectableWebContentsView* CreateInspectableContentsView(InspectableWebContentsImpl* inspectable_web_contents) { + return new InspectableWebContentsViewMac(inspectable_web_contents); +} + +InspectableWebContentsViewMac::InspectableWebContentsViewMac(InspectableWebContentsImpl* inspectable_web_contents) + : inspectable_web_contents_(inspectable_web_contents), + view_([[BRYInspectableWebContentsView alloc] initWithInspectableWebContentsViewMac:this]) { +} + +InspectableWebContentsViewMac::~InspectableWebContentsViewMac() { + [view_ removeObservers]; + CloseDevTools(); +} + +gfx::NativeView InspectableWebContentsViewMac::GetNativeView() const { + return view_.get(); +} + +void InspectableWebContentsViewMac::ShowDevTools() { + [view_ setDevToolsVisible:YES]; +} + +void InspectableWebContentsViewMac::CloseDevTools() { + if (IsDevToolsViewShowing()) + [view_ setDevToolsVisible:NO]; +} + +bool InspectableWebContentsViewMac::IsDevToolsViewShowing() { + return [view_ isDevToolsVisible]; +} + +bool InspectableWebContentsViewMac::IsDevToolsViewFocused() { + return [view_ isDevToolsFocused]; +} + +void InspectableWebContentsViewMac::SetIsDocked(bool docked) { + [view_ setIsDocked:docked]; +} + +void InspectableWebContentsViewMac::SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) { + [view_ setContentsResizingStrategy:strategy]; +} + +void InspectableWebContentsViewMac::SetTitle(const base::string16& title) { + [view_ setTitle:base::SysUTF16ToNSString(title)]; +} + +} diff --git a/vendor/brightray/browser/linux/libnotify_loader.cc b/vendor/brightray/browser/linux/libnotify_loader.cc new file mode 100644 index 0000000000..747108529a --- /dev/null +++ b/vendor/brightray/browser/linux/libnotify_loader.cc @@ -0,0 +1,132 @@ +// This is generated file. Do not modify directly. +// Path to the code generator: tools/generate_library_loader/generate_library_loader.py . + +#include "browser/linux/libnotify_loader.h" + +#include + +LibNotifyLoader::LibNotifyLoader() : loaded_(false) { +} + +LibNotifyLoader::~LibNotifyLoader() { + CleanUp(loaded_); +} + +bool LibNotifyLoader::Load(const std::string& library_name) { + if (loaded_) + return false; + + library_ = dlopen(library_name.c_str(), RTLD_LAZY); + if (!library_) + return false; + + notify_is_initted = + reinterpret_castnotify_is_initted)>( + dlsym(library_, "notify_is_initted")); + if (!notify_is_initted) { + CleanUp(true); + return false; + } + + notify_init = + reinterpret_castnotify_init)>( + dlsym(library_, "notify_init")); + if (!notify_init) { + CleanUp(true); + return false; + } + + notify_get_server_info = + reinterpret_castnotify_get_server_info)>( + dlsym(library_, "notify_get_server_info")); + if (!notify_get_server_info) { + CleanUp(true); + return false; + } + + notify_get_server_caps = + reinterpret_castnotify_get_server_caps)>( + dlsym(library_, "notify_get_server_caps")); + if (!notify_get_server_caps) { + CleanUp(true); + return false; + } + + notify_notification_new = + reinterpret_castnotify_notification_new)>( + dlsym(library_, "notify_notification_new")); + if (!notify_notification_new) { + CleanUp(true); + return false; + } + + notify_notification_add_action = + reinterpret_castnotify_notification_add_action)>( + dlsym(library_, "notify_notification_add_action")); + if (!notify_notification_add_action) { + CleanUp(true); + return false; + } + + notify_notification_set_image_from_pixbuf = + reinterpret_castnotify_notification_set_image_from_pixbuf)>( + dlsym(library_, "notify_notification_set_image_from_pixbuf")); + if (!notify_notification_set_image_from_pixbuf) { + CleanUp(true); + return false; + } + + notify_notification_set_timeout = + reinterpret_castnotify_notification_set_timeout)>( + dlsym(library_, "notify_notification_set_timeout")); + if (!notify_notification_set_timeout) { + CleanUp(true); + return false; + } + + notify_notification_set_hint_string = + reinterpret_castnotify_notification_set_hint_string)>( + dlsym(library_, "notify_notification_set_hint_string")); + if (!notify_notification_set_hint_string) { + CleanUp(true); + return false; + } + + notify_notification_show = + reinterpret_castnotify_notification_show)>( + dlsym(library_, "notify_notification_show")); + if (!notify_notification_show) { + CleanUp(true); + return false; + } + + notify_notification_close = + reinterpret_castnotify_notification_close)>( + dlsym(library_, "notify_notification_close")); + if (!notify_notification_close) { + CleanUp(true); + return false; + } + + loaded_ = true; + return true; +} + +void LibNotifyLoader::CleanUp(bool unload) { + if (unload) { + dlclose(library_); + library_ = NULL; + } + loaded_ = false; + notify_is_initted = NULL; + notify_init = NULL; + notify_get_server_info = NULL; + notify_get_server_caps = NULL; + notify_notification_new = NULL; + notify_notification_add_action = NULL; + notify_notification_set_image_from_pixbuf = NULL; + notify_notification_set_timeout = NULL; + notify_notification_set_hint_string = NULL; + notify_notification_show = NULL; + notify_notification_close = NULL; +} diff --git a/vendor/brightray/browser/linux/libnotify_loader.h b/vendor/brightray/browser/linux/libnotify_loader.h new file mode 100644 index 0000000000..825a349b39 --- /dev/null +++ b/vendor/brightray/browser/linux/libnotify_loader.h @@ -0,0 +1,44 @@ +// This is generated file. Do not modify directly. +// Path to the code generator: tools/generate_library_loader/generate_library_loader.py . + +#ifndef BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_LOADER_H_ +#define BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_LOADER_H_ + +#include + +#include + +class LibNotifyLoader { + public: + LibNotifyLoader(); + ~LibNotifyLoader(); + + bool Load(const std::string& library_name) + __attribute__((warn_unused_result)); + + bool loaded() const { return loaded_; } + + decltype(&::notify_is_initted) notify_is_initted; + decltype(&::notify_init) notify_init; + decltype(&::notify_get_server_caps) notify_get_server_caps; + decltype(&::notify_get_server_info) notify_get_server_info; + decltype(&::notify_notification_new) notify_notification_new; + decltype(&::notify_notification_add_action) notify_notification_add_action; + decltype(&::notify_notification_set_image_from_pixbuf) notify_notification_set_image_from_pixbuf; + decltype(&::notify_notification_set_timeout) notify_notification_set_timeout; + decltype(&::notify_notification_set_hint_string) notify_notification_set_hint_string; + decltype(&::notify_notification_show) notify_notification_show; + decltype(&::notify_notification_close) notify_notification_close; + + private: + void CleanUp(bool unload); + + void* library_; + bool loaded_; + + // Disallow copy constructor and assignment operator. + LibNotifyLoader(const LibNotifyLoader&); + void operator=(const LibNotifyLoader&); +}; + +#endif // BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_LOADER_H_ diff --git a/vendor/brightray/browser/linux/libnotify_notification.cc b/vendor/brightray/browser/linux/libnotify_notification.cc new file mode 100644 index 0000000000..a3f273c1c0 --- /dev/null +++ b/vendor/brightray/browser/linux/libnotify_notification.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/linux/libnotify_notification.h" + +#include "base/files/file_enumerator.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "browser/notification_delegate.h" +#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h" +#include "common/application_info.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace brightray { + +namespace { + +LibNotifyLoader libnotify_loader_; + +bool HasCapability(const std::string& capability) { + bool result = false; + GList* capabilities = libnotify_loader_.notify_get_server_caps(); + + if (g_list_find_custom(capabilities, capability.c_str(), (GCompareFunc) g_strcmp0) != NULL) + result = true; + + g_list_free_full(capabilities, g_free); + + return result; +} + +bool NotifierSupportsActions() { + if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER")) + return false; + + static bool notify_has_result = false; + static bool notify_result = false; + + if (notify_has_result) + return notify_result; + + notify_result = HasCapability("actions"); + return notify_result; +} + +void log_and_clear_error(GError* error, const char* context) { + LOG(ERROR) << context + << ": domain=" << error->domain + << " code=" << error->code + << " message=\"" << error->message << '"'; + g_error_free(error); +} + +} // namespace + +// static +Notification* Notification::Create(NotificationDelegate* delegate, + NotificationPresenter* presenter) { + return new LibnotifyNotification(delegate, presenter); +} + +// static +bool LibnotifyNotification::Initialize() { + if (!libnotify_loader_.Load("libnotify.so.4") && // most common one + !libnotify_loader_.Load("libnotify.so.5") && + !libnotify_loader_.Load("libnotify.so.1") && + !libnotify_loader_.Load("libnotify.so")) { + return false; + } + if (!libnotify_loader_.notify_is_initted() && + !libnotify_loader_.notify_init(GetApplicationName().c_str())) { + return false; + } + return true; +} + +LibnotifyNotification::LibnotifyNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter), + notification_(nullptr) { +} + +LibnotifyNotification::~LibnotifyNotification() { + g_signal_handlers_disconnect_by_data(notification_, this); + g_object_unref(notification_); +} + +void LibnotifyNotification::Show(const base::string16& title, + const base::string16& body, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) { + notification_ = libnotify_loader_.notify_notification_new( + base::UTF16ToUTF8(title).c_str(), + base::UTF16ToUTF8(body).c_str(), + nullptr); + + g_signal_connect( + notification_, "closed", G_CALLBACK(OnNotificationClosedThunk), this); + + // NB: On Unity and on any other DE using Notify-OSD, adding a notification + // action will cause the notification to display as a modal dialog box. + if (NotifierSupportsActions()) { + libnotify_loader_.notify_notification_add_action( + notification_, "default", "View", OnNotificationViewThunk, this, + nullptr); + } + + if (!icon.drawsNothing()) { + GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(icon); + libnotify_loader_.notify_notification_set_image_from_pixbuf( + notification_, pixbuf); + libnotify_loader_.notify_notification_set_timeout( + notification_, NOTIFY_EXPIRES_DEFAULT); + g_object_unref(pixbuf); + } + + if (!tag.empty()) { + GQuark id = g_quark_from_string(tag.c_str()); + g_object_set(G_OBJECT(notification_), "id", id, NULL); + } + + // Always try to append notifications. + // Unique tags can be used to prevent this. + if (HasCapability("append")) { + libnotify_loader_.notify_notification_set_hint_string( + notification_, "append", "true"); + } else if (HasCapability("x-canonical-append")) { + libnotify_loader_.notify_notification_set_hint_string( + notification_, "x-canonical-append", "true"); + } + + GError* error = nullptr; + libnotify_loader_.notify_notification_show(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_show"); + NotificationFailed(); + return; + } + + delegate()->NotificationDisplayed(); +} + +void LibnotifyNotification::Dismiss() { + GError* error = nullptr; + libnotify_loader_.notify_notification_close(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_close"); + Destroy(); + } +} + +void LibnotifyNotification::OnNotificationClosed( + NotifyNotification* notification) { + NotificationDismissed(); +} + +void LibnotifyNotification::OnNotificationView( + NotifyNotification* notification, char* action) { + NotificationClicked(); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/linux/libnotify_notification.h b/vendor/brightray/browser/linux/libnotify_notification.h new file mode 100644 index 0000000000..cb9384cb0e --- /dev/null +++ b/vendor/brightray/browser/linux/libnotify_notification.h @@ -0,0 +1,44 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ +#define BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ + +#include "browser/linux/libnotify_loader.h" +#include "browser/notification.h" +#include "ui/base/glib/glib_signal.h" + +namespace brightray { + +class LibnotifyNotification : public Notification { + public: + LibnotifyNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + virtual ~LibnotifyNotification(); + + static bool Initialize(); + + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) override; + void Dismiss() override; + + private: + CHROMEG_CALLBACK_0(LibnotifyNotification, void, OnNotificationClosed, + NotifyNotification*); + CHROMEG_CALLBACK_1(LibnotifyNotification, void, OnNotificationView, + NotifyNotification*, char*); + + NotifyNotification* notification_; + + DISALLOW_COPY_AND_ASSIGN(LibnotifyNotification); +}; + +} // namespace brightray + +#endif // BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ diff --git a/vendor/brightray/browser/linux/notification_presenter_linux.cc b/vendor/brightray/browser/linux/notification_presenter_linux.cc new file mode 100644 index 0000000000..c846fca665 --- /dev/null +++ b/vendor/brightray/browser/linux/notification_presenter_linux.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/linux/notification_presenter_linux.h" + +#include "browser/linux/libnotify_notification.h" + +namespace brightray { + +// static +NotificationPresenter* NotificationPresenter::Create() { + if (!LibnotifyNotification::Initialize()) + return nullptr; + return new NotificationPresenterLinux; +} + +NotificationPresenterLinux::NotificationPresenterLinux() { +} + +NotificationPresenterLinux::~NotificationPresenterLinux() { +} + +} // namespace brightray diff --git a/vendor/brightray/browser/linux/notification_presenter_linux.h b/vendor/brightray/browser/linux/notification_presenter_linux.h new file mode 100644 index 0000000000..ef43679948 --- /dev/null +++ b/vendor/brightray/browser/linux/notification_presenter_linux.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_LINUX_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_LINUX_H_ + +#include "browser/notification_presenter.h" + +namespace brightray { + +class NotificationPresenterLinux : public NotificationPresenter { + public: + NotificationPresenterLinux(); + ~NotificationPresenterLinux(); + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterLinux); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/mac/bry_application.h b/vendor/brightray/browser/mac/bry_application.h new file mode 100644 index 0000000000..9859486149 --- /dev/null +++ b/vendor/brightray/browser/mac/bry_application.h @@ -0,0 +1,7 @@ +#import "base/mac/scoped_sending_event.h" + +@interface BRYApplication : NSApplication { + BOOL _handlingSendEvent; +} + +@end diff --git a/vendor/brightray/browser/mac/bry_application.mm b/vendor/brightray/browser/mac/bry_application.mm new file mode 100644 index 0000000000..aca8392bc3 --- /dev/null +++ b/vendor/brightray/browser/mac/bry_application.mm @@ -0,0 +1,19 @@ +#import "bry_application.h" + +@interface BRYApplication () + +@property (nonatomic, assign, getter = isHandlingSendEvent) BOOL handlingSendEvent; + +@end + +@implementation BRYApplication + +@synthesize handlingSendEvent = _handlingSendEvent; + +- (void)sendEvent:(NSEvent *)theEvent +{ + base::mac::ScopedSendingEvent scopedSendingEvent; + [super sendEvent:theEvent]; +} + +@end diff --git a/vendor/brightray/browser/mac/bry_inspectable_web_contents_view.h b/vendor/brightray/browser/mac/bry_inspectable_web_contents_view.h new file mode 100644 index 0000000000..9819f2d767 --- /dev/null +++ b/vendor/brightray/browser/mac/bry_inspectable_web_contents_view.h @@ -0,0 +1,36 @@ +#import + +#include "browser/devtools_contents_resizing_strategy.h" + +#include "base/mac/scoped_nsobject.h" +#include "ui/base/cocoa/base_view.h" + +namespace brightray { +class InspectableWebContentsViewMac; +} + +using brightray::InspectableWebContentsViewMac; + +@interface BRYInspectableWebContentsView : BaseView { +@private + brightray::InspectableWebContentsViewMac* inspectableWebContentsView_; + + base::scoped_nsobject devtools_window_; + BOOL devtools_visible_; + BOOL devtools_docked_; + BOOL devtools_is_first_responder_; + + DevToolsContentsResizingStrategy strategy_; +} + +- (instancetype)initWithInspectableWebContentsViewMac:(InspectableWebContentsViewMac*)view; +- (void)removeObservers; +- (void)notifyDevToolsFocused; +- (void)setDevToolsVisible:(BOOL)visible; +- (BOOL)isDevToolsVisible; +- (BOOL)isDevToolsFocused; +- (void)setIsDocked:(BOOL)docked; +- (void)setContentsResizingStrategy:(const DevToolsContentsResizingStrategy&)strategy; +- (void)setTitle:(NSString*)title; + +@end diff --git a/vendor/brightray/browser/mac/bry_inspectable_web_contents_view.mm b/vendor/brightray/browser/mac/bry_inspectable_web_contents_view.mm new file mode 100644 index 0000000000..2903f5d6bb --- /dev/null +++ b/vendor/brightray/browser/mac/bry_inspectable_web_contents_view.mm @@ -0,0 +1,260 @@ +#include "browser/mac/bry_inspectable_web_contents_view.h" + +#include "browser/inspectable_web_contents_impl.h" +#include "browser/inspectable_web_contents_view_delegate.h" +#include "browser/inspectable_web_contents_view_mac.h" +#include "browser/mac/event_dispatching_window.h" + +#include "content/public/browser/render_widget_host_view.h" +#include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h" + +using namespace brightray; + +@implementation BRYInspectableWebContentsView + +- (instancetype)initWithInspectableWebContentsViewMac:(InspectableWebContentsViewMac*)view { + self = [super init]; + if (!self) + return nil; + + inspectableWebContentsView_ = view; + devtools_visible_ = NO; + devtools_docked_ = NO; + devtools_is_first_responder_ = NO; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(viewDidBecomeFirstResponder:) + name:kViewDidBecomeFirstResponder + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(parentWindowBecameMain:) + name:NSWindowDidBecomeMainNotification + object:nil]; + + auto contents = inspectableWebContentsView_->inspectable_web_contents()->GetWebContents(); + auto contentsView = contents->GetNativeView(); + [contentsView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:contentsView]; + + // See https://code.google.com/p/chromium/issues/detail?id=348490. + [self setWantsLayer:YES]; + + return self; +} + +- (void)removeObservers { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize { + [self adjustSubviews]; +} + +- (IBAction)showDevTools:(id)sender { + inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(); +} + +- (void)notifyDevToolsFocused { + if (inspectableWebContentsView_->GetDelegate()) + inspectableWebContentsView_->GetDelegate()->DevToolsFocused(); +} + +- (void)setDevToolsVisible:(BOOL)visible { + if (visible == devtools_visible_) + return; + + auto inspectable_web_contents = inspectableWebContentsView_->inspectable_web_contents(); + auto webContents = inspectable_web_contents->GetWebContents(); + auto devToolsWebContents = inspectable_web_contents->GetDevToolsWebContents(); + auto devToolsView = devToolsWebContents->GetNativeView(); + + if (visible && devtools_docked_) { + webContents->SetAllowOtherViews(true); + devToolsWebContents->SetAllowOtherViews(true); + } else { + webContents->SetAllowOtherViews(false); + } + + devtools_visible_ = visible; + if (devtools_docked_) { + if (visible) { + // Place the devToolsView under contentsView, notice that we didn't set + // sizes for them until the setContentsResizingStrategy message. + [self addSubview:devToolsView positioned:NSWindowBelow relativeTo:nil]; + [self adjustSubviews]; + + // Focus on web view. + devToolsWebContents->RestoreFocus(); + } else { + gfx::ScopedCocoaDisableScreenUpdates disabler; + [devToolsView removeFromSuperview]; + [self adjustSubviews]; + } + } else { + if (visible) { + [devtools_window_ makeKeyAndOrderFront:nil]; + } else { + [[self window] makeKeyAndOrderFront:nil]; + [devtools_window_ setDelegate:nil]; + [devtools_window_ close]; + devtools_window_.reset(); + } + } +} + +- (BOOL)isDevToolsVisible { + return devtools_visible_; +} + +- (BOOL)isDevToolsFocused { + if (devtools_docked_) { + return [[self window] isKeyWindow] && devtools_is_first_responder_; + } else { + return [devtools_window_ isKeyWindow]; + } +} + +- (void)setIsDocked:(BOOL)docked { + // Revert to no-devtools state. + [self setDevToolsVisible:NO]; + + // Switch to new state. + devtools_docked_ = docked; + if (!docked) { + auto inspectable_web_contents = inspectableWebContentsView_->inspectable_web_contents(); + auto devToolsWebContents = inspectable_web_contents->GetDevToolsWebContents(); + auto devToolsView = devToolsWebContents->GetNativeView(); + + auto styleMask = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask | + NSTexturedBackgroundWindowMask | + NSUnifiedTitleAndToolbarWindowMask; + devtools_window_.reset([[EventDispatchingWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES]); + [devtools_window_ setDelegate:self]; + [devtools_window_ setFrameAutosaveName:@"brightray.devtools"]; + [devtools_window_ setTitle:@"Developer Tools"]; + [devtools_window_ setReleasedWhenClosed:NO]; + [devtools_window_ setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; + [devtools_window_ setContentBorderThickness:24 forEdge:NSMaxYEdge]; + + NSView* contentView = [devtools_window_ contentView]; + devToolsView.frame = contentView.bounds; + devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + + [contentView addSubview:devToolsView]; + } + [self setDevToolsVisible:YES]; +} + +- (void)setContentsResizingStrategy:(const DevToolsContentsResizingStrategy&)strategy { + strategy_.CopyFrom(strategy); + [self adjustSubviews]; +} + +- (void)adjustSubviews { + if (![[self subviews] count]) + return; + + if (![self isDevToolsVisible] || devtools_window_) { + DCHECK_EQ(1u, [[self subviews] count]); + NSView* contents = [[self subviews] objectAtIndex:0]; + [contents setFrame:[self bounds]]; + return; + } + + NSView* devToolsView = [[self subviews] objectAtIndex:0]; + NSView* contentsView = [[self subviews] objectAtIndex:1]; + + DCHECK_EQ(2u, [[self subviews] count]); + + gfx::Rect new_devtools_bounds; + gfx::Rect new_contents_bounds; + ApplyDevToolsContentsResizingStrategy( + strategy_, gfx::Size(NSSizeToCGSize([self bounds].size)), + &new_devtools_bounds, &new_contents_bounds); + [devToolsView setFrame:[self flipRectToNSRect:new_devtools_bounds]]; + [contentsView setFrame:[self flipRectToNSRect:new_contents_bounds]]; +} + +- (void)setTitle:(NSString*)title { + [devtools_window_ setTitle:title]; +} + +- (void)viewDidBecomeFirstResponder:(NSNotification*)notification { + auto inspectable_web_contents = inspectableWebContentsView_->inspectable_web_contents(); + if (!inspectable_web_contents) + return; + auto webContents = inspectable_web_contents->GetWebContents(); + if (!webContents) + return; + auto webContentsView = webContents->GetNativeView(); + if (!webContentsView) + return; + + NSView* view = [notification object]; + if ([[webContentsView subviews] containsObject:view]) { + devtools_is_first_responder_ = NO; + return; + } + + auto devToolsWebContents = inspectable_web_contents->GetDevToolsWebContents(); + if (!devToolsWebContents) + return; + auto devToolsView = devToolsWebContents->GetNativeView(); + + if ([[devToolsView subviews] containsObject:view]) { + devtools_is_first_responder_ = YES; + [self notifyDevToolsFocused]; + } +} + +- (void)parentWindowBecameMain:(NSNotification*)notification { + NSWindow* parentWindow = [notification object]; + if ([self window] == parentWindow && devtools_docked_ && devtools_is_first_responder_) + [self notifyDevToolsFocused]; +} + +#pragma mark - NSWindowDelegate + +- (void)windowWillClose:(NSNotification*)notification { + if (inspectableWebContentsView_->inspectable_web_contents()) { + inspectableWebContentsView_->inspectable_web_contents()->CloseDevTools(); + } +} + +- (void)windowDidBecomeMain:(NSNotification*)notification { + content::WebContents* web_contents = + inspectableWebContentsView_->inspectable_web_contents()->GetDevToolsWebContents(); + if (!web_contents) + return; + + web_contents->RestoreFocus(); + + content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView(); + if (rwhv) + rwhv->SetActive(true); + + [self notifyDevToolsFocused]; +} + +- (void)windowDidResignMain:(NSNotification*)notification { + content::WebContents* web_contents = + inspectableWebContentsView_->inspectable_web_contents()->GetDevToolsWebContents(); + if (!web_contents) + return; + + web_contents->StoreFocus(); + + content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView(); + if (rwhv) + rwhv->SetActive(false); +} + +@end diff --git a/vendor/brightray/browser/mac/cocoa_notification.h b/vendor/brightray/browser/mac/cocoa_notification.h new file mode 100644 index 0000000000..f6ec9e9da6 --- /dev/null +++ b/vendor/brightray/browser/mac/cocoa_notification.h @@ -0,0 +1,42 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_MAC_COCOA_NOTIFICATION_H_ +#define BROWSER_MAC_COCOA_NOTIFICATION_H_ + +#import + +#include "base/mac/scoped_nsobject.h" +#include "browser/notification.h" + +namespace brightray { + +class CocoaNotification : public Notification { + public: + CocoaNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + ~CocoaNotification(); + + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) override; + void Dismiss() override; + + void NotificationDisplayed(); + + NSUserNotification* notification() const { return notification_; } + + private: + base::scoped_nsobject notification_; + + DISALLOW_COPY_AND_ASSIGN(CocoaNotification); +}; + +} // namespace brightray + +#endif // BROWSER_MAC_COCOA_NOTIFICATION_H_ diff --git a/vendor/brightray/browser/mac/cocoa_notification.mm b/vendor/brightray/browser/mac/cocoa_notification.mm new file mode 100644 index 0000000000..e7ed61dbf3 --- /dev/null +++ b/vendor/brightray/browser/mac/cocoa_notification.mm @@ -0,0 +1,68 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/cocoa_notification.h" + +#include "base/mac/mac_util.h" +#include "base/strings/sys_string_conversions.h" +#include "browser/notification_delegate.h" +#include "browser/notification_presenter.h" +#include "skia/ext/skia_utils_mac.h" + +namespace brightray { + +// static +Notification* Notification::Create(NotificationDelegate* delegate, + NotificationPresenter* presenter) { + return new CocoaNotification(delegate, presenter); +} + +CocoaNotification::CocoaNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter) { +} + +CocoaNotification::~CocoaNotification() { + [NSUserNotificationCenter.defaultUserNotificationCenter + removeDeliveredNotification:notification_]; +} + +void CocoaNotification::Show(const base::string16& title, + const base::string16& body, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) { + notification_.reset([[NSUserNotification alloc] init]); + [notification_ setTitle:base::SysUTF16ToNSString(title)]; + [notification_ setInformativeText:base::SysUTF16ToNSString(body)]; + + if ([notification_ respondsToSelector:@selector(setContentImage:)] && + !icon.drawsNothing()) { + NSImage* image = skia::SkBitmapToNSImageWithColorSpace( + icon, base::mac::GetGenericRGBColorSpace()); + [notification_ setContentImage:image]; + } + + if (silent) { + [notification_ setSoundName:nil]; + } else { + [notification_ setSoundName:NSUserNotificationDefaultSoundName]; + } + + [NSUserNotificationCenter.defaultUserNotificationCenter + deliverNotification:notification_]; +} + +void CocoaNotification::Dismiss() { + [NSUserNotificationCenter.defaultUserNotificationCenter + removeDeliveredNotification:notification_]; + NotificationDismissed(); +} + +void CocoaNotification::NotificationDisplayed() { + delegate()->NotificationDisplayed(); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/mac/event_dispatching_window.h b/vendor/brightray/browser/mac/event_dispatching_window.h new file mode 100644 index 0000000000..27ed9e6bf2 --- /dev/null +++ b/vendor/brightray/browser/mac/event_dispatching_window.h @@ -0,0 +1,19 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_EVENT_DISPATCHING_WINDOW_H_ +#define BROWSER_EVENT_DISPATCHING_WINDOW_H_ + +#import "ui/base/cocoa/underlay_opengl_hosting_window.h" + +@interface EventDispatchingWindow : UnderlayOpenGLHostingWindow { + @private + BOOL redispatchingEvent_; +} + +- (void)redispatchKeyEvent:(NSEvent*)event; + +@end + +#endif // BROWSER_EVENT_DISPATCHING_WINDOW_H_ diff --git a/vendor/brightray/browser/mac/event_dispatching_window.mm b/vendor/brightray/browser/mac/event_dispatching_window.mm new file mode 100644 index 0000000000..08e8edebb0 --- /dev/null +++ b/vendor/brightray/browser/mac/event_dispatching_window.mm @@ -0,0 +1,34 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/event_dispatching_window.h" + +@implementation EventDispatchingWindow + +- (void)sendEvent:(NSEvent*)event { + if (!redispatchingEvent_) + [super sendEvent:event]; +} + +- (BOOL)performKeyEquivalent:(NSEvent*)event { + if (redispatchingEvent_) + return NO; + else + return [super performKeyEquivalent:event]; + } + +- (void)redispatchKeyEvent:(NSEvent*)event { + NSEventType eventType = [event type]; + if (eventType != NSKeyDown && eventType != NSKeyUp && + eventType != NSFlagsChanged) { + return; + } + + // Redispatch the event. + redispatchingEvent_ = YES; + [NSApp sendEvent:event]; + redispatchingEvent_ = NO; +} + +@end diff --git a/vendor/brightray/browser/mac/notification_center_delegate.h b/vendor/brightray/browser/mac/notification_center_delegate.h new file mode 100644 index 0000000000..6bee83d411 --- /dev/null +++ b/vendor/brightray/browser/mac/notification_center_delegate.h @@ -0,0 +1,22 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_MAC_NOTIFICATION_DELEGATE_H_ +#define BROWSER_MAC_NOTIFICATION_DELEGATE_H_ + +#import + +namespace brightray { +class NotificationPresenterMac; +} + +@interface NotificationCenterDelegate : + NSObject { + @private + brightray::NotificationPresenterMac* presenter_; +} +- (instancetype)initWithPresenter:(brightray::NotificationPresenterMac*)presenter; +@end + +#endif // BROWSER_MAC_NOTIFICATION_DELEGATE_H_ diff --git a/vendor/brightray/browser/mac/notification_center_delegate.mm b/vendor/brightray/browser/mac/notification_center_delegate.mm new file mode 100644 index 0000000000..fd77e0685e --- /dev/null +++ b/vendor/brightray/browser/mac/notification_center_delegate.mm @@ -0,0 +1,41 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/notification_center_delegate.h" + +#include "browser/mac/cocoa_notification.h" +#include "browser/mac/notification_presenter_mac.h" + +@implementation NotificationCenterDelegate + +- (instancetype)initWithPresenter:(brightray::NotificationPresenterMac*)presenter { + self = [super init]; + if (!self) + return nil; + + presenter_ = presenter; + return self; +} + +- (void)userNotificationCenter:(NSUserNotificationCenter*)center + didDeliverNotification:(NSUserNotification*)notif { + auto notification = presenter_->GetNotification(notif); + if (notification) + notification->NotificationDisplayed(); +} + +- (void)userNotificationCenter:(NSUserNotificationCenter*)center + didActivateNotification:(NSUserNotification *)notif { + auto notification = presenter_->GetNotification(notif); + if (notification) + notification->NotificationClicked(); +} + +- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center + shouldPresentNotification:(NSUserNotification*)notification { + // Display notifications even if the app is active. + return YES; +} + +@end diff --git a/vendor/brightray/browser/mac/notification_presenter_mac.h b/vendor/brightray/browser/mac/notification_presenter_mac.h new file mode 100644 index 0000000000..825a1dada2 --- /dev/null +++ b/vendor/brightray/browser/mac/notification_presenter_mac.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_MAC_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_MAC_H_ + +#include "base/mac/scoped_nsobject.h" +#include "browser/mac/notification_center_delegate.h" +#include "browser/notification_presenter.h" + +namespace brightray { + +class CocoaNotification; + +class NotificationPresenterMac : public NotificationPresenter { + public: + CocoaNotification* GetNotification(NSUserNotification* notif); + + NotificationPresenterMac(); + ~NotificationPresenterMac(); + + private: + base::scoped_nsobject + notification_center_delegate_; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterMac); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/mac/notification_presenter_mac.mm b/vendor/brightray/browser/mac/notification_presenter_mac.mm new file mode 100644 index 0000000000..a37e9182fc --- /dev/null +++ b/vendor/brightray/browser/mac/notification_presenter_mac.mm @@ -0,0 +1,38 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/notification_presenter_mac.h" + +#include "browser/mac/cocoa_notification.h" +#include "browser/mac/notification_center_delegate.h" + +namespace brightray { + +// static +NotificationPresenter* NotificationPresenter::Create() { + return new NotificationPresenterMac; +} + +CocoaNotification* NotificationPresenterMac::GetNotification( + NSUserNotification* ns_notification) { + for (Notification* notification : notifications()) { + auto native_notification = static_cast(notification); + if ([native_notification->notification() isEqual:ns_notification]) + return native_notification; + } + return nullptr; +} + +NotificationPresenterMac::NotificationPresenterMac() + : notification_center_delegate_( + [[NotificationCenterDelegate alloc] initWithPresenter:this]) { + NSUserNotificationCenter.defaultUserNotificationCenter.delegate = + notification_center_delegate_; +} + +NotificationPresenterMac::~NotificationPresenterMac() { + NSUserNotificationCenter.defaultUserNotificationCenter.delegate = nil; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/media/media_capture_devices_dispatcher.cc b/vendor/brightray/browser/media/media_capture_devices_dispatcher.cc new file mode 100644 index 0000000000..93ec53557d --- /dev/null +++ b/vendor/brightray/browser/media/media_capture_devices_dispatcher.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/media/media_capture_devices_dispatcher.h" + +#include "base/logging.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/media_capture_devices.h" +#include "content/public/common/media_stream_request.h" + +namespace brightray { + +using content::BrowserThread; +using content::MediaStreamDevices; + +namespace { + +// Finds a device in |devices| that has |device_id|, or NULL if not found. +const content::MediaStreamDevice* FindDeviceWithId( + const content::MediaStreamDevices& devices, + const std::string& device_id) { + auto iter = devices.begin(); + for (; iter != devices.end(); ++iter) { + if (iter->id == device_id) { + return &(*iter); + } + } + return nullptr; +} + +const MediaStreamDevices& EmptyDevices() { + static MediaStreamDevices* devices = new MediaStreamDevices; + return *devices; +} + +} // namespace + +MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { + return base::Singleton::get(); +} + +MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() + : is_device_enumeration_disabled_(false) { + // MediaCaptureDevicesDispatcher is a singleton. It should be created on + // UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} + +const MediaStreamDevices& +MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (is_device_enumeration_disabled_) + return EmptyDevices(); + return content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); +} + +const MediaStreamDevices& +MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (is_device_enumeration_disabled_) + return EmptyDevices(); + return content::MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); +} + +void MediaCaptureDevicesDispatcher::GetDefaultDevices( + bool audio, + bool video, + content::MediaStreamDevices* devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(audio || video); + + if (audio) { + const content::MediaStreamDevice* device = GetFirstAvailableAudioDevice(); + if (device) + devices->push_back(*device); + } + + if (video) { + const content::MediaStreamDevice* device = GetFirstAvailableVideoDevice(); + if (device) + devices->push_back(*device); + } +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetRequestedAudioDevice( + const std::string& requested_audio_device_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); + const content::MediaStreamDevice* const device = + FindDeviceWithId(audio_devices, requested_audio_device_id); + return device; +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); + if (audio_devices.empty()) + return nullptr; + return &(*audio_devices.begin()); +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetRequestedVideoDevice( + const std::string& requested_video_device_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); + const content::MediaStreamDevice* const device = + FindDeviceWithId(video_devices, requested_video_device_id); + return device; +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); + if (video_devices.empty()) + return nullptr; + return &(*video_devices.begin()); +} + +void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() { + is_device_enumeration_disabled_ = true; +} + +void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() { +} + +void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() { +} + +void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged( + int render_process_id, + int render_view_id, + int page_request_id, + const GURL& security_origin, + content::MediaStreamType stream_type, + content::MediaRequestState state) { +} + +void MediaCaptureDevicesDispatcher::OnCreatingAudioStream( + int render_process_id, + int render_view_id) { +} + +void MediaCaptureDevicesDispatcher::OnSetCapturingLinkSecured( + int render_process_id, + int render_frame_id, + int page_request_id, + content::MediaStreamType stream_type, + bool is_secure) { +} + +} // namespace brightray diff --git a/vendor/brightray/browser/media/media_capture_devices_dispatcher.h b/vendor/brightray/browser/media/media_capture_devices_dispatcher.h new file mode 100644 index 0000000000..dbbd66d258 --- /dev/null +++ b/vendor/brightray/browser/media/media_capture_devices_dispatcher.h @@ -0,0 +1,84 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ +#define BRIGHTRAY_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ + +#include "base/callback.h" +#include "base/memory/singleton.h" +#include "content/public/browser/media_observer.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/common/media_stream_request.h" + +namespace brightray { + +// This singleton is used to receive updates about media events from the content +// layer. +class MediaCaptureDevicesDispatcher : public content::MediaObserver { + public: + static MediaCaptureDevicesDispatcher* GetInstance(); + + // Methods for observers. Called on UI thread. + const content::MediaStreamDevices& GetAudioCaptureDevices(); + const content::MediaStreamDevices& GetVideoCaptureDevices(); + + // Helper to get the default devices which can be used by the media request. + // Uses the first available devices if the default devices are not available. + // If the return list is empty, it means there is no available device on the + // OS. + // Called on the UI thread. + void GetDefaultDevices(bool audio, + bool video, + content::MediaStreamDevices* devices); + + // Helpers for picking particular requested devices, identified by raw id. + // If the device requested is not available it will return NULL. + const content::MediaStreamDevice* + GetRequestedAudioDevice(const std::string& requested_audio_device_id); + const content::MediaStreamDevice* + GetRequestedVideoDevice(const std::string& requested_video_device_id); + + // Returns the first available audio or video device, or NULL if no devices + // are available. + const content::MediaStreamDevice* GetFirstAvailableAudioDevice(); + const content::MediaStreamDevice* GetFirstAvailableVideoDevice(); + + // Unittests that do not require actual device enumeration should call this + // API on the singleton. It is safe to call this multiple times on the + // signleton. + void DisableDeviceEnumerationForTesting(); + + // Overridden from content::MediaObserver: + void OnAudioCaptureDevicesChanged() override; + void OnVideoCaptureDevicesChanged() override; + void OnMediaRequestStateChanged( + int render_process_id, + int render_view_id, + int page_request_id, + const GURL& security_origin, + content::MediaStreamType stream_type, + content::MediaRequestState state) override; + void OnCreatingAudioStream(int render_process_id, + int render_view_id) override; + void OnSetCapturingLinkSecured(int render_process_id, + int render_frame_id, + int page_request_id, + content::MediaStreamType stream_type, + bool is_secure) override; + + private: + friend struct base::DefaultSingletonTraits; + + MediaCaptureDevicesDispatcher(); + virtual ~MediaCaptureDevicesDispatcher(); + + // Flag used by unittests to disable device enumeration. + bool is_device_enumeration_disabled_; + + DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesDispatcher); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ diff --git a/vendor/brightray/browser/media/media_stream_devices_controller.cc b/vendor/brightray/browser/media/media_stream_devices_controller.cc new file mode 100644 index 0000000000..b3548bbe40 --- /dev/null +++ b/vendor/brightray/browser/media/media_stream_devices_controller.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/media/media_stream_devices_controller.h" + +#include "browser/media/media_capture_devices_dispatcher.h" + +#include "content/public/browser/desktop_media_id.h" +#include "content/public/common/media_stream_request.h" + +namespace brightray { + +namespace { + +bool HasAnyAvailableDevice() { + const content::MediaStreamDevices& audio_devices = + MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices(); + const content::MediaStreamDevices& video_devices = + MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices(); + + return !audio_devices.empty() || !video_devices.empty(); +} + +} // namespace + +MediaStreamDevicesController::MediaStreamDevicesController( + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) + : request_(request), + callback_(callback), + // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam + // and microphone to avoid popping two infobars. + microphone_requested_( + request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE || + request.request_type == content::MEDIA_OPEN_DEVICE_PEPPER_ONLY), + webcam_requested_( + request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE || + request.request_type == content::MEDIA_OPEN_DEVICE_PEPPER_ONLY) { +} + +MediaStreamDevicesController::~MediaStreamDevicesController() { + if (!callback_.is_null()) { + callback_.Run(content::MediaStreamDevices(), + content::MEDIA_DEVICE_INVALID_STATE, + std::unique_ptr()); + } +} + +bool MediaStreamDevicesController::TakeAction() { + // Do special handling of desktop screen cast. + if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE || + request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || + request_.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE || + request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { + HandleUserMediaRequest(); + return true; + } + + // Deny the request if there is no device attached to the OS. + if (!HasAnyAvailableDevice()) { + Deny(content::MEDIA_DEVICE_NO_HARDWARE); + return true; + } + + Accept(); + return true; +} + +void MediaStreamDevicesController::Accept() { + // Get the default devices for the request. + content::MediaStreamDevices devices; + if (microphone_requested_ || webcam_requested_) { + switch (request_.request_type) { + case content::MEDIA_OPEN_DEVICE_PEPPER_ONLY: { + const content::MediaStreamDevice* device = nullptr; + // For open device request pick the desired device or fall back to the + // first available of the given type. + if (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedAudioDevice(request_.requested_audio_device_id); + // TODO(wjia): Confirm this is the intended behavior. + if (!device) { + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetFirstAvailableAudioDevice(); + } + } else if (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { + // Pepper API opens only one device at a time. + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedVideoDevice(request_.requested_video_device_id); + // TODO(wjia): Confirm this is the intended behavior. + if (!device) { + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetFirstAvailableVideoDevice(); + } + } + if (device) + devices.push_back(*device); + break; + } case content::MEDIA_GENERATE_STREAM: { + bool needs_audio_device = microphone_requested_; + bool needs_video_device = webcam_requested_; + + // Get the exact audio or video device if an id is specified. + if (!request_.requested_audio_device_id.empty()) { + const content::MediaStreamDevice* audio_device = + MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedAudioDevice(request_.requested_audio_device_id); + if (audio_device) { + devices.push_back(*audio_device); + needs_audio_device = false; + } + } + if (!request_.requested_video_device_id.empty()) { + const content::MediaStreamDevice* video_device = + MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedVideoDevice(request_.requested_video_device_id); + if (video_device) { + devices.push_back(*video_device); + needs_video_device = false; + } + } + + // If either or both audio and video devices were requested but not + // specified by id, get the default devices. + if (needs_audio_device || needs_video_device) { + MediaCaptureDevicesDispatcher::GetInstance()-> + GetDefaultDevices(needs_audio_device, + needs_video_device, + &devices); + } + break; + } case content::MEDIA_DEVICE_ACCESS: + // Get the default devices for the request. + MediaCaptureDevicesDispatcher::GetInstance()-> + GetDefaultDevices(microphone_requested_, + webcam_requested_, + &devices); + break; + case content::MEDIA_ENUMERATE_DEVICES: + // Do nothing. + NOTREACHED(); + break; + } + } + + content::MediaResponseCallback cb = callback_; + callback_.Reset(); + cb.Run(devices, content::MEDIA_DEVICE_OK, std::unique_ptr()); +} + +void MediaStreamDevicesController::Deny(content::MediaStreamRequestResult result) { + content::MediaResponseCallback cb = callback_; + callback_.Reset(); + cb.Run(content::MediaStreamDevices(), + result, + std::unique_ptr()); +} + +void MediaStreamDevicesController::HandleUserMediaRequest() { + content::MediaStreamDevices devices; + + if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_TAB_AUDIO_CAPTURE, "", "")); + } + if (request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_TAB_VIDEO_CAPTURE, "", "")); + } + if (request_.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_DESKTOP_AUDIO_CAPTURE, "loopback", "System Audio")); + } + if (request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { + content::DesktopMediaID screen_id; + // If the device id wasn't specified then this is a screen capture request + // (i.e. chooseDesktopMedia() API wasn't used to generate device id). + if (request_.requested_video_device_id.empty()) { + screen_id = content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, + -1 /* kFullDesktopScreenId */); + } else { + screen_id = + content::DesktopMediaID::Parse(request_.requested_video_device_id); + } + + devices.push_back( + content::MediaStreamDevice(content::MEDIA_DESKTOP_VIDEO_CAPTURE, + screen_id.ToString(), "Screen")); + } + + content::MediaResponseCallback cb = callback_; + callback_.Reset(); + cb.Run(devices, + devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : + content::MEDIA_DEVICE_OK, + std::unique_ptr()); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/media/media_stream_devices_controller.h b/vendor/brightray/browser/media/media_stream_devices_controller.h new file mode 100644 index 0000000000..47aacfb66b --- /dev/null +++ b/vendor/brightray/browser/media/media_stream_devices_controller.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_ +#define BRIGHTRAY_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_ + +#include + +#include "content/public/browser/web_contents_delegate.h" + +namespace brightray { + +class MediaStreamDevicesController { + public: + MediaStreamDevicesController(const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback); + + virtual ~MediaStreamDevicesController(); + + // Accept or deny the request based on the default policy. + bool TakeAction(); + + // Explicitly accept or deny the request. + void Accept(); + void Deny(content::MediaStreamRequestResult result); + + private: + // Handle the request of desktop or tab screen cast. + void HandleUserMediaRequest(); + + // The original request for access to devices. + const content::MediaStreamRequest request_; + + // The callback that needs to be Run to notify WebRTC of whether access to + // audio/video devices was granted or not. + content::MediaResponseCallback callback_; + + bool microphone_requested_; + bool webcam_requested_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamDevicesController); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_ diff --git a/vendor/brightray/browser/net/devtools_network_conditions.cc b/vendor/brightray/browser/net/devtools_network_conditions.cc new file mode 100644 index 0000000000..0005c7f56c --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_conditions.cc @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium 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 "browser/net/devtools_network_conditions.h" + +namespace brightray { + +DevToolsNetworkConditions::DevToolsNetworkConditions(bool offline) + : offline_(offline), + latency_(0), + download_throughput_(0), + upload_throughput_(0) { +} + +DevToolsNetworkConditions::DevToolsNetworkConditions( + bool offline, + double latency, + double download_throughput, + double upload_throughput) + : offline_(offline), + latency_(latency), + download_throughput_(download_throughput), + upload_throughput_(upload_throughput) { +} + +DevToolsNetworkConditions::~DevToolsNetworkConditions() { +} + +bool DevToolsNetworkConditions::IsThrottling() const { + return !offline_ && ((latency_ != 0.0) || (download_throughput_ != 0.0) || + (upload_throughput_ != 0.0)); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_conditions.h b/vendor/brightray/browser/net/devtools_network_conditions.h new file mode 100644 index 0000000000..81aae86f8d --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_conditions.h @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium 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 BROWSER_DEVTOOLS_NETWORK_CONDITIONS_H_ +#define BROWSER_DEVTOOLS_NETWORK_CONDITIONS_H_ + +#include +#include + +#include "base/macros.h" +#include "url/gurl.h" + +namespace brightray { + +class DevToolsNetworkConditions { + public: + explicit DevToolsNetworkConditions(bool offline); + DevToolsNetworkConditions(bool offline, + double latency, + double download_throughput, + double upload_throughput); + ~DevToolsNetworkConditions(); + + bool IsThrottling() const; + + bool offline() const { return offline_; } + double latency() const { return latency_; } + double download_throughput() const { return download_throughput_; } + double upload_throughput() const { return upload_throughput_; } + + private: + const bool offline_; + const double latency_; + const double download_throughput_; + const double upload_throughput_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkConditions); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_CONDITIONS_H_ diff --git a/vendor/brightray/browser/net/devtools_network_controller.cc b/vendor/brightray/browser/net/devtools_network_controller.cc new file mode 100644 index 0000000000..cfa4e23471 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_controller.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_controller.h" + +#include "browser/net/devtools_network_conditions.h" +#include "browser/net/devtools_network_interceptor.h" +#include "browser/net/devtools_network_transaction.h" + +#include "base/bind.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace brightray { + +DevToolsNetworkController::DevToolsNetworkController() + : appcache_interceptor_(new DevToolsNetworkInterceptor) { +} + +DevToolsNetworkController::~DevToolsNetworkController() { +} + +void DevToolsNetworkController::SetNetworkState( + const std::string& client_id, + std::unique_ptr conditions) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + DevToolsNetworkInterceptor* interceptor = interceptors_.get(client_id); + if (!interceptor) { + if (!conditions) + return; + std::unique_ptr new_interceptor( + new DevToolsNetworkInterceptor); + new_interceptor->UpdateConditions(std::move(conditions)); + interceptors_.set(client_id, std::move(new_interceptor)); + } else { + if (!conditions) { + std::unique_ptr online_conditions( + new DevToolsNetworkConditions(false)); + interceptor->UpdateConditions(std::move(online_conditions)); + interceptors_.erase(client_id); + } else { + interceptor->UpdateConditions(std::move(conditions)); + } + } + + bool has_offline_interceptors = false; + auto it = interceptors_.begin(); + for (; it != interceptors_.end(); ++it) { + if (it->second->IsOffline()) { + has_offline_interceptors = true; + break; + } + } + + bool is_appcache_offline = appcache_interceptor_->IsOffline(); + if (is_appcache_offline != has_offline_interceptors) { + std::unique_ptr appcache_conditions( + new DevToolsNetworkConditions(has_offline_interceptors)); + appcache_interceptor_->UpdateConditions(std::move(appcache_conditions)); + } +} + +DevToolsNetworkInterceptor* +DevToolsNetworkController::GetInterceptor(const std::string& client_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!interceptors_.size() || client_id.empty()) + return nullptr; + + DevToolsNetworkInterceptor* interceptor = interceptors_.get(client_id); + if (!interceptor) + return nullptr; + + return interceptor; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_controller.h b/vendor/brightray/browser/net/devtools_network_controller.h new file mode 100644 index 0000000000..02bb698559 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_controller.h @@ -0,0 +1,38 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_CONTROLLER_H_ +#define BROWSER_DEVTOOLS_NETWORK_CONTROLLER_H_ + +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/macros.h" + +namespace brightray { + +class DevToolsNetworkConditions; +class DevToolsNetworkInterceptor; +class DevToolsNetworkTransaction; + +class DevToolsNetworkController { + public: + DevToolsNetworkController(); + virtual ~DevToolsNetworkController(); + + void SetNetworkState(const std::string& client_id, + std::unique_ptr conditions); + DevToolsNetworkInterceptor* GetInterceptor(const std::string& client_id); + + private: + using InterceptorMap = base::ScopedPtrHashMap>; + + std::unique_ptr appcache_interceptor_; + InterceptorMap interceptors_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkController); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_CONTROLLER_H_ diff --git a/vendor/brightray/browser/net/devtools_network_controller_handle.cc b/vendor/brightray/browser/net/devtools_network_controller_handle.cc new file mode 100644 index 0000000000..44b7b9c6c1 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_controller_handle.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_controller_handle.h" + +#include "base/bind.h" +#include "browser/net/devtools_network_conditions.h" +#include "browser/net/devtools_network_controller.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace brightray { + +DevToolsNetworkControllerHandle::DevToolsNetworkControllerHandle() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +DevToolsNetworkControllerHandle::~DevToolsNetworkControllerHandle() { + BrowserThread::DeleteSoon(BrowserThread::IO, + FROM_HERE, + controller_.release()); +} + +void DevToolsNetworkControllerHandle::SetNetworkState( + const std::string& client_id, + std::unique_ptr conditions) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&DevToolsNetworkControllerHandle::SetNetworkStateOnIO, + base::Unretained(this), client_id, base::Passed(&conditions))); +} + +DevToolsNetworkController* DevToolsNetworkControllerHandle::GetController() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + LazyInitialize(); + return controller_.get(); +} + +void DevToolsNetworkControllerHandle::LazyInitialize() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!controller_) + controller_.reset(new DevToolsNetworkController); +} + +void DevToolsNetworkControllerHandle::SetNetworkStateOnIO( + const std::string& client_id, + std::unique_ptr conditions) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + LazyInitialize(); + controller_->SetNetworkState(client_id, std::move(conditions)); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_controller_handle.h b/vendor/brightray/browser/net/devtools_network_controller_handle.h new file mode 100644 index 0000000000..b5c861f5cd --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_controller_handle.h @@ -0,0 +1,44 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_ +#define BROWSER_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_ + +#include +#include + +#include "base/macros.h" + +namespace brightray { + +class DevToolsNetworkConditions; +class DevToolsNetworkController; + +// A handle to manage an IO-thread DevToolsNetworkController on the IO thread +// while allowing SetNetworkState to be called from the UI thread. +class DevToolsNetworkControllerHandle { + public: + DevToolsNetworkControllerHandle(); + ~DevToolsNetworkControllerHandle(); + + // Called on the UI thread. + void SetNetworkState(const std::string& client_id, + std::unique_ptr conditions); + + // Called on the IO thread. + DevToolsNetworkController* GetController(); + + private: + void LazyInitialize(); + void SetNetworkStateOnIO(const std::string& client_id, + std::unique_ptr conditions); + + std::unique_ptr controller_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkControllerHandle); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_ diff --git a/vendor/brightray/browser/net/devtools_network_interceptor.cc b/vendor/brightray/browser/net/devtools_network_interceptor.cc new file mode 100644 index 0000000000..fa866b4aa1 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_interceptor.cc @@ -0,0 +1,288 @@ +// Copyright 2014 The Chromium 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 "browser/net/devtools_network_interceptor.h" + +#include +#include + +#include "base/time/time.h" +#include "browser/net/devtools_network_conditions.h" +#include "net/base/net_errors.h" + +namespace brightray { + +namespace { + +int64_t kPacketSize = 1500; + +base::TimeDelta CalculateTickLength(double throughput) { + if (!throughput) + return base::TimeDelta(); + + int64_t us_tick_length = (1000000L * kPacketSize) / throughput; + if (us_tick_length == 0) + us_tick_length = 1; + return base::TimeDelta::FromMicroseconds(us_tick_length); +} + +} // namespace + +DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord() { +} + +DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord( + const ThrottleRecord& other) = default; + +DevToolsNetworkInterceptor::ThrottleRecord::~ThrottleRecord() { +} + +DevToolsNetworkInterceptor::DevToolsNetworkInterceptor() + : conditions_(new DevToolsNetworkConditions(false)), + download_last_tick_(0), + upload_last_tick_(0), + weak_ptr_factory_(this) { +} + +DevToolsNetworkInterceptor::~DevToolsNetworkInterceptor() { +} + +base::WeakPtr +DevToolsNetworkInterceptor::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +void DevToolsNetworkInterceptor::FinishRecords( + ThrottleRecords* records, bool offline) { + ThrottleRecords temp; + temp.swap(*records); + for (const ThrottleRecord& record : temp) { + bool failed = offline && !record.is_upload; + record.callback.Run( + failed ? net::ERR_INTERNET_DISCONNECTED : record.result, + record.bytes); + } +} + +void DevToolsNetworkInterceptor::UpdateConditions( + std::unique_ptr conditions) { + DCHECK(conditions); + base::TimeTicks now = base::TimeTicks::Now(); + if (conditions_->IsThrottling()) + UpdateThrottled(now); + + conditions_ = std::move(conditions); + + bool offline = conditions_->offline(); + if (offline || !conditions_->IsThrottling()) { + timer_.Stop(); + FinishRecords(&download_, offline); + FinishRecords(&upload_, offline); + FinishRecords(&suspended_, offline); + return; + } + + // Throttling. + DCHECK(conditions_->download_throughput() != 0 || + conditions_->upload_throughput() != 0); + offset_ = now; + + download_last_tick_ = 0; + download_tick_length_ = CalculateTickLength( + conditions_->download_throughput()); + + upload_last_tick_ = 0; + upload_tick_length_ = CalculateTickLength(conditions_->upload_throughput()); + + latency_length_ = base::TimeDelta(); + double latency = conditions_->latency(); + if (latency > 0) + latency_length_ = base::TimeDelta::FromMillisecondsD(latency); + ArmTimer(now); +} + +uint64_t DevToolsNetworkInterceptor::UpdateThrottledRecords( + base::TimeTicks now, + ThrottleRecords* records, + uint64_t last_tick, + base::TimeDelta tick_length) { + if (tick_length.is_zero()) { + DCHECK(!records->size()); + return last_tick; + } + + int64_t new_tick = (now - offset_) / tick_length; + int64_t ticks = new_tick - last_tick; + + int64_t length = records->size(); + if (!length) + return new_tick; + + int64_t shift = ticks % length; + for (int64_t i = 0; i < length; ++i) { + (*records)[i].bytes -= + (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0); + } + std::rotate(records->begin(), records->begin() + shift, records->end()); + return new_tick; +} + +void DevToolsNetworkInterceptor::UpdateThrottled(base::TimeTicks now) { + download_last_tick_ = UpdateThrottledRecords( + now, &download_, download_last_tick_, download_tick_length_); + upload_last_tick_ = UpdateThrottledRecords( + now, &upload_, upload_last_tick_, upload_tick_length_); + UpdateSuspended(now); +} + +void DevToolsNetworkInterceptor::UpdateSuspended(base::TimeTicks now) { + int64_t activation_baseline = + (now - latency_length_ - base::TimeTicks()).InMicroseconds(); + ThrottleRecords suspended; + for (const ThrottleRecord& record : suspended_) { + if (record.send_end <= activation_baseline) { + if (record.is_upload) + upload_.push_back(record); + else + download_.push_back(record); + } else { + suspended.push_back(record); + } + } + suspended_.swap(suspended); +} + +void DevToolsNetworkInterceptor::CollectFinished( + ThrottleRecords* records, ThrottleRecords* finished) { + ThrottleRecords active; + for (const ThrottleRecord& record : *records) { + if (record.bytes < 0) + finished->push_back(record); + else + active.push_back(record); + } + records->swap(active); +} + +void DevToolsNetworkInterceptor::OnTimer() { + base::TimeTicks now = base::TimeTicks::Now(); + UpdateThrottled(now); + + ThrottleRecords finished; + CollectFinished(&download_, &finished); + CollectFinished(&upload_, &finished); + for (const ThrottleRecord& record : finished) + record.callback.Run(record.result, record.bytes); + + ArmTimer(now); +} + +base::TimeTicks DevToolsNetworkInterceptor::CalculateDesiredTime( + const ThrottleRecords& records, + uint64_t last_tick, + base::TimeDelta tick_length) { + int64_t min_ticks_left = 0x10000L; + size_t count = records.size(); + for (size_t i = 0; i < count; ++i) { + int64_t packets_left = (records[i].bytes + kPacketSize - 1) / kPacketSize; + int64_t ticks_left = (i + 1) + count * (packets_left - 1); + if (i == 0 || ticks_left < min_ticks_left) + min_ticks_left = ticks_left; + } + return offset_ + tick_length * (last_tick + min_ticks_left); +} + +void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) { + size_t suspend_count = suspended_.size(); + if (!download_.size() && !upload_.size() && !suspend_count) + return; + + base::TimeTicks desired_time = CalculateDesiredTime( + download_, download_last_tick_, download_tick_length_); + + base::TimeTicks upload_time = CalculateDesiredTime( + upload_, upload_last_tick_, upload_tick_length_); + if (upload_time < desired_time) + desired_time = upload_time; + + int64_t min_baseline = std::numeric_limits::max(); + for (size_t i = 0; i < suspend_count; ++i) { + if (suspended_[i].send_end < min_baseline) + min_baseline = suspended_[i].send_end; + } + if (suspend_count) { + base::TimeTicks activation_time = base::TimeTicks() + + base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_; + if (activation_time < desired_time) + desired_time = activation_time; + } + + timer_.Start(FROM_HERE, desired_time - now, + base::Bind(&DevToolsNetworkInterceptor::OnTimer, + base::Unretained(this))); +} + +int DevToolsNetworkInterceptor::StartThrottle( + int result, + int64_t bytes, + base::TimeTicks send_end, + bool start, + bool is_upload, + const ThrottleCallback& callback) { + if (result < 0) + return result; + + if (conditions_->offline()) + return is_upload ? result : net::ERR_INTERNET_DISCONNECTED; + + if ((is_upload && !conditions_->upload_throughput()) || + (!is_upload && !conditions_->download_throughput())) { + return result; + } + + ThrottleRecord record; + record.result = result; + record.bytes = bytes; + record.callback = callback; + record.is_upload = is_upload; + + base::TimeTicks now = base::TimeTicks::Now(); + UpdateThrottled(now); + if (start && latency_length_ != base::TimeDelta()) { + record.send_end = (send_end - base::TimeTicks()).InMicroseconds(); + suspended_.push_back(record); + UpdateSuspended(now); + } else { + if (is_upload) + upload_.push_back(record); + else + download_.push_back(record); + } + ArmTimer(now); + + return net::ERR_IO_PENDING; +} + +void DevToolsNetworkInterceptor::StopThrottle( + const ThrottleCallback& callback) { + RemoveRecord(&download_, callback); + RemoveRecord(&upload_, callback); + RemoveRecord(&suspended_, callback); +} + +void DevToolsNetworkInterceptor::RemoveRecord( + ThrottleRecords* records, const ThrottleCallback& callback) { + records->erase( + std::remove_if(records->begin(), records->end(), + [&callback](const ThrottleRecord& record){ + return record.callback.Equals(callback); + }), + records->end()); +} + +bool DevToolsNetworkInterceptor::IsOffline() { + return conditions_->offline(); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_interceptor.h b/vendor/brightray/browser/net/devtools_network_interceptor.h new file mode 100644 index 0000000000..4a04876b6f --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_interceptor.h @@ -0,0 +1,107 @@ +// Copyright 2014 The Chromium 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 BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ +#define BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/timer/timer.h" + +namespace base { +class TimeDelta; +class TimeTicks; +} + +namespace brightray { + +class DevToolsNetworkConditions; +class DevToolsNetworkTransaction; + +class DevToolsNetworkInterceptor { + public: + using ThrottleCallback = base::Callback; + + DevToolsNetworkInterceptor(); + virtual ~DevToolsNetworkInterceptor(); + + base::WeakPtr GetWeakPtr(); + + // Applies network emulation configuration. + void UpdateConditions(std::unique_ptr conditions); + + // Throttles with |is_upload == true| always succeed, even in offline mode. + int StartThrottle(int result, + int64_t bytes, + base::TimeTicks send_end, + bool start, + bool is_upload, + const ThrottleCallback& callback); + void StopThrottle(const ThrottleCallback& callback); + + bool IsOffline(); + + private: + struct ThrottleRecord { + public: + ThrottleRecord(); + ThrottleRecord(const ThrottleRecord& other); + ~ThrottleRecord(); + + int result; + int64_t bytes; + int64_t send_end; + bool is_upload; + ThrottleCallback callback; + }; + + using ThrottleRecords = std::vector; + + void FinishRecords(ThrottleRecords* records, bool offline); + + uint64_t UpdateThrottledRecords(base::TimeTicks now, + ThrottleRecords* records, + uint64_t last_tick, + base::TimeDelta tick_length); + void UpdateThrottled(base::TimeTicks now); + void UpdateSuspended(base::TimeTicks now); + + void CollectFinished(ThrottleRecords* records, ThrottleRecords* finished); + void OnTimer(); + + base::TimeTicks CalculateDesiredTime(const ThrottleRecords& records, + uint64_t last_tick, + base::TimeDelta tick_length); + void ArmTimer(base::TimeTicks now); + + void RemoveRecord(ThrottleRecords* records, const ThrottleCallback& callback); + + std::unique_ptr conditions_; + + // Throttables suspended for a "latency" period. + ThrottleRecords suspended_; + + // Throttables waiting for certain amount of transfer to be "accounted". + ThrottleRecords download_; + ThrottleRecords upload_; + + base::OneShotTimer timer_; + base::TimeTicks offset_; + base::TimeDelta download_tick_length_; + base::TimeDelta upload_tick_length_; + base::TimeDelta latency_length_; + uint64_t download_last_tick_; + uint64_t upload_last_tick_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkInterceptor); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ diff --git a/vendor/brightray/browser/net/devtools_network_protocol_handler.cc b/vendor/brightray/browser/net/devtools_network_protocol_handler.cc new file mode 100644 index 0000000000..584840533b --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_protocol_handler.cc @@ -0,0 +1,171 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_protocol_handler.h" + +#include "browser/browser_context.h" +#include "browser/net/devtools_network_conditions.h" +#include "browser/net/devtools_network_controller.h" + +#include "base/strings/stringprintf.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/devtools_agent_host.h" + + +namespace brightray { + +namespace { + +namespace params { + +const char kDownloadThroughput[] = "downloadThroughput"; +const char kLatency[] = "latency"; +const char kOffline[] = "offline"; +const char kUploadThroughput[] = "uploadThroughput"; +const char kResult[] = "result"; +const char kErrorCode[] = "code"; +const char kErrorMessage[] = "message"; + +} // namespace params + +const char kEmulateNetworkConditions[] = "Network.emulateNetworkConditions"; +const char kCanEmulateNetworkConditions[] = "Network.canEmulateNetworkConditions"; +const char kId[] = "id"; +const char kMethod[] = "method"; +const char kParams[] = "params"; +const char kError[] = "error"; +// JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object +const int kErrorInvalidParams = -32602; + + +bool ParseCommand(const base::DictionaryValue* command, + int* id, + std::string* method, + const base::DictionaryValue** params) { + if (!command) + return false; + + if (!command->GetInteger(kId, id) || *id < 0) + return false; + + if (!command->GetString(kMethod, method)) + return false; + + if (!command->GetDictionary(kParams, params)) + *params = nullptr; + + return true; +} + +std::unique_ptr +CreateSuccessResponse(int id, std::unique_ptr result) { + std::unique_ptr response(new base::DictionaryValue); + response->SetInteger(kId, id); + response->Set(params::kResult, result.release()); + return response; +} + +std::unique_ptr +CreateFailureResponse(int id, const std::string& param) { + std::unique_ptr response(new base::DictionaryValue); + auto error_object = new base::DictionaryValue; + response->Set(kError, error_object); + error_object->SetInteger(params::kErrorCode, kErrorInvalidParams); + error_object->SetString(params::kErrorMessage, + base::StringPrintf("Missing or Invalid '%s' parameter", param.c_str())); + return response; +} + +} // namespace + +DevToolsNetworkProtocolHandler::DevToolsNetworkProtocolHandler() { +} + +DevToolsNetworkProtocolHandler::~DevToolsNetworkProtocolHandler() { +} + +base::DictionaryValue* DevToolsNetworkProtocolHandler::HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command) { + int id = 0; + std::string method; + const base::DictionaryValue* params = nullptr; + + if (!ParseCommand(command, &id, &method, ¶ms)) + return nullptr; + + if (method == kEmulateNetworkConditions) + return EmulateNetworkConditions(agent_host, id, params).release(); + + if (method == kCanEmulateNetworkConditions) + return CanEmulateNetworkConditions(agent_host, id, params).release(); + + return nullptr; +} + +void DevToolsNetworkProtocolHandler::DevToolsAgentStateChanged( + content::DevToolsAgentHost* agent_host, + bool attached) { + std::unique_ptr conditions; + if (attached) + conditions.reset(new DevToolsNetworkConditions(false)); + UpdateNetworkState(agent_host, std::move(conditions)); +} + +std::unique_ptr +DevToolsNetworkProtocolHandler::CanEmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int id, + const base::DictionaryValue* params) { + std::unique_ptr result(new base::DictionaryValue); + result->SetBoolean(params::kResult, true); + return CreateSuccessResponse(id, std::move(result)); +} + +std::unique_ptr +DevToolsNetworkProtocolHandler::EmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int id, + const base::DictionaryValue* params) { + bool offline = false; + if (!params || !params->GetBoolean(params::kOffline, &offline)) + return CreateFailureResponse(id, params::kOffline); + + double latency = 0.0; + if (!params->GetDouble(params::kLatency, &latency)) + return CreateFailureResponse(id, params::kLatency); + if (latency < 0.0) + latency = 0.0; + + double download_throughput = 0.0; + if (!params->GetDouble(params::kDownloadThroughput, &download_throughput)) + return CreateFailureResponse(id, params::kDownloadThroughput); + if (download_throughput < 0.0) + download_throughput = 0.0; + + double upload_throughput = 0.0; + if (!params->GetDouble(params::kUploadThroughput, &upload_throughput)) + return CreateFailureResponse(id, params::kUploadThroughput); + if (upload_throughput < 0.0) + upload_throughput = 0.0; + + std::unique_ptr conditions( + new DevToolsNetworkConditions(offline, + latency, + download_throughput, + upload_throughput)); + UpdateNetworkState(agent_host, std::move(conditions)); + return std::unique_ptr(); +} + +void DevToolsNetworkProtocolHandler::UpdateNetworkState( + content::DevToolsAgentHost* agent_host, + std::unique_ptr conditions) { + auto browser_context = + static_cast(agent_host->GetBrowserContext()); + browser_context->network_controller_handle()->SetNetworkState( + agent_host->GetId(), std::move(conditions)); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_protocol_handler.h b/vendor/brightray/browser/net/devtools_network_protocol_handler.h new file mode 100644 index 0000000000..9ec577814c --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_protocol_handler.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium 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 BROWSER_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_ +#define BROWSER_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_ + +#include "base/macros.h" +#include "base/values.h" + +namespace content { +class DevToolsAgentHost; +} + +namespace brightray { + +class DevToolsNetworkConditions; + +class DevToolsNetworkProtocolHandler { + public: + DevToolsNetworkProtocolHandler(); + ~DevToolsNetworkProtocolHandler(); + + base::DictionaryValue* HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command); + void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host, + bool attached); + + private: + std::unique_ptr CanEmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int command_id, + const base::DictionaryValue* params); + std::unique_ptr EmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int command_id, + const base::DictionaryValue* params); + void UpdateNetworkState( + content::DevToolsAgentHost* agent_host, + std::unique_ptr conditions); + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkProtocolHandler); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_ diff --git a/vendor/brightray/browser/net/devtools_network_transaction.cc b/vendor/brightray/browser/net/devtools_network_transaction.cc new file mode 100644 index 0000000000..0a62346392 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_transaction.cc @@ -0,0 +1,303 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_transaction.h" + +#include "browser/net/devtools_network_controller.h" +#include "browser/net/devtools_network_upload_data_stream.h" +#include "net/base/load_timing_info.h" +#include "net/base/net_errors.h" +#include "net/base/upload_progress.h" +#include "net/http/http_network_transaction.h" +#include "net/http/http_request_info.h" +#include "net/socket/connection_attempts.h" + +namespace brightray { + +// static +const char + DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId[] = + "X-DevTools-Emulate-Network-Conditions-Client-Id"; + +DevToolsNetworkTransaction::DevToolsNetworkTransaction( + DevToolsNetworkController* controller, + std::unique_ptr transaction) + : throttled_byte_count_(0), + controller_(controller), + transaction_(std::move(transaction)), + request_(nullptr), + failed_(false) { + DCHECK(controller); +} + +DevToolsNetworkTransaction::~DevToolsNetworkTransaction() { + if (interceptor_ && !throttle_callback_.is_null()) + interceptor_->StopThrottle(throttle_callback_); +} + +void DevToolsNetworkTransaction::IOCallback( + const net::CompletionCallback& callback, bool start, int result) { + result = Throttle(callback, start, result); + if (result != net::ERR_IO_PENDING) + callback.Run(result); +} + +int DevToolsNetworkTransaction::Throttle( + const net::CompletionCallback& callback, bool start, int result) { + if (failed_) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_ || result < 0) + return result; + + base::TimeTicks send_end; + if (start) { + throttled_byte_count_ += transaction_->GetTotalReceivedBytes(); + net::LoadTimingInfo load_timing_info; + if (GetLoadTimingInfo(&load_timing_info)) + send_end = load_timing_info.send_end; + if (send_end.is_null()) + send_end = base::TimeTicks::Now(); + } + if (result > 0) + throttled_byte_count_ += result; + + throttle_callback_ = base::Bind(&DevToolsNetworkTransaction::ThrottleCallback, + base::Unretained(this), + callback); + int rv = interceptor_->StartThrottle(result, throttled_byte_count_, send_end, + start, false, throttle_callback_); + if (rv != net::ERR_IO_PENDING) + throttle_callback_.Reset(); + if (rv == net::ERR_INTERNET_DISCONNECTED) + Fail(); + return rv; +} + +void DevToolsNetworkTransaction::ThrottleCallback( + const net::CompletionCallback& callback, int result, int64_t bytes) { + DCHECK(!throttle_callback_.is_null()); + throttle_callback_.Reset(); + if (result == net::ERR_INTERNET_DISCONNECTED) + Fail(); + throttled_byte_count_ = bytes; + callback.Run(result); +} + +void DevToolsNetworkTransaction::Fail() { + DCHECK(request_); + DCHECK(!failed_); + failed_ = true; + transaction_->SetBeforeNetworkStartCallback(BeforeNetworkStartCallback()); + if (interceptor_) + interceptor_.reset(); +} + +bool DevToolsNetworkTransaction::CheckFailed() { + if (failed_) + return true; + if (interceptor_ && interceptor_->IsOffline()) { + Fail(); + return true; + } + return false; +} + +int DevToolsNetworkTransaction::Start( + const net::HttpRequestInfo* request, + const net::CompletionCallback& callback, + const net::BoundNetLog& net_log) { + DCHECK(request); + request_ = request; + + std::string client_id; + bool has_devtools_client_id = request_->extra_headers.HasHeader( + kDevToolsEmulateNetworkConditionsClientId); + if (has_devtools_client_id) { + custom_request_.reset(new net::HttpRequestInfo(*request_)); + custom_request_->extra_headers.GetHeader( + kDevToolsEmulateNetworkConditionsClientId, &client_id); + custom_request_->extra_headers.RemoveHeader( + kDevToolsEmulateNetworkConditionsClientId); + + if (request_->upload_data_stream) { + custom_upload_data_stream_.reset( + new DevToolsNetworkUploadDataStream(request_->upload_data_stream)); + custom_request_->upload_data_stream = custom_upload_data_stream_.get(); + } + + request_ = custom_request_.get(); + } + + DevToolsNetworkInterceptor* interceptor = controller_->GetInterceptor(client_id); + if (interceptor) { + interceptor_ = interceptor->GetWeakPtr(); + if (custom_upload_data_stream_) + custom_upload_data_stream_->SetInterceptor(interceptor); + } + + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + + if (!interceptor_) + return transaction_->Start(request_, callback, net_log); + + int result = transaction_->Start(request_, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true), + net_log); + return Throttle(callback, true, result); +} + +int DevToolsNetworkTransaction::RestartIgnoringLastError( + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) + return transaction_->RestartIgnoringLastError(callback); + + int result = transaction_->RestartIgnoringLastError( + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); +} + +int DevToolsNetworkTransaction::RestartWithCertificate( + net::X509Certificate* client_cert, + net::SSLPrivateKey* client_private_key, + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) { + return transaction_->RestartWithCertificate( + client_cert, client_private_key, callback); + } + + int result = transaction_->RestartWithCertificate( + client_cert, client_private_key, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); +} + +int DevToolsNetworkTransaction::RestartWithAuth( + const net::AuthCredentials& credentials, + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) + return transaction_->RestartWithAuth(credentials, callback); + + int result = transaction_->RestartWithAuth(credentials, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); +} + +bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() { + return transaction_->IsReadyToRestartForAuth(); +} + +int DevToolsNetworkTransaction::Read( + net::IOBuffer* buf, + int buf_len, + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) + return transaction_->Read(buf, buf_len, callback); + + int result = transaction_->Read(buf, buf_len, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, false)); + // URLRequestJob relies on synchronous end-of-stream notification. + if (result == 0) + return result; + return Throttle(callback, false, result); +} + +void DevToolsNetworkTransaction::StopCaching() { + transaction_->StopCaching(); +} + +bool DevToolsNetworkTransaction::GetFullRequestHeaders( + net::HttpRequestHeaders* headers) const { + return transaction_->GetFullRequestHeaders(headers); +} + +int64_t DevToolsNetworkTransaction::GetTotalReceivedBytes() const { + return transaction_->GetTotalReceivedBytes(); +} + +int64_t DevToolsNetworkTransaction::GetTotalSentBytes() const { + return transaction_->GetTotalSentBytes(); +} + +void DevToolsNetworkTransaction::DoneReading() { + transaction_->DoneReading(); +} + +const net::HttpResponseInfo* +DevToolsNetworkTransaction::GetResponseInfo() const { + return transaction_->GetResponseInfo(); +} + +net::LoadState DevToolsNetworkTransaction::GetLoadState() const { + return transaction_->GetLoadState(); +} + +net::UploadProgress DevToolsNetworkTransaction::GetUploadProgress() const { + return transaction_->GetUploadProgress(); +} + +void DevToolsNetworkTransaction::SetQuicServerInfo( + net::QuicServerInfo* info) { + transaction_->SetQuicServerInfo(info); +} + +bool DevToolsNetworkTransaction::GetLoadTimingInfo( + net::LoadTimingInfo* info) const { + return transaction_->GetLoadTimingInfo(info); +} + +bool DevToolsNetworkTransaction::GetRemoteEndpoint( + net::IPEndPoint* endpoint) const { + return transaction_->GetRemoteEndpoint(endpoint); +} + +void DevToolsNetworkTransaction::PopulateNetErrorDetails( + net::NetErrorDetails* details) const { + return transaction_->PopulateNetErrorDetails(details); +} + +void DevToolsNetworkTransaction::SetPriority(net::RequestPriority priority) { + transaction_->SetPriority(priority); +} + +void DevToolsNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper( + net::WebSocketHandshakeStreamBase::CreateHelper* helper) { + transaction_->SetWebSocketHandshakeStreamCreateHelper(helper); +} + +void DevToolsNetworkTransaction::SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) { + transaction_->SetBeforeNetworkStartCallback(callback); +} + +void DevToolsNetworkTransaction::SetBeforeHeadersSentCallback( + const BeforeHeadersSentCallback& callback) { + transaction_->SetBeforeHeadersSentCallback(callback); +} + +int DevToolsNetworkTransaction::ResumeNetworkStart() { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + return transaction_->ResumeNetworkStart(); +} + +void DevToolsNetworkTransaction::GetConnectionAttempts( + net::ConnectionAttempts* out) const { + transaction_->GetConnectionAttempts(out); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_transaction.h b/vendor/brightray/browser/net/devtools_network_transaction.h new file mode 100644 index 0000000000..3648ca71c7 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_transaction.h @@ -0,0 +1,109 @@ +// Copyright 2014 The Chromium 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 BROWSER_DEVTOOLS_NETWORK_TRANSACTION_H_ +#define BROWSER_DEVTOOLS_NETWORK_TRANSACTION_H_ + +#include + +#include "base/memory/weak_ptr.h" +#include "browser/net/devtools_network_interceptor.h" +#include "net/base/completion_callback.h" +#include "net/base/load_states.h" +#include "net/base/request_priority.h" +#include "net/http/http_transaction.h" +#include "net/websockets/websocket_handshake_stream_base.h" + +namespace brightray { + +class DevToolsNetworkController; +class DevToolsNetworkUploadDataStream; + +class DevToolsNetworkTransaction : public net::HttpTransaction { + public: + static const char kDevToolsEmulateNetworkConditionsClientId[]; + + DevToolsNetworkTransaction( + DevToolsNetworkController* controller, + std::unique_ptr network_transaction); + ~DevToolsNetworkTransaction() override; + + // HttpTransaction methods: + int Start(const net::HttpRequestInfo* request, + const net::CompletionCallback& callback, + const net::BoundNetLog& net_log) override; + int RestartIgnoringLastError( + const net::CompletionCallback& callback) override; + int RestartWithCertificate(net::X509Certificate* client_cert, + net::SSLPrivateKey* client_private_key, + const net::CompletionCallback& callback) override; + int RestartWithAuth(const net::AuthCredentials& credentials, + const net::CompletionCallback& callback) override; + bool IsReadyToRestartForAuth() override; + + int Read(net::IOBuffer* buf, + int buf_len, + const net::CompletionCallback& callback) override; + void StopCaching() override; + bool GetFullRequestHeaders(net::HttpRequestHeaders* headers) const override; + int64_t GetTotalReceivedBytes() const override; + int64_t GetTotalSentBytes() const override; + void DoneReading() override; + const net::HttpResponseInfo* GetResponseInfo() const override; + net::LoadState GetLoadState() const override; + net::UploadProgress GetUploadProgress() const override; + void SetQuicServerInfo(net::QuicServerInfo* quic_server_info) override; + bool GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override; + bool GetRemoteEndpoint(net::IPEndPoint* endpoint) const override; + void PopulateNetErrorDetails(net::NetErrorDetails* details) const override; + void SetPriority(net::RequestPriority priority) override; + void SetWebSocketHandshakeStreamCreateHelper( + net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) override; + void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) override; + void SetBeforeHeadersSentCallback( + const BeforeHeadersSentCallback& callback) override; + int ResumeNetworkStart() override; + void GetConnectionAttempts(net::ConnectionAttempts* out) const override; + + private: + void Fail(); + bool CheckFailed(); + + void IOCallback(const net::CompletionCallback& callback, + bool start, + int result); + int Throttle(const net::CompletionCallback& callback, + bool start, + int result); + void ThrottleCallback(const net::CompletionCallback& callback, + int result, + int64_t bytes); + + DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_; + int64_t throttled_byte_count_; + + DevToolsNetworkController* controller_; + base::WeakPtr interceptor_; + + // Modified upload data stream. Should be destructed after |custom_request_|. + std::unique_ptr custom_upload_data_stream_; + + // Modified request. Should be destructed after |transaction_|. + std::unique_ptr custom_request_; + + // Original network transaction. + std::unique_ptr transaction_; + + const net::HttpRequestInfo* request_; + + // True if Fail was already invoked. + bool failed_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransaction); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_TRANSACTION_H_ diff --git a/vendor/brightray/browser/net/devtools_network_transaction_factory.cc b/vendor/brightray/browser/net/devtools_network_transaction_factory.cc new file mode 100644 index 0000000000..7373c14dde --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_transaction_factory.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_transaction_factory.h" + +#include "browser/net/devtools_network_controller.h" +#include "browser/net/devtools_network_transaction.h" + +#include "content/public/browser/service_worker_context.h" +#include "net/base/net_errors.h" +#include "net/http/http_network_layer.h" +#include "net/http/http_network_transaction.h" + +namespace brightray { + +DevToolsNetworkTransactionFactory::DevToolsNetworkTransactionFactory( + DevToolsNetworkController* controller, + net::HttpNetworkSession* session) + : controller_(controller), + network_layer_(new net::HttpNetworkLayer(session)) { + std::set headers; + headers.insert( + DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId); + content::ServiceWorkerContext::AddExcludedHeadersForFetchEvent(headers); +} + +DevToolsNetworkTransactionFactory::~DevToolsNetworkTransactionFactory() { +} + +int DevToolsNetworkTransactionFactory::CreateTransaction( + net::RequestPriority priority, + std::unique_ptr* transaction) { + std::unique_ptr new_transaction; + int rv = network_layer_->CreateTransaction(priority, &new_transaction); + if (rv != net::OK) + return rv; + transaction->reset( + new DevToolsNetworkTransaction(controller_, std::move(new_transaction))); + return net::OK; +} + +net::HttpCache* DevToolsNetworkTransactionFactory::GetCache() { + return network_layer_->GetCache(); +} + +net::HttpNetworkSession* DevToolsNetworkTransactionFactory::GetSession() { + return network_layer_->GetSession(); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_transaction_factory.h b/vendor/brightray/browser/net/devtools_network_transaction_factory.h new file mode 100644 index 0000000000..a6c8f6a3c2 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_transaction_factory.h @@ -0,0 +1,38 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_ +#define BROWSER_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_ + +#include "base/macros.h" +#include "net/base/request_priority.h" +#include "net/http/http_transaction_factory.h" + +namespace brightray { + +class DevToolsNetworkController; + +class DevToolsNetworkTransactionFactory : public net::HttpTransactionFactory { + public: + explicit DevToolsNetworkTransactionFactory( + DevToolsNetworkController* controller, + net::HttpNetworkSession* session); + ~DevToolsNetworkTransactionFactory() override; + + // net::HttpTransactionFactory: + int CreateTransaction(net::RequestPriority priority, + std::unique_ptr* transaction) override; + net::HttpCache* GetCache() override; + net::HttpNetworkSession* GetSession() override; + + private: + DevToolsNetworkController* controller_; + std::unique_ptr network_layer_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransactionFactory); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_ diff --git a/vendor/brightray/browser/net/devtools_network_upload_data_stream.cc b/vendor/brightray/browser/net/devtools_network_upload_data_stream.cc new file mode 100644 index 0000000000..6eab91c67d --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_upload_data_stream.cc @@ -0,0 +1,96 @@ +// Copyright 2015 The Chromium 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 "browser/net/devtools_network_upload_data_stream.h" + +#include "net/base/net_errors.h" + +namespace brightray { + +DevToolsNetworkUploadDataStream::DevToolsNetworkUploadDataStream( + net::UploadDataStream* upload_data_stream) + : net::UploadDataStream(upload_data_stream->is_chunked(), + upload_data_stream->identifier()), + throttle_callback_( + base::Bind(&DevToolsNetworkUploadDataStream::ThrottleCallback, + base::Unretained(this))), + throttled_byte_count_(0), + upload_data_stream_(upload_data_stream) { +} + +DevToolsNetworkUploadDataStream::~DevToolsNetworkUploadDataStream() { + if (interceptor_) + interceptor_->StopThrottle(throttle_callback_); +} + +void DevToolsNetworkUploadDataStream::SetInterceptor( + DevToolsNetworkInterceptor* interceptor) { + DCHECK(!interceptor_); + if (interceptor) + interceptor_ = interceptor->GetWeakPtr(); +} + +bool DevToolsNetworkUploadDataStream::IsInMemory() const { + return false; +} + +int DevToolsNetworkUploadDataStream::InitInternal( + const net::BoundNetLog& net_log) { + throttled_byte_count_ = 0; + int result = upload_data_stream_->Init( + base::Bind(&DevToolsNetworkUploadDataStream::StreamInitCallback, + base::Unretained(this)), + net_log); + if (result == net::OK && !is_chunked()) + SetSize(upload_data_stream_->size()); + return result; +} + +void DevToolsNetworkUploadDataStream::StreamInitCallback(int result) { + if (!is_chunked()) + SetSize(upload_data_stream_->size()); + OnInitCompleted(result); +} + +int DevToolsNetworkUploadDataStream::ReadInternal( + net::IOBuffer* buf, int buf_len) { + int result = upload_data_stream_->Read(buf, buf_len, + base::Bind(&DevToolsNetworkUploadDataStream::StreamReadCallback, + base::Unretained(this))); + return ThrottleRead(result); +} + +void DevToolsNetworkUploadDataStream::StreamReadCallback(int result) { + result = ThrottleRead(result); + if (result != net::ERR_IO_PENDING) + OnReadCompleted(result); +} + +int DevToolsNetworkUploadDataStream::ThrottleRead(int result) { + if (is_chunked() && upload_data_stream_->IsEOF()) + SetIsFinalChunk(); + + if (!interceptor_ || result < 0) + return result; + + if (result > 0) + throttled_byte_count_ += result; + return interceptor_->StartThrottle(result, throttled_byte_count_, + base::TimeTicks(), false, true, throttle_callback_); +} + +void DevToolsNetworkUploadDataStream::ThrottleCallback( + int result, int64_t bytes) { + throttled_byte_count_ = bytes; + OnReadCompleted(result); +} + +void DevToolsNetworkUploadDataStream::ResetInternal() { + upload_data_stream_->Reset(); + throttled_byte_count_ = 0; + if (interceptor_) + interceptor_->StopThrottle(throttle_callback_); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net/devtools_network_upload_data_stream.h b/vendor/brightray/browser/net/devtools_network_upload_data_stream.h new file mode 100644 index 0000000000..8db7dd80c8 --- /dev/null +++ b/vendor/brightray/browser/net/devtools_network_upload_data_stream.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Chromium 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 BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ +#define BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ + +#include + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "browser/net/devtools_network_interceptor.h" +#include "net/base/completion_callback.h" +#include "net/base/upload_data_stream.h" + +namespace brightray { + +class DevToolsNetworkUploadDataStream : public net::UploadDataStream { + public: + // Supplied |upload_data_stream| must outlive this object. + explicit DevToolsNetworkUploadDataStream( + net::UploadDataStream* upload_data_stream); + ~DevToolsNetworkUploadDataStream() override; + + void SetInterceptor(DevToolsNetworkInterceptor* interceptor); + + private: + // net::UploadDataStream implementation. + bool IsInMemory() const override; + int InitInternal(const net::BoundNetLog& net_log) override; + int ReadInternal(net::IOBuffer* buf, int buf_len) override; + void ResetInternal() override; + + void StreamInitCallback(int result); + void StreamReadCallback(int result); + + int ThrottleRead(int result); + void ThrottleCallback(int result, int64_t bytes); + + DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_; + int64_t throttled_byte_count_; + + net::UploadDataStream* upload_data_stream_; + base::WeakPtr interceptor_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkUploadDataStream); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ diff --git a/vendor/brightray/browser/net_log.cc b/vendor/brightray/browser/net_log.cc new file mode 100644 index 0000000000..f141c7ef2e --- /dev/null +++ b/vendor/brightray/browser/net_log.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/net_log.h" + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/values.h" +#include "content/public/common/content_switches.h" +#include "net/log/net_log_util.h" + +namespace brightray { + +namespace { + +std::unique_ptr GetConstants() { + std::unique_ptr constants = net::GetNetConstants(); + + // Adding client information to constants dictionary. + auto* client_info = new base::DictionaryValue(); + client_info->SetString( + "command_line", + base::CommandLine::ForCurrentProcess()->GetCommandLineString()); + + constants->Set("clientInfo", client_info); + return constants; +} + +} // namespace + +NetLog::NetLog() { +} + +NetLog::~NetLog() { +} + +void NetLog::StartLogging(net::URLRequestContext* url_request_context) { + auto command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(switches::kLogNetLog)) + return; + + base::FilePath log_path = command_line->GetSwitchValuePath(switches::kLogNetLog); +#if defined(OS_WIN) + log_file_.reset(_wfopen(log_path.value().c_str(), L"w")); +#elif defined(OS_POSIX) + log_file_.reset(fopen(log_path.value().c_str(), "w")); +#endif + + if (!log_file_) { + LOG(ERROR) << "Could not open file: " << log_path.value() + << "for net logging"; + return; + } + + std::unique_ptr constants(GetConstants()); + write_to_file_observer_.StartObserving(this, + std::move(log_file_), + constants.get(), + url_request_context); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/net_log.h b/vendor/brightray/browser/net_log.h new file mode 100644 index 0000000000..e62c335350 --- /dev/null +++ b/vendor/brightray/browser/net_log.h @@ -0,0 +1,30 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NET_LOG_H_ +#define BROWSER_NET_LOG_H_ + +#include "base/files/scoped_file.h" +#include "net/log/net_log.h" +#include "net/log/write_to_file_net_log_observer.h" + +namespace brightray { + +class NetLog : public net::NetLog { + public: + NetLog(); + ~NetLog() override; + + void StartLogging(net::URLRequestContext* url_request_context); + + private: + base::ScopedFILE log_file_; + net::WriteToFileNetLogObserver write_to_file_observer_; + + DISALLOW_COPY_AND_ASSIGN(NetLog); +}; + +} // namespace brightray + +#endif // BROWSER_NET_LOG_H_ diff --git a/vendor/brightray/browser/network_delegate.cc b/vendor/brightray/browser/network_delegate.cc new file mode 100644 index 0000000000..e0ef20bb57 --- /dev/null +++ b/vendor/brightray/browser/network_delegate.cc @@ -0,0 +1,151 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/network_delegate.h" + +#include +#include + +#include "base/command_line.h" +#include "base/strings/string_split.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" + +namespace brightray { + +namespace { + +// Ignore the limit of 6 connections per host. +const char kIgnoreConnectionsLimit[] = "ignore-connections-limit"; + +} // namespace + +NetworkDelegate::NetworkDelegate() { + auto command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(kIgnoreConnectionsLimit)) { + std::string value = command_line->GetSwitchValueASCII(kIgnoreConnectionsLimit); + ignore_connections_limit_domains_ = base::SplitString( + value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + } +} + +NetworkDelegate::~NetworkDelegate() { +} + +int NetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) { + for (const auto& domain : ignore_connections_limit_domains_) { + if (request->url().DomainIs(domain)) { + // Allow unlimited concurrent connections. + request->SetPriority(net::MAXIMUM_PRIORITY); + request->SetLoadFlags(request->load_flags() | net::LOAD_IGNORE_LIMITS); + break; + } + } + + return net::OK; +} + +int NetworkDelegate::OnBeforeStartTransaction( + net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) { + return net::OK; +} + +void NetworkDelegate::OnStartTransaction( + net::URLRequest* request, + const net::HttpRequestHeaders& headers) { +} + +void NetworkDelegate::OnBeforeSendHeaders( + net::URLRequest* request, + const net::ProxyInfo& proxy_info, + const net::ProxyRetryInfoMap& proxy_retry_info, + net::HttpRequestHeaders* headers) { +} + +int NetworkDelegate::OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) { + return net::OK; +} + +void NetworkDelegate::OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) { +} + +void NetworkDelegate::OnResponseStarted(net::URLRequest* request) { +} + +void NetworkDelegate::OnNetworkBytesReceived(net::URLRequest* request, + int64_t bytes_read) { +} + +void NetworkDelegate::OnNetworkBytesSent(net::URLRequest* request, + int64_t bytes_sent) { +} + +void NetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { +} + +void NetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { +} + +void NetworkDelegate::OnPACScriptError(int line_number, + const base::string16& error) { +} + +NetworkDelegate::AuthRequiredResponse NetworkDelegate::OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) { + return AUTH_REQUIRED_RESPONSE_NO_ACTION; +} + +bool NetworkDelegate::OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) { + return true; +} + +bool NetworkDelegate::OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) { + return true; +} + +bool NetworkDelegate::OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const { + return true; +} + +bool NetworkDelegate::OnCanEnablePrivacyMode( + const GURL& url, + const GURL& first_party_for_cookies) const { + return false; +} + +bool NetworkDelegate::OnAreStrictSecureCookiesEnabled() const { + return true; +} + +bool NetworkDelegate::OnAreExperimentalCookieFeaturesEnabled() const { + return true; +} + +bool NetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader( + const net::URLRequest& request, + const GURL& target_url, + const GURL& referrer_url) const { + return false; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/network_delegate.h b/vendor/brightray/browser/network_delegate.h new file mode 100644 index 0000000000..752434222a --- /dev/null +++ b/vendor/brightray/browser/network_delegate.h @@ -0,0 +1,81 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NETWORK_DELEGATE_H_ +#define BRIGHTRAY_BROWSER_NETWORK_DELEGATE_H_ + +#include +#include + +#include "net/base/network_delegate.h" +#include "net/proxy/proxy_server.h" + +namespace brightray { + +class NetworkDelegate : public net::NetworkDelegate { + public: + NetworkDelegate(); + virtual ~NetworkDelegate(); + + protected: + int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) override; + int OnBeforeStartTransaction(net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) override; + void OnBeforeSendHeaders(net::URLRequest* request, + const net::ProxyInfo& proxy_info, + const net::ProxyRetryInfoMap& proxy_retry_info, + net::HttpRequestHeaders* headers) override; + void OnStartTransaction(net::URLRequest* request, + const net::HttpRequestHeaders& headers) override; + int OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) override; + void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnNetworkBytesReceived(net::URLRequest* request, + int64_t bytes_read) override; + void OnNetworkBytesSent(net::URLRequest* request, + int64_t bytes_sent) override; + void OnCompleted(net::URLRequest* request, bool started) override; + void OnURLRequestDestroyed(net::URLRequest* request) override; + void OnPACScriptError(int line_number, + const base::string16& error) override; + AuthRequiredResponse OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) override; + bool OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) override; + bool OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) override; + bool OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const override; + bool OnCanEnablePrivacyMode( + const GURL& url, + const GURL& first_party_for_cookies) const override; + bool OnAreStrictSecureCookiesEnabled() const override; + bool OnAreExperimentalCookieFeaturesEnabled() const override; + bool OnCancelURLRequestWithPolicyViolatingReferrerHeader( + const net::URLRequest& request, + const GURL& target_url, + const GURL& referrer_url) const override; + + private: + std::vector ignore_connections_limit_domains_; + + DISALLOW_COPY_AND_ASSIGN(NetworkDelegate); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/notification.cc b/vendor/brightray/browser/notification.cc new file mode 100644 index 0000000000..ba9df5446f --- /dev/null +++ b/vendor/brightray/browser/notification.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification.h" + +#include "browser/notification_delegate.h" +#include "browser/notification_presenter.h" + +namespace brightray { + +Notification::Notification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : delegate_(delegate), + presenter_(presenter), + weak_factory_(this) { +} + +Notification::~Notification() { + delegate()->NotificationDestroyed(); +} + +void Notification::NotificationClicked() { + delegate()->NotificationClick(); + Destroy(); +} + +void Notification::NotificationDismissed() { + delegate()->NotificationClosed(); + Destroy(); +} + +void Notification::NotificationFailed() { + delegate()->NotificationFailed(); + Destroy(); +} + +void Notification::Destroy() { + presenter()->RemoveNotification(this); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/notification.h b/vendor/brightray/browser/notification.h new file mode 100644 index 0000000000..afacd50c19 --- /dev/null +++ b/vendor/brightray/browser/notification.h @@ -0,0 +1,70 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NOTIFICATION_H_ +#define BROWSER_NOTIFICATION_H_ + +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" + +class GURL; +class SkBitmap; + +namespace brightray { + +class NotificationDelegate; +class NotificationPresenter; + +class Notification { + public: + // Shows the notification. + virtual void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) = 0; + // Closes the notification, this instance will be destroyed after the + // notification gets closed. + virtual void Dismiss() = 0; + + // Should be called by derived classes. + void NotificationClicked(); + void NotificationDismissed(); + void NotificationFailed(); + + base::WeakPtr GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + NotificationDelegate* delegate() const { return delegate_; } + NotificationPresenter* presenter() const { return presenter_; } + + protected: + Notification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + virtual ~Notification(); + + // delete this. + void Destroy(); + + private: + friend class NotificationPresenter; + + // Can only be called by NotificationPresenter, the caller is responsible of + // freeing the returned instance. + static Notification* Create(NotificationDelegate* delegate, + NotificationPresenter* presenter); + + NotificationDelegate* delegate_; + NotificationPresenter* presenter_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Notification); +}; + +} // namespace brightray + +#endif // BROWSER_NOTIFICATION_H_ diff --git a/vendor/brightray/browser/notification_delegate.h b/vendor/brightray/browser/notification_delegate.h new file mode 100644 index 0000000000..93512f7175 --- /dev/null +++ b/vendor/brightray/browser/notification_delegate.h @@ -0,0 +1,23 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NOTIFICATION_DELEGATE_H_ +#define BROWSER_NOTIFICATION_DELEGATE_H_ + +#include "content/public/browser/desktop_notification_delegate.h" + +namespace brightray { + +class NotificationDelegate : public content::DesktopNotificationDelegate { + public: + // The native Notification object is destroyed. + virtual void NotificationDestroyed() {} + + // Failed to send the notification. + virtual void NotificationFailed() {} +}; + +} // namespace brightray + +#endif // BROWSER_NOTIFICATION_DELEGATE_H_ diff --git a/vendor/brightray/browser/notification_delegate_adapter.cc b/vendor/brightray/browser/notification_delegate_adapter.cc new file mode 100644 index 0000000000..c0ad3d03f0 --- /dev/null +++ b/vendor/brightray/browser/notification_delegate_adapter.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification_delegate_adapter.h" + +namespace brightray { + +NotificationDelegateAdapter::NotificationDelegateAdapter( + std::unique_ptr delegate) + : delegate_(std::move(delegate)) { +} + +NotificationDelegateAdapter::~NotificationDelegateAdapter() { +} + +void NotificationDelegateAdapter::NotificationDestroyed() { + delete this; +} + +void NotificationDelegateAdapter::NotificationDisplayed() { + delegate_->NotificationDisplayed(); +} + +void NotificationDelegateAdapter::NotificationClosed() { + delegate_->NotificationClosed(); +} + +void NotificationDelegateAdapter::NotificationClick() { + delegate_->NotificationClick(); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/notification_delegate_adapter.h b/vendor/brightray/browser/notification_delegate_adapter.h new file mode 100644 index 0000000000..01f3284e2b --- /dev/null +++ b/vendor/brightray/browser/notification_delegate_adapter.h @@ -0,0 +1,38 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NOTIFICATION_DELEGATE_ADAPTER_H_ +#define BROWSER_NOTIFICATION_DELEGATE_ADAPTER_H_ + +#include + +#include "base/macros.h" +#include "browser/notification_delegate.h" + +namespace brightray { + +// Adapt the content::DesktopNotificationDelegate to NotificationDelegate. +class NotificationDelegateAdapter : public NotificationDelegate { + public: + explicit NotificationDelegateAdapter( + std::unique_ptr delegate); + ~NotificationDelegateAdapter() override; + + // NotificationDelegate: + void NotificationDestroyed() override; + + // content::DesktopNotificationDelegate: + void NotificationDisplayed() override; + void NotificationClosed() override; + void NotificationClick() override; + + private: + std::unique_ptr delegate_; + + DISALLOW_COPY_AND_ASSIGN(NotificationDelegateAdapter); +}; + +} // namespace brightray + +#endif // BROWSER_NOTIFICATION_DELEGATE_ADAPTER_H_ diff --git a/vendor/brightray/browser/notification_presenter.cc b/vendor/brightray/browser/notification_presenter.cc new file mode 100644 index 0000000000..ad46e292a2 --- /dev/null +++ b/vendor/brightray/browser/notification_presenter.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification_presenter.h" + +#include "browser/notification.h" + +namespace brightray { + +NotificationPresenter::NotificationPresenter() { +} + +NotificationPresenter::~NotificationPresenter() { + for (Notification* notification : notifications_) + delete notification; +} + +base::WeakPtr NotificationPresenter::CreateNotification( + NotificationDelegate* delegate) { + Notification* notification = Notification::Create(delegate, this); + notifications_.insert(notification); + return notification->GetWeakPtr(); +} + +void NotificationPresenter::RemoveNotification(Notification* notification) { + notifications_.erase(notification); + delete notification; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/notification_presenter.h b/vendor/brightray/browser/notification_presenter.h new file mode 100644 index 0000000000..b3dac3005d --- /dev/null +++ b/vendor/brightray/browser/notification_presenter.h @@ -0,0 +1,43 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_H_ + +#include + +#include "base/memory/weak_ptr.h" + +namespace brightray { + +class Notification; +class NotificationDelegate; + +class NotificationPresenter { + public: + static NotificationPresenter* Create(); + + virtual ~NotificationPresenter(); + + base::WeakPtr CreateNotification( + NotificationDelegate* delegate); + + std::set notifications() const { return notifications_; } + + protected: + NotificationPresenter(); + + private: + friend class Notification; + + void RemoveNotification(Notification* notification); + + std::set notifications_; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenter); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/permission_manager.cc b/vendor/brightray/browser/permission_manager.cc new file mode 100644 index 0000000000..f2b0d97d9b --- /dev/null +++ b/vendor/brightray/browser/permission_manager.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/permission_manager.h" + +#include "base/callback.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/permission_type.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" + +namespace brightray { + +PermissionManager::PermissionManager() { +} + +PermissionManager::~PermissionManager() { +} + +int PermissionManager::RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback& callback) { + if (permission == content::PermissionType::MIDI_SYSEX) { + content::ChildProcessSecurityPolicy::GetInstance()-> + GrantSendMidiSysExMessage(render_frame_host->GetProcess()->GetID()); + } + callback.Run(blink::mojom::PermissionStatus::GRANTED); + return kNoPendingOperation; +} + +int PermissionManager::RequestPermissions( + const std::vector& permissions, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback&)>& callback) { + std::vector permissionStatuses; + + for (auto permission : permissions) { + if (permission == content::PermissionType::MIDI_SYSEX) { + content::ChildProcessSecurityPolicy::GetInstance()-> + GrantSendMidiSysExMessage(render_frame_host->GetProcess()->GetID()); + } + + permissionStatuses.push_back(blink::mojom::PermissionStatus::GRANTED); + } + + callback.Run(permissionStatuses); + return kNoPendingOperation; +} + +void PermissionManager::CancelPermissionRequest(int request_id) { +} + +void PermissionManager::ResetPermission( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +blink::mojom::PermissionStatus PermissionManager::GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { + return blink::mojom::PermissionStatus::GRANTED; +} + +void PermissionManager::RegisterPermissionUsage( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +int PermissionManager::SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback& callback) { + return -1; +} + +void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id) { +} + +} // namespace brightray diff --git a/vendor/brightray/browser/permission_manager.h b/vendor/brightray/browser/permission_manager.h new file mode 100644 index 0000000000..67168ed2b3 --- /dev/null +++ b/vendor/brightray/browser/permission_manager.h @@ -0,0 +1,57 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_PERMISSION_MANAGER_H_ +#define BROWSER_PERMISSION_MANAGER_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "content/public/browser/permission_manager.h" + +namespace brightray { + +class PermissionManager : public content::PermissionManager { + public: + PermissionManager(); + ~PermissionManager() override; + + // content::PermissionManager: + int RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback& callback) override; + int RequestPermissions( + const std::vector& permissions, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback&)>& callback) override; + void CancelPermissionRequest(int request_id) override; + void ResetPermission(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + blink::mojom::PermissionStatus GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + void RegisterPermissionUsage(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + int SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback& callback) override; + void UnsubscribePermissionStatusChange(int subscription_id) override; + + private: + DISALLOW_COPY_AND_ASSIGN(PermissionManager); +}; + +} // namespace brightray + +#endif // BROWSER_PERMISSION_MANAGER_H_ diff --git a/vendor/brightray/browser/platform_notification_service.cc b/vendor/brightray/browser/platform_notification_service.cc new file mode 100644 index 0000000000..5c390db3f1 --- /dev/null +++ b/vendor/brightray/browser/platform_notification_service.cc @@ -0,0 +1,110 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/platform_notification_service.h" + +#include "browser/browser_client.h" +#include "browser/notification.h" +#include "browser/notification_delegate_adapter.h" +#include "browser/notification_presenter.h" +#include "content/public/common/platform_notification_data.h" +#include "content/public/common/notification_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace brightray { + +namespace { + +void RemoveNotification(base::WeakPtr notification) { + if (notification) + notification->Dismiss(); +} + +void OnWebNotificationAllowed( + brightray::BrowserClient* browser_client, + const SkBitmap& icon, + const content::PlatformNotificationData& data, + std::unique_ptr delegate, + base::Closure* cancel_callback, + bool audio_muted, + bool allowed) { + if (!allowed) + return; + auto presenter = browser_client->GetNotificationPresenter(); + if (!presenter) + return; + std::unique_ptr adapter( + new NotificationDelegateAdapter(std::move(delegate))); + auto notification = presenter->CreateNotification(adapter.get()); + if (notification) { + ignore_result(adapter.release()); // it will release itself automatically. + notification->Show(data.title, data.body, data.tag, data.icon, icon, + audio_muted ? true : data.silent); + *cancel_callback = base::Bind(&RemoveNotification, notification); + } +} + +} // namespace + +PlatformNotificationService::PlatformNotificationService( + BrowserClient* browser_client) + : browser_client_(browser_client), + render_process_id_(-1) { +} + +PlatformNotificationService::~PlatformNotificationService() {} + +blink::mojom::PermissionStatus PlatformNotificationService::CheckPermissionOnUIThread( + content::BrowserContext* browser_context, + const GURL& origin, + int render_process_id) { + render_process_id_ = render_process_id; + return blink::mojom::PermissionStatus::GRANTED; +} + +blink::mojom::PermissionStatus PlatformNotificationService::CheckPermissionOnIOThread( + content::ResourceContext* resource_context, + const GURL& origin, + int render_process_id) { + return blink::mojom::PermissionStatus::GRANTED; +} + +void PlatformNotificationService::DisplayNotification( + content::BrowserContext* browser_context, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources, + std::unique_ptr delegate, + base::Closure* cancel_callback) { + browser_client_->WebNotificationAllowed( + render_process_id_, + base::Bind(&OnWebNotificationAllowed, + browser_client_, + notification_resources.notification_icon, + notification_data, + base::Passed(&delegate), + cancel_callback)); +} + +void PlatformNotificationService::DisplayPersistentNotification( + content::BrowserContext* browser_context, + int64_t persistent_notification_id, + const GURL& origin, + const GURL& service_worker_origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources) { +} + +void PlatformNotificationService::ClosePersistentNotification( + content::BrowserContext* browser_context, + int64_t persistent_notification_id) { +} + +bool PlatformNotificationService::GetDisplayedPersistentNotifications( + content::BrowserContext* browser_context, + std::set* displayed_notifications) { + return false; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/platform_notification_service.h b/vendor/brightray/browser/platform_notification_service.h new file mode 100644 index 0000000000..1e17a6fbae --- /dev/null +++ b/vendor/brightray/browser/platform_notification_service.h @@ -0,0 +1,60 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_PLATFORM_NOTIFICATION_SERVICE_H_ +#define BROWSER_PLATFORM_NOTIFICATION_SERVICE_H_ + +#include "content/public/browser/browser_context.h" +#include "content/public/browser/platform_notification_service.h" + +namespace brightray { + +class BrowserClient; + +class PlatformNotificationService + : public content::PlatformNotificationService { + public: + explicit PlatformNotificationService(BrowserClient* browser_client); + ~PlatformNotificationService() override; + + protected: + // content::PlatformNotificationService: + blink::mojom::PermissionStatus CheckPermissionOnUIThread( + content::BrowserContext* browser_context, + const GURL& origin, + int render_process_id) override; + blink::mojom::PermissionStatus CheckPermissionOnIOThread( + content::ResourceContext* resource_context, + const GURL& origin, + int render_process_id) override; + void DisplayNotification(content::BrowserContext* browser_context, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources, + std::unique_ptr delegate, + base::Closure* cancel_callback) override; + void DisplayPersistentNotification( + content::BrowserContext* browser_context, + int64_t persistent_notification_id, + const GURL& service_worker_origin, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources) override; + void ClosePersistentNotification( + content::BrowserContext* browser_context, + int64_t persistent_notification_id) override; + bool GetDisplayedPersistentNotifications( + content::BrowserContext* browser_context, + std::set* displayed_notifications) override; + + private: + BrowserClient* browser_client_; + int render_process_id_; + + DISALLOW_COPY_AND_ASSIGN(PlatformNotificationService); +}; + +} // namespace brightray + +#endif // BROWSER_PLATFORM_NOTIFICATION_SERVICE_H_ diff --git a/vendor/brightray/browser/special_storage_policy.cc b/vendor/brightray/browser/special_storage_policy.cc new file mode 100644 index 0000000000..77a302bb1f --- /dev/null +++ b/vendor/brightray/browser/special_storage_policy.cc @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium 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 "browser/special_storage_policy.h" + +namespace brightray { + +SpecialStoragePolicy::SpecialStoragePolicy() { +} + +SpecialStoragePolicy::~SpecialStoragePolicy() { +} + +bool SpecialStoragePolicy::IsStorageProtected(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::IsStorageUnlimited(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::IsStorageDurable(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::IsStorageSessionOnly(const GURL& origin) { + return false; +} + +bool SpecialStoragePolicy::CanQueryDiskSize(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::HasSessionOnlyOrigins() { + return false; +} + +bool SpecialStoragePolicy::HasIsolatedStorage(const GURL& origin) { + return false; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/special_storage_policy.h b/vendor/brightray/browser/special_storage_policy.h new file mode 100644 index 0000000000..265df536cf --- /dev/null +++ b/vendor/brightray/browser/special_storage_policy.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium 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 BRIGHTRAY_BROWSER_SPECIAL_STORAGE_POLICY_H_ +#define BRIGHTRAY_BROWSER_SPECIAL_STORAGE_POLICY_H_ + +#include "storage/browser/quota/special_storage_policy.h" + +namespace brightray { + +class SpecialStoragePolicy : public storage::SpecialStoragePolicy { + public: + SpecialStoragePolicy(); + + // storage::SpecialStoragePolicy implementation. + bool IsStorageProtected(const GURL& origin) override; + bool IsStorageUnlimited(const GURL& origin) override; + bool IsStorageDurable(const GURL& origin) override; + bool IsStorageSessionOnly(const GURL& origin) override; + bool CanQueryDiskSize(const GURL& origin) override; + bool HasIsolatedStorage(const GURL& origin) override; + bool HasSessionOnlyOrigins() override; + + protected: + ~SpecialStoragePolicy() override; +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_SPECIAL_STORAGE_POLICY_H_ diff --git a/vendor/brightray/browser/url_request_context_getter.cc b/vendor/brightray/browser/url_request_context_getter.cc new file mode 100644 index 0000000000..71f339ce54 --- /dev/null +++ b/vendor/brightray/browser/url_request_context_getter.cc @@ -0,0 +1,384 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/url_request_context_getter.h" + +#include + +#include "browser/net/devtools_network_controller_handle.h" +#include "browser/net/devtools_network_transaction_factory.h" +#include "browser/net_log.h" +#include "browser/network_delegate.h" +#include "common/switches.h" + +#include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/worker_pool.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/cookie_store_factory.h" +#include "content/public/common/content_switches.h" +#include "net/base/host_mapping_rules.h" +#include "net/cert/cert_verifier.h" +#include "net/cert/ct_known_logs.h" +#include "net/cert/ct_log_verifier.h" +#include "net/cert/ct_policy_enforcer.h" +#include "net/cert/multi_log_ct_verifier.h" +#include "net/cookies/cookie_monster.h" +#include "net/dns/mapped_host_resolver.h" +#include "net/http/http_auth_filter.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_auth_preferences.h" +#include "net/http/http_server_properties_impl.h" +#include "net/log/net_log.h" +#include "net/proxy/dhcp_proxy_script_fetcher_factory.h" +#include "net/proxy/proxy_config.h" +#include "net/proxy/proxy_config_service.h" +#include "net/proxy/proxy_script_fetcher_impl.h" +#include "net/proxy/proxy_service.h" +#include "net/proxy/proxy_service_v8.h" +#include "net/ssl/channel_id_service.h" +#include "net/ssl/default_channel_id_store.h" +#include "net/ssl/ssl_config_service_defaults.h" +#include "net/url_request/data_protocol_handler.h" +#include "net/url_request/file_protocol_handler.h" +#include "net/url_request/static_http_user_agent_settings.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_context_storage.h" +#include "net/url_request/url_request_intercepting_job_factory.h" +#include "net/url_request/url_request_job_factory_impl.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/url_constants.h" +#include "storage/browser/quota/special_storage_policy.h" + +#if defined(USE_NSS_CERTS) +#include "net/cert_net/nss_ocsp.h" +#endif + +using content::BrowserThread; + +namespace brightray { + +std::string URLRequestContextGetter::Delegate::GetUserAgent() { + return base::EmptyString(); +} + +std::unique_ptr +URLRequestContextGetter::Delegate::CreateURLRequestJobFactory( + content::ProtocolHandlerMap* protocol_handlers) { + std::unique_ptr job_factory(new net::URLRequestJobFactoryImpl); + + for (auto& it : *protocol_handlers) { + job_factory->SetProtocolHandler( + it.first, base::WrapUnique(it.second.release())); + } + protocol_handlers->clear(); + + job_factory->SetProtocolHandler( + url::kDataScheme, base::WrapUnique(new net::DataProtocolHandler)); + job_factory->SetProtocolHandler( + url::kFileScheme, + base::WrapUnique(new net::FileProtocolHandler( + BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)))); + + return std::move(job_factory); +} + +net::HttpCache::BackendFactory* +URLRequestContextGetter::Delegate::CreateHttpCacheBackendFactory(const base::FilePath& base_path) { + base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache")); + return new net::HttpCache::DefaultBackend( + net::DISK_CACHE, + net::CACHE_BACKEND_DEFAULT, + cache_path, + 0, + BrowserThread::GetTaskRunnerForThread(BrowserThread::CACHE)); +} + +std::unique_ptr +URLRequestContextGetter::Delegate::CreateCertVerifier() { + return net::CertVerifier::CreateDefault(); +} + +net::SSLConfigService* URLRequestContextGetter::Delegate::CreateSSLConfigService() { + return new net::SSLConfigServiceDefaults; +} + +std::vector URLRequestContextGetter::Delegate::GetCookieableSchemes() { + return { "http", "https", "ws", "wss" }; +} + +URLRequestContextGetter::URLRequestContextGetter( + Delegate* delegate, + DevToolsNetworkControllerHandle* handle, + NetLog* net_log, + const base::FilePath& base_path, + bool in_memory, + base::MessageLoop* io_loop, + base::MessageLoop* file_loop, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector protocol_interceptors) + : delegate_(delegate), + network_controller_handle_(handle), + net_log_(net_log), + base_path_(base_path), + in_memory_(in_memory), + io_loop_(io_loop), + file_loop_(file_loop), + protocol_interceptors_(std::move(protocol_interceptors)), + job_factory_(nullptr), + shutting_down_(false) { + // Must first be created on the UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (protocol_handlers) + std::swap(protocol_handlers_, *protocol_handlers); + + if (delegate_) + user_agent_ = delegate_->GetUserAgent(); + + // We must create the proxy config service on the UI loop on Linux because it + // must synchronously run on the glib message loop. This will be passed to + // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). + proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService( + io_loop_->task_runner(), file_loop_->task_runner()); +} + +URLRequestContextGetter::~URLRequestContextGetter() { + // NotifyContextShuttingDown() must have been called. + DCHECK(!network_delegate_.get()); + DCHECK(!url_request_context_.get()); + DCHECK(!storage_.get()); +} + +void URLRequestContextGetter::NotifyContextShuttingDown() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + shutting_down_ = true; + + #if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(NULL); + #endif + + network_delegate_.reset(); + url_request_context_.reset(); + storage_.reset(); + + net::URLRequestContextGetter::NotifyContextShuttingDown(); +} + +net::HostResolver* URLRequestContextGetter::host_resolver() { + return url_request_context_->host_resolver(); +} + +net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (shutting_down_) { + return NULL; + } + + if (!url_request_context_.get()) { + auto& command_line = *base::CommandLine::ForCurrentProcess(); + url_request_context_.reset(new net::URLRequestContext); + +#if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(url_request_context_.get()); +#endif + + // --log-net-log + if (net_log_) { + net_log_->StartLogging(url_request_context_.get()); + url_request_context_->set_net_log(net_log_); + } + + network_delegate_.reset(delegate_->CreateNetworkDelegate()); + url_request_context_->set_network_delegate(network_delegate_.get()); + + storage_.reset(new net::URLRequestContextStorage(url_request_context_.get())); + + std::unique_ptr cookie_store = nullptr; + if (in_memory_) { + auto cookie_config = content::CookieStoreConfig(); + cookie_config.cookieable_schemes = delegate_->GetCookieableSchemes(); + cookie_store = content::CreateCookieStore(cookie_config); + } else { + auto cookie_config = content::CookieStoreConfig( + base_path_.Append(FILE_PATH_LITERAL("Cookies")), + content::CookieStoreConfig::EPHEMERAL_SESSION_COOKIES, + nullptr, nullptr); + cookie_config.cookieable_schemes = delegate_->GetCookieableSchemes(); + cookie_store = content::CreateCookieStore(cookie_config); + } + storage_->set_cookie_store(std::move(cookie_store)); + storage_->set_channel_id_service(base::WrapUnique( + new net::ChannelIDService(new net::DefaultChannelIDStore(nullptr), + base::WorkerPool::GetTaskRunner(true)))); + + std::string accept_lang = l10n_util::GetApplicationLocale(""); + storage_->set_http_user_agent_settings(base::WrapUnique( + new net::StaticHttpUserAgentSettings( + net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang), + user_agent_))); + + std::unique_ptr host_resolver(net::HostResolver::CreateDefaultResolver(nullptr)); + + // --host-resolver-rules + if (command_line.HasSwitch(::switches::kHostResolverRules)) { + std::unique_ptr remapped_resolver( + new net::MappedHostResolver(std::move(host_resolver))); + remapped_resolver->SetRulesFromString( + command_line.GetSwitchValueASCII(::switches::kHostResolverRules)); + host_resolver = std::move(remapped_resolver); + } + + // --proxy-server + net::DhcpProxyScriptFetcherFactory dhcp_factory; + if (command_line.HasSwitch(switches::kNoProxyServer)) { + storage_->set_proxy_service(net::ProxyService::CreateDirect()); + } else if (command_line.HasSwitch(switches::kProxyServer)) { + net::ProxyConfig proxy_config; + proxy_config.proxy_rules().ParseFromString( + command_line.GetSwitchValueASCII(switches::kProxyServer)); + proxy_config.proxy_rules().bypass_rules.ParseFromString( + command_line.GetSwitchValueASCII(switches::kProxyBypassList)); + storage_->set_proxy_service(net::ProxyService::CreateFixed(proxy_config)); + } else if (command_line.HasSwitch(switches::kProxyPacUrl)) { + auto proxy_config = net::ProxyConfig::CreateFromCustomPacURL( + GURL(command_line.GetSwitchValueASCII(switches::kProxyPacUrl))); + proxy_config.set_pac_mandatory(true); + storage_->set_proxy_service(net::ProxyService::CreateFixed( + proxy_config)); + } else { + storage_->set_proxy_service( + net::CreateProxyServiceUsingV8ProxyResolver( + std::move(proxy_config_service_), + new net::ProxyScriptFetcherImpl(url_request_context_.get()), + dhcp_factory.Create(url_request_context_.get()), + host_resolver.get(), + nullptr, + url_request_context_->network_delegate())); + } + + std::vector schemes; + schemes.push_back(std::string("basic")); + schemes.push_back(std::string("digest")); + schemes.push_back(std::string("ntlm")); + schemes.push_back(std::string("negotiate")); +#if defined(OS_POSIX) + http_auth_preferences_.reset(new net::HttpAuthPreferences(schemes, + std::string())); +#else + http_auth_preferences_.reset(new net::HttpAuthPreferences(schemes)); +#endif + + // --auth-server-whitelist + if (command_line.HasSwitch(switches::kAuthServerWhitelist)) { + http_auth_preferences_->set_server_whitelist( + command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist)); + } + + // --auth-negotiate-delegate-whitelist + if (command_line.HasSwitch(switches::kAuthNegotiateDelegateWhitelist)) { + http_auth_preferences_->set_delegate_whitelist( + command_line.GetSwitchValueASCII(switches::kAuthNegotiateDelegateWhitelist)); + } + + auto auth_handler_factory = + net::HttpAuthHandlerRegistryFactory::Create( + http_auth_preferences_.get(), host_resolver.get()); + + storage_->set_cert_verifier(delegate_->CreateCertVerifier()); + storage_->set_transport_security_state( + base::WrapUnique(new net::TransportSecurityState)); + storage_->set_ssl_config_service(delegate_->CreateSSLConfigService()); + storage_->set_http_auth_handler_factory(std::move(auth_handler_factory)); + std::unique_ptr server_properties( + new net::HttpServerPropertiesImpl); + storage_->set_http_server_properties(std::move(server_properties)); + + std::unique_ptr ct_verifier = + base::MakeUnique(); + ct_verifier->AddLogs(net::ct::CreateLogVerifiersForKnownLogs()); + storage_->set_cert_transparency_verifier(std::move(ct_verifier)); + storage_->set_ct_policy_enforcer(base::MakeUnique()); + + net::HttpNetworkSession::Params network_session_params; + net::URLRequestContextBuilder::SetHttpNetworkSessionComponents( + url_request_context_.get(), &network_session_params); + network_session_params.ignore_certificate_errors = false; + + // --disable-http2 + if (command_line.HasSwitch(switches::kDisableHttp2)) { + network_session_params.enable_http2 = false; + } + + // --ignore-certificate-errors + if (command_line.HasSwitch(switches::kIgnoreCertificateErrors)) + network_session_params.ignore_certificate_errors = true; + + // --host-rules + if (command_line.HasSwitch(switches::kHostRules)) { + host_mapping_rules_.reset(new net::HostMappingRules); + host_mapping_rules_->SetRulesFromString(command_line.GetSwitchValueASCII(switches::kHostRules)); + network_session_params.host_mapping_rules = host_mapping_rules_.get(); + } + + // Give |storage_| ownership at the end in case it's |mapped_host_resolver|. + storage_->set_host_resolver(std::move(host_resolver)); + network_session_params.host_resolver = url_request_context_->host_resolver(); + + http_network_session_.reset( + new net::HttpNetworkSession(network_session_params)); + std::unique_ptr backend; + if (in_memory_) { + backend = net::HttpCache::DefaultBackend::InMemory(0); + } else { + backend.reset(delegate_->CreateHttpCacheBackendFactory(base_path_)); + } + + if (network_controller_handle_) { + storage_->set_http_transaction_factory(base::WrapUnique( + new net::HttpCache( + base::WrapUnique(new DevToolsNetworkTransactionFactory( + network_controller_handle_->GetController(), http_network_session_.get())), + std::move(backend), + false))); + } else { + storage_->set_http_transaction_factory(base::WrapUnique( + new net::HttpCache(http_network_session_.get(), + std::move(backend), + false))); + } + + std::unique_ptr job_factory = + delegate_->CreateURLRequestJobFactory(&protocol_handlers_); + job_factory_ = job_factory.get(); + + // Set up interceptors in the reverse order. + std::unique_ptr top_job_factory = + std::move(job_factory); + content::URLRequestInterceptorScopedVector::reverse_iterator it; + for (it = protocol_interceptors_.rbegin(); + it != protocol_interceptors_.rend(); + ++it) { + top_job_factory.reset(new net::URLRequestInterceptingJobFactory( + std::move(top_job_factory), base::WrapUnique(*it))); + } + protocol_interceptors_.weak_clear(); + + storage_->set_job_factory(std::move(top_job_factory)); + } + + return url_request_context_.get(); +} + +scoped_refptr URLRequestContextGetter::GetNetworkTaskRunner() const { + return BrowserThread::GetTaskRunnerForThread(BrowserThread::IO); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/url_request_context_getter.h b/vendor/brightray/browser/url_request_context_getter.h new file mode 100644 index 0000000000..b5bb749d37 --- /dev/null +++ b/vendor/brightray/browser/url_request_context_getter.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_URL_REQUEST_CONTEXT_GETTER_H_ +#define BRIGHTRAY_BROWSER_URL_REQUEST_CONTEXT_GETTER_H_ + +#include "base/files/file_path.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/content_browser_client.h" +#include "net/http/http_cache.h" +#include "net/http/url_security_manager.h" +#include "net/url_request/url_request_context_getter.h" + +namespace base { +class MessageLoop; +} + +namespace net { +class HostMappingRules; +class HostResolver; +class HttpAuthPreferences; +class NetworkDelegate; +class ProxyConfigService; +class URLRequestContextStorage; +class URLRequestJobFactory; +} + +namespace brightray { + +class DevToolsNetworkControllerHandle; +class NetLog; + +class URLRequestContextGetter : public net::URLRequestContextGetter { + public: + class Delegate { + public: + Delegate() {} + virtual ~Delegate() {} + + virtual net::NetworkDelegate* CreateNetworkDelegate() { return NULL; } + virtual std::string GetUserAgent(); + virtual std::unique_ptr + CreateURLRequestJobFactory( + content::ProtocolHandlerMap* protocol_handlers); + virtual net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory( + const base::FilePath& base_path); + virtual std::unique_ptr CreateCertVerifier(); + virtual net::SSLConfigService* CreateSSLConfigService(); + virtual std::vector GetCookieableSchemes(); + }; + + URLRequestContextGetter( + Delegate* delegate, + DevToolsNetworkControllerHandle* handle, + NetLog* net_log, + const base::FilePath& base_path, + bool in_memory, + base::MessageLoop* io_loop, + base::MessageLoop* file_loop, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector protocol_interceptors); + virtual ~URLRequestContextGetter(); + + // net::URLRequestContextGetter: + net::URLRequestContext* GetURLRequestContext() override; + scoped_refptr GetNetworkTaskRunner() const override; + + net::HostResolver* host_resolver(); + net::URLRequestJobFactory* job_factory() const { return job_factory_; } + + void NotifyContextShuttingDown(); + private: + Delegate* delegate_; + + DevToolsNetworkControllerHandle* network_controller_handle_; + NetLog* net_log_; + base::FilePath base_path_; + bool in_memory_; + base::MessageLoop* io_loop_; + base::MessageLoop* file_loop_; + + std::string user_agent_; + + std::unique_ptr proxy_config_service_; + std::unique_ptr network_delegate_; + std::unique_ptr storage_; + std::unique_ptr url_request_context_; + std::unique_ptr host_mapping_rules_; + std::unique_ptr http_auth_preferences_; + std::unique_ptr http_network_session_; + content::ProtocolHandlerMap protocol_handlers_; + content::URLRequestInterceptorScopedVector protocol_interceptors_; + + net::URLRequestJobFactory* job_factory_; // weak ref + + bool shutting_down_; + + DISALLOW_COPY_AND_ASSIGN(URLRequestContextGetter); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/views/inspectable_web_contents_view_views.cc b/vendor/brightray/browser/views/inspectable_web_contents_view_views.cc new file mode 100644 index 0000000000..fb1f8493cd --- /dev/null +++ b/vendor/brightray/browser/views/inspectable_web_contents_view_views.cc @@ -0,0 +1,223 @@ +#include "browser/views/inspectable_web_contents_view_views.h" + +#include "browser/inspectable_web_contents_delegate.h" +#include "browser/inspectable_web_contents_impl.h" +#include "browser/inspectable_web_contents_view_delegate.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/window/client_view.h" + +namespace brightray { + +namespace { + +class DevToolsWindowDelegate : public views::ClientView, + public views::WidgetDelegate { + public: + DevToolsWindowDelegate(InspectableWebContentsViewViews* shell, + views::View* view, + views::Widget* widget) + : views::ClientView(widget, view), + shell_(shell), + view_(view), + widget_(widget), + title_(base::ASCIIToUTF16("Developer Tools")) { + // A WidgetDelegate should be deleted on DeleteDelegate. + set_owned_by_client(); + + if (shell->GetDelegate()) + icon_ = shell->GetDelegate()->GetDevToolsWindowIcon(); + } + virtual ~DevToolsWindowDelegate() {} + + void SetWindowTitle(const base::string16& title) { title_ = title; } + + // views::WidgetDelegate: + void DeleteDelegate() override { delete this; } + views::View* GetInitiallyFocusedView() override { return view_; } + bool CanResize() const override { return true; } + bool CanMaximize() const override { return true; } + bool CanMinimize() const override { return true; } + base::string16 GetWindowTitle() const override { return title_; } + gfx::ImageSkia GetWindowAppIcon() override { return GetWindowIcon(); } + gfx::ImageSkia GetWindowIcon() override { return icon_; } + views::Widget* GetWidget() override { return widget_; } + const views::Widget* GetWidget() const override { return widget_; } + views::View* GetContentsView() override { return view_; } + views::ClientView* CreateClientView(views::Widget* widget) override { return this; } + + // views::ClientView: + bool CanClose() override { + shell_->inspectable_web_contents()->CloseDevTools(); + return false; + } + + private: + InspectableWebContentsViewViews* shell_; + views::View* view_; + views::Widget* widget_; + base::string16 title_; + gfx::ImageSkia icon_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsWindowDelegate); +}; + +} // namespace + +InspectableWebContentsView* CreateInspectableContentsView( + InspectableWebContentsImpl* inspectable_web_contents) { + return new InspectableWebContentsViewViews(inspectable_web_contents); +} + +InspectableWebContentsViewViews::InspectableWebContentsViewViews( + InspectableWebContentsImpl* inspectable_web_contents) + : inspectable_web_contents_(inspectable_web_contents), + devtools_window_web_view_(nullptr), + contents_web_view_(nullptr), + devtools_web_view_(new views::WebView(nullptr)), + devtools_visible_(false), + devtools_window_delegate_(nullptr) { + set_owned_by_client(); + + if (inspectable_web_contents_->GetWebContents()->GetNativeView()) { + views::WebView* contents_web_view = new views::WebView(nullptr); + contents_web_view->SetWebContents(inspectable_web_contents_->GetWebContents()); + contents_web_view_ = contents_web_view; + } else { + contents_web_view_ = new views::Label(base::ASCIIToUTF16("No content under offscreen mode")); + } + + devtools_web_view_->SetVisible(false); + AddChildView(devtools_web_view_); + AddChildView(contents_web_view_); +} + +InspectableWebContentsViewViews::~InspectableWebContentsViewViews() { + if (devtools_window_) + inspectable_web_contents()->SaveDevToolsBounds(devtools_window_->GetWindowBoundsInScreen()); +} + +views::View* InspectableWebContentsViewViews::GetView() { + return this; +} + +views::View* InspectableWebContentsViewViews::GetWebView() { + return contents_web_view_; +} + +void InspectableWebContentsViewViews::ShowDevTools() { + if (devtools_visible_) + return; + + devtools_visible_ = true; + if (devtools_window_) { + devtools_window_web_view_->SetWebContents( + inspectable_web_contents_->GetDevToolsWebContents()); + devtools_window_->SetBounds(inspectable_web_contents()->GetDevToolsBounds()); + devtools_window_->Show(); + } else { + devtools_web_view_->SetVisible(true); + devtools_web_view_->SetWebContents( + inspectable_web_contents_->GetDevToolsWebContents()); + devtools_web_view_->RequestFocus(); + Layout(); + } +} + +void InspectableWebContentsViewViews::CloseDevTools() { + if (!devtools_visible_) + return; + + devtools_visible_ = false; + if (devtools_window_) { + inspectable_web_contents()->SaveDevToolsBounds(devtools_window_->GetWindowBoundsInScreen()); + devtools_window_.reset(); + devtools_window_web_view_ = nullptr; + devtools_window_delegate_ = nullptr; + } else { + devtools_web_view_->SetVisible(false); + devtools_web_view_->SetWebContents(NULL); + Layout(); + } +} + +bool InspectableWebContentsViewViews::IsDevToolsViewShowing() { + return devtools_visible_; +} + +bool InspectableWebContentsViewViews::IsDevToolsViewFocused() { + if (devtools_window_web_view_) + return devtools_window_web_view_->HasFocus(); + else if (devtools_web_view_) + return devtools_web_view_->HasFocus(); + else + return false; +} + +void InspectableWebContentsViewViews::SetIsDocked(bool docked) { + CloseDevTools(); + + if (!docked) { + devtools_window_.reset(new views::Widget); + devtools_window_web_view_ = new views::WebView(NULL); + devtools_window_delegate_ = new DevToolsWindowDelegate(this, + devtools_window_web_view_, + devtools_window_.get()); + + views::Widget::InitParams params; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.delegate = GetDevToolsWindowDelegate(); + params.bounds = inspectable_web_contents()->GetDevToolsBounds(); + +#if defined(USE_X11) + params.wm_role_name = "devtools"; + if (GetDelegate()) + GetDelegate()->GetDevToolsWindowWMClass(¶ms.wm_class_name, ¶ms.wm_class_class); +#endif + + devtools_window_->Init(params); + devtools_window_->UpdateWindowIcon(); + } + + ShowDevTools(); +} + +void InspectableWebContentsViewViews::SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) { + strategy_.CopyFrom(strategy); + Layout(); +} + +void InspectableWebContentsViewViews::SetTitle(const base::string16& title) { + if (devtools_window_) { + GetDevToolsWindowDelegate()->SetWindowTitle(title); + devtools_window_->UpdateWindowTitle(); + } +} + +void InspectableWebContentsViewViews::Layout() { + if (!devtools_web_view_->visible()) { + contents_web_view_->SetBoundsRect(GetContentsBounds()); + return; + } + + gfx::Size container_size(width(), height()); + gfx::Rect new_devtools_bounds; + gfx::Rect new_contents_bounds; + ApplyDevToolsContentsResizingStrategy(strategy_, container_size, + &new_devtools_bounds, &new_contents_bounds); + + // DevTools cares about the specific position, so we have to compensate RTL + // layout here. + new_devtools_bounds.set_x(GetMirroredXForRect(new_devtools_bounds)); + new_contents_bounds.set_x(GetMirroredXForRect(new_contents_bounds)); + + devtools_web_view_->SetBoundsRect(new_devtools_bounds); + contents_web_view_->SetBoundsRect(new_contents_bounds); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/views/inspectable_web_contents_view_views.h b/vendor/brightray/browser/views/inspectable_web_contents_view_views.h new file mode 100644 index 0000000000..8205cc5f06 --- /dev/null +++ b/vendor/brightray/browser/views/inspectable_web_contents_view_views.h @@ -0,0 +1,71 @@ +#ifndef BROWSER_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_ +#define BROWSER_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_ + +#include "browser/devtools_contents_resizing_strategy.h" +#include "browser/inspectable_web_contents_view.h" + +#include "base/compiler_specific.h" +#include "ui/views/view.h" + +namespace views { +class WebView; +class Widget; +} + +namespace brightray { + +namespace { +class DevToolsWindowDelegate; +} + +class InspectableWebContentsImpl; + +class InspectableWebContentsViewViews : public InspectableWebContentsView, + public views::View { + public: + explicit InspectableWebContentsViewViews( + InspectableWebContentsImpl* inspectable_web_contents_impl); + ~InspectableWebContentsViewViews(); + + DevToolsWindowDelegate* GetDevToolsWindowDelegate() const { + return devtools_window_delegate_; + } + + // InspectableWebContentsView: + views::View* GetView() override; + views::View* GetWebView() override; + void ShowDevTools() override; + void CloseDevTools() override; + bool IsDevToolsViewShowing() override; + bool IsDevToolsViewFocused() override; + void SetIsDocked(bool docked) override; + void SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) override; + void SetTitle(const base::string16& title) override; + + InspectableWebContentsImpl* inspectable_web_contents() { + return inspectable_web_contents_; + } + + private: + // views::View: + void Layout() override; + + // Owns us. + InspectableWebContentsImpl* inspectable_web_contents_; + + std::unique_ptr devtools_window_; + views::WebView* devtools_window_web_view_; + views::View* contents_web_view_; + views::WebView* devtools_web_view_; + + DevToolsContentsResizingStrategy strategy_; + bool devtools_visible_; + DevToolsWindowDelegate* devtools_window_delegate_; + + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsViewViews); +}; + +} // namespace brightray + +#endif // BROWSER_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_ diff --git a/vendor/brightray/browser/views/views_delegate.cc b/vendor/brightray/browser/views/views_delegate.cc new file mode 100644 index 0000000000..a18d52a6ae --- /dev/null +++ b/vendor/brightray/browser/views/views_delegate.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/views/views_delegate.h" + +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/native_widget_aura.h" + +#if defined(OS_LINUX) +#include "ui/views/linux_ui/linux_ui.h" +#endif + +namespace brightray { + +ViewsDelegate::ViewsDelegate() { +} + +ViewsDelegate::~ViewsDelegate() { +} + +void ViewsDelegate::SaveWindowPlacement(const views::Widget* window, + const std::string& window_name, + const gfx::Rect& bounds, + ui::WindowShowState show_state) { +} + +bool ViewsDelegate::GetSavedWindowPlacement( + const views::Widget* widget, + const std::string& window_name, + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + return false; +} + +void ViewsDelegate::NotifyAccessibilityEvent( + views::View* view, ui::AXEvent event_type) { +} + +void ViewsDelegate::NotifyMenuItemFocused( + const base::string16& menu_name, + const base::string16& menu_item_name, + int item_index, + int item_count, + bool has_submenu) { +} + +#if defined(OS_WIN) +HICON ViewsDelegate::GetDefaultWindowIcon() const { + // Use current exe's icon as default window icon. + return LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1 /* IDR_MAINFRAME */)); +} + +HICON ViewsDelegate::GetSmallWindowIcon() const { + return GetDefaultWindowIcon(); +} + +bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const { + return false; +} + +#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) +gfx::ImageSkia* ViewsDelegate::GetDefaultWindowIcon() const { + return NULL; +} +#endif + +views::NonClientFrameView* ViewsDelegate::CreateDefaultNonClientFrameView( + views::Widget* widget) { + return NULL; +} + +void ViewsDelegate::AddRef() { +} + +void ViewsDelegate::ReleaseRef() { +} + +content::WebContents* ViewsDelegate::CreateWebContents( + content::BrowserContext* browser_context, + content::SiteInstance* site_instance) { + return NULL; +} + +void ViewsDelegate::OnBeforeWidgetInit( + views::Widget::InitParams* params, + views::internal::NativeWidgetDelegate* delegate) { + // If we already have a native_widget, we don't have to try to come + // up with one. + if (params->native_widget) + return; + + if (params->parent && + params->type != views::Widget::InitParams::TYPE_MENU && + params->type != views::Widget::InitParams::TYPE_TOOLTIP) { + params->native_widget = new views::NativeWidgetAura(delegate); + } else { + params->native_widget = new views::DesktopNativeWidgetAura(delegate); + } +} + + +base::TimeDelta ViewsDelegate::GetDefaultTextfieldObscuredRevealDuration() { + return base::TimeDelta(); +} + +bool ViewsDelegate::WindowManagerProvidesTitleBar(bool maximized) { +#if defined(OS_LINUX) + // On Ubuntu Unity, the system always provides a title bar for maximized + // windows. + views::LinuxUI* ui = views::LinuxUI::instance(); + return maximized && ui && ui->UnityIsRunning(); +#else + return false; +#endif +} + +} // namespace brightray diff --git a/vendor/brightray/browser/views/views_delegate.h b/vendor/brightray/browser/views/views_delegate.h new file mode 100644 index 0000000000..157660aa5d --- /dev/null +++ b/vendor/brightray/browser/views/views_delegate.h @@ -0,0 +1,65 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_VIEWS_VIEWS_DELEGATE_H_ +#define BRIGHTRAY_BROWSER_VIEWS_VIEWS_DELEGATE_H_ + +#include "base/compiler_specific.h" +#include "ui/views/views_delegate.h" + +namespace brightray { + +class ViewsDelegate : public views::ViewsDelegate { + public: + ViewsDelegate(); + virtual ~ViewsDelegate(); + + protected: + // views::ViewsDelegate: + void SaveWindowPlacement(const views::Widget* window, + const std::string& window_name, + const gfx::Rect& bounds, + ui::WindowShowState show_state) override; + bool GetSavedWindowPlacement( + const views::Widget* widget, + const std::string& window_name, + gfx::Rect* bounds, + ui::WindowShowState* show_state) const override; + void NotifyAccessibilityEvent( + views::View* view, ui::AXEvent event_type) override; + void NotifyMenuItemFocused(const base::string16& menu_name, + const base::string16& menu_item_name, + int item_index, + int item_count, + bool has_submenu) override; + +#if defined(OS_WIN) + HICON GetDefaultWindowIcon() const override; + HICON GetSmallWindowIcon() const override; + bool IsWindowInMetro(gfx::NativeWindow window) const override; +#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) + gfx::ImageSkia* GetDefaultWindowIcon() const override; +#endif + views::NonClientFrameView* CreateDefaultNonClientFrameView( + views::Widget* widget) override; + void AddRef() override; + void ReleaseRef() override; + content::WebContents* CreateWebContents( + content::BrowserContext* browser_context, + content::SiteInstance* site_instance) override; + void OnBeforeWidgetInit( + views::Widget::InitParams* params, + views::internal::NativeWidgetDelegate* delegate) override; + base::TimeDelta GetDefaultTextfieldObscuredRevealDuration() override; + bool WindowManagerProvidesTitleBar(bool maximized) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ViewsDelegate); +}; + + + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_VIEWS_VIEWS_DELEGATE_H_ diff --git a/vendor/brightray/browser/web_ui_controller_factory.cc b/vendor/brightray/browser/web_ui_controller_factory.cc new file mode 100644 index 0000000000..c59e5bb0c6 --- /dev/null +++ b/vendor/brightray/browser/web_ui_controller_factory.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/web_ui_controller_factory.h" + +#include "browser/devtools_ui.h" +#include "base/memory/singleton.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" +#include "content/public/common/url_constants.h" + +namespace brightray { + +namespace { + +const char kChromeUIDevToolsBundledHost[] = "devtools"; + +} + +// static +WebUIControllerFactory* WebUIControllerFactory::GetInstance() { + return base::Singleton::get(); +} + +WebUIControllerFactory::WebUIControllerFactory() { +} + +WebUIControllerFactory::~WebUIControllerFactory() { +} + +content::WebUI::TypeID WebUIControllerFactory::GetWebUIType( + content::BrowserContext* browser_context, const GURL& url) const { + if (url.host() == kChromeUIDevToolsBundledHost) { + return const_cast(this); + } + + return content::WebUI::kNoWebUI; +} + +bool WebUIControllerFactory::UseWebUIForURL( + content::BrowserContext* browser_context, const GURL& url) const { + return GetWebUIType(browser_context, url) != content::WebUI::kNoWebUI; +} + +bool WebUIControllerFactory::UseWebUIBindingsForURL( + content::BrowserContext* browser_context, const GURL& url) const { + return UseWebUIForURL(browser_context, url); +} + +content::WebUIController* WebUIControllerFactory::CreateWebUIControllerForURL( + content::WebUI* web_ui, const GURL& url) const { + if (url.host() == kChromeUIDevToolsBundledHost) { + auto browser_context = web_ui->GetWebContents()->GetBrowserContext(); + return new DevToolsUI(browser_context, web_ui); + } + return nullptr; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/web_ui_controller_factory.h b/vendor/brightray/browser/web_ui_controller_factory.h new file mode 100644 index 0000000000..ca86de6baa --- /dev/null +++ b/vendor/brightray/browser/web_ui_controller_factory.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_WEB_UI_CONTROLLER_FACTORY_H_ +#define BRIGHTRAY_BROWSER_WEB_UI_CONTROLLER_FACTORY_H_ + +#include "base/macros.h" +#include "content/public/browser/web_ui.h" +#include "content/public/browser/web_ui_controller_factory.h" + +namespace base { +template struct DefaultSingletonTraits; +} + +namespace brightray { + +class BrowserContext; + +class WebUIControllerFactory : public content::WebUIControllerFactory { + public: + static WebUIControllerFactory* GetInstance(); + + WebUIControllerFactory(); + virtual ~WebUIControllerFactory(); + + content::WebUI::TypeID GetWebUIType( + content::BrowserContext* browser_context, const GURL& url) const override; + bool UseWebUIForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + bool UseWebUIBindingsForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + content::WebUIController* CreateWebUIControllerForURL( + content::WebUI* web_ui, + const GURL& url) const override; + + private: + friend struct base::DefaultSingletonTraits; + + DISALLOW_COPY_AND_ASSIGN(WebUIControllerFactory); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/browser/win/notification_presenter_win.cc b/vendor/brightray/browser/win/notification_presenter_win.cc new file mode 100644 index 0000000000..107b62ea76 --- /dev/null +++ b/vendor/brightray/browser/win/notification_presenter_win.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/win/notification_presenter_win.h" + +#include "base/files/file_util.h" +#include "base/md5.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/windows_version.h" +#include "browser/win/windows_toast_notification.h" +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/common/platform_notification_data.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" + +#pragma comment(lib, "runtimeobject.lib") + +namespace brightray { + +namespace { + +bool SaveIconToPath(const SkBitmap& bitmap, const base::FilePath& path) { + std::vector png_data; + if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data)) + return false; + + char* data = reinterpret_cast(&png_data[0]); + int size = static_cast(png_data.size()); + return base::WriteFile(path, data, size) == size; +} + +} // namespace + +// static +NotificationPresenter* NotificationPresenter::Create() { + if (!WindowsToastNotification::Initialize()) + return nullptr; + std::unique_ptr presenter(new NotificationPresenterWin); + if (!presenter->Init()) + return nullptr; + return presenter.release(); +} + +NotificationPresenterWin::NotificationPresenterWin() { +} + +NotificationPresenterWin::~NotificationPresenterWin() { +} + +bool NotificationPresenterWin::Init() { + return temp_dir_.CreateUniqueTempDir(); +} + +base::string16 NotificationPresenterWin::SaveIconToFilesystem( + const SkBitmap& icon, const GURL& origin) { + std::string filename = base::MD5String(origin.spec()) + ".png"; + base::FilePath path = temp_dir_.path().Append(base::UTF8ToUTF16(filename)); + if (base::PathExists(path)) + return path.value(); + if (SaveIconToPath(icon, path)) + return path.value(); + return base::UTF8ToUTF16(origin.spec()); +} + +} // namespace brightray diff --git a/vendor/brightray/browser/win/notification_presenter_win.h b/vendor/brightray/browser/win/notification_presenter_win.h new file mode 100644 index 0000000000..7bcf1a9909 --- /dev/null +++ b/vendor/brightray/browser/win/notification_presenter_win.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +// Usage Example (JavaScript: +// var windowsNotification = new Notification("Test Title", { +// body: "Hi, I'm an example body. How are you?", +// icon: "file:///C:/Path/To/Your/Image.png" +// }); + +// windowsNotification.onshow = function () { console.log("Notification shown") }; +// windowsNotification.onclick = function () { console.log("Notification clicked") }; +// windowsNotification.onclose = function () { console.log("Notification dismissed") }; + +#ifndef BRIGHTRAY_BROWSER_WIN_NOTIFICATION_PRESENTER_WIN_H_ +#define BRIGHTRAY_BROWSER_WIN_NOTIFICATION_PRESENTER_WIN_H_ + +#include "base/files/scoped_temp_dir.h" +#include "base/strings/string16.h" +#include "browser/notification_presenter.h" + +class GURL; +class SkBitmap; + +namespace brightray { + +class NotificationPresenterWin : public NotificationPresenter { + public: + NotificationPresenterWin(); + ~NotificationPresenterWin(); + + bool Init(); + + base::string16 SaveIconToFilesystem(const SkBitmap& icon, const GURL& origin); + + private: + base::ScopedTempDir temp_dir_; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterWin); +}; + +} // namespace + +#endif // BRIGHTRAY_BROWSER_WIN_NOTIFICATION_PRESENTER_WIN_H_ diff --git a/vendor/brightray/browser/win/scoped_hstring.cc b/vendor/brightray/browser/win/scoped_hstring.cc new file mode 100644 index 0000000000..082ebe7622 --- /dev/null +++ b/vendor/brightray/browser/win/scoped_hstring.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/win/scoped_hstring.h" + +#include + +ScopedHString::ScopedHString(const wchar_t* source) + : str_(nullptr) { + Reset(source); +} + +ScopedHString::ScopedHString(const std::wstring& source) + : str_(nullptr) { + Reset(source); +} + +ScopedHString::ScopedHString() : str_(nullptr) { +} + +ScopedHString::~ScopedHString() { + Reset(); +} + +void ScopedHString::Reset() { + if (str_) { + WindowsDeleteString(str_); + str_ = nullptr; + } +} + +void ScopedHString::Reset(const wchar_t* source) { + Reset(); + WindowsCreateString(source, wcslen(source), &str_); +} + +void ScopedHString::Reset(const std::wstring& source) { + Reset(); + WindowsCreateString(source.c_str(), source.length(), &str_); +} diff --git a/vendor/brightray/browser/win/scoped_hstring.h b/vendor/brightray/browser/win/scoped_hstring.h new file mode 100644 index 0000000000..67e4fe67fd --- /dev/null +++ b/vendor/brightray/browser/win/scoped_hstring.h @@ -0,0 +1,41 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_WIN_SCOPED_HSTRING_H_ +#define BRIGHTRAY_BROWSER_WIN_SCOPED_HSTRING_H_ + +#include + +#include +#include + +#include "base/macros.h" + +class ScopedHString { + public: + // Copy from |source|. + ScopedHString(const wchar_t* source); + ScopedHString(const std::wstring& source); + // Create empty string. + ScopedHString(); + ~ScopedHString(); + + // Sets to |source|. + void Reset(); + void Reset(const wchar_t* source); + void Reset(const std::wstring& source); + + // Returns string. + operator HSTRING() const { return str_; } + + // Whether there is a string created. + bool success() const { return str_; } + + private: + HSTRING str_; + + DISALLOW_COPY_AND_ASSIGN(ScopedHString); +}; + +#endif // BRIGHTRAY_BROWSER_WIN_SCOPED_HSTRING_H_ diff --git a/vendor/brightray/browser/win/windows_toast_notification.cc b/vendor/brightray/browser/win/windows_toast_notification.cc new file mode 100644 index 0000000000..4d4b5bfc34 --- /dev/null +++ b/vendor/brightray/browser/win/windows_toast_notification.cc @@ -0,0 +1,412 @@ +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// Thanks to both of those folks mentioned above who first thought up a bunch of this code +// and released it as MIT to the world. + +#include "browser/win/windows_toast_notification.h" + +#include + +#include "base/strings/utf_string_conversions.h" +#include "browser/notification_delegate.h" +#include "browser/win/scoped_hstring.h" +#include "browser/win/notification_presenter_win.h" +#include "common/application_info.h" +#include "content/public/browser/browser_thread.h" + +using namespace ABI::Windows::Data::Xml::Dom; + +namespace brightray { + +namespace { + +bool GetAppUserModelId(ScopedHString* app_id) { + PWSTR current_app_id; + if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(¤t_app_id))) { + app_id->Reset(current_app_id); + CoTaskMemFree(current_app_id); + } else { + app_id->Reset(base::UTF8ToUTF16(GetApplicationName())); + } + return app_id->success(); +} + +} // namespace + +// static +Notification* Notification::Create(NotificationDelegate* delegate, + NotificationPresenter* presenter) { + return new WindowsToastNotification(delegate, presenter); +} + +// static +ComPtr + WindowsToastNotification::toast_manager_; + +// static +ComPtr + WindowsToastNotification::toast_notifier_; + +// static +bool WindowsToastNotification::Initialize() { + // Just initialize, don't care if it fails or already initialized. + Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + + ScopedHString toast_manager_str( + RuntimeClass_Windows_UI_Notifications_ToastNotificationManager); + if (!toast_manager_str.success()) + return false; + if (FAILED(Windows::Foundation::GetActivationFactory(toast_manager_str, + &toast_manager_))) + return false; + + ScopedHString app_id; + if (!GetAppUserModelId(&app_id)) + return false; + + return SUCCEEDED( + toast_manager_->CreateToastNotifierWithId(app_id, &toast_notifier_)); +} + +WindowsToastNotification::WindowsToastNotification( + NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter) { +} + +WindowsToastNotification::~WindowsToastNotification() { + // Remove the notification on exit. + if (toast_notification_) { + RemoveCallbacks(toast_notification_.Get()); + Dismiss(); + } +} + +void WindowsToastNotification::Show( + const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) { + auto presenter_win = static_cast(presenter()); + std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url); + + ComPtr toast_xml; + if(FAILED(GetToastXml(toast_manager_.Get(), title, msg, icon_path, silent, &toast_xml))) { + NotificationFailed(); + return; + } + + ScopedHString toast_str( + RuntimeClass_Windows_UI_Notifications_ToastNotification); + if (!toast_str.success()) { + NotificationFailed(); + return; + } + + ComPtr toast_factory; + if (FAILED(Windows::Foundation::GetActivationFactory(toast_str, + &toast_factory))) { + NotificationFailed(); + return; + } + + if (FAILED(toast_factory->CreateToastNotification(toast_xml.Get(), + &toast_notification_))) { + NotificationFailed(); + return; + } + + if (!SetupCallbacks(toast_notification_.Get())) { + NotificationFailed(); + return; + } + + if (FAILED(toast_notifier_->Show(toast_notification_.Get()))) { + NotificationFailed(); + return; + } + + delegate()->NotificationDisplayed(); +} + +void WindowsToastNotification::Dismiss() { + toast_notifier_->Hide(toast_notification_.Get()); +} + +bool WindowsToastNotification::GetToastXml( + ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* toastManager, + const std::wstring& title, + const std::wstring& msg, + const std::wstring& icon_path, + const bool silent, + IXmlDocument** toast_xml) { + ABI::Windows::UI::Notifications::ToastTemplateType template_type; + if (title.empty() || msg.empty()) { + // Single line toast. + template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01 : + ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText01; + if (FAILED(toast_manager_->GetTemplateContent(template_type, toast_xml))) + return false; + if (!SetXmlText(*toast_xml, title.empty() ? msg : title)) + return false; + } else { + // Title and body toast. + template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02 : + ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText02; + if (FAILED(toastManager->GetTemplateContent(template_type, toast_xml))) + return false; + if (!SetXmlText(*toast_xml, title, msg)) + return false; + } + + // Configure the toast's notification sound + if (silent) { + if (FAILED(SetXmlAudioSilent(*toast_xml))) + return false; + } + + // Configure the toast's image + if (!icon_path.empty()) + return SetXmlImage(*toast_xml, icon_path); + + return true; +} + +bool WindowsToastNotification::SetXmlAudioSilent( + IXmlDocument* doc) { + ScopedHString tag(L"toast"); + if (!tag.success()) + return false; + + ComPtr node_list; + if (FAILED(doc->GetElementsByTagName(tag, &node_list))) + return false; + + ComPtr root; + if (FAILED(node_list->Item(0, &root))) + return false; + + ComPtr audio_element; + ScopedHString audio_str(L"audio"); + if (FAILED(doc->CreateElement(audio_str, &audio_element))) + return false; + + ComPtr audio_node_tmp; + if (FAILED(audio_element.As(&audio_node_tmp))) + return false; + + // Append audio node to toast xml + ComPtr audio_node; + if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node))) + return false; + + // Create silent attribute + ComPtr attributes; + if (FAILED(audio_node->get_Attributes(&attributes))) + return false; + + ComPtr silent_attribute; + ScopedHString silent_str(L"silent"); + if (FAILED(doc->CreateAttribute(silent_str, &silent_attribute))) + return false; + + ComPtr silent_attribute_node; + if (FAILED(silent_attribute.As(&silent_attribute_node))) + return false; + + // Set silent attribute to true + ScopedHString silent_value(L"true"); + if (!silent_value.success()) + return false; + + ComPtr silent_text; + if (FAILED(doc->CreateTextNode(silent_value, &silent_text))) + return false; + + ComPtr silent_node; + if (FAILED(silent_text.As(&silent_node))) + return false; + + ComPtr child_node; + if (FAILED(silent_attribute_node->AppendChild(silent_node.Get(), &child_node))) + return false; + + ComPtr silent_attribute_pnode; + return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), &silent_attribute_pnode)); +} + +bool WindowsToastNotification::SetXmlText( + IXmlDocument* doc, const std::wstring& text) { + ScopedHString tag; + ComPtr node_list; + if (!GetTextNodeList(&tag, doc, &node_list, 1)) + return false; + + ComPtr node; + if (FAILED(node_list->Item(0, &node))) + return false; + + return AppendTextToXml(doc, node.Get(), text); +} + +bool WindowsToastNotification::SetXmlText( + IXmlDocument* doc, const std::wstring& title, const std::wstring& body) { + ScopedHString tag; + ComPtr node_list; + if (!GetTextNodeList(&tag, doc, &node_list, 2)) + return false; + + ComPtr node; + if (FAILED(node_list->Item(0, &node))) + return false; + + if (!AppendTextToXml(doc, node.Get(), title)) + return false; + + if (FAILED(node_list->Item(1, &node))) + return false; + + return AppendTextToXml(doc, node.Get(), body); +} + +bool WindowsToastNotification::SetXmlImage( + IXmlDocument* doc, const std::wstring& icon_path) { + ScopedHString tag(L"image"); + if (!tag.success()) + return false; + + ComPtr node_list; + if (FAILED(doc->GetElementsByTagName(tag, &node_list))) + return false; + + ComPtr image_node; + if (FAILED(node_list->Item(0, &image_node))) + return false; + + ComPtr attrs; + if (FAILED(image_node->get_Attributes(&attrs))) + return false; + + ScopedHString src(L"src"); + if (!src.success()) + return false; + + ComPtr src_attr; + if (FAILED(attrs->GetNamedItem(src, &src_attr))) + return false; + + ScopedHString img_path(icon_path.c_str()); + if (!img_path.success()) + return false; + + ComPtr src_text; + if (FAILED(doc->CreateTextNode(img_path, &src_text))) + return false; + + ComPtr src_node; + if (FAILED(src_text.As(&src_node))) + return false; + + ComPtr child_node; + return SUCCEEDED(src_attr->AppendChild(src_node.Get(), &child_node)); +} + +bool WindowsToastNotification::GetTextNodeList( + ScopedHString* tag, + IXmlDocument* doc, + IXmlNodeList** node_list, + uint32_t req_length) { + tag->Reset(L"text"); + if (!tag->success()) + return false; + + if (FAILED(doc->GetElementsByTagName(*tag, node_list))) + return false; + + uint32_t node_length; + if (FAILED((*node_list)->get_Length(&node_length))) + return false; + + return node_length >= req_length; +} + +bool WindowsToastNotification::AppendTextToXml( + IXmlDocument* doc, IXmlNode* node, const std::wstring& text) { + ScopedHString str(text); + if (!str.success()) + return false; + + ComPtr xml_text; + if (FAILED(doc->CreateTextNode(str, &xml_text))) + return false; + + ComPtr text_node; + if (FAILED(xml_text.As(&text_node))) + return false; + + ComPtr append_node; + return SUCCEEDED(node->AppendChild(text_node.Get(), &append_node)); +} + +bool WindowsToastNotification::SetupCallbacks( + ABI::Windows::UI::Notifications::IToastNotification* toast) { + event_handler_ = Make(this); + if (FAILED(toast->add_Activated(event_handler_.Get(), &activated_token_))) + return false; + + if (FAILED(toast->add_Dismissed(event_handler_.Get(), &dismissed_token_))) + return false; + + return SUCCEEDED(toast->add_Failed(event_handler_.Get(), &failed_token_)); +} + +bool WindowsToastNotification::RemoveCallbacks( + ABI::Windows::UI::Notifications::IToastNotification* toast) { + if (FAILED(toast->remove_Activated(activated_token_))) + return false; + + if (FAILED(toast->remove_Dismissed(dismissed_token_))) + return false; + + return SUCCEEDED(toast->remove_Failed(failed_token_)); +} + +/* +/ Toast Event Handler +*/ +ToastEventHandler::ToastEventHandler(Notification* notification) + : notification_(notification->GetWeakPtr()) { +} + +ToastEventHandler::~ToastEventHandler() { +} + +IFACEMETHODIMP ToastEventHandler::Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, IInspectable* args) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Notification::NotificationClicked, notification_)); + return S_OK; +} + +IFACEMETHODIMP ToastEventHandler::Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Notification::NotificationDismissed, notification_)); + return S_OK; +} + +IFACEMETHODIMP ToastEventHandler::Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Notification::NotificationFailed, notification_)); + return S_OK; +} + +} // namespace brightray diff --git a/vendor/brightray/browser/win/windows_toast_notification.h b/vendor/brightray/browser/win/windows_toast_notification.h new file mode 100644 index 0000000000..fcbe78faa4 --- /dev/null +++ b/vendor/brightray/browser/win/windows_toast_notification.h @@ -0,0 +1,111 @@ +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// Thanks to both of those folks mentioned above who first thought up a bunch of this code +// and released it as MIT to the world. + +#ifndef BRIGHTRAY_BROWSER_WIN_WINDOWS_TOAST_NOTIFICATION_H_ +#define BRIGHTRAY_BROWSER_WIN_WINDOWS_TOAST_NOTIFICATION_H_ + +#include +#include +#include + +#include "browser/notification.h" + +using namespace Microsoft::WRL; + +class ScopedHString; + +namespace brightray { + +using DesktopToastActivatedEventHandler = + ABI::Windows::Foundation::ITypedEventHandler; +using DesktopToastDismissedEventHandler = + ABI::Windows::Foundation::ITypedEventHandler; +using DesktopToastFailedEventHandler = + ABI::Windows::Foundation::ITypedEventHandler; + +class WindowsToastNotification : public Notification { + public: + // Should only be called by NotificationPresenterWin. + static bool Initialize(); + + WindowsToastNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + ~WindowsToastNotification(); + + protected: + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) override; + void Dismiss() override; + + private: + friend class ToastEventHandler; + + bool GetToastXml(ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* toastManager, + const std::wstring& title, + const std::wstring& msg, + const std::wstring& icon_path, + const bool silent, + ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml); + bool SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc); + bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + const std::wstring& text); + bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + const std::wstring& title, + const std::wstring& body); + bool SetXmlImage(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + const std::wstring& icon_path); + bool GetTextNodeList(ScopedHString* tag, + ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + ABI::Windows::Data::Xml::Dom::IXmlNodeList** nodeList, + uint32_t reqLength); + bool AppendTextToXml(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + ABI::Windows::Data::Xml::Dom::IXmlNode* node, + const std::wstring& text); + bool SetupCallbacks(ABI::Windows::UI::Notifications::IToastNotification* toast); + bool RemoveCallbacks(ABI::Windows::UI::Notifications::IToastNotification* toast); + + static ComPtr toast_manager_; + static ComPtr toast_notifier_; + + EventRegistrationToken activated_token_; + EventRegistrationToken dismissed_token_; + EventRegistrationToken failed_token_; + + ComPtr event_handler_; + ComPtr toast_notification_; + + DISALLOW_COPY_AND_ASSIGN(WindowsToastNotification); +}; + + +class ToastEventHandler : public RuntimeClass, + DesktopToastActivatedEventHandler, + DesktopToastDismissedEventHandler, + DesktopToastFailedEventHandler> { + public: + ToastEventHandler(Notification* notification); + ~ToastEventHandler(); + + IFACEMETHODIMP Invoke(ABI::Windows::UI::Notifications::IToastNotification* sender, IInspectable* args); + IFACEMETHODIMP Invoke(ABI::Windows::UI::Notifications::IToastNotification* sender, ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e); + IFACEMETHODIMP Invoke(ABI::Windows::UI::Notifications::IToastNotification* sender, ABI::Windows::UI::Notifications::IToastFailedEventArgs* e); + + private: + base::WeakPtr notification_; // weak ref. + + DISALLOW_COPY_AND_ASSIGN(ToastEventHandler); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_WIN_WINDOWS_TOAST_NOTIFICATION_H_ diff --git a/vendor/brightray/common/application_info.h b/vendor/brightray/common/application_info.h new file mode 100644 index 0000000000..25b02c0d23 --- /dev/null +++ b/vendor/brightray/common/application_info.h @@ -0,0 +1,13 @@ +#ifndef BRIGHTRAY_COMMON_APPLICATION_INFO_H_ +#define BRIGHTRAY_COMMON_APPLICATION_INFO_H_ + +#include + +namespace brightray { + +std::string GetApplicationName(); +std::string GetApplicationVersion(); + +} + +#endif diff --git a/vendor/brightray/common/application_info_mac.mm b/vendor/brightray/common/application_info_mac.mm new file mode 100644 index 0000000000..ee9f9bbf4c --- /dev/null +++ b/vendor/brightray/common/application_info_mac.mm @@ -0,0 +1,30 @@ +#import "common/application_info.h" + +#import "common/mac/foundation_util.h" +#import "common/mac/main_application_bundle.h" + +#import "base/strings/sys_string_conversions.h" + +namespace brightray { + +namespace { + +std::string ApplicationInfoDictionaryValue(NSString* key) { + return base::SysNSStringToUTF8([MainApplicationBundle().infoDictionary objectForKey:key]); +} + +std::string ApplicationInfoDictionaryValue(CFStringRef key) { + return ApplicationInfoDictionaryValue(base::mac::CFToNSCast(key)); +} + +} + +std::string GetApplicationName() { + return ApplicationInfoDictionaryValue(kCFBundleNameKey); +} + +std::string GetApplicationVersion() { + return ApplicationInfoDictionaryValue(@"CFBundleShortVersionString"); +} + +} diff --git a/vendor/brightray/common/application_info_win.cc b/vendor/brightray/common/application_info_win.cc new file mode 100644 index 0000000000..22635711cb --- /dev/null +++ b/vendor/brightray/common/application_info_win.cc @@ -0,0 +1,24 @@ +#include + +#include "common/application_info.h" + +#include "base/file_version_info.h" +#include "base/strings/utf_string_conversions.h" + +namespace brightray { + +std::string GetApplicationName() { + auto module = GetModuleHandle(nullptr); + std::unique_ptr info( + FileVersionInfo::CreateFileVersionInfoForModule(module)); + return base::UTF16ToUTF8(info->product_name()); +} + +std::string GetApplicationVersion() { + auto module = GetModuleHandle(nullptr); + std::unique_ptr info( + FileVersionInfo::CreateFileVersionInfoForModule(module)); + return base::UTF16ToUTF8(info->product_version()); +} + +} // namespace brightray diff --git a/vendor/brightray/common/content_client.cc b/vendor/brightray/common/content_client.cc new file mode 100644 index 0000000000..ebe066be61 --- /dev/null +++ b/vendor/brightray/common/content_client.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "common/content_client.h" + +#include "common/application_info.h" + +#include "base/strings/stringprintf.h" +#include "base/strings/string_util.h" +#include "content/public/common/user_agent.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace brightray { + +std::string GetProductInternal() { + auto name = GetApplicationName(); + base::RemoveChars(name, base::kWhitespaceASCII, &name); + return base::StringPrintf("%s/%s", + name.c_str(), GetApplicationVersion().c_str()); +} + +std::string GetBrightrayUserAgent() { + return content::BuildUserAgentFromProduct(GetProductInternal()); +} + +ContentClient::ContentClient() { +} + +ContentClient::~ContentClient() { +} + +std::string ContentClient::GetProduct() const { + return GetProductInternal(); +} + +std::string ContentClient::GetUserAgent() const { + return GetBrightrayUserAgent(); +} + +base::string16 ContentClient::GetLocalizedString(int message_id) const { + return l10n_util::GetStringUTF16(message_id); +} + +base::StringPiece ContentClient::GetDataResource( + int resource_id, ui::ScaleFactor scale_factor) const { + return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale( + resource_id, scale_factor); +} + +gfx::Image& ContentClient::GetNativeImageNamed(int resource_id) const { + return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( + resource_id); +} + +base::RefCountedMemory* ContentClient::GetDataResourceBytes( + int resource_id) const { + return ResourceBundle::GetSharedInstance().LoadDataResourceBytes(resource_id); +} + +} // namespace brightray diff --git a/vendor/brightray/common/content_client.h b/vendor/brightray/common/content_client.h new file mode 100644 index 0000000000..fda90d0ac7 --- /dev/null +++ b/vendor/brightray/common/content_client.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_COMMON_CONTENT_CLIENT_H_ +#define BRIGHTRAY_COMMON_CONTENT_CLIENT_H_ + +#include "base/compiler_specific.h" +#include "content/public/common/content_client.h" + +namespace brightray { + +std::string GetBrightrayUserAgent(); + +class ContentClient : public content::ContentClient { + public: + ContentClient(); + ~ContentClient() override; + + private: + std::string GetProduct() const override; + std::string GetUserAgent() const override; + base::string16 GetLocalizedString(int message_id) const override; + base::StringPiece GetDataResource(int resource_id, + ui::ScaleFactor) const override; + gfx::Image& GetNativeImageNamed(int resource_id) const override; + base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; + + DISALLOW_COPY_AND_ASSIGN(ContentClient); +}; + +} // namespace brightray + +#endif diff --git a/vendor/brightray/common/mac/foundation_util.h b/vendor/brightray/common/mac/foundation_util.h new file mode 100644 index 0000000000..3b7e6a66a9 --- /dev/null +++ b/vendor/brightray/common/mac/foundation_util.h @@ -0,0 +1,15 @@ +#ifndef BRIGHTRAY_COMMON_MAC_FOUNDATION_UTIL_H_ +#define BRIGHTRAY_COMMON_MAC_FOUNDATION_UTIL_H_ + +// This header exists to work around incompatibilities between +// base/mac/foundation_util.h and the 10.8 SDK. + +#import + +// base/mac/foundation_util.h contains an incompatible declaration of +// NSSearchPathDirectory, so here we #define it to be something else. +#define NSSearchPathDirectory NSSearchPathDirectory___PRE_10_8 +#import "base/mac/foundation_util.h" +#undef NSSearchPathDirectory + +#endif diff --git a/vendor/brightray/common/mac/main_application_bundle.h b/vendor/brightray/common/mac/main_application_bundle.h new file mode 100644 index 0000000000..78e6bc200b --- /dev/null +++ b/vendor/brightray/common/mac/main_application_bundle.h @@ -0,0 +1,21 @@ +#ifndef BRIGHTRAY_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_ +#define BRIGHTRAY_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_ + +@class NSBundle; + +namespace base { +class FilePath; +} + +namespace brightray { + +// The "main" application bundle is the outermost bundle for this logical +// application. E.g., if you have MyApp.app and +// MyApp.app/Contents/Frameworks/MyApp Helper.app, the main application bundle +// is MyApp.app, no matter which executable is currently running. +NSBundle* MainApplicationBundle(); +base::FilePath MainApplicationBundlePath(); + +} // namespace brightray + +#endif diff --git a/vendor/brightray/common/mac/main_application_bundle.mm b/vendor/brightray/common/mac/main_application_bundle.mm new file mode 100644 index 0000000000..3e386b40dc --- /dev/null +++ b/vendor/brightray/common/mac/main_application_bundle.mm @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#import "common/mac/main_application_bundle.h" + +#include "base/files/file_path.h" +#include "base/mac/bundle_locations.h" +#include "base/mac/foundation_util.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" + +namespace brightray { + +namespace { + +bool HasMainProcessKey() { + NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary]; + return [[info_dictionary objectForKey:@"ElectronMainProcess"] boolValue] != NO; +} + +} // namespace + +base::FilePath MainApplicationBundlePath() { + // Start out with the path to the running executable. + base::FilePath path; + PathService::Get(base::FILE_EXE, &path); + + // Up to Contents. + if (!HasMainProcessKey() && + base::EndsWith(path.value(), " Helper", base::CompareCase::SENSITIVE)) { + // The running executable is the helper. Go up five steps: + // Contents/Frameworks/Helper.app/Contents/MacOS/Helper + // ^ to here ^ from here + path = path.DirName().DirName().DirName().DirName().DirName(); + } else { + // One step up to MacOS, another to Contents. + path = path.DirName().DirName(); + } + DCHECK_EQ(path.BaseName().value(), "Contents"); + + // Up one more level to the .app. + path = path.DirName(); + DCHECK_EQ(path.BaseName().Extension(), ".app"); + + return path; +} + +NSBundle* MainApplicationBundle() { + return [NSBundle bundleWithPath:base::mac::FilePathToNSString(MainApplicationBundlePath())]; +} + +} // namespace brightray diff --git a/vendor/brightray/common/main_delegate.cc b/vendor/brightray/common/main_delegate.cc new file mode 100644 index 0000000000..2ab29d9367 --- /dev/null +++ b/vendor/brightray/common/main_delegate.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "common/main_delegate.h" + +#include + +#include "browser/browser_client.h" +#include "common/content_client.h" + +#include "base/command_line.h" +#include "base/path_service.h" +#include "content/public/common/content_switches.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_switches.h" + +namespace brightray { + +namespace { + +// Returns true if this subprocess type needs the ResourceBundle initialized +// and resources loaded. +bool SubprocessNeedsResourceBundle(const std::string& process_type) { + return +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // The zygote process opens the resources for the renderers. + process_type == switches::kZygoteProcess || +#endif +#if defined(OS_MACOSX) + // Mac needs them too for scrollbar related images and for sandbox + // profiles. +#if !defined(DISABLE_NACL) + process_type == switches::kNaClLoaderProcess || +#endif + process_type == switches::kPpapiPluginProcess || + process_type == switches::kPpapiBrokerProcess || + process_type == switches::kGpuProcess || +#endif + process_type == switches::kRendererProcess || + process_type == switches::kUtilityProcess; +} + +} // namespace + +void InitializeResourceBundle(const std::string& locale) { + // Load locales. + ui::ResourceBundle::InitSharedInstanceWithLocale( + locale, nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES); + + // Load other resource files. + base::FilePath path; +#if defined(OS_MACOSX) + path = GetResourcesPakFilePath(); +#else + base::FilePath pak_dir; + PathService::Get(base::DIR_MODULE, &pak_dir); + path = pak_dir.Append(FILE_PATH_LITERAL("electron_resources.pak")); +#endif + + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + bundle.AddDataPackFromPath(path, ui::SCALE_FACTOR_NONE); +#if !defined(OS_MACOSX) + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("chrome_100_percent.pak")), + ui::SCALE_FACTOR_100P); + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("chrome_200_percent.pak")), + ui::SCALE_FACTOR_200P); +#endif +} + + +MainDelegate::MainDelegate() { +} + +MainDelegate::~MainDelegate() { +} + +std::unique_ptr MainDelegate::CreateContentClient() { + return std::unique_ptr(new ContentClient); +} + +bool MainDelegate::BasicStartupComplete(int* exit_code) { + content_client_ = CreateContentClient(); + SetContentClient(content_client_.get()); +#if defined(OS_MACOSX) + OverrideChildProcessPath(); + OverrideFrameworkBundlePath(); +#endif + return false; +} + +void MainDelegate::PreSandboxStartup() { + auto cmd = *base::CommandLine::ForCurrentProcess(); + std::string process_type = cmd.GetSwitchValueASCII(switches::kProcessType); + + // Initialize ResourceBundle which handles files loaded from external + // sources. The language should have been passed in to us from the + // browser process as a command line flag. + if (SubprocessNeedsResourceBundle(process_type)) { + std::string locale = cmd.GetSwitchValueASCII(switches::kLang); + InitializeResourceBundle(locale); + } +} + +void MainDelegate::ProcessExiting(const std::string& process_type) { + if (SubprocessNeedsResourceBundle(process_type)) + ResourceBundle::CleanupSharedInstance(); +} + +content::ContentBrowserClient* MainDelegate::CreateContentBrowserClient() { + browser_client_ = CreateBrowserClient(); + return browser_client_.get(); +} + +std::unique_ptr MainDelegate::CreateBrowserClient() { + return std::unique_ptr(new BrowserClient); +} + +} // namespace brightray diff --git a/vendor/brightray/common/main_delegate.h b/vendor/brightray/common/main_delegate.h new file mode 100644 index 0000000000..276c376f9b --- /dev/null +++ b/vendor/brightray/common/main_delegate.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_COMMON_MAIN_DELEGATE_H_ +#define BRIGHTRAY_COMMON_MAIN_DELEGATE_H_ + +#include + +#include "base/macros.h" +#include "content/public/app/content_main_delegate.h" + +namespace base { +class FilePath; +} + +namespace ui { +class ResourceBundle; +} + +namespace brightray { + +class BrowserClient; +class ContentClient; + +void InitializeResourceBundle(const std::string& locale); +base::FilePath GetResourcesPakFilePath(); + +class MainDelegate : public content::ContentMainDelegate { + public: + MainDelegate(); + ~MainDelegate() override; + + protected: + // Subclasses can override this to provide their own ContentClient + // implementation. + virtual std::unique_ptr CreateContentClient(); + + // Subclasses can override this to provide their own BrowserClient + // implementation. + virtual std::unique_ptr CreateBrowserClient(); + +#if defined(OS_MACOSX) + // Subclasses can override this to custom the paths of child process and + // framework bundle. + virtual void OverrideChildProcessPath(); + virtual void OverrideFrameworkBundlePath(); +#endif + + bool BasicStartupComplete(int* exit_code) override; + void PreSandboxStartup() override; + void ProcessExiting(const std::string& process_type) override; + private: + content::ContentBrowserClient* CreateContentBrowserClient() override; + + std::unique_ptr content_client_; + std::unique_ptr browser_client_; + + DISALLOW_COPY_AND_ASSIGN(MainDelegate); +}; + +} // namespace brightray +#endif diff --git a/vendor/brightray/common/main_delegate_mac.mm b/vendor/brightray/common/main_delegate_mac.mm new file mode 100644 index 0000000000..383818605a --- /dev/null +++ b/vendor/brightray/common/main_delegate_mac.mm @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#import "main_delegate.h" + +#include "common/application_info.h" +#include "common/mac/foundation_util.h" +#include "common/mac/main_application_bundle.h" + +#include "base/command_line.h" +#include "base/mac/bundle_locations.h" +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "content/public/common/content_paths.h" +#include "content/public/common/content_switches.h" + +namespace brightray { + +namespace { + +base::FilePath GetFrameworksPath() { + return MainApplicationBundlePath().Append("Contents").Append("Frameworks"); +} + +} + +base::FilePath GetResourcesPakFilePath() { + auto path = [base::mac::FrameworkBundle() pathForResource:@"electron_resources" ofType:@"pak"]; + return base::mac::NSStringToFilePath(path); +} + +void MainDelegate::OverrideFrameworkBundlePath() { + base::FilePath helper_path = + GetFrameworksPath().Append(GetApplicationName() + " Framework.framework"); + base::mac::SetOverrideFrameworkBundlePath(helper_path); +} + +void MainDelegate::OverrideChildProcessPath() { + base::FilePath helper_path = GetFrameworksPath().Append(GetApplicationName() + " Helper.app") + .Append("Contents") + .Append("MacOS") + .Append(GetApplicationName() + " Helper"); + + PathService::Override(content::CHILD_PROCESS_EXE, helper_path); +} + +} diff --git a/vendor/brightray/common/switches.cc b/vendor/brightray/common/switches.cc new file mode 100644 index 0000000000..c46fe3bda9 --- /dev/null +++ b/vendor/brightray/common/switches.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "common/switches.h" + +namespace brightray { + +namespace switches { + +// Comma-separated list of rules that control how hostnames are mapped. +// +// For example: +// "MAP * 127.0.0.1" --> Forces all hostnames to be mapped to 127.0.0.1 +// "MAP *.google.com proxy" --> Forces all google.com subdomains to be +// resolved to "proxy". +// "MAP test.com [::1]:77 --> Forces "test.com" to resolve to IPv6 loopback. +// Will also force the port of the resulting +// socket address to be 77. +// "MAP * baz, EXCLUDE www.google.com" --> Remaps everything to "baz", +// except for "www.google.com". +// +// These mappings apply to the endpoint host in a net::URLRequest (the TCP +// connect and host resolver in a direct connection, and the CONNECT in an http +// proxy connection, and the endpoint host in a SOCKS proxy connection). +const char kHostRules[] = "host-rules"; + +// Don't use a proxy server, always make direct connections. Overrides any +// other proxy server flags that are passed. +const char kNoProxyServer[] = "no-proxy-server"; + +// Uses a specified proxy server, overrides system settings. This switch only +// affects HTTP and HTTPS requests. +const char kProxyServer[] = "proxy-server"; + +// Bypass specified proxy for the given semi-colon-separated list of hosts. This +// flag has an effect only when --proxy-server is set. +const char kProxyBypassList[] = "proxy-bypass-list"; + +// Uses the pac script at the given URL. +const char kProxyPacUrl[] = "proxy-pac-url"; + +// Disable HTTP/2 and SPDY/3.1 protocols. +const char kDisableHttp2[] = "disable-http2"; + +// Whitelist containing servers for which Integrated Authentication is enabled. +const char kAuthServerWhitelist[] = "auth-server-whitelist"; + +// Whitelist containing servers for which Kerberos delegation is allowed. +const char kAuthNegotiateDelegateWhitelist[] = "auth-negotiate-delegate-whitelist"; + +// Ignores certificate-related errors. +const char kIgnoreCertificateErrors[] = "ignore-certificate-errors"; + +} // namespace switches + +} // namespace brightray diff --git a/vendor/brightray/common/switches.h b/vendor/brightray/common/switches.h new file mode 100644 index 0000000000..3aad50656b --- /dev/null +++ b/vendor/brightray/common/switches.h @@ -0,0 +1,26 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BRIGHTRAY_COMMON_SWITCHES_H_ +#define BRIGHTRAY_COMMON_SWITCHES_H_ + +namespace brightray { + +namespace switches { + +extern const char kHostRules[]; +extern const char kNoProxyServer[]; +extern const char kProxyServer[]; +extern const char kProxyBypassList[]; +extern const char kProxyPacUrl[]; +extern const char kDisableHttp2[]; +extern const char kAuthServerWhitelist[]; +extern const char kAuthNegotiateDelegateWhitelist[]; +extern const char kIgnoreCertificateErrors[]; + +} // namespace switches + +} // namespace brightray + +#endif // BRIGHTRAY_COMMON_SWITCHES_H_ diff --git a/vendor/brightray/script/bootstrap b/vendor/brightray/script/bootstrap new file mode 100755 index 0000000000..e241606c2d --- /dev/null +++ b/vendor/brightray/script/bootstrap @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +import argparse +import errno +import os +import subprocess +import sys + + +SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor') +DOWNLOAD_DIR = os.path.join(VENDOR_DIR, 'download') + + +def main(): + args = parse_args() + if (args.libcc_source_path != None and + args.libcc_shared_library_path != None and + args.libcc_static_library_path != None): + pass + elif (args.libcc_source_path != None or + args.libcc_shared_library_path != None or + args.libcc_static_library_path != None): + print "Error: All options of libchromiumcontent are required OR let " \ + "brightray choose it" + sys.exit(0) + update_submodules() + setup_libchromiumcontent(args.dev, args.commit, args.target_arch, args.url, + args.libcc_source_path, + args.libcc_shared_library_path, + args.libcc_static_library_path) + + +def parse_args(): + parser = argparse.ArgumentParser(description='Bootstrap this project') + parser.add_argument('-c', '--commit', required=True, + help='The commit of libchromiumcontent to download.') + parser.add_argument('-d', '--dev', action='store_true', + help='Do not download static_library build') + parser.add_argument('--target_arch', required=True, + help='The arch of libchromiumcontent to download.') + parser.add_argument('url', help='The base URL from which to download ' + 'libchromiumcontent (i.e., the URL you passed to ' + 'libchromiumcontent\'s script/upload script') + parser.add_argument('--libcc_source_path', required=False, + help='The source path of libchromiumcontent. ' \ + 'NOTE: All options of libchromiumcontent are ' + 'required OR let brightray choose it') + parser.add_argument('--libcc_shared_library_path', required=False, + help='The shared library path of libchromiumcontent. ' \ + 'NOTE: All options of libchromiumcontent are ' \ + 'required OR let brightray choose it') + parser.add_argument('--libcc_static_library_path', required=False, + help='The static library path of libchromiumcontent. ' \ + 'NOTE: All options of libchromiumcontent are ' \ + 'required OR let brightray choose it') + return parser.parse_args() + + +def update_submodules(): + return (subprocess.call(['git', 'submodule', 'sync', '--quiet']) or + subprocess.call(['git', 'submodule', 'update', '--init', + '--recursive'])) + + +def setup_libchromiumcontent(is_dev, commit, target_arch, url, + libcc_source_path, + libcc_shared_library_path, + libcc_static_library_path): + target_dir = os.path.join(DOWNLOAD_DIR, 'libchromiumcontent') + download = os.path.join(VENDOR_DIR, 'libchromiumcontent', 'script', + 'download') + args = ['-f', '-c', commit, '--target_arch', target_arch, url, target_dir] + if (libcc_source_path != None and + libcc_shared_library_path != None and + libcc_static_library_path != None): + args += ['--libcc_source_path', libcc_source_path, + '--libcc_shared_library_path', libcc_shared_library_path, + '--libcc_static_library_path', libcc_static_library_path] + mkdir_p(target_dir) + else: + mkdir_p(DOWNLOAD_DIR) + if is_dev: + subprocess.check_call([sys.executable, download] + args) + else: + subprocess.check_call([sys.executable, download, '-s'] + args) + + +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/vendor/brightray/script/build b/vendor/brightray/script/build new file mode 100755 index 0000000000..6ee7da97a9 --- /dev/null +++ b/vendor/brightray/script/build @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +import os +import subprocess +import sys + + +SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +GYP = os.path.join(SOURCE_ROOT, 'vendor', 'gyp', 'gyp_main.py') + + +def main(): + os.chdir(SOURCE_ROOT) + return (run_gyp() or build()) + + +def run_gyp(): + env = os.environ.copy() + gyp_pylib = os.path.join(os.path.dirname(GYP), 'pylib') + env['PYTHONPATH'] = os.path.pathsep.join([gyp_pylib, + env.get('PYTHONPATH', '')]) + env['GYP_DEFINES'] = 'libchromiumcontent_component=static_library' + return subprocess.call([sys.executable, GYP, '--depth', '.', + '-Ibrightray.gypi', 'brightray.gyp'], env=env) + + +def build(): + if sys.platform == 'darwin': + return subprocess.call(['xcodebuild']) + if sys.platform == 'linux2': + return subprocess.call(['make']) + + assert sys.platform == 'win32', sys.platform + program_files = os.environ.get('PROGRAMFILES(X86)', os.environ['PROGRAMFILES']) + msbuild = os.path.join(program_files, 'MSBuild', '12.0', 'Bin', 'MSBuild.exe') + return subprocess.call([msbuild, 'brightray.sln']) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/vendor/brightray/script/cibuild b/vendor/brightray/script/cibuild new file mode 100755 index 0000000000..bfe1ec92b8 --- /dev/null +++ b/vendor/brightray/script/cibuild @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +import os +import subprocess +import sys + + +SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +# We're in JENKINS_ROOT/workspace/brightray. Walk up to JENKINS_ROOT. +JENKINS_ROOT = os.path.dirname(os.path.dirname(SOURCE_ROOT)) +S3_CREDENTIALS_FILE = os.path.join(JENKINS_ROOT, 'config', 's3credentials') + + +def main(): + if not os.path.isfile(S3_CREDENTIALS_FILE): + return 'Error: Can\'t find {0}'.format(S3_CREDENTIALS_FILE) + copy_to_environment(S3_CREDENTIALS_FILE) + + url = 'https://{0}.s3.amazonaws.com/libchromiumcontent'.format(os.environ['JANKY_ARTIFACTS_S3_BUCKET']) + return (run_script('bootstrap', url) or + run_script('build') or + run_script('cpplint')) + + +def copy_to_environment(credentials_file): + with open(credentials_file, 'r') as f: + for line in f: + key, value = line.strip().split('=') + value = value.strip("'") + os.environ[key] = value + + +def run_script(script, *args): + script = os.path.join('script', script) + sys.stderr.write('+ {0}\n'.format(script)) + sys.stderr.flush() + return subprocess.call([sys.executable, script] + list(args)) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/vendor/brightray/script/cpplint b/vendor/brightray/script/cpplint new file mode 100755 index 0000000000..ff09db9e7d --- /dev/null +++ b/vendor/brightray/script/cpplint @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import fnmatch +import os +import subprocess +import sys + + +SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +CPPLINT = os.path.join(SOURCE_ROOT, 'vendor', 'google-styleguide', 'trunk', 'cpplint', 'cpplint.py') +LINE_LENGTH = 100 + +IGNORED_FILES = [ + os.path.join('browser', 'mac', 'bry_inspectable_web_contents_view.h'), +] + +FILTERS = [ + '-build/header_guard', + '-build/include_what_you_use', + '-legal/copyright', + # cpplint doesn't like the BOOL& parameters that ui::WindowImpl uses. + '-runtime/references', +] + + +def main(): + os.chdir(SOURCE_ROOT) + files = list_files(['browser', 'common'], + ['*.cc', '*.h']) + return cpplint(set(files) - set(IGNORED_FILES)) + + +def list_files(directories, filters): + matches = [] + for directory in directories: + for root, _, filenames, in os.walk(directory): + for f in filters: + for filename in fnmatch.filter(filenames, f): + matches.append(os.path.join(root, filename)) + return matches + + +def cpplint(files): + return subprocess.call([sys.executable, CPPLINT, '--linelength=%d' % LINE_LENGTH, '--filter=' + ','.join(FILTERS)] + list(files)) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/vendor/brightray/tools/mac/change_mach_o_flags.py b/vendor/brightray/tools/mac/change_mach_o_flags.py new file mode 100755 index 0000000000..4547cb75f7 --- /dev/null +++ b/vendor/brightray/tools/mac/change_mach_o_flags.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE-CHROMIUM file. + +"""Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] + +Arranges for the executable at |executable_path| to have its data (heap) +pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have +the PIE (position independent executable) bit set to enable ASLR (address +space layout randomization). With --executable-heap or --no-pie, the +respective bits are cleared instead of set, making the heap executable or +disabling PIE/ASLR. + +This script is able to operate on thin (single-architecture) Mach-O files +and fat (universal, multi-architecture) files. When operating on fat files, +it will set or clear the bits for each architecture contained therein. + +NON-EXECUTABLE HEAP + +Traditionally in Mac OS X, 32-bit processes did not have data pages set to +prohibit execution. Although user programs could call mprotect and +mach_vm_protect to deny execution of code in data pages, the kernel would +silently ignore such requests without updating the page tables, and the +hardware would happily execute code on such pages. 64-bit processes were +always given proper hardware protection of data pages. This behavior was +controllable on a system-wide level via the vm.allow_data_exec sysctl, which +is set by default to 1. The bit with value 1 (set by default) allows code +execution on data pages for 32-bit processes, and the bit with value 2 +(clear by default) does the same for 64-bit processes. + +In Mac OS X 10.7, executables can "opt in" to having hardware protection +against code execution on data pages applied. This is done by setting a new +bit in the |flags| field of an executable's |mach_header|. When +MH_NO_HEAP_EXECUTION is set, proper protections will be applied, regardless +of the setting of vm.allow_data_exec. See xnu-1699.22.73/osfmk/vm/vm_map.c +override_nx and xnu-1699.22.73/bsd/kern/mach_loader.c load_machfile. + +The Apple toolchain has been revised to set the MH_NO_HEAP_EXECUTION when +producing executables, provided that -allow_heap_execute is not specified +at link time. Only linkers shipping with Xcode 4.0 and later (ld64-123.2 and +later) have this ability. See ld64-123.2.1/src/ld/Options.cpp +Options::reconfigureDefaults() and +ld64-123.2.1/src/ld/HeaderAndLoadCommands.hpp +HeaderAndLoadCommandsAtom::flags(). + +This script sets the MH_NO_HEAP_EXECUTION bit on Mach-O executables. It is +intended for use with executables produced by a linker that predates Apple's +modifications to set this bit itself. It is also useful for setting this bit +for non-i386 executables, including x86_64 executables. Apple's linker only +sets it for 32-bit i386 executables, presumably under the assumption that +the value of vm.allow_data_exec is set in stone. However, if someone were to +change vm.allow_data_exec to 2 or 3, 64-bit x86_64 executables would run +without hardware protection against code execution on data pages. This +script can set the bit for x86_64 executables, guaranteeing that they run +with appropriate protection even when vm.allow_data_exec has been tampered +with. + +POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION + +This script sets or clears the MH_PIE bit in an executable's Mach-O header, +enabling or disabling position independence on Mac OS X 10.5 and later. +Processes running position-independent executables have varying levels of +ASLR protection depending on the OS release. The main executable's load +address, shared library load addresess, and the heap and stack base +addresses may be randomized. Position-independent executables are produced +by supplying the -pie flag to the linker (or defeated by supplying -no_pie). +Executables linked with a deployment target of 10.7 or higher have PIE on +by default. + +This script is never strictly needed during the build to enable PIE, as all +linkers used are recent enough to support -pie. However, it's used to +disable the PIE bit as needed on already-linked executables. +""" + +import optparse +import os +import struct +import sys + + +# +FAT_MAGIC = 0xcafebabe +FAT_CIGAM = 0xbebafeca + +# +MH_MAGIC = 0xfeedface +MH_CIGAM = 0xcefaedfe +MH_MAGIC_64 = 0xfeedfacf +MH_CIGAM_64 = 0xcffaedfe +MH_EXECUTE = 0x2 +MH_PIE = 0x00200000 +MH_NO_HEAP_EXECUTION = 0x01000000 + + +class MachOError(Exception): + """A class for exceptions thrown by this module.""" + + pass + + +def CheckedSeek(file, offset): + """Seeks the file-like object at |file| to offset |offset| and raises a + MachOError if anything funny happens.""" + + file.seek(offset, os.SEEK_SET) + new_offset = file.tell() + if new_offset != offset: + raise MachOError, \ + 'seek: expected offset %d, observed %d' % (offset, new_offset) + + +def CheckedRead(file, count): + """Reads |count| bytes from the file-like |file| object, raising a + MachOError if any other number of bytes is read.""" + + bytes = file.read(count) + if len(bytes) != count: + raise MachOError, \ + 'read: expected length %d, observed %d' % (count, len(bytes)) + + return bytes + + +def ReadUInt32(file, endian): + """Reads an unsinged 32-bit integer from the file-like |file| object, + treating it as having endianness specified by |endian| (per the |struct| + module), and returns it as a number. Raises a MachOError if the proper + length of data can't be read from |file|.""" + + bytes = CheckedRead(file, 4) + + (uint32,) = struct.unpack(endian + 'I', bytes) + return uint32 + + +def ReadMachHeader(file, endian): + """Reads an entire |mach_header| structure () from the + file-like |file| object, treating it as having endianness specified by + |endian| (per the |struct| module), and returns a 7-tuple of its members + as numbers. Raises a MachOError if the proper length of data can't be read + from |file|.""" + + bytes = CheckedRead(file, 28) + + magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \ + struct.unpack(endian + '7I', bytes) + return magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags + + +def ReadFatArch(file): + """Reads an entire |fat_arch| structure () from the file-like + |file| object, treating it as having endianness specified by |endian| + (per the |struct| module), and returns a 5-tuple of its members as numbers. + Raises a MachOError if the proper length of data can't be read from + |file|.""" + + bytes = CheckedRead(file, 20) + + cputype, cpusubtype, offset, size, align = struct.unpack('>5I', bytes) + return cputype, cpusubtype, offset, size, align + + +def WriteUInt32(file, uint32, endian): + """Writes |uint32| as an unsinged 32-bit integer to the file-like |file| + object, treating it as having endianness specified by |endian| (per the + |struct| module).""" + + bytes = struct.pack(endian + 'I', uint32) + assert len(bytes) == 4 + + file.write(bytes) + + +def HandleMachOFile(file, options, offset=0): + """Seeks the file-like |file| object to |offset|, reads its |mach_header|, + and rewrites the header's |flags| field if appropriate. The header's + endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported + (mach_header and mach_header_64). Raises MachOError if used on a header that + does not have a known magic number or is not of type MH_EXECUTE. The + MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field + according to |options| and written to |file| if any changes need to be made. + If already set or clear as specified by |options|, nothing is written.""" + + CheckedSeek(file, offset) + magic = ReadUInt32(file, '<') + if magic == MH_MAGIC or magic == MH_MAGIC_64: + endian = '<' + elif magic == MH_CIGAM or magic == MH_CIGAM_64: + endian = '>' + else: + raise MachOError, \ + 'Mach-O file at offset %d has illusion of magic' % offset + + CheckedSeek(file, offset) + magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \ + ReadMachHeader(file, endian) + assert magic == MH_MAGIC or magic == MH_MAGIC_64 + if filetype != MH_EXECUTE: + raise MachOError, \ + 'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \ + (offset, filetype) + + original_flags = flags + + if options.no_heap_execution: + flags |= MH_NO_HEAP_EXECUTION + else: + flags &= ~MH_NO_HEAP_EXECUTION + + if options.pie: + flags |= MH_PIE + else: + flags &= ~MH_PIE + + if flags != original_flags: + CheckedSeek(file, offset + 24) + WriteUInt32(file, flags, endian) + + +def HandleFatFile(file, options, fat_offset=0): + """Seeks the file-like |file| object to |offset| and loops over its + |fat_header| entries, calling HandleMachOFile for each.""" + + CheckedSeek(file, fat_offset) + magic = ReadUInt32(file, '>') + assert magic == FAT_MAGIC + + nfat_arch = ReadUInt32(file, '>') + + for index in xrange(0, nfat_arch): + cputype, cpusubtype, offset, size, align = ReadFatArch(file) + assert size >= 28 + + # HandleMachOFile will seek around. Come back here after calling it, in + # case it sought. + fat_arch_offset = file.tell() + HandleMachOFile(file, options, offset) + CheckedSeek(file, fat_arch_offset) + + +def main(me, args): + parser = optparse.OptionParser('%prog [options] ') + parser.add_option('--executable-heap', action='store_false', + dest='no_heap_execution', default=True, + help='Clear the MH_NO_HEAP_EXECUTION bit') + parser.add_option('--no-pie', action='store_false', + dest='pie', default=True, + help='Clear the MH_PIE bit') + (options, loose_args) = parser.parse_args(args) + if len(loose_args) != 1: + parser.print_usage() + return 1 + + executable_path = loose_args[0] + executable_file = open(executable_path, 'rb+') + + magic = ReadUInt32(executable_file, '<') + if magic == FAT_CIGAM: + # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian. + HandleFatFile(executable_file, options) + elif magic == MH_MAGIC or magic == MH_CIGAM or \ + magic == MH_MAGIC_64 or magic == MH_CIGAM_64: + HandleMachOFile(executable_file, options) + else: + raise MachOError, '%s is not a Mach-O or fat file' % executable_file + + executable_file.close() + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[0], sys.argv[1:])) diff --git a/vendor/brightray/tools/mac/make_more_helpers.sh b/vendor/brightray/tools/mac/make_more_helpers.sh new file mode 100755 index 0000000000..ee4f21105b --- /dev/null +++ b/vendor/brightray/tools/mac/make_more_helpers.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE-CHROMIUM file. + +# Usage: make_more_helpers.sh +# +# This script creates additional helper .app bundles for Chromium, based on +# the existing helper .app bundle, changing their Mach-O header's flags to +# enable and disable various features. Based on Chromium Helper.app, it will +# create Chromium Helper EH.app, which has the MH_NO_HEAP_EXECUTION bit +# cleared to support Chromium child processes that require an executable heap, +# and Chromium Helper NP.app, which has the MH_PIE bit cleared to support +# Chromium child processes that cannot tolerate ASLR. +# +# This script expects to be called from the chrome_exe target as a postbuild, +# and operates directly within the built-up browser app's versioned directory. +# +# Each helper is adjusted by giving it the proper bundle name, renaming the +# executable, adjusting several Info.plist keys, and changing the executable's +# Mach-O flags. + +set -eu + +make_helper() { + local containing_dir="${1}" + local app_name="${2}" + local feature="${3}" + local flags="${4}" + + local helper_name="${app_name} Helper" + local helper_stem="${containing_dir}/${helper_name}" + local original_helper="${helper_stem}.app" + if [[ ! -d "${original_helper}" ]]; then + echo "${0}: error: ${original_helper} is a required directory" >& 2 + exit 1 + fi + local original_helper_exe="${original_helper}/Contents/MacOS/${helper_name}" + if [[ ! -f "${original_helper_exe}" ]]; then + echo "${0}: error: ${original_helper_exe} is a required file" >& 2 + exit 1 + fi + + local feature_helper="${helper_stem} ${feature}.app" + + rsync -acC --delete --include '*.so' "${original_helper}/" "${feature_helper}" + + local helper_feature="${helper_name} ${feature}" + local helper_feature_exe="${feature_helper}/Contents/MacOS/${helper_feature}" + mv "${feature_helper}/Contents/MacOS/${helper_name}" "${helper_feature_exe}" + + local change_flags="$(dirname "${0}")/change_mach_o_flags.py" + "${change_flags}" ${flags} "${helper_feature_exe}" + + local feature_info="${feature_helper}/Contents/Info" + local feature_info_plist="${feature_info}.plist" + + defaults write "${feature_info}" "CFBundleDisplayName" "${helper_feature}" + defaults write "${feature_info}" "CFBundleExecutable" "${helper_feature}" + + cfbundleid="$(defaults read "${feature_info}" "CFBundleIdentifier")" + feature_cfbundleid="${cfbundleid}.${feature}" + defaults write "${feature_info}" "CFBundleIdentifier" "${feature_cfbundleid}" + + cfbundlename="$(defaults read "${feature_info}" "CFBundleName")" + feature_cfbundlename="${cfbundlename} ${feature}" + defaults write "${feature_info}" "CFBundleName" "${feature_cfbundlename}" + + # As usual, defaults might have put the plist into whatever format excites + # it, but Info.plists get converted back to the expected XML format. + plutil -convert xml1 "${feature_info_plist}" + + # `defaults` also changes the file permissions, so make the file + # world-readable again. + chmod a+r "${feature_info_plist}" +} + +if [[ ${#} -ne 2 ]]; then + echo "usage: ${0} " >& 2 + exit 1 +fi + +DIRECTORY_WITHIN_CONTENTS="${1}" +APP_NAME="${2}" + +CONTENTS_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}" +CONTAINING_DIR="${CONTENTS_DIR}/${DIRECTORY_WITHIN_CONTENTS}" + +make_helper "${CONTAINING_DIR}" "${APP_NAME}" "EH" "--executable-heap" +make_helper "${CONTAINING_DIR}" "${APP_NAME}" "NP" "--no-pie" diff --git a/vendor/brightray/vendor/.gitignore b/vendor/brightray/vendor/.gitignore new file mode 100644 index 0000000000..0920e14cbc --- /dev/null +++ b/vendor/brightray/vendor/.gitignore @@ -0,0 +1 @@ +/download/ diff --git a/vendor/brightray/vendor/google-styleguide b/vendor/brightray/vendor/google-styleguide new file mode 160000 index 0000000000..ba88c8a53f --- /dev/null +++ b/vendor/brightray/vendor/google-styleguide @@ -0,0 +1 @@ +Subproject commit ba88c8a53f1b563c43fc063cc048e5efdc238c18 diff --git a/vendor/brightray/vendor/gyp b/vendor/brightray/vendor/gyp new file mode 160000 index 0000000000..549e55a227 --- /dev/null +++ b/vendor/brightray/vendor/gyp @@ -0,0 +1 @@ +Subproject commit 549e55a22765ccf99e46b419b2dff6338bcac64a diff --git a/vendor/brightray/vendor/libchromiumcontent b/vendor/brightray/vendor/libchromiumcontent new file mode 160000 index 0000000000..d715734c03 --- /dev/null +++ b/vendor/brightray/vendor/libchromiumcontent @@ -0,0 +1 @@ +Subproject commit d715734c03b0c892ea66695ae63fc0db9c3fc027 diff --git a/vendor/crashpad b/vendor/crashpad index 51f78aefa8..6a147931e7 160000 --- a/vendor/crashpad +++ b/vendor/crashpad @@ -1 +1 @@ -Subproject commit 51f78aefa872211022ae2d44260cbfd07a6aeb6a +Subproject commit 6a147931e75848020c051bd800c291b01bbff7e0 diff --git a/vendor/native_mate b/vendor/native_mate deleted file mode 160000 index b5e5de626c..0000000000 --- a/vendor/native_mate +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b5e5de626c6a57e44c7e6448d8bbaaac475d493c diff --git a/vendor/node b/vendor/node deleted file mode 160000 index 3e1d061a4f..0000000000 --- a/vendor/node +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3e1d061a4f146831b7df9e4fc47329e3d269f6d1

x}uu4r((I`hA5$U_g7*kK^gK9a% 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 XwT-mOH0+)~a(sN3^2?yf00937;wKMD literal 0 HcmV?d00001 diff --git a/app/resources/beta/brave_linux.png b/app/resources/beta/brave_linux.png new file mode 100644 index 0000000000000000000000000000000000000000..11c841c89737c8a410b866c414b7452252151bf0 GIT binary patch literal 89236 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 literal 0 HcmV?d00001 diff --git a/app/resources/beta/brave_win.ico b/app/resources/beta/brave_win.ico new file mode 100644 index 0000000000000000000000000000000000000000..b9fc6ed202c84075ba5ae241d7db52d28c368fe3 GIT binary patch literal 85344 zcmV)WK(4<400966000000096X09pqD09F7105C8B0096X0B9xv0I&xE03aX$0096X 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