|
@@ -0,0 +1,10639 @@
|
|
|
|
+From: uazo <uazo@users.noreply.github.com>
|
|
|
|
+Date: Fri, 13 Aug 2021 17:10:47 +0000
|
|
|
|
+Subject: Experimental user scripts support
|
|
|
|
+
|
|
|
|
+Activate the user scripts functionality for Android,
|
|
|
|
+as it is available in the Desktop version.
|
|
|
|
+
|
|
|
|
+It is possible to add user scripts in two ways: by
|
|
|
|
+selecting files from the picker in the settings or
|
|
|
|
+downloading the scripts and opening them from downloads
|
|
|
|
+(only if such files end with '.user.js').
|
|
|
|
+
|
|
|
|
+New imported scripts are disabled by default: they
|
|
|
|
+can be activated via the UI.
|
|
|
|
+
|
|
|
|
+Parsed user script headers are: name, version, description,
|
|
|
|
+include, exclude, match, exclude_match (only http and
|
|
|
|
+https), run-at (document-start, document-end,
|
|
|
|
+document-idle), homepage, url_source
|
|
|
|
+
|
|
|
|
+The UI also allows you to see the source of the script.
|
|
|
|
+
|
|
|
|
+See also: components/user_scripts/README.md
|
|
|
|
+---
|
|
|
|
+ chrome/android/BUILD.gn | 5 +
|
|
|
|
+ .../android/java/res/xml/main_preferences.xml | 5 +
|
|
|
|
+ .../browser/download/DownloadUtils.java | 6 +
|
|
|
|
+ .../init/ProcessInitializationHandler.java | 3 +
|
|
|
|
+ chrome/android/java_sources.gni | 3 +
|
|
|
|
+ chrome/browser/BUILD.gn | 5 +
|
|
|
|
+ chrome/browser/about_flags.cc | 5 +
|
|
|
|
+ .../browser/chrome_content_browser_client.cc | 3 +-
|
|
|
|
+ chrome/browser/flag_descriptions.cc | 5 +
|
|
|
|
+ chrome/browser/flag_descriptions.h | 3 +
|
|
|
|
+ chrome/browser/prefs/browser_prefs.cc | 2 +
|
|
|
|
+ chrome/browser/profiles/BUILD.gn | 1 +
|
|
|
|
+ ...hrome_browser_main_extra_parts_profiles.cc | 3 +
|
|
|
|
+ chrome/browser/profiles/profile_manager.cc | 9 +
|
|
|
|
+ chrome/browser/profiles/renderer_updater.cc | 10 +-
|
|
|
|
+ chrome/browser/profiles/renderer_updater.h | 1 +
|
|
|
|
+ .../webui/chrome_web_ui_controller_factory.cc | 3 +
|
|
|
|
+ chrome/chrome_paks.gni | 2 +
|
|
|
|
+ chrome/common/renderer_configuration.mojom | 1 +
|
|
|
|
+ chrome/renderer/BUILD.gn | 1 +
|
|
|
|
+ .../chrome_content_renderer_client.cc | 37 +
|
|
|
|
+ .../renderer/chrome_render_thread_observer.cc | 3 +
|
|
|
|
+ components/components_strings.grd | 1 +
|
|
|
|
+ components/user_scripts/README.md | 150 ++++
|
|
|
|
+ components/user_scripts/android/BUILD.gn | 80 ++
|
|
|
|
+ .../java/res/layout/accept_script_item.xml | 160 ++++
|
|
|
|
+ .../java/res/layout/accept_script_list.xml | 10 +
|
|
|
|
+ .../java/res/layout/scripts_preference.xml | 40 +
|
|
|
|
+ .../android/java/res/values/dimens.xml | 11 +
|
|
|
|
+ .../java/res/xml/userscripts_preferences.xml | 34 +
|
|
|
|
+ .../user_scripts/UserScriptsUtils.java | 84 ++
|
|
|
|
+ .../user_scripts/FragmentWindowAndroid.java | 89 ++
|
|
|
|
+ .../user_scripts/IUserScriptsUtils.java | 22 +
|
|
|
|
+ .../components/user_scripts/ScriptInfo.java | 37 +
|
|
|
|
+ .../user_scripts/ScriptListBaseAdapter.java | 163 ++++
|
|
|
|
+ .../user_scripts/ScriptListPreference.java | 171 ++++
|
|
|
|
+ .../user_scripts/UserScriptsBridge.java | 200 +++++
|
|
|
|
+ .../user_scripts/UserScriptsPreferences.java | 116 +++
|
|
|
|
+ .../user_scripts/android/java_sources.gni | 18 +
|
|
|
|
+ .../android/user_scripts_bridge.cc | 173 ++++
|
|
|
|
+ .../android/user_scripts_bridge.h | 31 +
|
|
|
|
+ components/user_scripts/browser/BUILD.gn | 82 ++
|
|
|
|
+ .../user_scripts/browser/file_task_runner.cc | 40 +
|
|
|
|
+ .../user_scripts/browser/file_task_runner.h | 34 +
|
|
|
|
+ .../browser/resources/browser_resources.grd | 14 +
|
|
|
|
+ .../browser/resources/user-script-ui/BUILD.gn | 12 +
|
|
|
|
+ .../user-script-ui/user-scripts-ui.html | 14 +
|
|
|
|
+ .../user-script-ui/user-scripts-ui.js | 9 +
|
|
|
|
+ .../browser/ui/user_scripts_ui.cc | 148 ++++
|
|
|
|
+ .../user_scripts/browser/ui/user_scripts_ui.h | 39 +
|
|
|
|
+ .../browser/user_script_loader.cc | 721 ++++++++++++++++
|
|
|
|
+ .../user_scripts/browser/user_script_loader.h | 170 ++++
|
|
|
|
+ .../browser/user_script_pref_info.cc | 34 +
|
|
|
|
+ .../browser/user_script_pref_info.h | 72 ++
|
|
|
|
+ .../user_scripts/browser/user_script_prefs.cc | 276 ++++++
|
|
|
|
+ .../user_scripts/browser/user_script_prefs.h | 62 ++
|
|
|
|
+ .../browser/userscripts_browser_client.cc | 78 ++
|
|
|
|
+ .../browser/userscripts_browser_client.h | 62 ++
|
|
|
|
+ components/user_scripts/common/BUILD.gn | 49 ++
|
|
|
|
+ components/user_scripts/common/constants.h | 21 +
|
|
|
|
+ components/user_scripts/common/error_utils.cc | 54 ++
|
|
|
|
+ components/user_scripts/common/error_utils.h | 24 +
|
|
|
|
+ .../common/extension_message_generator.cc | 29 +
|
|
|
|
+ .../common/extension_message_generator.h | 11 +
|
|
|
|
+ .../user_scripts/common/extension_messages.cc | 40 +
|
|
|
|
+ .../user_scripts/common/extension_messages.h | 71 ++
|
|
|
|
+ components/user_scripts/common/host_id.cc | 31 +
|
|
|
|
+ components/user_scripts/common/host_id.h | 35 +
|
|
|
|
+ .../user_scripts/common/script_constants.h | 33 +
|
|
|
|
+ components/user_scripts/common/url_pattern.cc | 803 ++++++++++++++++++
|
|
|
|
+ components/user_scripts/common/url_pattern.h | 302 +++++++
|
|
|
|
+ .../user_scripts/common/url_pattern_set.cc | 334 ++++++++
|
|
|
|
+ .../user_scripts/common/url_pattern_set.h | 161 ++++
|
|
|
|
+ components/user_scripts/common/user_script.cc | 317 +++++++
|
|
|
|
+ components/user_scripts/common/user_script.h | 403 +++++++++
|
|
|
|
+ .../common/user_scripts_features.cc | 32 +
|
|
|
|
+ .../common/user_scripts_features.h | 34 +
|
|
|
|
+ components/user_scripts/common/view_type.cc | 39 +
|
|
|
|
+ components/user_scripts/common/view_type.h | 48 ++
|
|
|
|
+ components/user_scripts/renderer/BUILD.gn | 67 ++
|
|
|
|
+ .../renderer/extension_frame_helper.cc | 96 +++
|
|
|
|
+ .../renderer/extension_frame_helper.h | 92 ++
|
|
|
|
+ .../user_scripts/renderer/injection_host.cc | 12 +
|
|
|
|
+ .../user_scripts/renderer/injection_host.h | 42 +
|
|
|
|
+ .../renderer/resources/greasemonkey_api.js | 82 ++
|
|
|
|
+ .../user_scripts_renderer_resources.grd | 14 +
|
|
|
|
+ .../user_scripts/renderer/script_context.cc | 215 +++++
|
|
|
|
+ .../user_scripts/renderer/script_context.h | 70 ++
|
|
|
|
+ .../user_scripts/renderer/script_injection.cc | 343 ++++++++
|
|
|
|
+ .../user_scripts/renderer/script_injection.h | 155 ++++
|
|
|
|
+ .../renderer/script_injection_callback.cc | 25 +
|
|
|
|
+ .../renderer/script_injection_callback.h | 39 +
|
|
|
|
+ .../renderer/script_injection_manager.cc | 417 +++++++++
|
|
|
|
+ .../renderer/script_injection_manager.h | 102 +++
|
|
|
|
+ .../user_scripts/renderer/script_injector.h | 96 +++
|
|
|
|
+ .../user_scripts/renderer/scripts_run_info.cc | 31 +
|
|
|
|
+ .../user_scripts/renderer/scripts_run_info.h | 70 ++
|
|
|
|
+ .../renderer/user_script_injector.cc | 228 +++++
|
|
|
|
+ .../renderer/user_script_injector.h | 87 ++
|
|
|
|
+ .../user_scripts/renderer/user_script_set.cc | 259 ++++++
|
|
|
|
+ .../user_scripts/renderer/user_script_set.h | 102 +++
|
|
|
|
+ .../renderer/user_script_set_manager.cc | 77 ++
|
|
|
|
+ .../renderer/user_script_set_manager.h | 62 ++
|
|
|
|
+ .../renderer/user_scripts_dispatcher.cc | 36 +
|
|
|
|
+ .../renderer/user_scripts_dispatcher.h | 48 ++
|
|
|
|
+ .../renderer/user_scripts_renderer_client.cc | 105 +++
|
|
|
|
+ .../renderer/user_scripts_renderer_client.h | 36 +
|
|
|
|
+ .../renderer/web_ui_injection_host.cc | 40 +
|
|
|
|
+ .../renderer/web_ui_injection_host.h | 28 +
|
|
|
|
+ .../strings/userscripts_strings.grdp | 55 ++
|
|
|
|
+ tools/gritsettings/resource_ids.spec | 6 +
|
|
|
|
+ 111 files changed, 9579 insertions(+), 2 deletions(-)
|
|
|
|
+ create mode 100644 components/user_scripts/README.md
|
|
|
|
+ create mode 100755 components/user_scripts/android/BUILD.gn
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/res/layout/accept_script_item.xml
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/res/layout/accept_script_list.xml
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/res/layout/scripts_preference.xml
|
|
|
|
+ create mode 100755 components/user_scripts/android/java/res/values/dimens.xml
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/res/xml/userscripts_preferences.xml
|
|
|
|
+ create mode 100755 components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java
|
|
|
|
+ create mode 100755 components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java
|
|
|
|
+ create mode 100644 components/user_scripts/android/java_sources.gni
|
|
|
|
+ create mode 100644 components/user_scripts/android/user_scripts_bridge.cc
|
|
|
|
+ create mode 100644 components/user_scripts/android/user_scripts_bridge.h
|
|
|
|
+ create mode 100755 components/user_scripts/browser/BUILD.gn
|
|
|
|
+ create mode 100755 components/user_scripts/browser/file_task_runner.cc
|
|
|
|
+ create mode 100755 components/user_scripts/browser/file_task_runner.h
|
|
|
|
+ create mode 100644 components/user_scripts/browser/resources/browser_resources.grd
|
|
|
|
+ create mode 100644 components/user_scripts/browser/resources/user-script-ui/BUILD.gn
|
|
|
|
+ create mode 100644 components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html
|
|
|
|
+ create mode 100644 components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js
|
|
|
|
+ create mode 100644 components/user_scripts/browser/ui/user_scripts_ui.cc
|
|
|
|
+ create mode 100644 components/user_scripts/browser/ui/user_scripts_ui.h
|
|
|
|
+ create mode 100755 components/user_scripts/browser/user_script_loader.cc
|
|
|
|
+ create mode 100755 components/user_scripts/browser/user_script_loader.h
|
|
|
|
+ create mode 100644 components/user_scripts/browser/user_script_pref_info.cc
|
|
|
|
+ create mode 100644 components/user_scripts/browser/user_script_pref_info.h
|
|
|
|
+ create mode 100644 components/user_scripts/browser/user_script_prefs.cc
|
|
|
|
+ create mode 100644 components/user_scripts/browser/user_script_prefs.h
|
|
|
|
+ create mode 100755 components/user_scripts/browser/userscripts_browser_client.cc
|
|
|
|
+ create mode 100755 components/user_scripts/browser/userscripts_browser_client.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/BUILD.gn
|
|
|
|
+ create mode 100755 components/user_scripts/common/constants.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/error_utils.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/error_utils.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/extension_message_generator.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/extension_message_generator.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/extension_messages.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/extension_messages.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/host_id.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/host_id.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/script_constants.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/url_pattern.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/url_pattern.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/url_pattern_set.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/url_pattern_set.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/user_script.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/user_script.h
|
|
|
|
+ create mode 100644 components/user_scripts/common/user_scripts_features.cc
|
|
|
|
+ create mode 100644 components/user_scripts/common/user_scripts_features.h
|
|
|
|
+ create mode 100755 components/user_scripts/common/view_type.cc
|
|
|
|
+ create mode 100755 components/user_scripts/common/view_type.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/BUILD.gn
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/extension_frame_helper.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/extension_frame_helper.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/injection_host.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/injection_host.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/resources/greasemonkey_api.js
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_context.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_context.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injection.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injection.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injection_callback.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injection_callback.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injection_manager.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injection_manager.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/script_injector.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/scripts_run_info.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/scripts_run_info.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_script_injector.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_script_injector.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_script_set.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_script_set.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_script_set_manager.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_script_set_manager.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_scripts_dispatcher.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_scripts_dispatcher.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_scripts_renderer_client.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/user_scripts_renderer_client.h
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/web_ui_injection_host.cc
|
|
|
|
+ create mode 100755 components/user_scripts/renderer/web_ui_injection_host.h
|
|
|
|
+ create mode 100755 components/user_scripts/strings/userscripts_strings.grdp
|
|
|
|
+
|
|
|
|
+diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
|
|
|
|
+--- a/chrome/android/BUILD.gn
|
|
|
|
++++ b/chrome/android/BUILD.gn
|
|
|
|
+@@ -252,6 +252,10 @@ android_resources("chrome_app_java_resources") {
|
|
|
|
+ "//third_party/androidx:androidx_preference_preference_java",
|
|
|
|
+ "//third_party/androidx:androidx_recyclerview_recyclerview_java",
|
|
|
|
+ ]
|
|
|
|
++
|
|
|
|
++ # this need to be into android_resources("chrome_app_java_resources") section because
|
|
|
|
++ # android:java_resources are packed *_percent.pak and placed in the executable folder
|
|
|
|
++ deps += [ "//components/user_scripts/android:java_resources" ]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (enable_vr) {
|
|
|
|
+@@ -524,6 +528,7 @@ android_library("chrome_java") {
|
|
|
|
+ "//components/ukm/android:java",
|
|
|
|
+ "//components/url_formatter/android:url_formatter_java",
|
|
|
|
+ "//components/user_prefs/android:java",
|
|
|
|
++ "//components/user_scripts/android:java",
|
|
|
|
+ "//components/variations/android:variations_java",
|
|
|
|
+ "//components/version_info/android:version_constants_java",
|
|
|
|
+ "//components/viz/common:common_java",
|
|
|
|
+diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
|
|
|
|
+--- a/chrome/android/java/res/xml/main_preferences.xml
|
|
|
|
++++ b/chrome/android/java/res/xml/main_preferences.xml
|
|
|
|
+@@ -86,6 +86,11 @@
|
|
|
|
+ android:key="useragent_settings"
|
|
|
|
+ android:order="20"
|
|
|
|
+ android:title="@string/prefs_useragent_settings"/>
|
|
|
|
++ <Preference
|
|
|
|
++ android:fragment="org.chromium.components.user_scripts.UserScriptsPreferences"
|
|
|
|
++ android:key="userscripts_settings"
|
|
|
|
++ android:order="20"
|
|
|
|
++ android:title="@string/prefs_userscripts_settings"/>
|
|
|
|
+ <Preference
|
|
|
|
+ android:fragment="org.chromium.chrome.browser.language.settings.LanguageSettings"
|
|
|
|
+ android:key="languages"
|
|
|
|
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
|
|
|
|
+--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
|
|
|
|
++++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
|
|
|
|
+@@ -68,6 +68,7 @@ import org.chromium.content_public.browser.BrowserStartupController;
|
|
|
|
+ import org.chromium.content_public.browser.LoadUrlParams;
|
|
|
|
+ import org.chromium.ui.base.DeviceFormFactor;
|
|
|
|
+ import org.chromium.ui.widget.Toast;
|
|
|
|
++import org.chromium.chrome.browser.user_scripts.UserScriptsUtils;
|
|
|
|
+
|
|
|
|
+ import java.io.File;
|
|
|
|
+
|
|
|
|
+@@ -419,6 +420,11 @@ public class DownloadUtils {
|
|
|
|
+ public static boolean openFile(String filePath, String mimeType, String downloadGuid,
|
|
|
|
+ OTRProfileID otrProfileID, String originalUrl, String referrer,
|
|
|
|
+ @DownloadOpenSource int source, Context context) {
|
|
|
|
++ if (UserScriptsUtils.getInstance().openFile(filePath, mimeType, downloadGuid,
|
|
|
|
++ originalUrl, referrer,
|
|
|
|
++ getUriForItem(filePath))) {
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
+ DownloadMetrics.recordDownloadOpen(source, mimeType);
|
|
|
|
+ DownloadManagerService service = DownloadManagerService.getDownloadManagerService();
|
|
|
|
+
|
|
|
|
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
|
|
|
+--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
|
|
|
++++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
|
|
|
+@@ -112,6 +112,8 @@ import java.util.Date;
|
|
|
|
+ import java.util.List;
|
|
|
|
+ import java.util.Locale;
|
|
|
|
+
|
|
|
|
++import org.chromium.chrome.browser.user_scripts.UserScriptsUtils;
|
|
|
|
++
|
|
|
|
+ /**
|
|
|
|
+ * Handles the initialization dependences of the browser process. This is meant to handle the
|
|
|
|
+ * initialization that is not tied to any particular Activity, and the logic that should only be
|
|
|
|
+@@ -297,6 +299,7 @@ public class ProcessInitializationHandler {
|
|
|
|
+
|
|
|
|
+ DefaultBrowserInfo.initBrowserFetcher();
|
|
|
|
+
|
|
|
|
++ UserScriptsUtils.Initialize();
|
|
|
|
+ AfterStartupTaskUtils.setStartupComplete();
|
|
|
|
+
|
|
|
|
+ PartnerBrowserCustomizations.getInstance().setOnInitializeAsyncFinished(
|
|
|
|
+diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
|
|
|
|
+--- a/chrome/android/java_sources.gni
|
|
|
|
++++ b/chrome/android/java_sources.gni
|
|
|
|
+@@ -22,6 +22,7 @@ import("//components/feed/features.gni")
|
|
|
|
+ import("//components/offline_pages/buildflags/features.gni")
|
|
|
|
+ import("//components/omnibox/browser/test_java_sources.gni")
|
|
|
|
+ import("//device/vr/buildflags/buildflags.gni")
|
|
|
|
++import("//components/user_scripts/android/java_sources.gni")
|
|
|
|
+
|
|
|
|
+ chrome_java_sources += public_autofill_assistant_java_sources
|
|
|
|
+
|
|
|
|
+@@ -59,3 +60,5 @@ if (enable_arcore) {
|
|
|
|
+ "java/src/org/chromium/chrome/browser/vr/ArDelegateImpl.java",
|
|
|
|
+ ]
|
|
|
|
+ }
|
|
|
|
++
|
|
|
|
++chrome_java_sources += userscripts_java_sources
|
|
|
|
+diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
|
|
|
+--- a/chrome/browser/BUILD.gn
|
|
|
|
++++ b/chrome/browser/BUILD.gn
|
|
|
|
+@@ -3489,6 +3489,11 @@ static_library("browser") {
|
|
|
|
+ ]
|
|
|
|
+ deps += [ "//chrome/android/modules/dev_ui/provider:native" ]
|
|
|
|
+ }
|
|
|
|
++ deps += [
|
|
|
|
++ "//components/user_scripts/common",
|
|
|
|
++ "//components/user_scripts/browser",
|
|
|
|
++ "//components/user_scripts/android",
|
|
|
|
++ ]
|
|
|
|
+ } else {
|
|
|
|
+ #!is_android
|
|
|
|
+ sources += [
|
|
|
|
+diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
|
|
|
|
+--- a/chrome/browser/about_flags.cc
|
|
|
|
++++ b/chrome/browser/about_flags.cc
|
|
|
|
+@@ -151,6 +151,7 @@
|
|
|
|
+ #include "components/translate/core/browser/translate_ranker_impl.h"
|
|
|
|
+ #include "components/translate/core/common/translate_util.h"
|
|
|
|
+ #include "components/ui_devtools/switches.h"
|
|
|
|
++#include "components/user_scripts/common/user_scripts_features.h"
|
|
|
|
+ #include "components/version_info/version_info.h"
|
|
|
|
+ #include "components/viz/common/features.h"
|
|
|
|
+ #include "components/viz/common/switches.h"
|
|
|
|
+@@ -6531,6 +6532,10 @@ const FeatureEntry kFeatureEntries[] = {
|
|
|
|
+ FEATURE_VALUE_TYPE(features::kUserDataSnapshot)},
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++ {"enable-userscripts-log", flag_descriptions::kEnableLoggingUserScriptsName,
|
|
|
|
++ flag_descriptions::kEnableLoggingUserScriptsDescription, kOsDesktop | kOsAndroid,
|
|
|
|
++ FEATURE_VALUE_TYPE(user_scripts::features::kEnableLoggingUserScripts)},
|
|
|
|
++
|
|
|
|
+ #if defined(OS_WIN)
|
|
|
|
+ {"run-video-capture-service-in-browser",
|
|
|
|
+ flag_descriptions::kRunVideoCaptureServiceInBrowserProcessName,
|
|
|
|
+diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
|
|
|
|
+--- a/chrome/browser/chrome_content_browser_client.cc
|
|
|
|
++++ b/chrome/browser/chrome_content_browser_client.cc
|
|
|
|
+@@ -4659,7 +4659,8 @@ ChromeContentBrowserClient::CreateURLLoaderThrottles(
|
|
|
|
+ chrome::mojom::DynamicParams dynamic_params = {
|
|
|
|
+ profile->GetPrefs()->GetBoolean(prefs::kForceGoogleSafeSearch),
|
|
|
|
+ profile->GetPrefs()->GetInteger(prefs::kForceYouTubeRestrict),
|
|
|
|
+- profile->GetPrefs()->GetString(prefs::kAllowedDomainsForApps)};
|
|
|
|
++ profile->GetPrefs()->GetString(prefs::kAllowedDomainsForApps),
|
|
|
|
++ false /*-> allow_userscript, don't care */};
|
|
|
|
+ result.push_back(std::make_unique<GoogleURLLoaderThrottle>(
|
|
|
|
+ #if defined(OS_ANDROID)
|
|
|
|
+ client_data_header, is_tab_large_enough,
|
|
|
|
+diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
|
|
|
|
+--- a/chrome/browser/flag_descriptions.cc
|
|
|
|
++++ b/chrome/browser/flag_descriptions.cc
|
|
|
|
+@@ -5393,6 +5393,11 @@ const char kUserDataSnapshotDescription[] =
|
|
|
|
+ "update and restoring them after a version rollback.";
|
|
|
|
+ #endif // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
|
|
|
|
+
|
|
|
|
++const char kEnableLoggingUserScriptsName[] = "Enable logging user scripts component";
|
|
|
|
++const char kEnableLoggingUserScriptsDescription[] =
|
|
|
|
++ "Enables logging for troubleshooting feature. "
|
|
|
|
++ "Enabling logs may make browsing slower.";
|
|
|
|
++
|
|
|
|
+ #if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_MAC)
|
|
|
|
+ const char kWebShareName[] = "Web Share";
|
|
|
|
+ const char kWebShareDescription[] =
|
|
|
|
+diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
|
|
|
|
+--- a/chrome/browser/flag_descriptions.h
|
|
|
|
++++ b/chrome/browser/flag_descriptions.h
|
|
|
|
+@@ -3132,6 +3132,9 @@ extern const char kUserDataSnapshotName[];
|
|
|
|
+ extern const char kUserDataSnapshotDescription[];
|
|
|
|
+ #endif // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
|
|
|
|
+
|
|
|
|
++extern const char kEnableLoggingUserScriptsName[];
|
|
|
|
++extern const char kEnableLoggingUserScriptsDescription[];
|
|
|
|
++
|
|
|
|
+ #if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_MAC)
|
|
|
|
+ extern const char kWebShareName[];
|
|
|
|
+ extern const char kWebShareDescription[];
|
|
|
|
+diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
|
|
|
|
+--- a/chrome/browser/prefs/browser_prefs.cc
|
|
|
|
++++ b/chrome/browser/prefs/browser_prefs.cc
|
|
|
|
+@@ -237,6 +237,7 @@
|
|
|
|
+ #include "components/ntp_tiles/popular_sites_impl.h"
|
|
|
|
+ #include "components/permissions/contexts/geolocation_permission_context_android.h"
|
|
|
|
+ #include "components/query_tiles/tile_service_prefs.h"
|
|
|
|
++#include "components/user_scripts/browser/user_script_prefs.h"
|
|
|
|
+ #else // defined(OS_ANDROID)
|
|
|
|
+ #include "chrome/browser/cart/cart_service.h"
|
|
|
|
+ #include "chrome/browser/device_api/device_service_impl.h"
|
|
|
|
+@@ -1197,6 +1198,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
|
|
|
|
+ translate::TranslatePrefs::RegisterProfilePrefs(registry);
|
|
|
|
+ omnibox::RegisterProfilePrefs(registry);
|
|
|
|
+ ZeroSuggestProvider::RegisterProfilePrefs(registry);
|
|
|
|
++ user_scripts::UserScriptsPrefs::RegisterProfilePrefs(registry);
|
|
|
|
+
|
|
|
|
+ #if BUILDFLAG(ENABLE_SESSION_SERVICE)
|
|
|
|
+ RegisterSessionServiceLogProfilePrefs(registry);
|
|
|
|
+diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
|
|
|
|
+--- a/chrome/browser/profiles/BUILD.gn
|
|
|
|
++++ b/chrome/browser/profiles/BUILD.gn
|
|
|
|
+@@ -45,6 +45,7 @@ source_set("profile") {
|
|
|
|
+ "//components/profile_metrics",
|
|
|
|
+ "//components/sync/driver",
|
|
|
|
+ "//components/variations",
|
|
|
|
++ "//components/user_scripts/browser",
|
|
|
|
+ "//content/public/browser",
|
|
|
|
+ "//extensions/buildflags",
|
|
|
|
+ ]
|
|
|
|
+diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
|
|
|
|
+--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
|
|
|
|
++++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
|
|
|
|
+@@ -216,6 +216,8 @@
|
|
|
|
+ #include "chrome/browser/lacros/cert_db_initializer_factory.h"
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++#include "components/user_scripts/browser/userscripts_browser_client.h"
|
|
|
|
++
|
|
|
|
+ namespace chrome {
|
|
|
|
+
|
|
|
|
+ void AddProfilesExtraParts(ChromeBrowserMainParts* main_parts) {
|
|
|
|
+@@ -516,6 +518,7 @@ void ChromeBrowserMainExtraPartsProfiles::
|
|
|
|
+ #endif
|
|
|
|
+ WebDataServiceFactory::GetInstance();
|
|
|
|
+ webrtc_event_logging::WebRtcEventLogManagerKeyedServiceFactory::GetInstance();
|
|
|
|
++ user_scripts::UserScriptsBrowserClient::GetInstance();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void ChromeBrowserMainExtraPartsProfiles::PreProfileInit() {
|
|
|
|
+diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
|
|
|
|
+--- a/chrome/browser/profiles/profile_manager.cc
|
|
|
|
++++ b/chrome/browser/profiles/profile_manager.cc
|
|
|
|
+@@ -117,6 +117,8 @@
|
|
|
|
+ #include "extensions/common/manifest.h"
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++#include "components/user_scripts/browser/userscripts_browser_client.h"
|
|
|
|
++
|
|
|
|
+ #if BUILDFLAG(ENABLE_SESSION_SERVICE)
|
|
|
|
+ #include "chrome/browser/sessions/session_service_factory.h"
|
|
|
|
+ #endif
|
|
|
|
+@@ -1539,6 +1541,13 @@ void ProfileManager::DoFinalInitForServices(Profile* profile,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endif
|
|
|
|
++
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* userscript_client =
|
|
|
|
++ user_scripts::UserScriptsBrowserClient::GetInstance();
|
|
|
|
++ if (userscript_client) {
|
|
|
|
++ userscript_client->SetProfile(profile);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
+ #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
|
|
|
|
+ // Initialization needs to happen after extension system initialization (for
|
|
|
|
+ // extension::ManagementPolicy) and InitProfileUserPrefs (for setting the
|
|
|
|
+diff --git a/chrome/browser/profiles/renderer_updater.cc b/chrome/browser/profiles/renderer_updater.cc
|
|
|
|
+--- a/chrome/browser/profiles/renderer_updater.cc
|
|
|
|
++++ b/chrome/browser/profiles/renderer_updater.cc
|
|
|
|
+@@ -29,6 +29,8 @@
|
|
|
|
+ #include "chrome/browser/ash/login/signin/oauth2_login_manager_factory.h"
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++#include "components/user_scripts/browser/user_script_prefs.h"
|
|
|
|
++
|
|
|
|
+ namespace {
|
|
|
|
+
|
|
|
|
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
|
|
|
|
+@@ -79,6 +81,7 @@ RendererUpdater::RendererUpdater(Profile* profile) : profile_(profile) {
|
|
|
|
+ force_google_safesearch_.Init(prefs::kForceGoogleSafeSearch, pref_service);
|
|
|
|
+ force_youtube_restrict_.Init(prefs::kForceYouTubeRestrict, pref_service);
|
|
|
|
+ allowed_domains_for_apps_.Init(prefs::kAllowedDomainsForApps, pref_service);
|
|
|
|
++ activate_userscripts_.Init(user_scripts::prefs::kUserScriptsEnabled, pref_service);
|
|
|
|
+
|
|
|
|
+ pref_change_registrar_.Init(pref_service);
|
|
|
|
+ pref_change_registrar_.Add(
|
|
|
|
+@@ -93,6 +96,10 @@ RendererUpdater::RendererUpdater(Profile* profile) : profile_(profile) {
|
|
|
|
+ prefs::kAllowedDomainsForApps,
|
|
|
|
+ base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
|
|
|
|
+ base::Unretained(this)));
|
|
|
|
++ pref_change_registrar_.Add(
|
|
|
|
++ user_scripts::prefs::kUserScriptsEnabled,
|
|
|
|
++ base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
|
|
|
|
++ base::Unretained(this)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ RendererUpdater::~RendererUpdater() {
|
|
|
|
+@@ -241,5 +248,6 @@ void RendererUpdater::UpdateRenderer(
|
|
|
|
+ ->SetConfiguration(chrome::mojom::DynamicParams::New(
|
|
|
|
+ force_google_safesearch_.GetValue(),
|
|
|
|
+ force_youtube_restrict_.GetValue(),
|
|
|
|
+- allowed_domains_for_apps_.GetValue()));
|
|
|
|
++ allowed_domains_for_apps_.GetValue(),
|
|
|
|
++ activate_userscripts_.GetValue()));
|
|
|
|
+ }
|
|
|
|
+diff --git a/chrome/browser/profiles/renderer_updater.h b/chrome/browser/profiles/renderer_updater.h
|
|
|
|
+--- a/chrome/browser/profiles/renderer_updater.h
|
|
|
|
++++ b/chrome/browser/profiles/renderer_updater.h
|
|
|
|
+@@ -82,6 +82,7 @@ class RendererUpdater : public KeyedService,
|
|
|
|
+
|
|
|
|
+ // Prefs that we sync to the renderers.
|
|
|
|
+ BooleanPrefMember force_google_safesearch_;
|
|
|
|
++ BooleanPrefMember activate_userscripts_;
|
|
|
|
+ IntegerPrefMember force_youtube_restrict_;
|
|
|
|
+ StringPrefMember allowed_domains_for_apps_;
|
|
|
|
+
|
|
|
|
+diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
|
|
|
|
+--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
|
|
|
|
++++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
|
|
|
|
+@@ -85,6 +85,7 @@
|
|
|
|
+ #include "components/security_interstitials/content/urls.h"
|
|
|
|
+ #include "components/signin/public/base/signin_buildflags.h"
|
|
|
|
+ #include "components/site_engagement/content/site_engagement_service.h"
|
|
|
|
++#include "components/user_scripts/browser/ui/user_scripts_ui.h"
|
|
|
|
+ #include "content/public/browser/web_contents.h"
|
|
|
|
+ #include "content/public/browser/web_ui.h"
|
|
|
|
+ #include "content/public/common/content_client.h"
|
|
|
|
+@@ -678,6 +679,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
|
|
|
|
+ return &NewWebUI<UserActionsUI>;
|
|
|
|
+ if (url.host_piece() == chrome::kChromeUIVersionHost)
|
|
|
|
+ return &NewWebUI<VersionUI>;
|
|
|
|
++ if (url.host_piece() == user_scripts::kChromeUIUserScriptsHost)
|
|
|
|
++ return &NewWebUI<user_scripts::UserScriptsUI>;
|
|
|
|
+
|
|
|
|
+ #if !defined(OS_ANDROID)
|
|
|
|
+ #if !defined(OS_CHROMEOS)
|
|
|
|
+diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
|
|
|
|
+--- a/chrome/chrome_paks.gni
|
|
|
|
++++ b/chrome/chrome_paks.gni
|
|
|
|
+@@ -105,6 +105,7 @@ template("chrome_extra_paks") {
|
|
|
|
+ "$root_gen_dir/third_party/blink/public/resources/inspector_overlay_resources.pak",
|
|
|
|
+ "$root_gen_dir/ui/resources/webui_generated_resources.pak",
|
|
|
|
+ "$root_gen_dir/ui/resources/webui_resources.pak",
|
|
|
|
++ "$root_gen_dir/chrome/userscripts_browser_resources.pak",
|
|
|
|
+ ]
|
|
|
|
+ deps = [
|
|
|
|
+ "//base/tracing/protos:chrome_track_event_resources",
|
|
|
|
+@@ -120,6 +121,7 @@ template("chrome_extra_paks") {
|
|
|
|
+ "//third_party/blink/public:devtools_inspector_resources",
|
|
|
|
+ "//third_party/blink/public:resources",
|
|
|
|
+ "//ui/resources",
|
|
|
|
++ "//components/user_scripts/browser:userscripts_browser_resources_grit",
|
|
|
|
+ ]
|
|
|
|
+ if (defined(invoker.deps)) {
|
|
|
|
+ deps += invoker.deps
|
|
|
|
+diff --git a/chrome/common/renderer_configuration.mojom b/chrome/common/renderer_configuration.mojom
|
|
|
|
+--- a/chrome/common/renderer_configuration.mojom
|
|
|
|
++++ b/chrome/common/renderer_configuration.mojom
|
|
|
|
+@@ -12,6 +12,7 @@ struct DynamicParams {
|
|
|
|
+ bool force_safe_search = true;
|
|
|
|
+ int32 youtube_restrict = 0;
|
|
|
|
+ string allowed_domains_for_apps;
|
|
|
|
++ bool allow_userscript = false;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ interface ChromeOSListener {
|
|
|
|
+diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
|
|
|
|
+--- a/chrome/renderer/BUILD.gn
|
|
|
|
++++ b/chrome/renderer/BUILD.gn
|
|
|
|
+@@ -171,6 +171,7 @@ static_library("renderer") {
|
|
|
|
+ "//components/feed:feature_list",
|
|
|
|
+ "//components/feed/content/renderer:feed_renderer",
|
|
|
|
+ "//components/history_clusters/core",
|
|
|
|
++ "//components/user_scripts/renderer",
|
|
|
|
+ "//components/network_hints/renderer",
|
|
|
|
+ "//components/no_state_prefetch/common",
|
|
|
|
+ "//components/no_state_prefetch/renderer",
|
|
|
|
+diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
|
|
|
|
+--- a/chrome/renderer/chrome_content_renderer_client.cc
|
|
|
|
++++ b/chrome/renderer/chrome_content_renderer_client.cc
|
|
|
|
+@@ -254,6 +254,9 @@
|
|
|
|
+ #include "chrome/renderer/supervised_user/supervised_user_error_page_controller_delegate_impl.h"
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++#include "components/user_scripts/common/user_scripts_features.h"
|
|
|
|
++#include "components/user_scripts/renderer/user_scripts_renderer_client.h"
|
|
|
|
++
|
|
|
|
+ using autofill::AutofillAgent;
|
|
|
|
+ using autofill::AutofillAssistantAgent;
|
|
|
|
+ using autofill::PasswordAutofillAgent;
|
|
|
|
+@@ -419,6 +422,12 @@ void ChromeContentRendererClient::RenderThreadStarted() {
|
|
|
|
+ WebString::FromASCII(extensions::kExtensionScheme));
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++ user_scripts::UserScriptsRendererClient* userscript_client =
|
|
|
|
++ user_scripts::UserScriptsRendererClient::GetInstance();
|
|
|
|
++ if (userscript_client) {
|
|
|
|
++ userscript_client->RenderThreadStarted();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
+ #if BUILDFLAG(ENABLE_SPELLCHECK)
|
|
|
|
+ if (!spellcheck_)
|
|
|
|
+ InitSpellCheck();
|
|
|
|
+@@ -555,6 +564,13 @@ void ChromeContentRendererClient::RenderFrameCreated(
|
|
|
|
+ render_frame, registry);
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
++ user_scripts::UserScriptsRendererClient* userscript_client =
|
|
|
|
++ user_scripts::UserScriptsRendererClient::GetInstance();
|
|
|
|
++ if (userscript_client) {
|
|
|
|
++ userscript_client->RenderFrameCreated(
|
|
|
|
++ render_frame, registry);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
+ #if BUILDFLAG(ENABLE_PLUGINS)
|
|
|
|
+ new PepperHelper(render_frame);
|
|
|
|
+ #endif
|
|
|
|
+@@ -1546,7 +1562,14 @@ void ChromeContentRendererClient::RunScriptsAtDocumentStart(
|
|
|
|
+ ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentStart(
|
|
|
|
+ render_frame);
|
|
|
|
+ // |render_frame| might be dead by now.
|
|
|
|
++ static_assert(false, "Compiler error: extensions cannot be enabled with user scripts");
|
|
|
|
+ #endif
|
|
|
|
++ user_scripts::UserScriptsRendererClient* userscript_client =
|
|
|
|
++ user_scripts::UserScriptsRendererClient::GetInstance();
|
|
|
|
++ if (userscript_client) {
|
|
|
|
++ userscript_client->RunScriptsAtDocumentStart(
|
|
|
|
++ render_frame);
|
|
|
|
++ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void ChromeContentRendererClient::RunScriptsAtDocumentEnd(
|
|
|
|
+@@ -1555,7 +1578,14 @@ void ChromeContentRendererClient::RunScriptsAtDocumentEnd(
|
|
|
|
+ ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentEnd(
|
|
|
|
+ render_frame);
|
|
|
|
+ // |render_frame| might be dead by now.
|
|
|
|
++ static_assert(false, "Compiler error: extensions cannot be enabled with user scripts");
|
|
|
|
+ #endif
|
|
|
|
++ user_scripts::UserScriptsRendererClient* userscript_client =
|
|
|
|
++ user_scripts::UserScriptsRendererClient::GetInstance();
|
|
|
|
++ if (userscript_client) {
|
|
|
|
++ userscript_client->RunScriptsAtDocumentEnd(
|
|
|
|
++ render_frame);
|
|
|
|
++ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void ChromeContentRendererClient::RunScriptsAtDocumentIdle(
|
|
|
|
+@@ -1564,7 +1594,14 @@ void ChromeContentRendererClient::RunScriptsAtDocumentIdle(
|
|
|
|
+ ChromeExtensionsRendererClient::GetInstance()->RunScriptsAtDocumentIdle(
|
|
|
|
+ render_frame);
|
|
|
|
+ // |render_frame| might be dead by now.
|
|
|
|
++ static_assert(false, "Compiler error: extensions cannot be enabled with user scripts");
|
|
|
|
+ #endif
|
|
|
|
++ user_scripts::UserScriptsRendererClient* userscript_client =
|
|
|
|
++ user_scripts::UserScriptsRendererClient::GetInstance();
|
|
|
|
++ if (userscript_client) {
|
|
|
|
++ userscript_client->RunScriptsAtDocumentIdle(
|
|
|
|
++ render_frame);
|
|
|
|
++ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void ChromeContentRendererClient::
|
|
|
|
+diff --git a/chrome/renderer/chrome_render_thread_observer.cc b/chrome/renderer/chrome_render_thread_observer.cc
|
|
|
|
+--- a/chrome/renderer/chrome_render_thread_observer.cc
|
|
|
|
++++ b/chrome/renderer/chrome_render_thread_observer.cc
|
|
|
|
+@@ -59,6 +59,8 @@
|
|
|
|
+ #include "third_party/blink/public/web/web_security_policy.h"
|
|
|
|
+ #include "third_party/blink/public/web/web_view.h"
|
|
|
|
+
|
|
|
|
++#include "components/user_scripts/renderer/user_scripts_renderer_client.h"
|
|
|
|
++
|
|
|
|
+ #if BUILDFLAG(ENABLE_EXTENSIONS)
|
|
|
|
+ #include "chrome/renderer/extensions/extension_localization_peer.h"
|
|
|
|
+ #endif
|
|
|
|
+@@ -256,6 +258,7 @@ void ChromeRenderThreadObserver::SetInitialConfiguration(
|
|
|
|
+ void ChromeRenderThreadObserver::SetConfiguration(
|
|
|
|
+ chrome::mojom::DynamicParamsPtr params) {
|
|
|
|
+ *GetDynamicConfigParams() = std::move(*params);
|
|
|
|
++ user_scripts::UserScriptsRendererClient::GetInstance()->ConfigurationUpdated();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void ChromeRenderThreadObserver::SetContentSettingRules(
|
|
|
|
+diff --git a/components/components_strings.grd b/components/components_strings.grd
|
|
|
|
+--- a/components/components_strings.grd
|
|
|
|
++++ b/components/components_strings.grd
|
|
|
|
+@@ -335,6 +335,7 @@
|
|
|
|
+ <part file="undo_strings.grdp" />
|
|
|
|
+ <part file="version_ui_strings.grdp" />
|
|
|
|
+ <part file="webapps_strings.grdp" />
|
|
|
|
++ <part file="user_scripts/strings/userscripts_strings.grdp" />
|
|
|
|
+
|
|
|
|
+ <if expr="not is_android and not is_ios">
|
|
|
|
+ <part file="management_strings.grdp" />
|
|
|
|
+diff --git a/components/user_scripts/README.md b/components/user_scripts/README.md
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/README.md
|
|
|
|
+@@ -0,0 +1,150 @@
|
|
|
|
++# Userscripts support for Bromite
|
|
|
|
++
|
|
|
|
++UserScript support is under user setting currently disabled by default: when disabled, no code that can impact navigation safety is active.
|
|
|
|
++
|
|
|
|
++Activation allows the use of userscripts in Bromite. It is possible to add them in two ways:
|
|
|
|
++- by selecting files from the file picker in the settings
|
|
|
|
++- downloading the scripts and opening it from downloads (only if ends with .user.js)
|
|
|
|
++The new imported scripts are disabled by default: they can be activated via the menu visible on the ui.
|
|
|
|
++
|
|
|
|
++Userscript support is currently the one provided by the desktop version. The enabled headers are:
|
|
|
|
++
|
|
|
|
++- `@name`
|
|
|
|
++- `@version`
|
|
|
|
++- `@description`
|
|
|
|
++- `@url` or `@homepage`
|
|
|
|
++- `@include`, `@exclude`, `@match`, `@exclude_match` for the url pattern (only http e https)
|
|
|
|
++- `@run-at`
|
|
|
|
++ - `document-start`
|
|
|
|
++ Start the script after the documentElement is created, but before anything else happens
|
|
|
|
++ - `document-end`
|
|
|
|
++ Start the script after the entire document is parsed. Same as DOMContentLoaded
|
|
|
|
++ - `document-idle`
|
|
|
|
++ Start the script sometime after DOMContentLoaded, as soon as the document is "idle". Currently this uses the simple heuristic of: min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no particular injection point is guaranteed
|
|
|
|
++
|
|
|
|
++The url-patterns are so defined:
|
|
|
|
++```
|
|
|
|
++// <url-pattern> := <scheme>://<host><port><path> | '<all_urls>'
|
|
|
|
++// <scheme> := '*' | 'http' | 'https'
|
|
|
|
++// <host> := '*' | <IPv4 address> | [<IPv6 address>] |
|
|
|
|
++// '*.' <anychar except '/' and '*'>+
|
|
|
|
++// <port> := [':' ('*' | <port number between 0 and 65535>)]
|
|
|
|
++// <path> := '/' <any chars>
|
|
|
|
++//
|
|
|
|
++// * Host is not used when the scheme is 'file'.
|
|
|
|
++// * The path can have embedded '*' characters which act as glob wildcards.
|
|
|
|
++// * '<all_urls>' is a special pattern that matches any valid URL that contains
|
|
|
|
++// a valid scheme (as specified by valid_schemes_).
|
|
|
|
++// * The '*' scheme pattern excludes file URLs.
|
|
|
|
++//
|
|
|
|
++// Examples of valid patterns:
|
|
|
|
++// - http://*/*
|
|
|
|
++// - http://*/foo*
|
|
|
|
++// - https://*.google.com/foo*bar
|
|
|
|
++// - file://monkey*
|
|
|
|
++// - http://127.0.0.1/*
|
|
|
|
++// - http://[2607:f8b0:4005:805::200e]/*
|
|
|
|
++//
|
|
|
|
++// Examples of invalid patterns:
|
|
|
|
++// - http://* -- path not specified
|
|
|
|
++// - http://*foo/bar -- * not allowed as substring of host component
|
|
|
|
++// - http://foo.*.bar/baz -- * must be first component
|
|
|
|
++// - http:/bar -- scheme separator not found
|
|
|
|
++// - foo://* -- invalid scheme
|
|
|
|
++// - chrome:// -- we don't support chrome internal URLs
|
|
|
|
++```
|
|
|
|
++
|
|
|
|
++---
|
|
|
|
++## **Beware of the scripts you enter: they can be a source of security problems, you are injecting code into your navigation**.
|
|
|
|
++---
|
|
|
|
++## Technical aspects
|
|
|
|
++
|
|
|
|
++`user_scripts/common` and `user_scripts/renderer` is the closest to the current chromium code: few changes there, mostly eliminated the superfluous extension related code.
|
|
|
|
++
|
|
|
|
++In `user_scripts/browser` you find the actual management (in the browser process) and in `android` basically the settings ui.
|
|
|
|
++
|
|
|
|
++At startup it tries to read all files in the `userscripts folder` in `/data/user/0/org.bromite.bromite/app_chrome/userscripts`: this could be a critical process because a crash would prevent the browser from opening, and that's why there is a crash counter that automatically disables the feature after three attempts if it encounters a problem during startup.
|
|
|
|
++
|
|
|
|
++The java ui allows userscript management: addition, deletion and activation/deactivation. Any errors while reading the scripts are presented to the user: scripts with errors cannot be activated. There is also the visualization of the script source and the open of its homepage (if foreseen) in incognito browsing.
|
|
|
|
++
|
|
|
|
++There is also support for an on-line help at https://github.com/bromite/bromite/wiki/UserScripts.
|
|
|
|
++
|
|
|
|
++
|
|
|
|
++Entry points are `components/user_scripts/browser/userscripts_browser_client.cc` and `components/user_scripts/renderer/user_scripts_renderer_client.cc`: the two attach to the browser and the renderer process.
|
|
|
|
++
|
|
|
|
++for userscripts_browser_client.cc
|
|
|
|
++- `chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc`
|
|
|
|
++builds the browser side. also gpu process passes here, but the call is avoided.
|
|
|
|
++
|
|
|
|
++- `chrome/browser/profiles/profile_manager.cc`
|
|
|
|
++set the profile
|
|
|
|
++
|
|
|
|
++for renderer/user_scripts_renderer_client.cc
|
|
|
|
++- `chrome/renderer/chrome_content_renderer_client.cc`
|
|
|
|
++at the renderer side
|
|
|
|
++
|
|
|
|
++the two sides have a life of their own and can communicate only via ipc, the renderer does not have access to the disk while the browser does only via its own task runner file (`components/user_scripts/browser/file_task_runner.cc`).
|
|
|
|
++
|
|
|
|
++## BROWSER PROCESS
|
|
|
|
++Once the profile is set, it istance the `components/user_scripts/browser/user_script_loader.cc` and starts it.
|
|
|
|
++This loads all the files in the folder into the runner file and interprets them. Control then passes to
|
|
|
|
++`components/user_scripts/browser/user_script_prefs.cc` which verifies through the default profile what the user wants active.
|
|
|
|
++At that point it passes through IPC to the renderer only the list of active scripts.
|
|
|
|
++
|
|
|
|
++## RENDERER PROCESS
|
|
|
|
++Each time a frame is created, the script pattern is checked and it is injected into the three stages (START, IDLE, END).
|
|
|
|
++The logic is all in `components/user_scripts/renderer/user_script_set.cc`.
|
|
|
|
++
|
|
|
|
++## Simple example
|
|
|
|
++Here you find a working example that eliminates the google popup, useful in always incognito:
|
|
|
|
++```
|
|
|
|
++// ==UserScript==
|
|
|
|
++// @name Remove Google Consent
|
|
|
|
++// @namespace google
|
|
|
|
++// @version 0.0.1
|
|
|
|
++// @description Autohide Accepts Cookies
|
|
|
|
++// @author uazo
|
|
|
|
++// @match https://*.google.com/search?*
|
|
|
|
++// @grant none
|
|
|
|
++// @run-at document-start
|
|
|
|
++// ==/UserScript==
|
|
|
|
++
|
|
|
|
++(function() {
|
|
|
|
++ 'use strict';
|
|
|
|
++
|
|
|
|
++ var prepareStyleSheet = function() {
|
|
|
|
++ var style = document.createElement('style');
|
|
|
|
++ //style.setAttribute('media', 'screen');
|
|
|
|
++ style.appendChild(document.createTextNode(''));
|
|
|
|
++ document.head.appendChild(style);
|
|
|
|
++ style.sheet.insertRule('body { overflow:scroll !important;position:unset !important }');
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ var hideConsent = function() {
|
|
|
|
++ document.getElementById("lb").style.display = "none";
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ var checkElementThenRun = function(selector, func) {
|
|
|
|
++ var el = document.querySelector(selector);
|
|
|
|
++ if ( el == null ) {
|
|
|
|
++ if (window.requestAnimationFrame != undefined) {
|
|
|
|
++ window.requestAnimationFrame(function(){ checkElementThenRun(selector, func)});
|
|
|
|
++ } else {
|
|
|
|
++ document.addEventListener('readystatechange', function(e) {
|
|
|
|
++ if (document.readyState == 'complete') {
|
|
|
|
++ func();
|
|
|
|
++ }
|
|
|
|
++ });
|
|
|
|
++ }
|
|
|
|
++ } else {
|
|
|
|
++ func();
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ document.cookie = 'CONSENT=YES+IT.it+V13+BX;domain=.google.com';
|
|
|
|
++ checkElementThenRun('head', prepareStyleSheet);
|
|
|
|
++ checkElementThenRun('#lb', hideConsent);
|
|
|
|
++})();
|
|
|
|
++```
|
|
|
|
++
|
|
|
|
++See also: https://github.com/bromite/bromite/pull/857
|
|
|
|
+diff --git a/components/user_scripts/android/BUILD.gn b/components/user_scripts/android/BUILD.gn
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/BUILD.gn
|
|
|
|
+@@ -0,0 +1,80 @@
|
|
|
|
++# This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++# Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++# it under the terms of the GNU General Public License as published by
|
|
|
|
++# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++# (at your option) any later version.
|
|
|
|
++
|
|
|
|
++# Bromite is distributed in the hope that it will be useful,
|
|
|
|
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++# GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++# You should have received a copy of the GNU General Public License
|
|
|
|
++# along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++
|
|
|
|
++import("//build/config/android/rules.gni")
|
|
|
|
++
|
|
|
|
++generate_jni("user_scripts_jni_headers") {
|
|
|
|
++ sources = [ "java/src/org/chromium/components/user_scripts/UserScriptsBridge.java" ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++android_resources("java_resources") {
|
|
|
|
++ sources = [
|
|
|
|
++ "java/res/xml/userscripts_preferences.xml",
|
|
|
|
++ "java/res/layout/accept_script_item.xml",
|
|
|
|
++ "java/res/layout/accept_script_list.xml",
|
|
|
|
++ "java/res/layout/scripts_preference.xml",
|
|
|
|
++ "java/res/values/dimens.xml"
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ deps = [
|
|
|
|
++ "//components/browser_ui/strings/android:browser_ui_strings_grd",
|
|
|
|
++ "//components/browser_ui/styles/android:java_resources",
|
|
|
|
++ "//components/strings:components_strings_grd",
|
|
|
|
++ "//ui/android:ui_java_resources",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++android_library("java") {
|
|
|
|
++ sources = [
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java",
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java",
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/UserScriptsBridge.java",
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java",
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java",
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/ScriptListPreference.java",
|
|
|
|
++ "java/src/org/chromium/components/user_scripts/ScriptInfo.java",
|
|
|
|
++ ]
|
|
|
|
++ deps = [
|
|
|
|
++ ":java_resources",
|
|
|
|
++ "//base:base_java",
|
|
|
|
++ "//base:jni_java",
|
|
|
|
++ "//components/embedder_support/android:browser_context_java",
|
|
|
|
++ "//components/browser_ui/settings/android:java",
|
|
|
|
++ "//components/browser_ui/widget/android:java",
|
|
|
|
++ "//content/public/android:content_java",
|
|
|
|
++ "//components/prefs/android:java",
|
|
|
|
++ "//third_party/android_deps:android_support_v7_appcompat_java",
|
|
|
|
++ "//third_party/androidx:androidx_annotation_annotation_java",
|
|
|
|
++ "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
|
|
|
|
++ "//third_party/androidx:androidx_preference_preference_java",
|
|
|
|
++ "//ui/android:ui_java",
|
|
|
|
++ ]
|
|
|
|
++ resources_package = "org.chromium.components.user_scripts"
|
|
|
|
++ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++source_set("android") {
|
|
|
|
++ sources = [
|
|
|
|
++ "user_scripts_bridge.cc",
|
|
|
|
++ "user_scripts_bridge.h",
|
|
|
|
++ ]
|
|
|
|
++ deps = [
|
|
|
|
++ ":user_scripts_jni_headers",
|
|
|
|
++ "//base",
|
|
|
|
++ "//components/user_scripts/browser",
|
|
|
|
++ "//components/permissions",
|
|
|
|
++ "//content/public/browser",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/android/java/res/layout/accept_script_item.xml b/components/user_scripts/android/java/res/layout/accept_script_item.xml
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/res/layout/accept_script_item.xml
|
|
|
|
+@@ -0,0 +1,160 @@
|
|
|
|
++<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
++<!-- Copyright 2017 The Chromium Authors. All rights reserved.
|
|
|
|
++ Use of this source code is governed by a BSD-style license that can be
|
|
|
|
++ found in the LICENSE file. -->
|
|
|
|
++
|
|
|
|
++<LinearLayout
|
|
|
|
++ xmlns:android="http://schemas.android.com/apk/res/android"
|
|
|
|
++ xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
|
|
++ xmlns:tools="http://schemas.android.com/tools"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:orientation="vertical"
|
|
|
|
++ android:layout_gravity="center_vertical" >
|
|
|
|
++
|
|
|
|
++ <LinearLayout
|
|
|
|
++ android:layout_marginStart="0dp"
|
|
|
|
++ android:paddingStart="@dimen/draggable_list_item_padding"
|
|
|
|
++ android:paddingEnd="@dimen/draggable_list_item_padding"
|
|
|
|
++ style="@style/ListItemContainer">
|
|
|
|
++
|
|
|
|
++ <Switch
|
|
|
|
++ android:id="@+id/switch_widget"
|
|
|
|
++ android:layout_width="wrap_content"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:paddingEnd="15dp"
|
|
|
|
++ android:focusable="false"
|
|
|
|
++ android:background="@null" />
|
|
|
|
++
|
|
|
|
++ <LinearLayout
|
|
|
|
++ android:layout_width="0dp"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:layout_weight="1"
|
|
|
|
++ android:orientation="vertical"
|
|
|
|
++ android:layout_gravity="center_vertical" >
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/title"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ style="@style/PreferenceTitle" />
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/description"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ style="@style/PreferenceSummary" />
|
|
|
|
++
|
|
|
|
++ <LinearLayout
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:layout_weight="1"
|
|
|
|
++ android:paddingTop="5dp"
|
|
|
|
++ android:layout_gravity="center_vertical" >
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:layout_width="wrap_content"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:paddingEnd="5dp"
|
|
|
|
++ style="@style/PreferenceSummary"
|
|
|
|
++ android:text="@string/scripts_item_version"
|
|
|
|
++ android:textStyle="bold" />
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/version"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ style="@style/PreferenceSummary" />
|
|
|
|
++
|
|
|
|
++ </LinearLayout>
|
|
|
|
++
|
|
|
|
++ <LinearLayout
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:layout_weight="1"
|
|
|
|
++ android:layout_gravity="center_vertical" >
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:layout_width="wrap_content"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:paddingEnd="5dp"
|
|
|
|
++ style="@style/PreferenceSummary"
|
|
|
|
++ android:text="@string/scripts_item_filename"
|
|
|
|
++ android:textStyle="bold" />
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/file"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ style="@style/PreferenceSummary" />
|
|
|
|
++
|
|
|
|
++ </LinearLayout>
|
|
|
|
++
|
|
|
|
++ <LinearLayout
|
|
|
|
++ android:id="@+id/url_container"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:layout_weight="1"
|
|
|
|
++ android:layout_gravity="center_vertical" >
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:layout_width="wrap_content"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:paddingEnd="5dp"
|
|
|
|
++ style="@style/PreferenceSummary"
|
|
|
|
++ android:text="@string/scripts_item_url"
|
|
|
|
++ android:textStyle="bold" />
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/url"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:autoLink="web"
|
|
|
|
++ android:focusable="true"
|
|
|
|
++ android:linksClickable="true" />
|
|
|
|
++
|
|
|
|
++ </LinearLayout>
|
|
|
|
++
|
|
|
|
++ </LinearLayout>
|
|
|
|
++
|
|
|
|
++ <org.chromium.components.browser_ui.widget.listmenu.ListMenuButton
|
|
|
|
++ android:id="@+id/more"
|
|
|
|
++ android:layout_width="wrap_content"
|
|
|
|
++ android:layout_height="match_parent"
|
|
|
|
++ android:paddingStart="@dimen/default_list_row_padding"
|
|
|
|
++ android:paddingEnd="@dimen/default_list_row_padding"
|
|
|
|
++ android:background="@null"
|
|
|
|
++ android:src="@drawable/ic_more_vert_24dp"
|
|
|
|
++ app:menuMaxWidth="@dimen/pref_scripts_item_popup_width"
|
|
|
|
++ app:tint="@color/default_icon_color_tint_list"
|
|
|
|
++ tools:ignore="ContentDescription" />
|
|
|
|
++
|
|
|
|
++ </LinearLayout>
|
|
|
|
++
|
|
|
|
++ <LinearLayout
|
|
|
|
++ android:id="@+id/error_layout"
|
|
|
|
++ android:layout_marginStart="0dp"
|
|
|
|
++ android:paddingStart="@dimen/draggable_list_item_padding"
|
|
|
|
++ android:paddingEnd="2dp"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ style="@style/ListItemContainer">
|
|
|
|
++
|
|
|
|
++ <org.chromium.ui.widget.ChromeImageView
|
|
|
|
++ android:id="@+id/icon"
|
|
|
|
++ android:layout_width="40dp"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:paddingEnd="15dp"
|
|
|
|
++ android:adjustViewBounds="true"
|
|
|
|
++ android:importantForAccessibility="no"
|
|
|
|
++ app:srcCompat="@drawable/ic_error_outline_red_24dp"/>
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/error"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:textColor="#F00"
|
|
|
|
++ style="@style/PreferenceSummary" />
|
|
|
|
++
|
|
|
|
++ </LinearLayout>
|
|
|
|
++</LinearLayout>
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/res/layout/accept_script_list.xml b/components/user_scripts/android/java/res/layout/accept_script_list.xml
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/res/layout/accept_script_list.xml
|
|
|
|
+@@ -0,0 +1,10 @@
|
|
|
|
++<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
++<!-- Copyright 2017 The Chromium Authors. All rights reserved.
|
|
|
|
++ Use of this source code is governed by a BSD-style license that can be
|
|
|
|
++ found in the LICENSE file. -->
|
|
|
|
++
|
|
|
|
++<androidx.recyclerview.widget.RecyclerView
|
|
|
|
++ xmlns:android="http://schemas.android.com/apk/res/android"
|
|
|
|
++ android:id="@+id/script_list"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content" />
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/res/layout/scripts_preference.xml b/components/user_scripts/android/java/res/layout/scripts_preference.xml
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/res/layout/scripts_preference.xml
|
|
|
|
+@@ -0,0 +1,40 @@
|
|
|
|
++<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
++<!-- Copyright 2017 The Chromium Authors. All rights reserved.
|
|
|
|
++ Use of this source code is governed by a BSD-style license that can be
|
|
|
|
++ found in the LICENSE file. -->
|
|
|
|
++
|
|
|
|
++<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
|
|
++ android:id="@+id/accept_scripts_list_container"
|
|
|
|
++ style="@style/PreferenceLayout"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:paddingStart="0dp"
|
|
|
|
++ android:paddingEnd="0dp"
|
|
|
|
++ android:padding="0dp"
|
|
|
|
++ android:orientation="vertical" >
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:padding="@dimen/draggable_list_item_padding"
|
|
|
|
++ android:text="@string/scripts_list_description" />
|
|
|
|
++
|
|
|
|
++ <FrameLayout
|
|
|
|
++ android:id="@android:id/widget_frame"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content" />
|
|
|
|
++
|
|
|
|
++ <TextView
|
|
|
|
++ android:id="@+id/add_script"
|
|
|
|
++ android:layout_width="match_parent"
|
|
|
|
++ android:layout_height="wrap_content"
|
|
|
|
++ android:background="?attr/selectableItemBackground"
|
|
|
|
++ android:clickable="true"
|
|
|
|
++ android:gravity="center_vertical"
|
|
|
|
++ android:padding="@dimen/draggable_list_item_padding"
|
|
|
|
++ android:paddingStart="@dimen/pref_scripts_add_button_padding"
|
|
|
|
++ android:drawablePadding="@dimen/pref_scripts_add_button_padding"
|
|
|
|
++ android:text="@string/add_script"
|
|
|
|
++ style="@style/PreferenceTitle" />
|
|
|
|
++
|
|
|
|
++</LinearLayout>
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/res/values/dimens.xml b/components/user_scripts/android/java/res/values/dimens.xml
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/res/values/dimens.xml
|
|
|
|
+@@ -0,0 +1,11 @@
|
|
|
|
++<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
++<!-- 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. -->
|
|
|
|
++
|
|
|
|
++<resources xmlns:tools="http://schemas.android.com/tools">
|
|
|
|
++
|
|
|
|
++ <dimen name="pref_scripts_add_button_padding">24dp</dimen>
|
|
|
|
++ <dimen name="pref_scripts_item_popup_width">260dp</dimen>
|
|
|
|
++
|
|
|
|
++</resources>
|
|
|
|
+diff --git a/components/user_scripts/android/java/res/xml/userscripts_preferences.xml b/components/user_scripts/android/java/res/xml/userscripts_preferences.xml
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/res/xml/userscripts_preferences.xml
|
|
|
|
+@@ -0,0 +1,34 @@
|
|
|
|
++<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
++<!--
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++-->
|
|
|
|
++
|
|
|
|
++<PreferenceScreen
|
|
|
|
++ xmlns:android="http://schemas.android.com/apk/res/android"
|
|
|
|
++ xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
|
|
++
|
|
|
|
++ <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
|
|
|
|
++ android:key="enabled_switch"
|
|
|
|
++ android:title="@string/option_userscript_flag"
|
|
|
|
++ android:summaryOn="@string/option_userscript_flag_on"
|
|
|
|
++ android:summaryOff="@string/option_userscript_flag_off" />
|
|
|
|
++
|
|
|
|
++ <org.chromium.components.user_scripts.ScriptListPreference
|
|
|
|
++ android:key="script_list"
|
|
|
|
++ android:layout="@layout/scripts_preference"
|
|
|
|
++ android:widgetLayout="@layout/accept_script_list" />
|
|
|
|
++
|
|
|
|
++</PreferenceScreen>
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java b/components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java
|
|
|
|
+@@ -0,0 +1,84 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.chrome.browser.user_scripts;
|
|
|
|
++
|
|
|
|
++import android.content.Context;
|
|
|
|
++import android.content.Intent;
|
|
|
|
++import android.provider.Browser;
|
|
|
|
++import android.provider.MediaStore;
|
|
|
|
++import android.net.Uri;
|
|
|
|
++
|
|
|
|
++import org.chromium.base.ContextUtils;
|
|
|
|
++import org.chromium.base.ContentUriUtils;
|
|
|
|
++import org.chromium.base.IntentUtils;
|
|
|
|
++
|
|
|
|
++import org.chromium.chrome.browser.IntentHandler;
|
|
|
|
++import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
|
|
|
|
++import org.chromium.components.browser_ui.settings.SettingsLauncher;
|
|
|
|
++
|
|
|
|
++import org.chromium.components.user_scripts.UserScriptsPreferences;
|
|
|
|
++import org.chromium.components.user_scripts.UserScriptsBridge;
|
|
|
|
++import org.chromium.components.user_scripts.IUserScriptsUtils;
|
|
|
|
++
|
|
|
|
++public class UserScriptsUtils implements IUserScriptsUtils
|
|
|
|
++{
|
|
|
|
++ private static UserScriptsUtils instance;
|
|
|
|
++
|
|
|
|
++ private UserScriptsUtils() {}
|
|
|
|
++
|
|
|
|
++ public static void Initialize() {
|
|
|
|
++ instance = new UserScriptsUtils();
|
|
|
|
++ UserScriptsBridge.registerUtils(instance);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static UserScriptsUtils getInstance() {
|
|
|
|
++ return instance;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public boolean openFile(String filePath, String mimeType, String downloadGuid,
|
|
|
|
++ String originalUrl, String referrer, Uri contentUri) {
|
|
|
|
++ if (UserScriptsBridge.isEnabled() == false) return false;
|
|
|
|
++
|
|
|
|
++ Context context = ContextUtils.getApplicationContext();
|
|
|
|
++
|
|
|
|
++ if (ContentUriUtils.isContentUri(filePath))
|
|
|
|
++ filePath = ContentUriUtils.getDisplayName(contentUri, context,
|
|
|
|
++ MediaStore.MediaColumns.DISPLAY_NAME);
|
|
|
|
++
|
|
|
|
++ if (filePath.toUpperCase().endsWith(".USER.JS") == false) return false;
|
|
|
|
++
|
|
|
|
++ SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
|
|
|
|
++ Intent intent = settingsLauncher.createSettingsActivityIntent(
|
|
|
|
++ context, UserScriptsPreferences.class.getName(),
|
|
|
|
++ UserScriptsPreferences.createFragmentArgsForInstall(filePath));
|
|
|
|
++ IntentUtils.safeStartActivity(context, intent);
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public void openSourceFile(String scriptKey) {
|
|
|
|
++ Context context = ContextUtils.getApplicationContext();
|
|
|
|
++
|
|
|
|
++ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("chrome://user-scripts/?key=" + scriptKey));
|
|
|
|
++ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
|
|
|
|
++ intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
|
|
|
|
++ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
++ intent.setPackage(context.getPackageName());
|
|
|
|
++ IntentHandler.startChromeLauncherActivityForTrustedIntent(intent);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/FragmentWindowAndroid.java
|
|
|
|
+@@ -0,0 +1,89 @@
|
|
|
|
++// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
|
|
++// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
++// found in the LICENSE file.
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++import android.annotation.TargetApi;
|
|
|
|
++import android.app.Activity;
|
|
|
|
++import android.content.Context;
|
|
|
|
++import android.content.Intent;
|
|
|
|
++import android.content.IntentSender;
|
|
|
|
++import android.os.Build;
|
|
|
|
++import android.view.View;
|
|
|
|
++
|
|
|
|
++import androidx.fragment.app.Fragment;
|
|
|
|
++
|
|
|
|
++import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
|
|
|
|
++import org.chromium.ui.base.ActivityAndroidPermissionDelegate;
|
|
|
|
++import org.chromium.ui.base.ImmutableWeakReference;
|
|
|
|
++import org.chromium.ui.base.IntentRequestTracker;
|
|
|
|
++import org.chromium.ui.base.IntentRequestTracker.Delegate;
|
|
|
|
++import org.chromium.ui.base.WindowAndroid;
|
|
|
|
++
|
|
|
|
++import java.lang.ref.WeakReference;
|
|
|
|
++
|
|
|
|
++/**
|
|
|
|
++ * Implements intent sending for a fragment based window. This should be created when
|
|
|
|
++ * onAttach() is called on the fragment, and destroyed when onDetach() is called.
|
|
|
|
++ */
|
|
|
|
++public class FragmentWindowAndroid extends WindowAndroid {
|
|
|
|
++ private Fragment mFragment;
|
|
|
|
++
|
|
|
|
++ private static class TrackerDelegateImpl implements Delegate {
|
|
|
|
++ private final Fragment mFragment;
|
|
|
|
++ // This WeakReference is purely to avoid gc churn of creating a new WeakReference in
|
|
|
|
++ // every getActivity call. It is not needed for correctness.
|
|
|
|
++ private ImmutableWeakReference<Activity> mActivityWeakRefHolder;
|
|
|
|
++
|
|
|
|
++ /**
|
|
|
|
++ * Create an instance of delegate for the given fragment that will own the
|
|
|
|
++ * IntentRequestTracker.
|
|
|
|
++ * @param fragment The fragment that owns the IntentRequestTracker.
|
|
|
|
++ */
|
|
|
|
++ private TrackerDelegateImpl(Fragment fragment) {
|
|
|
|
++ mFragment = fragment;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public boolean startActivityForResult(Intent intent, int requestCode) {
|
|
|
|
++ mFragment.startActivityForResult(intent, requestCode, null);
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public boolean startIntentSenderForResult(IntentSender intentSender, int requestCode) {
|
|
|
|
++ try {
|
|
|
|
++ mFragment.startIntentSenderForResult(
|
|
|
|
++ intentSender, requestCode, new Intent(), 0, 0, 0, null);
|
|
|
|
++ } catch (IntentSender.SendIntentException e) {
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void finishActivity(int requestCode) {
|
|
|
|
++ Activity activity = getActivity().get();
|
|
|
|
++ if (activity == null) return;
|
|
|
|
++ activity.finishActivity(requestCode);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public final WeakReference<Activity> getActivity() {
|
|
|
|
++ if (mActivityWeakRefHolder == null
|
|
|
|
++ || mActivityWeakRefHolder.get() != mFragment.getActivity()) {
|
|
|
|
++ mActivityWeakRefHolder = new ImmutableWeakReference<>(mFragment.getActivity());
|
|
|
|
++ }
|
|
|
|
++ return mActivityWeakRefHolder;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ FragmentWindowAndroid(Context context, Fragment fragment) {
|
|
|
|
++ super(context, IntentRequestTracker.createFromDelegate(new TrackerDelegateImpl(fragment)));
|
|
|
|
++ mFragment = fragment;
|
|
|
|
++
|
|
|
|
++ setKeyboardDelegate(new ActivityKeyboardVisibilityDelegate(getActivity()));
|
|
|
|
++ setAndroidPermissionDelegate(new ActivityAndroidPermissionDelegate(getActivity()));
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/IUserScriptsUtils.java
|
|
|
|
+@@ -0,0 +1,22 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++public interface IUserScriptsUtils {
|
|
|
|
++ public void openSourceFile(String scriptKey);
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptInfo.java
|
|
|
|
+@@ -0,0 +1,37 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++import java.time.LocalDateTime;
|
|
|
|
++
|
|
|
|
++public class ScriptInfo {
|
|
|
|
++ public String Key;
|
|
|
|
++ public String Name;
|
|
|
|
++ public String Description;
|
|
|
|
++ public String Version;
|
|
|
|
++ public String FilePath;
|
|
|
|
++ public String UrlSource;
|
|
|
|
++
|
|
|
|
++ public boolean Enabled;
|
|
|
|
++ public LocalDateTime InstallTime;
|
|
|
|
++
|
|
|
|
++ public String ParserError;
|
|
|
|
++ public boolean ForceDisabled;
|
|
|
|
++
|
|
|
|
++ public ScriptInfo() {}
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListBaseAdapter.java
|
|
|
|
+@@ -0,0 +1,163 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++import android.content.Context;
|
|
|
|
++import android.view.LayoutInflater;
|
|
|
|
++import android.view.MotionEvent;
|
|
|
|
++import android.view.View;
|
|
|
|
++import android.view.ViewGroup;
|
|
|
|
++import android.view.accessibility.AccessibilityManager;
|
|
|
|
++import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
|
|
|
|
++import android.widget.ImageView;
|
|
|
|
++import android.widget.TextView;
|
|
|
|
++import android.widget.Switch;
|
|
|
|
++import android.widget.CompoundButton;
|
|
|
|
++import android.widget.LinearLayout;
|
|
|
|
++
|
|
|
|
++import androidx.annotation.DrawableRes;
|
|
|
|
++import androidx.annotation.NonNull;
|
|
|
|
++import androidx.core.view.ViewCompat;
|
|
|
|
++import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
|
|
|
++
|
|
|
|
++import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter;
|
|
|
|
++import org.chromium.components.browser_ui.widget.dragreorder.DragStateDelegate;
|
|
|
|
++import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton;
|
|
|
|
++import org.chromium.components.browser_ui.widget.listmenu.ListMenuButtonDelegate;
|
|
|
|
++import org.chromium.ui.widget.ChromeImageView;
|
|
|
|
++
|
|
|
|
++import java.util.ArrayList;
|
|
|
|
++import java.util.List;
|
|
|
|
++
|
|
|
|
++public class ScriptListBaseAdapter extends DragReorderableListAdapter<ScriptInfo> {
|
|
|
|
++
|
|
|
|
++ class ItemClickListener {
|
|
|
|
++ void onScriptOnOff(boolean Enabled) {}
|
|
|
|
++ void onScriptClicked() {}
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ static class ScriptInfoRowViewHolder extends ViewHolder {
|
|
|
|
++ private TextView mTitle;
|
|
|
|
++ private TextView mDescription;
|
|
|
|
++ private TextView mVersion;
|
|
|
|
++ private TextView mFile;
|
|
|
|
++ private TextView mUrl;
|
|
|
|
++ private TextView mError;
|
|
|
|
++ private Switch mSwitch;
|
|
|
|
++ private ChromeImageView mIcon;
|
|
|
|
++ private LinearLayout mErrorLayout;
|
|
|
|
++ private LinearLayout mUrlContainer;
|
|
|
|
++
|
|
|
|
++ private ListMenuButton mMoreButton;
|
|
|
|
++
|
|
|
|
++ private CompoundButton.OnCheckedChangeListener mOnOffListener;
|
|
|
|
++
|
|
|
|
++ ScriptInfoRowViewHolder(View view) {
|
|
|
|
++ super(view);
|
|
|
|
++
|
|
|
|
++ mSwitch = view.findViewById(R.id.switch_widget);
|
|
|
|
++ mTitle = view.findViewById(R.id.title);
|
|
|
|
++ mDescription = view.findViewById(R.id.description);
|
|
|
|
++ mVersion = view.findViewById(R.id.version);
|
|
|
|
++ mFile = view.findViewById(R.id.file);
|
|
|
|
++ mUrl = view.findViewById(R.id.url);
|
|
|
|
++ mUrlContainer = view.findViewById(R.id.url_container);
|
|
|
|
++ mError = view.findViewById(R.id.error);
|
|
|
|
++ mIcon = view.findViewById(R.id.icon);
|
|
|
|
++ mErrorLayout = view.findViewById(R.id.error_layout);
|
|
|
|
++
|
|
|
|
++ mMoreButton = view.findViewById(R.id.more);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ protected void updateScriptInfo(ScriptInfo item) {
|
|
|
|
++ mSwitch.setOnCheckedChangeListener(null);
|
|
|
|
++ mSwitch.setChecked(item.Enabled);
|
|
|
|
++ mSwitch.setOnCheckedChangeListener(mOnOffListener);
|
|
|
|
++
|
|
|
|
++ mSwitch.setEnabled(true);
|
|
|
|
++ if (item.ForceDisabled) {
|
|
|
|
++ mSwitch.setEnabled(false);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ mTitle.setText(item.Name);
|
|
|
|
++ mDescription.setText(item.Description);
|
|
|
|
++ mVersion.setText(item.Version);
|
|
|
|
++ mFile.setText(item.Key);
|
|
|
|
++ mUrl.setText(item.UrlSource);
|
|
|
|
++ mError.setText(item.ParserError);
|
|
|
|
++
|
|
|
|
++ mUrl.setVisibility(View.VISIBLE);
|
|
|
|
++ if (item.UrlSource == null || item.UrlSource.isEmpty()) {
|
|
|
|
++ mUrlContainer.setVisibility(View.GONE);
|
|
|
|
++ }
|
|
|
|
++ mErrorLayout.setVisibility(View.VISIBLE);
|
|
|
|
++ if (item.ParserError == null || item.ParserError.isEmpty()) {
|
|
|
|
++ mErrorLayout.setVisibility(View.GONE);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ void setMenuButtonDelegate(@NonNull ListMenuButtonDelegate delegate) {
|
|
|
|
++ mMoreButton.setVisibility(View.VISIBLE);
|
|
|
|
++ mMoreButton.setDelegate(delegate);
|
|
|
|
++ // Set item row end padding 0 when MenuButton is visible.
|
|
|
|
++ ViewCompat.setPaddingRelative(itemView, ViewCompat.getPaddingStart(itemView),
|
|
|
|
++ itemView.getPaddingTop(), 0, itemView.getPaddingBottom());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ void setItemListener(@NonNull ItemClickListener listener) {
|
|
|
|
++ mOnOffListener = (buttonView, isChecked) -> listener.onScriptOnOff(isChecked);
|
|
|
|
++ mSwitch.setOnCheckedChangeListener(mOnOffListener);
|
|
|
|
++ itemView.setOnClickListener(view -> listener.onScriptClicked());
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ScriptListBaseAdapter(Context context) {
|
|
|
|
++ super(context);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
|
|
|
|
++ View row = LayoutInflater.from(viewGroup.getContext())
|
|
|
|
++ .inflate(R.layout.accept_script_item, viewGroup, false);
|
|
|
|
++ return new ScriptInfoRowViewHolder(row);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onBindViewHolder(ViewHolder viewHolder, int i) {
|
|
|
|
++ ((ScriptInfoRowViewHolder) viewHolder).updateScriptInfo(mElements.get(i));
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ void setDisplayedScriptInfo(List<ScriptInfo> values) {
|
|
|
|
++ mElements = new ArrayList<>(values);
|
|
|
|
++ notifyDataSetChanged();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ protected void setOrder(List<ScriptInfo> order) {
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ protected boolean isActivelyDraggable(ViewHolder viewHolder) {
|
|
|
|
++ return isPassivelyDraggable(viewHolder);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ protected boolean isPassivelyDraggable(ViewHolder viewHolder) {
|
|
|
|
++ return viewHolder instanceof ScriptInfoRowViewHolder;
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/ScriptListPreference.java
|
|
|
|
+@@ -0,0 +1,171 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItem;
|
|
|
|
++import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItemWithEndIcon;
|
|
|
|
++
|
|
|
|
++import android.content.Context;
|
|
|
|
++import android.content.Intent;
|
|
|
|
++import android.provider.Browser;
|
|
|
|
++import android.net.Uri;
|
|
|
|
++import android.util.AttributeSet;
|
|
|
|
++import android.widget.TextView;
|
|
|
|
++import android.widget.Toast;
|
|
|
|
++
|
|
|
|
++import androidx.preference.Preference;
|
|
|
|
++import androidx.preference.PreferenceViewHolder;
|
|
|
|
++import androidx.recyclerview.widget.DividerItemDecoration;
|
|
|
|
++import androidx.recyclerview.widget.LinearLayoutManager;
|
|
|
|
++import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
++import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
|
|
|
++
|
|
|
|
++import org.chromium.ui.base.WindowAndroid;
|
|
|
|
++import org.chromium.ui.base.ActivityWindowAndroid;
|
|
|
|
++
|
|
|
|
++import org.chromium.base.ApplicationStatus;
|
|
|
|
++import org.chromium.base.ContextUtils;
|
|
|
|
++import org.chromium.components.browser_ui.widget.TintedDrawable;
|
|
|
|
++import org.chromium.components.browser_ui.widget.listmenu.BasicListMenu;
|
|
|
|
++import org.chromium.components.browser_ui.widget.listmenu.ListMenu;
|
|
|
|
++import org.chromium.components.browser_ui.widget.listmenu.ListMenuItemProperties;
|
|
|
|
++import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
|
|
|
|
++
|
|
|
|
++import org.chromium.components.user_scripts.ScriptListBaseAdapter;
|
|
|
|
++import org.chromium.components.user_scripts.FragmentWindowAndroid;
|
|
|
|
++
|
|
|
|
++import java.util.List;
|
|
|
|
++
|
|
|
|
++public class ScriptListPreference extends Preference {
|
|
|
|
++ private static class ScriptListAdapter
|
|
|
|
++ extends ScriptListBaseAdapter {
|
|
|
|
++ private final Context mContext;
|
|
|
|
++
|
|
|
|
++ ScriptListAdapter(Context context) {
|
|
|
|
++ super(context);
|
|
|
|
++ mContext = context;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onBindViewHolder(ViewHolder holder, int position) {
|
|
|
|
++ super.onBindViewHolder(holder, position);
|
|
|
|
++
|
|
|
|
++ final ScriptInfo info = getItemByPosition(position);
|
|
|
|
++
|
|
|
|
++ ModelList menuItems = new ModelList();
|
|
|
|
++
|
|
|
|
++ menuItems.add(buildMenuListItem(R.string.remove, 0, 0, true));
|
|
|
|
++ menuItems.add(buildMenuListItem(R.string.scripts_view_source, 0, 0,
|
|
|
|
++ info.ParserError == null || info.ParserError.isEmpty()));
|
|
|
|
++
|
|
|
|
++ ListMenu.Delegate delegate = (model) -> {
|
|
|
|
++ int textId = model.get(ListMenuItemProperties.TITLE_ID);
|
|
|
|
++ if (textId == R.string.remove) {
|
|
|
|
++ UserScriptsBridge.RemoveScript(info.Key);
|
|
|
|
++ } else if (textId == R.string.scripts_view_source) {
|
|
|
|
++ UserScriptsBridge.getUtils().openSourceFile(info.Key);
|
|
|
|
++ }
|
|
|
|
++ };
|
|
|
|
++ ((ScriptInfoRowViewHolder) holder)
|
|
|
|
++ .setMenuButtonDelegate(() -> new BasicListMenu(mContext, menuItems, delegate));
|
|
|
|
++ ((ScriptInfoRowViewHolder) holder)
|
|
|
|
++ .setItemListener(new ScriptListBaseAdapter.ItemClickListener() {
|
|
|
|
++ @Override
|
|
|
|
++ public void onScriptOnOff(boolean Enabled) {
|
|
|
|
++ UserScriptsBridge.SetScriptEnabled(info.Key, Enabled);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onScriptClicked() {}
|
|
|
|
++ });
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // @Override
|
|
|
|
++ public void onDataUpdated(boolean enabled) {
|
|
|
|
++ List<ScriptInfo> list = UserScriptsBridge.getUserScriptItems();
|
|
|
|
++ if (enabled == false) {
|
|
|
|
++ for (ScriptInfo script : list) {
|
|
|
|
++ script.ForceDisabled = true;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ setDisplayedScriptInfo(list);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ private TextView mAddButton;
|
|
|
|
++ private RecyclerView mRecyclerView;
|
|
|
|
++ private ScriptListAdapter mAdapter;
|
|
|
|
++ private FragmentWindowAndroid mWindowAndroid;
|
|
|
|
++
|
|
|
|
++ public ScriptListPreference(Context context, AttributeSet attrs) {
|
|
|
|
++ super(context, attrs);
|
|
|
|
++ mAdapter = new ScriptListAdapter(context);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public void setWindowAndroid(FragmentWindowAndroid windowAndroid) {
|
|
|
|
++ mWindowAndroid = windowAndroid;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onBindViewHolder(PreferenceViewHolder holder) {
|
|
|
|
++ super.onBindViewHolder(holder);
|
|
|
|
++
|
|
|
|
++ mAddButton = (TextView) holder.findViewById(R.id.add_script);
|
|
|
|
++ mAddButton.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
|
|
|
++ TintedDrawable.constructTintedDrawable(
|
|
|
|
++ getContext(), R.drawable.plus, R.color.default_control_color_active),
|
|
|
|
++ null, null, null);
|
|
|
|
++ mAddButton.setOnClickListener(view -> {
|
|
|
|
++ UserScriptsBridge.SelectAndAddScriptFromFile(mWindowAndroid);
|
|
|
|
++ });
|
|
|
|
++
|
|
|
|
++ mRecyclerView = (RecyclerView) holder.findViewById(R.id.script_list);
|
|
|
|
++ LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
|
|
|
++ mRecyclerView.setLayoutManager(layoutManager);
|
|
|
|
++ mRecyclerView.addItemDecoration(
|
|
|
|
++ new DividerItemDecoration(getContext(), layoutManager.getOrientation()));
|
|
|
|
++ mRecyclerView.setEnabled(this.isEnabled());
|
|
|
|
++ UserScriptsBridge.RegisterLoadCallback(this);
|
|
|
|
++
|
|
|
|
++ // We do not want the RecyclerView to be announced by screen readers every time
|
|
|
|
++ // the view is bound.
|
|
|
|
++ if (mRecyclerView.getAdapter() != mAdapter) {
|
|
|
|
++ mRecyclerView.setAdapter(mAdapter);
|
|
|
|
++ // Initialize script list.
|
|
|
|
++ mAdapter.onDataUpdated(this.isEnabled());
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void setEnabled (boolean enabled) {
|
|
|
|
++ super.setEnabled(enabled);
|
|
|
|
++ if (mRecyclerView != null) mRecyclerView.setEnabled(enabled);
|
|
|
|
++ NotifyScriptsChanged();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public void NotifyScriptsChanged() {
|
|
|
|
++ mAdapter.onDataUpdated(this.isEnabled());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public void OnUserScriptLoaded(boolean result, String error) {
|
|
|
|
++ if (result == false) {
|
|
|
|
++ Toast toast = Toast.makeText(getContext(), error, Toast.LENGTH_LONG);
|
|
|
|
++ toast.show();
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsBridge.java
|
|
|
|
+@@ -0,0 +1,200 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++import java.util.ArrayList;
|
|
|
|
++import java.util.List;
|
|
|
|
++import java.lang.ref.WeakReference;
|
|
|
|
++
|
|
|
|
++import org.json.JSONArray;
|
|
|
|
++import org.json.JSONException;
|
|
|
|
++import org.json.JSONObject;
|
|
|
|
++
|
|
|
|
++import android.content.Context;
|
|
|
|
++import android.content.Intent;
|
|
|
|
++import android.net.Uri;
|
|
|
|
++import androidx.annotation.Nullable;
|
|
|
|
++import android.app.AlertDialog;
|
|
|
|
++import android.content.DialogInterface;
|
|
|
|
++
|
|
|
|
++import org.chromium.base.annotations.CalledByNative;
|
|
|
|
++import org.chromium.base.annotations.JNINamespace;
|
|
|
|
++import org.chromium.base.annotations.NativeMethods;
|
|
|
|
++import org.chromium.base.Log;
|
|
|
|
++import org.chromium.ui.base.WindowAndroid;
|
|
|
|
++
|
|
|
|
++import org.chromium.components.user_scripts.ScriptListPreference;
|
|
|
|
++import org.chromium.components.user_scripts.IUserScriptsUtils;
|
|
|
|
++
|
|
|
|
++@JNINamespace("user_scripts")
|
|
|
|
++public class UserScriptsBridge {
|
|
|
|
++ private static final String TAG = "UserScript";
|
|
|
|
++
|
|
|
|
++ static WeakReference<ScriptListPreference> observer;
|
|
|
|
++
|
|
|
|
++ private static IUserScriptsUtils utilInstance;
|
|
|
|
++
|
|
|
|
++ public static void registerUtils(IUserScriptsUtils instance) {
|
|
|
|
++ utilInstance = instance;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static IUserScriptsUtils getUtils() {
|
|
|
|
++ return utilInstance;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static boolean isEnabled() {
|
|
|
|
++ return UserScriptsBridgeJni.get().isEnabled();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void setEnabled(boolean enabled) {
|
|
|
|
++ UserScriptsBridgeJni.get().setEnabled(enabled);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void RemoveScript(String key) {
|
|
|
|
++ UserScriptsBridgeJni.get().removeScript(key);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void SetScriptEnabled(String key,
|
|
|
|
++ boolean enabled) {
|
|
|
|
++ UserScriptsBridgeJni.get().setScriptEnabled(key, enabled);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void Reload() {
|
|
|
|
++ UserScriptsBridgeJni.get().reload();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void SelectAndAddScriptFromFile(WindowAndroid window) {
|
|
|
|
++ Context context = window.getContext().get();
|
|
|
|
++
|
|
|
|
++ Intent fileSelector = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
|
|
|
++ fileSelector.addCategory(Intent.CATEGORY_OPENABLE);
|
|
|
|
++ fileSelector.setType("*/*");
|
|
|
|
++ fileSelector.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
|
++
|
|
|
|
++ window.showIntent(fileSelector,
|
|
|
|
++ new WindowAndroid.IntentCallback() {
|
|
|
|
++ @Override
|
|
|
|
++ public void onIntentCompleted(int resultCode, Intent data) {
|
|
|
|
++ if (data == null) return;
|
|
|
|
++ Uri filePath = data.getData();
|
|
|
|
++ TryToInstall(context, filePath.toString());
|
|
|
|
++ }
|
|
|
|
++ },
|
|
|
|
++ null);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void TryToInstall(Context context, String ScriptFullPath) {
|
|
|
|
++ DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
|
|
|
++ @Override
|
|
|
|
++ public void onClick(DialogInterface dialog, int which) {
|
|
|
|
++ switch (which){
|
|
|
|
++ case DialogInterface.BUTTON_POSITIVE:
|
|
|
|
++ UserScriptsBridgeJni.get().tryToInstall(ScriptFullPath);
|
|
|
|
++ break;
|
|
|
|
++
|
|
|
|
++ case DialogInterface.BUTTON_NEGATIVE:
|
|
|
|
++ break;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ String message = context.getString(R.string.ask_to_install, ScriptFullPath);
|
|
|
|
++
|
|
|
|
++ AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
|
|
++ builder.setMessage(message)
|
|
|
|
++ .setPositiveButton(context.getString(R.string.yes), dialogClickListener)
|
|
|
|
++ .setNegativeButton(context.getString(R.string.no), dialogClickListener)
|
|
|
|
++ .show();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static List<ScriptInfo> getUserScriptItems() {
|
|
|
|
++ List<ScriptInfo> list = new ArrayList<>();
|
|
|
|
++ try {
|
|
|
|
++ String json = UserScriptsBridgeJni.get().getScriptsInfo();
|
|
|
|
++
|
|
|
|
++ JSONObject jsonObject = new JSONObject(json);
|
|
|
|
++
|
|
|
|
++ JSONArray scripts = jsonObject.names();
|
|
|
|
++ if (scripts != null) {
|
|
|
|
++ Log.i(TAG, "User Scripts Loaded: %s", json);
|
|
|
|
++ Log.i(TAG, "Totals scripts: %s", Integer.toString(scripts.length()));
|
|
|
|
++ for (int i = 0; i < scripts.length(); i++) {
|
|
|
|
++ String key = (String) scripts.get(i);
|
|
|
|
++ JSONObject script = jsonObject.getJSONObject(key);
|
|
|
|
++
|
|
|
|
++ ScriptInfo si = new ScriptInfo();
|
|
|
|
++ si.Key = key;
|
|
|
|
++ list.add(si);
|
|
|
|
++
|
|
|
|
++ if(script.has("name")) si.Name = script.getString("name");
|
|
|
|
++ if(script.has("description")) si.Description = script.getString("description");
|
|
|
|
++ if(script.has("version")) si.Version = script.getString("version");
|
|
|
|
++ if(script.has("file_path")) si.FilePath = script.getString("file_path");
|
|
|
|
++ if(script.has("url_source")) si.UrlSource = script.getString("url_source");
|
|
|
|
++ if(script.has("parser_error")) si.ParserError = script.getString("parser_error");
|
|
|
|
++ if(script.has("force_disabled")) si.ForceDisabled = script.getBoolean("force_disabled");;
|
|
|
|
++ si.Enabled = script.getBoolean("enabled");
|
|
|
|
++ }
|
|
|
|
++ } else {
|
|
|
|
++ Log.i(TAG, "User Scripts list empty");
|
|
|
|
++ }
|
|
|
|
++ } catch (Exception e) {
|
|
|
|
++ Log.e(TAG, "User Scripts Load Error", e.toString());
|
|
|
|
++ }
|
|
|
|
++ return list;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static void RegisterLoadCallback(ScriptListPreference caller) {
|
|
|
|
++ UserScriptsBridgeJni.get().registerLoadCallback();
|
|
|
|
++ observer = new WeakReference<ScriptListPreference>(caller);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @CalledByNative
|
|
|
|
++ private static void shouldRefreshUserScriptList() {
|
|
|
|
++ ScriptListPreference reference = observer.get();
|
|
|
|
++ if (reference != null) {
|
|
|
|
++ reference.NotifyScriptsChanged();
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @CalledByNative
|
|
|
|
++ private static void onUserScriptLoaded(boolean result, String error) {
|
|
|
|
++ ScriptListPreference reference = observer.get();
|
|
|
|
++ if (reference != null) {
|
|
|
|
++ reference.OnUserScriptLoaded(result, error);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @NativeMethods
|
|
|
|
++ interface Natives {
|
|
|
|
++ boolean isEnabled();
|
|
|
|
++ void setEnabled(boolean enabled);
|
|
|
|
++
|
|
|
|
++ String getScriptsInfo();
|
|
|
|
++
|
|
|
|
++ void removeScript(String scriptKey);
|
|
|
|
++ void setScriptEnabled(String scriptKey, boolean enabled);
|
|
|
|
++
|
|
|
|
++ void reload();
|
|
|
|
++ void selectAndAddScriptFromFile(WindowAndroid window);
|
|
|
|
++ void tryToInstall(String scriptFullPath);
|
|
|
|
++
|
|
|
|
++ void registerLoadCallback();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java/src/org/chromium/components/user_scripts/UserScriptsPreferences.java
|
|
|
|
+@@ -0,0 +1,116 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++package org.chromium.components.user_scripts;
|
|
|
|
++
|
|
|
|
++import android.app.Activity;
|
|
|
|
++import android.content.Context;
|
|
|
|
++import android.content.Intent;
|
|
|
|
++import android.os.Bundle;
|
|
|
|
++import android.provider.Browser;
|
|
|
|
++import android.net.Uri;
|
|
|
|
++import android.view.MenuItem;
|
|
|
|
++import android.view.View;
|
|
|
|
++
|
|
|
|
++import androidx.preference.Preference;
|
|
|
|
++import androidx.preference.PreferenceFragmentCompat;
|
|
|
|
++
|
|
|
|
++import org.chromium.base.Log;
|
|
|
|
++import org.chromium.ui.base.WindowAndroid;
|
|
|
|
++import org.chromium.ui.base.ActivityWindowAndroid;
|
|
|
|
++
|
|
|
|
++import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
|
|
|
|
++import org.chromium.components.browser_ui.settings.SettingsUtils;
|
|
|
|
++import org.chromium.components.browser_ui.settings.TextMessagePreference;
|
|
|
|
++
|
|
|
|
++import org.chromium.components.user_scripts.UserScriptsBridge;
|
|
|
|
++import org.chromium.components.user_scripts.FragmentWindowAndroid;
|
|
|
|
++
|
|
|
|
++public class UserScriptsPreferences
|
|
|
|
++ extends PreferenceFragmentCompat
|
|
|
|
++ implements SettingsUtils.ISupportHelpAndFeedback {
|
|
|
|
++
|
|
|
|
++ private static final String PREF_ENABLED_SWITCH = "enabled_switch";
|
|
|
|
++ private static final String PREF_SCRIPTLISTPREFERENCE = "script_list";
|
|
|
|
++
|
|
|
|
++ public static final String EXTRA_SCRIPT_FILE = "org.chromium.chrome.preferences.script_file";
|
|
|
|
++
|
|
|
|
++ private FragmentWindowAndroid mWindowAndroid;
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onDestroy() {
|
|
|
|
++ if (mWindowAndroid != null) mWindowAndroid.destroy();
|
|
|
|
++ super.onDestroy();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
|
|
|
++ getActivity().setTitle(R.string.prefs_userscripts_settings);
|
|
|
|
++ SettingsUtils.addPreferencesFromResource(this, R.xml.userscripts_preferences);
|
|
|
|
++
|
|
|
|
++ ChromeSwitchPreference enabledSwitch =
|
|
|
|
++ (ChromeSwitchPreference) findPreference(PREF_ENABLED_SWITCH);
|
|
|
|
++ ScriptListPreference listPreference =
|
|
|
|
++ (ScriptListPreference) findPreference(PREF_SCRIPTLISTPREFERENCE);
|
|
|
|
++
|
|
|
|
++ boolean enabled = UserScriptsBridge.isEnabled();
|
|
|
|
++ enabledSwitch.setChecked(enabled);
|
|
|
|
++ listPreference.setEnabled(enabled);
|
|
|
|
++ enabledSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
|
|
|
|
++ UserScriptsBridge.setEnabled((boolean) newValue);
|
|
|
|
++ listPreference.setEnabled((boolean) newValue);
|
|
|
|
++ return true;
|
|
|
|
++ });
|
|
|
|
++
|
|
|
|
++ mWindowAndroid = new FragmentWindowAndroid(getContext(), this);
|
|
|
|
++ listPreference.setWindowAndroid(mWindowAndroid);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
|
++ // handle picker callback from SelectFileDialog
|
|
|
|
++ mWindowAndroid.getIntentRequestTracker().onActivityResult(requestCode, resultCode, data);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public void onHelpAndFeebackPressed() {
|
|
|
|
++ Context context = getContext();
|
|
|
|
++
|
|
|
|
++ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/bromite/bromite/wiki/UserScripts"));
|
|
|
|
++ // Let Chromium know that this intent is from Chromium, so that it does not close the app when
|
|
|
|
++ // the user presses 'back' button.
|
|
|
|
++ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
|
|
|
|
++ intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
|
|
|
|
++ intent.setPackage(context.getPackageName());
|
|
|
|
++ context.startActivity(intent);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ public static Bundle createFragmentArgsForInstall(String filePath) {
|
|
|
|
++ Bundle fragmentArgs = new Bundle();
|
|
|
|
++ fragmentArgs.putSerializable(EXTRA_SCRIPT_FILE, filePath);
|
|
|
|
++ return fragmentArgs;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ @Override
|
|
|
|
++ public void onActivityCreated(Bundle savedInstanceState) {
|
|
|
|
++ super.onActivityCreated(savedInstanceState);
|
|
|
|
++
|
|
|
|
++ String scriptToInstall = (String)getArguments().getSerializable(EXTRA_SCRIPT_FILE);
|
|
|
|
++ if (scriptToInstall != null) {
|
|
|
|
++ UserScriptsBridge.TryToInstall(getContext(), scriptToInstall);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/android/java_sources.gni b/components/user_scripts/android/java_sources.gni
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/java_sources.gni
|
|
|
|
+@@ -0,0 +1,18 @@
|
|
|
|
++# This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++# Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++# it under the terms of the GNU General Public License as published by
|
|
|
|
++# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++# (at your option) any later version.
|
|
|
|
++
|
|
|
|
++# Bromite is distributed in the hope that it will be useful,
|
|
|
|
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++# GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++# You should have received a copy of the GNU General Public License
|
|
|
|
++# along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++
|
|
|
|
++userscripts_java_sources = [
|
|
|
|
++ "//components/user_scripts/android/java/src/org/chromium/chrome/browser/user_scripts/UserScriptsUtils.java",
|
|
|
|
++]
|
|
|
|
+diff --git a/components/user_scripts/android/user_scripts_bridge.cc b/components/user_scripts/android/user_scripts_bridge.cc
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/user_scripts_bridge.cc
|
|
|
|
+@@ -0,0 +1,173 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include <jni.h>
|
|
|
|
++#include <algorithm>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++#include <sstream>
|
|
|
|
++#include <iterator>
|
|
|
|
++
|
|
|
|
++#include "base/android/callback_android.h"
|
|
|
|
++#include "base/android/jni_android.h"
|
|
|
|
++#include "base/android/jni_array.h"
|
|
|
|
++#include "base/android/jni_string.h"
|
|
|
|
++#include "base/android/scoped_java_ref.h"
|
|
|
|
++#include "ui/android/window_android.h"
|
|
|
|
++
|
|
|
|
++#include "components/user_scripts/android/user_scripts_jni_headers/UserScriptsBridge_jni.h"
|
|
|
|
++#include "../browser/userscripts_browser_client.h"
|
|
|
|
++#include "user_scripts_bridge.h"
|
|
|
|
++
|
|
|
|
++using base::android::AttachCurrentThread;
|
|
|
|
++using base::android::ConvertJavaStringToUTF8;
|
|
|
|
++using base::android::ConvertUTF16ToJavaString;
|
|
|
|
++using base::android::ConvertUTF8ToJavaString;
|
|
|
|
++using base::android::JavaParamRef;
|
|
|
|
++using base::android::JavaRef;
|
|
|
|
++using base::android::ScopedJavaGlobalRef;
|
|
|
|
++using base::android::ScopedJavaLocalRef;
|
|
|
|
++using content::BrowserContext;
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++user_scripts::UserScriptsBrowserClient* GetUserScriptsBrowserClient() {
|
|
|
|
++ return user_scripts::UserScriptsBrowserClient::GetInstance();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++class CallbackObserver : public user_scripts::UserScriptLoader::Observer {
|
|
|
|
++ private:
|
|
|
|
++ void OnScriptsLoaded(user_scripts::UserScriptLoader* loader,
|
|
|
|
++ content::BrowserContext* browser_context) override {
|
|
|
|
++ user_scripts::ShouldRefreshUserScriptList(base::android::AttachCurrentThread());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ void OnUserScriptLoaded(user_scripts::UserScriptLoader* loader,
|
|
|
|
++ bool result, const std::string& error) override {
|
|
|
|
++ user_scripts::OnUserScriptLoaded(base::android::AttachCurrentThread(),
|
|
|
|
++ result, error);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ void OnUserScriptLoaderDestroyed(user_scripts::UserScriptLoader* loader) override {}
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++CallbackObserver* g_userscripts_loader_observer = NULL;
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++static jboolean JNI_UserScriptsBridge_IsEnabled(
|
|
|
|
++ JNIEnv* env) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return false;
|
|
|
|
++ return client->GetPrefs()->IsEnabled();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_SetEnabled(
|
|
|
|
++ JNIEnv* env,
|
|
|
|
++ jboolean is_enabled) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++ client->GetPrefs()->SetEnabled(is_enabled);
|
|
|
|
++ client->GetLoader()->StartLoad();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static base::android::ScopedJavaLocalRef<jstring> JNI_UserScriptsBridge_GetScriptsInfo(
|
|
|
|
++ JNIEnv* env) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return ConvertUTF8ToJavaString(env, {});
|
|
|
|
++
|
|
|
|
++ std::string json = client->GetPrefs()->GetScriptsInfo();
|
|
|
|
++ return ConvertUTF8ToJavaString(env, json);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_RemoveScript(
|
|
|
|
++ JNIEnv* env,
|
|
|
|
++ const JavaParamRef<jstring>& jscript_key) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++
|
|
|
|
++ std::string script_key = base::android::ConvertJavaStringToUTF8(jscript_key);
|
|
|
|
++ client->GetLoader()->RemoveScript(script_key);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_SetScriptEnabled(
|
|
|
|
++ JNIEnv* env,
|
|
|
|
++ const JavaParamRef<jstring>& jscript_key,
|
|
|
|
++ jboolean is_enabled) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++
|
|
|
|
++ std::string script_key = base::android::ConvertJavaStringToUTF8(jscript_key);
|
|
|
|
++ client->GetLoader()->SetScriptEnabled(script_key, is_enabled);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_Reload(
|
|
|
|
++ JNIEnv* env) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++
|
|
|
|
++ client->GetLoader()->StartLoad();
|
|
|
|
++ user_scripts::ShouldRefreshUserScriptList(env);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_SelectAndAddScriptFromFile(
|
|
|
|
++ JNIEnv* env,
|
|
|
|
++ const JavaParamRef<jobject>& jwindow_android) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++
|
|
|
|
++ client->GetLoader()->SelectAndAddScriptFromFile(
|
|
|
|
++ ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_TryToInstall(JNIEnv* env,
|
|
|
|
++ const JavaParamRef<jstring>& jscript_path) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++
|
|
|
|
++ std::string script_path = base::android::ConvertJavaStringToUTF8(jscript_path);
|
|
|
|
++ base::FilePath path(script_path);
|
|
|
|
++
|
|
|
|
++ client->GetLoader()->TryToInstall(path);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void JNI_UserScriptsBridge_RegisterLoadCallback(
|
|
|
|
++ JNIEnv* env) {
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = GetUserScriptsBrowserClient();
|
|
|
|
++ if (client == NULL) return;
|
|
|
|
++
|
|
|
|
++ if (g_userscripts_loader_observer == NULL) {
|
|
|
|
++ g_userscripts_loader_observer = new CallbackObserver();
|
|
|
|
++ client->GetLoader()->AddObserver(g_userscripts_loader_observer);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void ShouldRefreshUserScriptList(JNIEnv* env) {
|
|
|
|
++ Java_UserScriptsBridge_shouldRefreshUserScriptList(env);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void OnUserScriptLoaded(JNIEnv* env,
|
|
|
|
++ bool result, const std::string& error) {
|
|
|
|
++ base::android::ScopedJavaLocalRef<jstring> j_error =
|
|
|
|
++ base::android::ConvertUTF8ToJavaString(env, error);
|
|
|
|
++
|
|
|
|
++ Java_UserScriptsBridge_onUserScriptLoaded(env, result, j_error);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/android/user_scripts_bridge.h b/components/user_scripts/android/user_scripts_bridge.h
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/android/user_scripts_bridge.h
|
|
|
|
+@@ -0,0 +1,31 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef COMPONENTS_USERSCRIPS_HELPER_ANDROID_H_
|
|
|
|
++#define COMPONENTS_USERSCRIPS_HELPER_ANDROID_H_
|
|
|
|
++
|
|
|
|
++#include <jni.h>
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++static void ShouldRefreshUserScriptList(JNIEnv* env);
|
|
|
|
++static void OnUserScriptLoaded(JNIEnv* env,
|
|
|
|
++ bool result, const std::string& error);
|
|
|
|
++
|
|
|
|
++} // namespace user_scripts
|
|
|
|
++
|
|
|
|
++#endif
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/BUILD.gn b/components/user_scripts/browser/BUILD.gn
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/BUILD.gn
|
|
|
|
+@@ -0,0 +1,82 @@
|
|
|
|
++# This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++# Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++# it under the terms of the GNU General Public License as published by
|
|
|
|
++# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++# (at your option) any later version.
|
|
|
|
++
|
|
|
|
++# Bromite is distributed in the hope that it will be useful,
|
|
|
|
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++# GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++# You should have received a copy of the GNU General Public License
|
|
|
|
++# along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++
|
|
|
|
++import("//build/config/features.gni")
|
|
|
|
++import("//tools/grit/grit_rule.gni")
|
|
|
|
++
|
|
|
|
++group("browser") {
|
|
|
|
++ public_deps = [
|
|
|
|
++ "//components/user_scripts/browser:browser_sources",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++source_set("browser_sources") {
|
|
|
|
++ visibility = [ "./*" ]
|
|
|
|
++
|
|
|
|
++ sources = [
|
|
|
|
++ "file_task_runner.cc",
|
|
|
|
++ "file_task_runner.h",
|
|
|
|
++ "userscripts_browser_client.cc",
|
|
|
|
++ "userscripts_browser_client.h",
|
|
|
|
++ "user_script_loader.cc",
|
|
|
|
++ "user_script_loader.h",
|
|
|
|
++ "user_script_prefs.cc",
|
|
|
|
++ "user_script_prefs.h",
|
|
|
|
++ "user_script_pref_info.cc",
|
|
|
|
++ "user_script_pref_info.h",
|
|
|
|
++ "ui/user_scripts_ui.h",
|
|
|
|
++ "ui/user_scripts_ui.cc",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ deps = [
|
|
|
|
++ ":userscripts_browser_resources",
|
|
|
|
++ "//base:i18n",
|
|
|
|
++ "//components/keyed_service/content",
|
|
|
|
++ "//components/keyed_service/core",
|
|
|
|
++ "//components/pref_registry",
|
|
|
|
++ "//components/prefs",
|
|
|
|
++ "//content/public/browser",
|
|
|
|
++ "//crypto:platform",
|
|
|
|
++ "//components/user_scripts/common",
|
|
|
|
++ "//services/device/public/mojom",
|
|
|
|
++ "//services/preferences/public/cpp",
|
|
|
|
++ "//services/service_manager/public/cpp",
|
|
|
|
++ "//third_party/blink/public/common",
|
|
|
|
++ "//ui/display",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ public_deps = [
|
|
|
|
++ "//content/public/common",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ configs += [
|
|
|
|
++ "//build/config:precompiled_headers",
|
|
|
|
++ "//build/config/compiler:wexit_time_destructors",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++group("closure_compile") {
|
|
|
|
++ deps = [ "resources/user-script-ui:closure_compile" ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++grit("userscripts_browser_resources") {
|
|
|
|
++ source = "resources/browser_resources.grd"
|
|
|
|
++
|
|
|
|
++ output_dir = "$root_gen_dir/chrome"
|
|
|
|
++ outputs = [
|
|
|
|
++ "grit/userscripts_browser_resources.h",
|
|
|
|
++ "userscripts_browser_resources.pak",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/browser/file_task_runner.cc b/components/user_scripts/browser/file_task_runner.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/file_task_runner.cc
|
|
|
|
+@@ -0,0 +1,40 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include "file_task_runner.h"
|
|
|
|
++
|
|
|
|
++#include "base/sequenced_task_runner.h"
|
|
|
|
++#include "base/task/lazy_thread_pool_task_runner.h"
|
|
|
|
++#include "base/task/task_traits.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++base::LazyThreadPoolSequencedTaskRunner g_us_task_runner =
|
|
|
|
++ LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
|
|
|
|
++ base::TaskTraits(base::MayBlock(),
|
|
|
|
++ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
|
|
|
|
++ base::TaskPriority::USER_VISIBLE));
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++scoped_refptr<base::SequencedTaskRunner> GetUserScriptsFileTaskRunner() {
|
|
|
|
++ return g_us_task_runner.Get();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace user_scripts
|
|
|
|
+diff --git a/components/user_scripts/browser/file_task_runner.h b/components/user_scripts/browser/file_task_runner.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/file_task_runner.h
|
|
|
|
+@@ -0,0 +1,34 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_BROWSER_FILE_TASK_RUNNER_H_
|
|
|
|
++#define USERSCRIPTS_BROWSER_FILE_TASK_RUNNER_H_
|
|
|
|
++
|
|
|
|
++#include "base/memory/ref_counted.h"
|
|
|
|
++#include "base/task/task_traits.h"
|
|
|
|
++
|
|
|
|
++namespace base {
|
|
|
|
++class SequencedTaskRunner;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++scoped_refptr<base::SequencedTaskRunner> GetUserScriptsFileTaskRunner();
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_BROWSER_FILE_TASK_RUNNER_H_
|
|
|
|
+diff --git a/components/user_scripts/browser/resources/browser_resources.grd b/components/user_scripts/browser/resources/browser_resources.grd
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/resources/browser_resources.grd
|
|
|
|
+@@ -0,0 +1,14 @@
|
|
|
|
++<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
|
|
|
|
++ <outputs>
|
|
|
|
++ <output filename="grit/userscripts_browser_resources.h" type="rc_header">
|
|
|
|
++ <emit emit_type='prepend'></emit>
|
|
|
|
++ </output>
|
|
|
|
++ <output filename="userscripts_browser_resources.pak" type="data_package" />
|
|
|
|
++ </outputs>
|
|
|
|
++ <release seq="1">
|
|
|
|
++ <includes>
|
|
|
|
++ <include name="IDR_USER_SCRIPTS_HTML" file="user-script-ui\user-scripts-ui.html" type="BINDATA" />
|
|
|
|
++ <include name="IDR_USER_SCRIPTS_JS" file="user-script-ui\user-scripts-ui.js" type="BINDATA" />
|
|
|
|
++ </includes>
|
|
|
|
++ </release>
|
|
|
|
++</grit>
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/resources/user-script-ui/BUILD.gn b/components/user_scripts/browser/resources/user-script-ui/BUILD.gn
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/resources/user-script-ui/BUILD.gn
|
|
|
|
+@@ -0,0 +1,12 @@
|
|
|
|
++import("//third_party/closure_compiler/compile_js.gni")
|
|
|
|
++
|
|
|
|
++js_type_check("closure_compile") {
|
|
|
|
++ deps = [ ":view_script_source" ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++js_library("view_script_source") {
|
|
|
|
++ deps = [
|
|
|
|
++ "//ui/webui/resources/js:cr.m",
|
|
|
|
++ "//ui/webui/resources/js:util.m",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.html
|
|
|
|
+@@ -0,0 +1,14 @@
|
|
|
|
++<!doctype html>
|
|
|
|
++<html lang="en">
|
|
|
|
++<head>
|
|
|
|
++ <meta charset="utf-8">
|
|
|
|
++ <title>Local State Debug Page</title>
|
|
|
|
++ <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
|
|
|
|
++ <script type="module" src="user-scripts-ui.js"></script>
|
|
|
|
++</head>
|
|
|
|
++<body>
|
|
|
|
++ <pre id="content">
|
|
|
|
++ Loading Script Source file...
|
|
|
|
++ </pre>
|
|
|
|
++</body>
|
|
|
|
++</html>
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/resources/user-script-ui/user-scripts-ui.js
|
|
|
|
+@@ -0,0 +1,9 @@
|
|
|
|
++import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
|
|
|
|
++import {$} from 'chrome://resources/js/util.m.js';
|
|
|
|
++
|
|
|
|
++document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
++ const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
++ sendWithPromise('requestSource', urlParams.get('key')).then(textContent => {
|
|
|
|
++ $('content').textContent = textContent[0].content;
|
|
|
|
++ });
|
|
|
|
++});
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/ui/user_scripts_ui.cc b/components/user_scripts/browser/ui/user_scripts_ui.cc
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/ui/user_scripts_ui.cc
|
|
|
|
+@@ -0,0 +1,148 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++
|
|
|
|
++#include "base/bind.h"
|
|
|
|
++#include "base/json/json_string_value_serializer.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/writable_shared_memory_region.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++
|
|
|
|
++#include "chrome/browser/browser_process.h"
|
|
|
|
++#include "chrome/browser/profiles/profile.h"
|
|
|
|
++#include "chrome/common/url_constants.h"
|
|
|
|
++#include "chrome/grit/userscripts_browser_resources.h"
|
|
|
|
++#include "components/prefs/pref_service.h"
|
|
|
|
++#include "content/public/browser/web_ui.h"
|
|
|
|
++#include "content/public/browser/web_ui_controller.h"
|
|
|
|
++#include "content/public/browser/web_ui_data_source.h"
|
|
|
|
++#include "content/public/browser/web_ui_message_handler.h"
|
|
|
|
++
|
|
|
|
++#include "user_scripts_ui.h"
|
|
|
|
++#include "../userscripts_browser_client.h"
|
|
|
|
++#include "../../common/user_script.h"
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++class UserScriptsUIHandler : public content::WebUIMessageHandler {
|
|
|
|
++ public:
|
|
|
|
++ UserScriptsUIHandler();
|
|
|
|
++ ~UserScriptsUIHandler() override;
|
|
|
|
++
|
|
|
|
++ // content::WebUIMessageHandler:
|
|
|
|
++ void RegisterMessages() override;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ void HandleRequestSource(base::Value::ConstListView args);
|
|
|
|
++ void OnScriptsLoaded(
|
|
|
|
++ const std::string callback_id,
|
|
|
|
++ const std::string script_key,
|
|
|
|
++ std::unique_ptr<user_scripts::UserScriptList> user_scripts);
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<user_scripts::UserScriptList> loaded_scripts_;
|
|
|
|
++
|
|
|
|
++ base::WeakPtrFactory<UserScriptsUIHandler> weak_factory_{this};
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(UserScriptsUIHandler);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++UserScriptsUIHandler::UserScriptsUIHandler()
|
|
|
|
++ : loaded_scripts_(new user_scripts::UserScriptList()) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptsUIHandler::~UserScriptsUIHandler() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsUIHandler::RegisterMessages() {
|
|
|
|
++ web_ui()->RegisterMessageCallback(
|
|
|
|
++ "requestSource",
|
|
|
|
++ base::BindRepeating(&UserScriptsUIHandler::HandleRequestSource,
|
|
|
|
++ base::Unretained(this)));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsUIHandler::HandleRequestSource(base::Value::ConstListView args) {
|
|
|
|
++ AllowJavascript();
|
|
|
|
++ if (args.size() < 2) return;
|
|
|
|
++
|
|
|
|
++ std::string callback_id = args[0].GetString();
|
|
|
|
++ std::string script_key = args[1].GetString();
|
|
|
|
++ if (script_key.empty()) {
|
|
|
|
++ std::string json = "Missing key value.";
|
|
|
|
++ ResolveJavascriptCallback(base::Value(callback_id), base::Value(json));
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ user_scripts::UserScriptsBrowserClient* client = user_scripts::UserScriptsBrowserClient::GetInstance();
|
|
|
|
++ if (client == NULL) {
|
|
|
|
++ std::string json = "User scripts disabled.";
|
|
|
|
++ ResolveJavascriptCallback(base::Value(callback_id), base::Value(json));
|
|
|
|
++ } else {
|
|
|
|
++ std::unique_ptr<user_scripts::UserScriptList> scripts_to_load =
|
|
|
|
++ std::move(loaded_scripts_);
|
|
|
|
++ scripts_to_load->clear();
|
|
|
|
++
|
|
|
|
++ client->GetLoader()->LoadScripts(std::move(scripts_to_load),
|
|
|
|
++ base::BindOnce(
|
|
|
|
++ &UserScriptsUIHandler::OnScriptsLoaded,
|
|
|
|
++ weak_factory_.GetWeakPtr(),
|
|
|
|
++ callback_id, script_key)
|
|
|
|
++ );
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsUIHandler::OnScriptsLoaded(
|
|
|
|
++ const std::string callback_id,
|
|
|
|
++ const std::string script_key,
|
|
|
|
++ std::unique_ptr<user_scripts::UserScriptList> user_scripts) {
|
|
|
|
++ loaded_scripts_ = std::move(user_scripts);
|
|
|
|
++
|
|
|
|
++ base::ListValue response;
|
|
|
|
++ for (const std::unique_ptr<user_scripts::UserScript>& script : *loaded_scripts_) {
|
|
|
|
++ if (script->key() == script_key) {
|
|
|
|
++ auto scriptData = std::make_unique<base::DictionaryValue>();
|
|
|
|
++ for (const std::unique_ptr<user_scripts::UserScript::File>& js_file :
|
|
|
|
++ script->js_scripts()) {
|
|
|
|
++ base::StringPiece contents = js_file->GetContent();
|
|
|
|
++ scriptData->SetString("content", contents.data());
|
|
|
|
++ }
|
|
|
|
++ response.Append(std::move(scriptData));
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ResolveJavascriptCallback(base::Value(callback_id), response);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++UserScriptsUI::UserScriptsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
|
|
|
|
++ content::WebUIDataSource* html_source =
|
|
|
|
++ content::WebUIDataSource::Create(kChromeUIUserScriptsHost);
|
|
|
|
++ html_source->SetDefaultResource(IDR_USER_SCRIPTS_HTML);
|
|
|
|
++ html_source->AddResourcePath("user-scripts-ui.js", IDR_USER_SCRIPTS_JS);
|
|
|
|
++ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
|
|
|
|
++ web_ui->AddMessageHandler(std::make_unique<UserScriptsUIHandler>());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptsUI::~UserScriptsUI() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/browser/ui/user_scripts_ui.h b/components/user_scripts/browser/ui/user_scripts_ui.h
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/ui/user_scripts_ui.h
|
|
|
|
+@@ -0,0 +1,39 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_BROWSER_UI_USER_SCRIPTS_UI_H_
|
|
|
|
++#define USERSCRIPTS_BROWSER_UI_USER_SCRIPTS_UI_H_
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "content/public/browser/web_ui_controller.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++const char kChromeUIUserScriptsHost[] = "user-scripts";
|
|
|
|
++
|
|
|
|
++class UserScriptsUI : public content::WebUIController {
|
|
|
|
++ public:
|
|
|
|
++ explicit UserScriptsUI(content::WebUI* web_ui);
|
|
|
|
++ ~UserScriptsUI() override;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(UserScriptsUI);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/user_script_loader.cc b/components/user_scripts/browser/user_script_loader.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/user_script_loader.cc
|
|
|
|
+@@ -0,0 +1,721 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include "user_script_loader.h"
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "base/bind.h"
|
|
|
|
++#include "base/memory/writable_shared_memory_region.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "base/strings/strcat.h"
|
|
|
|
++#include "base/files/file.h"
|
|
|
|
++#include "base/files/file_util.h"
|
|
|
|
++#include "base/files/file_enumerator.h"
|
|
|
|
++#include "base/i18n/file_util_icu.h"
|
|
|
|
++#include "base/path_service.h"
|
|
|
|
++#include "base/base_paths_android.h"
|
|
|
|
++#include "base/strings/utf_string_conversions.h"
|
|
|
|
++#include "base/android/content_uri_utils.h"
|
|
|
|
++#include "base/android/jni_android.h"
|
|
|
|
++#include "base/task/task_traits.h"
|
|
|
|
++#include "base/version.h"
|
|
|
|
++
|
|
|
|
++#include "crypto/sha2.h"
|
|
|
|
++#include "base/base64.h"
|
|
|
|
++
|
|
|
|
++#include "build/build_config.h"
|
|
|
|
++#include "content/public/browser/browser_context.h"
|
|
|
|
++#include "content/public/browser/browser_task_traits.h"
|
|
|
|
++#include "content/public/browser/browser_thread.h"
|
|
|
|
++#include "content/public/browser/notification_service.h"
|
|
|
|
++#include "content/public/browser/notification_types.h"
|
|
|
|
++#include "content/public/browser/render_process_host.h"
|
|
|
|
++#include "ui/shell_dialogs/select_file_dialog.h"
|
|
|
|
++#include "content/browser/file_system_access/file_system_chooser.h"
|
|
|
|
++#include "chrome/browser/ui/chrome_select_file_policy.h"
|
|
|
|
++#include "ui/android/window_android.h"
|
|
|
|
++
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++#include "../common/extension_messages.h"
|
|
|
|
++#include "file_task_runner.h"
|
|
|
|
++#include "user_script_prefs.h"
|
|
|
|
++#include "user_script_pref_info.h"
|
|
|
|
++
|
|
|
|
++using content::BrowserThread;
|
|
|
|
++using content::BrowserContext;
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++static const base::StringPiece kUserScriptBegin("// ==UserScript==");
|
|
|
|
++static const base::StringPiece kUserScriptEnd("// ==/UserScript==");
|
|
|
|
++static const base::StringPiece kNamespaceDeclaration("// @namespace");
|
|
|
|
++static const base::StringPiece kNameDeclaration("// @name");
|
|
|
|
++static const base::StringPiece kVersionDeclaration("// @version");
|
|
|
|
++static const base::StringPiece kDescriptionDeclaration("// @description");
|
|
|
|
++static const base::StringPiece kIncludeDeclaration("// @include");
|
|
|
|
++static const base::StringPiece kExcludeDeclaration("// @exclude");
|
|
|
|
++static const base::StringPiece kMatchDeclaration("// @match");
|
|
|
|
++static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match");
|
|
|
|
++static const base::StringPiece kRunAtDeclaration("// @run-at");
|
|
|
|
++static const base::StringPiece kRunAtDocumentStartValue("document-start");
|
|
|
|
++static const base::StringPiece kRunAtDocumentEndValue("document-end");
|
|
|
|
++static const base::StringPiece kRunAtDocumentIdleValue("document-idle");
|
|
|
|
++static const base::StringPiece kUrlSourceDeclaration("// @url");
|
|
|
|
++static const base::StringPiece kUrlHomePageDeclaration("// @homepage");
|
|
|
|
++
|
|
|
|
++// internal use
|
|
|
|
++static const base::StringPiece kParserError("// @error");
|
|
|
|
++static const base::StringPiece kForceDisabled("// @disabled");
|
|
|
|
++
|
|
|
|
++bool invalidChar(unsigned char c)
|
|
|
|
++{
|
|
|
|
++ return !(c>=0 && c <128);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void stripUnicode(std::string& str)
|
|
|
|
++{
|
|
|
|
++ str.erase(remove_if(str.begin(),str.end(), invalidChar), str.end());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// Helper function to parse greasesmonkey headers
|
|
|
|
++bool GetDeclarationValue(const base::StringPiece& line,
|
|
|
|
++ const base::StringPiece& prefix,
|
|
|
|
++ std::string* value) {
|
|
|
|
++ base::StringPiece::size_type index = line.find(prefix);
|
|
|
|
++ if (index == base::StringPiece::npos)
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ std::string temp(line.data() + index + prefix.length(),
|
|
|
|
++ line.length() - index - prefix.length());
|
|
|
|
++
|
|
|
|
++ if (temp.empty() || !base::IsUnicodeWhitespace(temp[0]))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text,
|
|
|
|
++ std::unique_ptr<UserScript>& script,
|
|
|
|
++ bool *found_metadata,
|
|
|
|
++ std::string& error_message) {
|
|
|
|
++ // http://wiki.greasespot.net/Metadata_block
|
|
|
|
++ base::StringPiece line;
|
|
|
|
++ size_t line_start = 0;
|
|
|
|
++ size_t line_end = line_start;
|
|
|
|
++ *found_metadata = false;
|
|
|
|
++
|
|
|
|
++ while (line_start < script_text.length()) {
|
|
|
|
++ line_end = script_text.find('\n', line_start);
|
|
|
|
++
|
|
|
|
++ // Handle the case where there is no trailing newline in the file.
|
|
|
|
++ if (line_end == std::string::npos)
|
|
|
|
++ line_end = script_text.length() - 1;
|
|
|
|
++
|
|
|
|
++ line = base::StringPiece(script_text.data() + line_start,
|
|
|
|
++ line_end - line_start);
|
|
|
|
++
|
|
|
|
++ if (!*found_metadata) {
|
|
|
|
++ if (base::StartsWith(line, kUserScriptBegin))
|
|
|
|
++ *found_metadata = true;
|
|
|
|
++ } else {
|
|
|
|
++ if (base::StartsWith(line, kUserScriptEnd))
|
|
|
|
++ break;
|
|
|
|
++
|
|
|
|
++ std::string value;
|
|
|
|
++ if (GetDeclarationValue(line, kIncludeDeclaration, &value)) {
|
|
|
|
++ // We escape some characters that MatchPattern() considers special.
|
|
|
|
++ base::ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
|
|
|
|
++ base::ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
|
|
|
|
++ script->add_glob(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
|
|
|
|
++ base::ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
|
|
|
|
++ base::ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
|
|
|
|
++ script->add_exclude_glob(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
|
|
|
|
++ script->set_name_space(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
|
|
|
|
++ script->set_name(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) {
|
|
|
|
++ base::Version version(value);
|
|
|
|
++ if (version.IsValid())
|
|
|
|
++ script->set_version(version.GetString());
|
|
|
|
++ } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
|
|
|
|
++ script->set_description(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
|
|
|
|
++ URLPattern pattern(UserScript::ValidUserScriptSchemes());
|
|
|
|
++ if (URLPattern::ParseResult::kSuccess != pattern.Parse(value)) {
|
|
|
|
++ error_message = "Invalid UserScript Schema " + value;
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ script->add_url_pattern(pattern);
|
|
|
|
++ } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) {
|
|
|
|
++ URLPattern exclude(UserScript::ValidUserScriptSchemes());
|
|
|
|
++ if (URLPattern::ParseResult::kSuccess != exclude.Parse(value)) {
|
|
|
|
++ error_message = "Invalid UserScript Schema " + value;
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ script->add_exclude_url_pattern(exclude);
|
|
|
|
++ } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
|
|
|
|
++ if (value == kRunAtDocumentStartValue)
|
|
|
|
++ script->set_run_location(UserScript::DOCUMENT_START);
|
|
|
|
++ else if (value == kRunAtDocumentEndValue)
|
|
|
|
++ script->set_run_location(UserScript::DOCUMENT_END);
|
|
|
|
++ else if (value == kRunAtDocumentIdleValue)
|
|
|
|
++ script->set_run_location(UserScript::DOCUMENT_IDLE);
|
|
|
|
++ else {
|
|
|
|
++ error_message = "Invalid RunAtDeclaration " + value;
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ } else if (GetDeclarationValue(line, kUrlSourceDeclaration, &value) ||
|
|
|
|
++ GetDeclarationValue(line, kUrlHomePageDeclaration, &value)) {
|
|
|
|
++ script->set_url_source(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kParserError, &value)) {
|
|
|
|
++ script->set_parser_error(value);
|
|
|
|
++ } else if (GetDeclarationValue(line, kForceDisabled, &value)) {
|
|
|
|
++ script->set_force_disabled();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // TODO(aa): Handle more types of metadata.
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ line_start = line_end + 1;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // If no patterns were specified, default to @include *. This is what
|
|
|
|
++ // Greasemonkey does.
|
|
|
|
++ if (script->globs().empty() && script->url_patterns().is_empty())
|
|
|
|
++ script->add_glob("*");
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++bool LoadUserScriptFromFile(
|
|
|
|
++ const base::FilePath& user_script_path, const GURL& original_url,
|
|
|
|
++ std::unique_ptr<UserScript>& script,
|
|
|
|
++ bool* found_metadata,
|
|
|
|
++ std::u16string* error) {
|
|
|
|
++
|
|
|
|
++ base::File infile;
|
|
|
|
++ if (user_script_path.IsContentUri()) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: path " << user_script_path << " is a content uri";
|
|
|
|
++
|
|
|
|
++ infile = OpenContentUriForRead(user_script_path);
|
|
|
|
++ } else {
|
|
|
|
++ infile = base::File(user_script_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!infile.IsValid()) {
|
|
|
|
++ *error = u"Cannot open script source.";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ auto length = infile.GetLength();
|
|
|
|
++ if (length<=0) {
|
|
|
|
++ *error = u"File is empty.";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ auto buffer = std::vector<char>(length);
|
|
|
|
++ int bytes_read = infile.Read(0, buffer.data(), length);
|
|
|
|
++ if (bytes_read == -1) {
|
|
|
|
++ *error = u"Could not read source file.";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ std::string content(buffer.begin(), buffer.end());
|
|
|
|
++ if (!base::IsStringUTF8(content)) {
|
|
|
|
++ *error = u"User script must be UTF8 encoded.";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ std::string detailed_error;
|
|
|
|
++ bool parseResult = UserScriptLoader::ParseMetadataHeader(content, script,
|
|
|
|
++ found_metadata, detailed_error);
|
|
|
|
++ if (parseResult == false || *found_metadata == false) {
|
|
|
|
++ std::u16string detailed_error16;
|
|
|
|
++ base::UTF8ToUTF16(detailed_error.c_str(), detailed_error.length(), &detailed_error16);
|
|
|
|
++ *error = base::StrCat({u"Invalid script header. ", detailed_error16});
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ script->set_match_origin_as_fallback(MatchOriginAsFallbackBehavior::kNever);
|
|
|
|
++
|
|
|
|
++ // remove unicode chars and set content into File
|
|
|
|
++ stripUnicode(content);
|
|
|
|
++ std::unique_ptr<UserScript::File> file(new UserScript::File());
|
|
|
|
++ file->set_content(content);
|
|
|
|
++ file->set_url(GURL(/*script_key*/ "script.js")); // name doesn't matter
|
|
|
|
++
|
|
|
|
++ // create SHA256 of file
|
|
|
|
++ char raw[crypto::kSHA256Length] = {0};
|
|
|
|
++ std::string key;
|
|
|
|
++ crypto::SHA256HashString(content, raw, crypto::kSHA256Length);
|
|
|
|
++ base::Base64Encode(base::StringPiece(raw, crypto::kSHA256Length), &key);
|
|
|
|
++ file->set_key(key);
|
|
|
|
++
|
|
|
|
++ script->js_scripts().push_back(std::move(file));
|
|
|
|
++
|
|
|
|
++ // add into key the filename
|
|
|
|
++ // this value is used in ui to discriminate scripts
|
|
|
|
++ script->set_key(user_script_path.BaseName().value());
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++bool GetOrCreatePath(base::FilePath& path) {
|
|
|
|
++ base::PathService::Get(base::DIR_ANDROID_APP_DATA, &path);
|
|
|
|
++ path = path.AppendASCII("userscripts");
|
|
|
|
++
|
|
|
|
++ // create snippets directory if not exists
|
|
|
|
++ if (!base::PathExists(path)) {
|
|
|
|
++ LOG(INFO) << "Path " << path << " doesn't exists. Creating";
|
|
|
|
++ base::File::Error error = base::File::FILE_OK;
|
|
|
|
++ if (!base::CreateDirectoryAndGetError(path, &error)) {
|
|
|
|
++ LOG(ERROR) <<
|
|
|
|
++ "UserScriptLoader: failed to create directory: " << path
|
|
|
|
++ << " with error code " << error;
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++void LoadUserScripts(UserScriptList* user_scripts_list) {
|
|
|
|
++ base::FilePath path;
|
|
|
|
++ if (GetOrCreatePath(path) == false) return;
|
|
|
|
++
|
|
|
|
++ // enumerate all files from script path
|
|
|
|
++ // we accept all files, but we check if it's a real
|
|
|
|
++ // userscript
|
|
|
|
++ base::FileEnumerator dir_enum(
|
|
|
|
++ path,
|
|
|
|
++ /*recursive=*/false, base::FileEnumerator::FILES);
|
|
|
|
++ base::FilePath full_name;
|
|
|
|
++ while (full_name = dir_enum.Next(), !full_name.empty()) {
|
|
|
|
++ std::unique_ptr<UserScript> userscript(new UserScript());
|
|
|
|
++
|
|
|
|
++ std::u16string error;
|
|
|
|
++ bool found_metadata;
|
|
|
|
++ if (LoadUserScriptFromFile(full_name, GURL(), userscript, &found_metadata, &error)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: Found user script " << userscript->name() <<
|
|
|
|
++ "-" << userscript->version() <<
|
|
|
|
++ "-" << userscript->description();
|
|
|
|
++
|
|
|
|
++ userscript->set_file_path(full_name.AsUTF8Unsafe());
|
|
|
|
++ user_scripts_list->push_back(std::move(userscript));
|
|
|
|
++ } else {
|
|
|
|
++ LOG(ERROR) << "UserScriptLoader: load error " << error;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptLoader::UserScriptLoader(BrowserContext* browser_context,
|
|
|
|
++ UserScriptsPrefs* prefs)
|
|
|
|
++ : loaded_scripts_(new UserScriptList()),
|
|
|
|
++ ready_(false),
|
|
|
|
++ browser_context_(browser_context),
|
|
|
|
++ prefs_(prefs) {}
|
|
|
|
++
|
|
|
|
++UserScriptLoader::~UserScriptLoader() {
|
|
|
|
++ for (auto& observer : observers_)
|
|
|
|
++ observer.OnUserScriptLoaderDestroyed(this);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::OnRenderProcessHostCreated(
|
|
|
|
++ content::RenderProcessHost* process_host) {
|
|
|
|
++ if (initial_load_complete()) {
|
|
|
|
++ SendUpdate(process_host, shared_memory_);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::SetReady(bool ready) {
|
|
|
|
++ bool was_ready = ready_;
|
|
|
|
++ ready_ = ready;
|
|
|
|
++ if (ready_ && !was_ready)
|
|
|
|
++ AttemptLoad();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::AttemptLoad() {
|
|
|
|
++ int tryOut = prefs_->GetCurrentStartupTryout();
|
|
|
|
++ if (tryOut >= 3) {
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: Possible crash detected. UserScript disabled";
|
|
|
|
++ prefs_->SetEnabled(false);
|
|
|
|
++ } else {
|
|
|
|
++ prefs_->StartupTryout(tryOut+1);
|
|
|
|
++ StartLoad();
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::StartLoad() {
|
|
|
|
++ DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
++
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: StartLoad";
|
|
|
|
++
|
|
|
|
++ // Reload any loaded scripts, and clear out |loaded_scripts_| to indicate that
|
|
|
|
++ // the scripts aren't currently ready.
|
|
|
|
++ std::unique_ptr<UserScriptList> scripts_to_load = std::move(loaded_scripts_);
|
|
|
|
++ scripts_to_load->clear();
|
|
|
|
++
|
|
|
|
++ if (prefs_->IsEnabled()) {
|
|
|
|
++ LoadScripts(std::move(scripts_to_load),
|
|
|
|
++ base::BindOnce(&UserScriptLoader::OnScriptsLoaded,
|
|
|
|
++ weak_factory_.GetWeakPtr()));
|
|
|
|
++ } else {
|
|
|
|
++ OnScriptsLoaded(std::move(scripts_to_load));
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++base::ReadOnlySharedMemoryRegion UserScriptLoader::Serialize(
|
|
|
|
++ const UserScriptList& scripts) {
|
|
|
|
++ base::Pickle pickle;
|
|
|
|
++ pickle.WriteUInt32(scripts.size());
|
|
|
|
++ for (const std::unique_ptr<UserScript>& script : scripts) {
|
|
|
|
++ // TODO(aa): This can be replaced by sending content script metadata to
|
|
|
|
++ // renderers along with other extension data in ExtensionMsg_Loaded.
|
|
|
|
++ // See crbug.com/70516.
|
|
|
|
++ script->Pickle(&pickle);
|
|
|
|
++ // Write scripts as 'data' so that we can read it out in the slave without
|
|
|
|
++ // allocating a new string.
|
|
|
|
++ for (const std::unique_ptr<UserScript::File>& js_file :
|
|
|
|
++ script->js_scripts()) {
|
|
|
|
++ base::StringPiece contents = js_file->GetContent();
|
|
|
|
++ pickle.WriteData(contents.data(), contents.length());
|
|
|
|
++ }
|
|
|
|
++ for (const std::unique_ptr<UserScript::File>& css_file :
|
|
|
|
++ script->css_scripts()) {
|
|
|
|
++ base::StringPiece contents = css_file->GetContent();
|
|
|
|
++ pickle.WriteData(contents.data(), contents.length());
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Create the shared memory object.
|
|
|
|
++ base::MappedReadOnlyRegion shared_memory =
|
|
|
|
++ base::ReadOnlySharedMemoryRegion::Create(pickle.size());
|
|
|
|
++ if (!shared_memory.IsValid())
|
|
|
|
++ return {};
|
|
|
|
++
|
|
|
|
++ // Copy the pickle to shared memory.
|
|
|
|
++ memcpy(shared_memory.mapping.memory(), pickle.data(), pickle.size());
|
|
|
|
++ return std::move(shared_memory.region);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::AddObserver(Observer* observer) {
|
|
|
|
++ observers_.AddObserver(observer);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::RemoveObserver(Observer* observer) {
|
|
|
|
++ observers_.RemoveObserver(observer);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::OnScriptsLoaded(
|
|
|
|
++ std::unique_ptr<UserScriptList> user_scripts) {
|
|
|
|
++
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: OnScriptsLoaded";
|
|
|
|
++
|
|
|
|
++ // Check user preferences for loaded user scripts
|
|
|
|
++ prefs_->CompareWithPrefs(*user_scripts);
|
|
|
|
++ loaded_scripts_ = std::move(user_scripts);
|
|
|
|
++
|
|
|
|
++ base::ReadOnlySharedMemoryRegion shared_memory;
|
|
|
|
++ shared_memory =
|
|
|
|
++ UserScriptLoader::Serialize(*loaded_scripts_);
|
|
|
|
++
|
|
|
|
++ if (!shared_memory.IsValid()) {
|
|
|
|
++ // This can happen if we run out of file descriptors. In that case, we
|
|
|
|
++ // have a choice between silently omitting all user scripts for new tabs,
|
|
|
|
++ // by nulling out shared_memory_, or only silently omitting new ones by
|
|
|
|
++ // leaving the existing object in place. The second seems less bad, even
|
|
|
|
++ // though it removes the possibility that freeing the shared memory block
|
|
|
|
++ // would open up enough FDs for long enough for a retry to succeed.
|
|
|
|
++
|
|
|
|
++ // Pretend the extension change didn't happen.
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // We've got scripts ready to go.
|
|
|
|
++ shared_memory_ = std::move(shared_memory);
|
|
|
|
++
|
|
|
|
++ for (content::RenderProcessHost::iterator i(
|
|
|
|
++ content::RenderProcessHost::AllHostsIterator());
|
|
|
|
++ !i.IsAtEnd(); i.Advance()) {
|
|
|
|
++ SendUpdate(i.GetCurrentValue(), shared_memory_);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // DCHECK(false); trying crash
|
|
|
|
++ prefs_->StartupTryout(0);
|
|
|
|
++
|
|
|
|
++ for (auto& observer : observers_)
|
|
|
|
++ observer.OnScriptsLoaded(this, browser_context_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::SendUpdate(
|
|
|
|
++ content::RenderProcessHost* process,
|
|
|
|
++ const base::ReadOnlySharedMemoryRegion& shared_memory) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: SendUpdate";
|
|
|
|
++
|
|
|
|
++ // If the process is being started asynchronously, early return. We'll end up
|
|
|
|
++ // calling InitUserScripts when it's created which will call this again.
|
|
|
|
++ base::ProcessHandle handle = process->GetProcess().Handle();
|
|
|
|
++ if (!handle)
|
|
|
|
++ return;
|
|
|
|
++
|
|
|
|
++ base::ReadOnlySharedMemoryRegion region_for_process =
|
|
|
|
++ shared_memory.Duplicate();
|
|
|
|
++ if (!region_for_process.IsValid())
|
|
|
|
++ return;
|
|
|
|
++
|
|
|
|
++ process->Send(new ExtensionMsg_UpdateUserScripts(
|
|
|
|
++ std::move(region_for_process)));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void LoadScriptsOnFileTaskRunner(
|
|
|
|
++ std::unique_ptr<UserScriptList> user_scripts,
|
|
|
|
++ UserScriptLoader::LoadScriptsCallback callback) {
|
|
|
|
++ DCHECK(GetUserScriptsFileTaskRunner()->RunsTasksInCurrentSequence());
|
|
|
|
++ DCHECK(user_scripts.get());
|
|
|
|
++
|
|
|
|
++ // load user scripts from path
|
|
|
|
++ LoadUserScripts(user_scripts.get());
|
|
|
|
++
|
|
|
|
++ // Explicit priority to prevent unwanted task priority inheritance.
|
|
|
|
++ content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
|
|
|
|
++ ->PostTask(FROM_HERE,
|
|
|
|
++ base::BindOnce(std::move(callback), std::move(user_scripts)));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::LoadScripts(
|
|
|
|
++ std::unique_ptr<UserScriptList> user_scripts,
|
|
|
|
++ LoadScriptsCallback callback) {
|
|
|
|
++ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
|
|
|
++
|
|
|
|
++ GetUserScriptsFileTaskRunner()->PostTask(
|
|
|
|
++ FROM_HERE,
|
|
|
|
++ base::BindOnce(&LoadScriptsOnFileTaskRunner, std::move(user_scripts),
|
|
|
|
++ std::move(callback)));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void RemoveScriptsOnFileTaskRunner(
|
|
|
|
++ const std::string& script_id,
|
|
|
|
++ UserScriptLoader::RemoveScriptCallback callback) {
|
|
|
|
++ DCHECK(GetUserScriptsFileTaskRunner()->RunsTasksInCurrentSequence());
|
|
|
|
++
|
|
|
|
++ base::FilePath path;
|
|
|
|
++ if (GetOrCreatePath(path)) {
|
|
|
|
++ base::FilePath file = path.Append(script_id);
|
|
|
|
++ if (base::DeleteFile(file) == false) {
|
|
|
|
++ LOG(ERROR) <<
|
|
|
|
++ "ERROR: failed to delete file : " << path;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
|
|
|
|
++ ->PostTask(FROM_HERE,
|
|
|
|
++ base::BindOnce(std::move(callback)));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::OnScriptRemoved() {
|
|
|
|
++ StartLoad();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::RemoveScript(const std::string& script_id) {
|
|
|
|
++ if (!prefs_->IsEnabled()) return;
|
|
|
|
++ prefs_->RemoveScriptFromPrefs(script_id);
|
|
|
|
++
|
|
|
|
++ GetUserScriptsFileTaskRunner()->PostTask(
|
|
|
|
++ FROM_HERE,
|
|
|
|
++ base::BindOnce(&RemoveScriptsOnFileTaskRunner,
|
|
|
|
++ std::move(script_id),
|
|
|
|
++ base::BindOnce(&UserScriptLoader::OnScriptRemoved,
|
|
|
|
++ weak_factory_.GetWeakPtr())));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::SetScriptEnabled(const std::string& script_id, bool is_enabled) {
|
|
|
|
++ if (!prefs_->IsEnabled()) return;
|
|
|
|
++ prefs_->SetScriptEnabled(script_id, is_enabled);
|
|
|
|
++ StartLoad();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::SelectAndAddScriptFromFile(ui::WindowAndroid* nativeWindow) {
|
|
|
|
++ DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
++
|
|
|
|
++ if (!prefs_->IsEnabled()) return;
|
|
|
|
++
|
|
|
|
++ dialog_ = ui::SelectFileDialog::Create(
|
|
|
|
++ this, std::make_unique<ChromeSelectFilePolicy>(nullptr /*web_contents*/));
|
|
|
|
++
|
|
|
|
++ ui::SelectFileDialog::FileTypeInfo allowed_file_info;
|
|
|
|
++ allowed_file_info.extensions = {{FILE_PATH_LITERAL("js")}};
|
|
|
|
++ allowed_file_info.allowed_paths =
|
|
|
|
++ ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
|
|
|
|
++ base::FilePath suggested_name;
|
|
|
|
++
|
|
|
|
++ std::vector<std::u16string> types;
|
|
|
|
++ types.push_back(u"*/*"); /*= java SelectFileDialog.ALL_TYPES*/
|
|
|
|
++ std::pair<std::vector<std::u16string>, bool> accept_types = std::make_pair(
|
|
|
|
++ types, false /*use_media_capture*/);
|
|
|
|
++
|
|
|
|
++ dialog_->SelectFile(
|
|
|
|
++ ui::SelectFileDialog::SELECT_OPEN_FILE,
|
|
|
|
++ std::u16string() /* dialog title*/, suggested_name, &allowed_file_info,
|
|
|
|
++ 0 /* file type index */, std::string() /* default file extension */,
|
|
|
|
++ nativeWindow,
|
|
|
|
++ &accept_types /* params */);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++
|
|
|
|
++void LoadScriptFromPathOnFileTaskRunner(
|
|
|
|
++ const base::FilePath& path,
|
|
|
|
++ const std::string& display_name,
|
|
|
|
++ UserScriptLoader::LoadSingleScriptCallback callback ) {
|
|
|
|
++ DCHECK(GetUserScriptsFileTaskRunner()->RunsTasksInCurrentSequence());
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<UserScript> userscript(new UserScript());
|
|
|
|
++ std::u16string error;
|
|
|
|
++ bool found_metadata = false;
|
|
|
|
++ bool loaded = LoadUserScriptFromFile(path, GURL(), userscript, &found_metadata, &error);
|
|
|
|
++
|
|
|
|
++ bool result = loaded;
|
|
|
|
++ if (result || found_metadata) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: found " << userscript->name() <<
|
|
|
|
++ "-" << userscript->version() <<
|
|
|
|
++ "-" << userscript->description();
|
|
|
|
++ base::FilePath destination;
|
|
|
|
++ if (GetOrCreatePath(destination) == false) {
|
|
|
|
++ error = u"Cannot create destination.";
|
|
|
|
++ } else {
|
|
|
|
++ // we need an unique file name
|
|
|
|
++ if (display_name.empty() == false) {
|
|
|
|
++ userscript->set_key(display_name);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // filename is original filename or display_name
|
|
|
|
++ std::string file_name(userscript->key());
|
|
|
|
++ base::i18n::ReplaceIllegalCharactersInPath(&file_name, '_');
|
|
|
|
++ destination = destination.Append(file_name);
|
|
|
|
++
|
|
|
|
++ if (destination.ReferencesParent()) {
|
|
|
|
++ error = u"Invalid file name.";
|
|
|
|
++ result = false;
|
|
|
|
++ } else if (base::PathExists(destination)) {
|
|
|
|
++ error = u"User script already loaded.";
|
|
|
|
++ result = false;
|
|
|
|
++ } else {
|
|
|
|
++ if (loaded) {
|
|
|
|
++ // if is a correct userscript, copy it
|
|
|
|
++ result = base::CopyFile(path, destination);
|
|
|
|
++ if (result == false) {
|
|
|
|
++ error = u"Copy error.";
|
|
|
|
++ }
|
|
|
|
++ } else {
|
|
|
|
++ // else, there is a parser error
|
|
|
|
++ // write minimal values and the error string, so UI can show it
|
|
|
|
++ std::string combined_string = base::StrCat({
|
|
|
|
++ kUserScriptBegin, "\n",
|
|
|
|
++ kNamespaceDeclaration, " ", userscript->name_space(), "\n",
|
|
|
|
++ kNameDeclaration, " ", userscript->name(), "\n",
|
|
|
|
++ kVersionDeclaration, " ", userscript->version(), "\n",
|
|
|
|
++ kDescriptionDeclaration, " ", userscript->description(), "\n",
|
|
|
|
++ kUrlSourceDeclaration, " ", userscript->url_source(), "\n",
|
|
|
|
++ kParserError, " ", base::UTF16ToASCII(error), "\n",
|
|
|
|
++ kForceDisabled, " true\n",
|
|
|
|
++ kUserScriptEnd, "\n"
|
|
|
|
++ });
|
|
|
|
++
|
|
|
|
++ if (!base::WriteFile(destination, combined_string)) {
|
|
|
|
++ error = u"Cannot write.";
|
|
|
|
++ result = false;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!error.empty()) {
|
|
|
|
++ LOG(ERROR) << "UserScriptLoader: load error " << error;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // return to callback with eventually the error
|
|
|
|
++ const std::string string_error = base::UTF16ToASCII(error);
|
|
|
|
++ content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
|
|
|
|
++ ->PostTask(FROM_HERE,
|
|
|
|
++ base::BindOnce(std::move(callback), result,
|
|
|
|
++ std::move(string_error)));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::TryToInstall(const base::FilePath& script_path) {
|
|
|
|
++ if (!prefs_->IsEnabled()) return;
|
|
|
|
++
|
|
|
|
++ std::u16string file_display_name;
|
|
|
|
++ base::MaybeGetFileDisplayName(script_path, &file_display_name);
|
|
|
|
++
|
|
|
|
++ std::string display_name = script_path.BaseName().value();
|
|
|
|
++ if (base::IsStringASCII(file_display_name))
|
|
|
|
++ display_name = base::UTF16ToASCII(file_display_name);
|
|
|
|
++
|
|
|
|
++ GetUserScriptsFileTaskRunner()->PostTask(
|
|
|
|
++ FROM_HERE,
|
|
|
|
++ base::BindOnce(
|
|
|
|
++ &LoadScriptFromPathOnFileTaskRunner,
|
|
|
|
++ script_path, display_name,
|
|
|
|
++ base::BindOnce(
|
|
|
|
++ &UserScriptLoader::LoadScriptFromPathOnFileTaskRunnerCallback,
|
|
|
|
++ weak_factory_.GetWeakPtr()
|
|
|
|
++ )
|
|
|
|
++ ));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::FileSelected(
|
|
|
|
++ const base::FilePath& path, int index, void* params) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptLoader: FileSelected " << path;
|
|
|
|
++
|
|
|
|
++ UserScriptLoader::TryToInstall(path);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::LoadScriptFromPathOnFileTaskRunnerCallback(
|
|
|
|
++ bool result, const std::string& error) {
|
|
|
|
++ for (auto& observer : observers_)
|
|
|
|
++ observer.OnUserScriptLoaded(this, result, error);
|
|
|
|
++
|
|
|
|
++ StartLoad();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptLoader::FileSelectionCanceled(
|
|
|
|
++ void* params) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/browser/user_script_loader.h b/components/user_scripts/browser/user_script_loader.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/user_script_loader.h
|
|
|
|
+@@ -0,0 +1,170 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_BROWSER_USER_SCRIPT_LOADER_H_
|
|
|
|
++#define USERSCRIPTS_BROWSER_USER_SCRIPT_LOADER_H_
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <memory>
|
|
|
|
++#include <set>
|
|
|
|
++
|
|
|
|
++#include "base/callback_forward.h"
|
|
|
|
++#include "base/compiler_specific.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/read_only_shared_memory_region.h"
|
|
|
|
++#include "base/memory/weak_ptr.h"
|
|
|
|
++#include "base/observer_list.h"
|
|
|
|
++#include "content/public/browser/render_process_host_creation_observer.h"
|
|
|
|
++#include "ui/shell_dialogs/select_file_dialog.h"
|
|
|
|
++#include "content/browser/file_system_access/file_system_chooser.h"
|
|
|
|
++#include "ui/android/window_android.h"
|
|
|
|
++
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "user_script_prefs.h"
|
|
|
|
++
|
|
|
|
++namespace base {
|
|
|
|
++class ReadOnlySharedMemoryRegion;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace content {
|
|
|
|
++class BrowserContext;
|
|
|
|
++class RenderProcessHost;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// Manages one "logical unit" of user scripts in shared memory by constructing a
|
|
|
|
++// new shared memory region when the set of scripts changes. Also notifies
|
|
|
|
++// renderers of new shared memory region when new renderers appear, or when
|
|
|
|
++// script reloading completes. Script loading lives on the file thread.
|
|
|
|
++class UserScriptLoader : public content::RenderProcessHostCreationObserver,
|
|
|
|
++ public ui::SelectFileDialog::Listener {
|
|
|
|
++ public:
|
|
|
|
++ using LoadScriptsCallback =
|
|
|
|
++ base::OnceCallback<void(std::unique_ptr<UserScriptList>)>;
|
|
|
|
++ using LoadSingleScriptCallback =
|
|
|
|
++ base::OnceCallback<void(bool result, const std::string& error)>;
|
|
|
|
++
|
|
|
|
++ using RemoveScriptCallback =
|
|
|
|
++ base::OnceCallback<void()>;
|
|
|
|
++ class Observer {
|
|
|
|
++ public:
|
|
|
|
++ virtual void OnScriptsLoaded(UserScriptLoader* loader,
|
|
|
|
++ content::BrowserContext* browser_context) = 0;
|
|
|
|
++ virtual void OnUserScriptLoaderDestroyed(UserScriptLoader* loader) = 0;
|
|
|
|
++ virtual void OnUserScriptLoaded(UserScriptLoader* loader,
|
|
|
|
++ bool result, const std::string& error) = 0;
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // Parses the includes out of |script| and returns them in |includes|.
|
|
|
|
++ static bool ParseMetadataHeader(const base::StringPiece& script_text,
|
|
|
|
++ std::unique_ptr<UserScript>& script,
|
|
|
|
++ bool *found_metadata,
|
|
|
|
++ std::string& error_message);
|
|
|
|
++
|
|
|
|
++ UserScriptLoader(content::BrowserContext* browser_context,
|
|
|
|
++ UserScriptsPrefs* prefs);
|
|
|
|
++ //const HostID& host_id);
|
|
|
|
++ ~UserScriptLoader() override;
|
|
|
|
++
|
|
|
|
++ // Initiates procedure to start loading scripts on the file thread.
|
|
|
|
++ void StartLoad();
|
|
|
|
++
|
|
|
|
++ // Returns true if we have any scripts ready.
|
|
|
|
++ bool initial_load_complete() const { return shared_memory_.IsValid(); }
|
|
|
|
++
|
|
|
|
++ // Pickle user scripts and return pointer to the shared memory.
|
|
|
|
++ static base::ReadOnlySharedMemoryRegion Serialize(
|
|
|
|
++ const user_scripts::UserScriptList& scripts);
|
|
|
|
++
|
|
|
|
++ // Adds or removes observers.
|
|
|
|
++ void AddObserver(Observer* observer);
|
|
|
|
++ void RemoveObserver(Observer* observer);
|
|
|
|
++
|
|
|
|
++ // Sets the flag if the initial set of hosts has finished loading; if it's
|
|
|
|
++ // set to be true, calls AttempLoad() to bootstrap.
|
|
|
|
++ void SetReady(bool ready);
|
|
|
|
++
|
|
|
|
++ void RemoveScript(const std::string& script_id);
|
|
|
|
++ void SetScriptEnabled(const std::string& script_id, bool is_enabled);
|
|
|
|
++
|
|
|
|
++ void SelectAndAddScriptFromFile(ui::WindowAndroid* wa);
|
|
|
|
++ void TryToInstall(const base::FilePath& script_path);
|
|
|
|
++
|
|
|
|
++ void LoadScripts(std::unique_ptr<UserScriptList> user_scripts,
|
|
|
|
++ LoadScriptsCallback callback);
|
|
|
|
++
|
|
|
|
++ protected:
|
|
|
|
++ content::BrowserContext* browser_context() const { return browser_context_; }
|
|
|
|
++
|
|
|
|
++ UserScriptsPrefs* prefs() const { return prefs_; }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ void OnRenderProcessHostCreated(
|
|
|
|
++ content::RenderProcessHost* process_host) override;
|
|
|
|
++
|
|
|
|
++ // Attempts to initiate a load.
|
|
|
|
++ void AttemptLoad();
|
|
|
|
++
|
|
|
|
++ // Called once we have finished loading the scripts on the file thread.
|
|
|
|
++ void OnScriptsLoaded(std::unique_ptr<UserScriptList> user_scripts);
|
|
|
|
++
|
|
|
|
++ // Sends the renderer process a new set of user scripts. If
|
|
|
|
++ // |changed_hosts| is not empty, this signals that only the scripts from
|
|
|
|
++ // those hosts should be updated. Otherwise, all hosts will be
|
|
|
|
++ // updated.
|
|
|
|
++ void SendUpdate(content::RenderProcessHost* process,
|
|
|
|
++ const base::ReadOnlySharedMemoryRegion& shared_memory);
|
|
|
|
++
|
|
|
|
++ // Contains the scripts that were found the last time scripts were updated.
|
|
|
|
++ base::ReadOnlySharedMemoryRegion shared_memory_;
|
|
|
|
++
|
|
|
|
++ // List of scripts that are currently loaded. This is null when a load is in
|
|
|
|
++ // progress.
|
|
|
|
++ std::unique_ptr<UserScriptList> loaded_scripts_;
|
|
|
|
++
|
|
|
|
++ // If the initial set of hosts has finished loading.
|
|
|
|
++ bool ready_;
|
|
|
|
++
|
|
|
|
++ // The browser_context for which the scripts managed here are installed.
|
|
|
|
++ content::BrowserContext* browser_context_;
|
|
|
|
++
|
|
|
|
++ // Manage load and store from preferences
|
|
|
|
++ UserScriptsPrefs* prefs_;
|
|
|
|
++
|
|
|
|
++ // The associated observers.
|
|
|
|
++ base::ObserverList<Observer>::Unchecked observers_;
|
|
|
|
++
|
|
|
|
++ void OnScriptRemoved();
|
|
|
|
++
|
|
|
|
++ // Manage file dialog requests
|
|
|
|
++ scoped_refptr<ui::SelectFileDialog> dialog_;
|
|
|
|
++ void FileSelected(const base::FilePath& path,
|
|
|
|
++ int index, void* params) override;
|
|
|
|
++ void FileSelectionCanceled(void* params) override;
|
|
|
|
++ void LoadScriptFromPathOnFileTaskRunnerCallback(
|
|
|
|
++ bool result, const std::string& error );
|
|
|
|
++
|
|
|
|
++ base::WeakPtrFactory<UserScriptLoader> weak_factory_{this};
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(UserScriptLoader);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_BROWSER_USER_SCRIPT_LOADER_H_
|
|
|
|
+diff --git a/components/user_scripts/browser/user_script_pref_info.cc b/components/user_scripts/browser/user_script_pref_info.cc
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/user_script_pref_info.cc
|
|
|
|
+@@ -0,0 +1,34 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include "user_script_pref_info.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++UserScriptsListPrefs::ScriptInfo::ScriptInfo(const std::string& name,
|
|
|
|
++ const std::string& description,
|
|
|
|
++ const base::Time& install_time,
|
|
|
|
++ bool enabled)
|
|
|
|
++ : install_time(install_time),
|
|
|
|
++ enabled(enabled),
|
|
|
|
++ name_(name),
|
|
|
|
++ description_(description) {}
|
|
|
|
++
|
|
|
|
++UserScriptsListPrefs::ScriptInfo::ScriptInfo(const ScriptInfo& other) = default;
|
|
|
|
++UserScriptsListPrefs::ScriptInfo::~ScriptInfo() = default;
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/user_script_pref_info.h b/components/user_scripts/browser/user_script_pref_info.h
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/user_script_pref_info.h
|
|
|
|
+@@ -0,0 +1,72 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_BROWSER_USERSCRIPT_PREF_INFO_H_
|
|
|
|
++#define USERSCRIPTS_BROWSER_USERSCRIPT_PREF_INFO_H_
|
|
|
|
++
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "base/time/time.h"
|
|
|
|
++#include "components/keyed_service/core/keyed_service.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class UserScriptsListPrefs : public KeyedService {
|
|
|
|
++ public:
|
|
|
|
++ struct ScriptInfo {
|
|
|
|
++ ScriptInfo(const std::string& name,
|
|
|
|
++ const std::string& description,
|
|
|
|
++ const base::Time& install_time,
|
|
|
|
++ bool enabled);
|
|
|
|
++ ScriptInfo(const ScriptInfo& other);
|
|
|
|
++ ~ScriptInfo();
|
|
|
|
++
|
|
|
|
++ const std::string& name() const { return name_; }
|
|
|
|
++ void set_name(const std::string& name) { name_ = name; }
|
|
|
|
++
|
|
|
|
++ const std::string& description() const { return description_; }
|
|
|
|
++ void set_description(const std::string& description) { description_ = description; }
|
|
|
|
++
|
|
|
|
++ const std::string& version() const { return version_; }
|
|
|
|
++ void set_version(const std::string& version) { version_ = version; }
|
|
|
|
++
|
|
|
|
++ const std::string& file_path() const { return file_path_; }
|
|
|
|
++ void set_file_path(const std::string& file_path) { file_path_ = file_path; }
|
|
|
|
++
|
|
|
|
++ const std::string& url_source() const { return url_source_; }
|
|
|
|
++ void set_url_source(const std::string& url_source) { url_source_ = url_source; }
|
|
|
|
++
|
|
|
|
++ const std::string& parser_error() const { return parser_error_; }
|
|
|
|
++ void set_parser_error(const std::string& parser_error) { parser_error_ = parser_error; }
|
|
|
|
++
|
|
|
|
++ base::Time install_time;
|
|
|
|
++ bool enabled;
|
|
|
|
++
|
|
|
|
++ bool force_disabled;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ std::string name_;
|
|
|
|
++ std::string description_;
|
|
|
|
++ std::string version_;
|
|
|
|
++ std::string file_path_;
|
|
|
|
++ std::string url_source_;
|
|
|
|
++ std::string parser_error_;
|
|
|
|
++ };
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_BROWSER_USERSCRIPT_PREF_INFO_H_
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/user_script_prefs.cc b/components/user_scripts/browser/user_script_prefs.cc
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/user_script_prefs.cc
|
|
|
|
+@@ -0,0 +1,276 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "base/strings/string_number_conversions.h"
|
|
|
|
++#include "base/json/json_writer.h"
|
|
|
|
++
|
|
|
|
++#include "chrome/browser/browser_process.h"
|
|
|
|
++#include "chrome/browser/profiles/profile.h"
|
|
|
|
++#include "chrome/browser/profiles/profile_manager.h"
|
|
|
|
++
|
|
|
|
++#include "components/prefs/pref_service.h"
|
|
|
|
++#include "components/prefs/scoped_user_pref_update.h"
|
|
|
|
++#include "components/pref_registry/pref_registry_syncable.h"
|
|
|
|
++#include "user_script_prefs.h"
|
|
|
|
++#include "user_script_pref_info.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace prefs {
|
|
|
|
++ const char kUserScriptsEnabled[] = "userscripts.enabled";
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++const char kUserScriptsStartup[] = "userscripts.startup";
|
|
|
|
++
|
|
|
|
++const char kUserScriptsList[] = "userscripts.scripts";
|
|
|
|
++const char kScriptIsEnabled[] = "enabled";
|
|
|
|
++const char kScriptName[] = "name";
|
|
|
|
++const char kScriptDescription[] = "description";
|
|
|
|
++const char kScriptVersion[] = "version";
|
|
|
|
++const char kScriptInstallTime[] = "install_time";
|
|
|
|
++const char kScriptFilePath[] = "file_path";
|
|
|
|
++const char kScriptUrlSource[] = "url_source";
|
|
|
|
++const char kScriptParserError[] = "parser_error";
|
|
|
|
++const char kScriptForceDisabled[] = "force_disabled";
|
|
|
|
++
|
|
|
|
++class PrefUpdate : public DictionaryPrefUpdate {
|
|
|
|
++ public:
|
|
|
|
++ PrefUpdate(PrefService* service,
|
|
|
|
++ const std::string& id,
|
|
|
|
++ const std::string& path)
|
|
|
|
++ : DictionaryPrefUpdate(service, path), id_(id) {}
|
|
|
|
++
|
|
|
|
++ ~PrefUpdate() override = default;
|
|
|
|
++
|
|
|
|
++ base::DictionaryValue* Get() override {
|
|
|
|
++ base::DictionaryValue* dict = DictionaryPrefUpdate::Get();
|
|
|
|
++ base::Value* dict_item =
|
|
|
|
++ dict->FindKeyOfType(id_, base::Value::Type::DICTIONARY);
|
|
|
|
++ if (!dict_item)
|
|
|
|
++ dict_item = dict->SetKey(id_, base::Value(base::Value::Type::DICTIONARY));
|
|
|
|
++ return static_cast<base::DictionaryValue*>(dict_item);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ const std::string id_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(PrefUpdate);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++bool GetInt64FromPref(const base::DictionaryValue* dict,
|
|
|
|
++ const std::string& key,
|
|
|
|
++ int64_t* value) {
|
|
|
|
++ DCHECK(dict);
|
|
|
|
++ const std::string* value_str = dict->FindStringKey(key);
|
|
|
|
++ if (!value_str) {
|
|
|
|
++ VLOG(2) << "Can't find key in local pref dictionary. Invalid key: " << key
|
|
|
|
++ << ".";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!base::StringToInt64(*value_str, value)) {
|
|
|
|
++ VLOG(2) << "Can't change string to int64_t. Invalid string value: "
|
|
|
|
++ << *value_str << ".";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptsPrefs::UserScriptsPrefs(
|
|
|
|
++ PrefService* prefs)
|
|
|
|
++ : prefs_(prefs) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++void UserScriptsPrefs::RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
|
|
|
|
++ registry->RegisterBooleanPref(prefs::kUserScriptsEnabled, false);
|
|
|
|
++ registry->RegisterIntegerPref(kUserScriptsStartup, 0);
|
|
|
|
++ registry->RegisterDictionaryPref(kUserScriptsList);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptsPrefs::IsEnabled() {
|
|
|
|
++ return prefs_->GetBoolean(prefs::kUserScriptsEnabled);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsPrefs::SetEnabled(bool enabled) {
|
|
|
|
++ prefs_->SetBoolean(prefs::kUserScriptsEnabled, enabled);
|
|
|
|
++ prefs_->CommitPendingWrite();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsPrefs::StartupTryout(int number) {
|
|
|
|
++ prefs_->SetInteger(kUserScriptsStartup, number);
|
|
|
|
++ prefs_->CommitPendingWrite();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int UserScriptsPrefs::GetCurrentStartupTryout() {
|
|
|
|
++ return prefs_->GetInteger(kUserScriptsStartup);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsPrefs::CompareWithPrefs(UserScriptList& user_scripts) {
|
|
|
|
++ if (IsEnabled() == false) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScriptsPrefs: disabled by user";
|
|
|
|
++
|
|
|
|
++ user_scripts.clear();
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ std::vector<std::string> all_scripts;
|
|
|
|
++
|
|
|
|
++ auto it = user_scripts.begin();
|
|
|
|
++ while (it != user_scripts.end())
|
|
|
|
++ {
|
|
|
|
++ std::string key = it->get()->key();
|
|
|
|
++ all_scripts.push_back(key);
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<UserScriptsListPrefs::ScriptInfo> scriptInfo =
|
|
|
|
++ UserScriptsPrefs::CreateScriptInfoFromPrefs(key);
|
|
|
|
++
|
|
|
|
++ // add or update prefs
|
|
|
|
++ scriptInfo->set_name(it->get()->name());
|
|
|
|
++ scriptInfo->set_description(it->get()->description());
|
|
|
|
++ scriptInfo->set_version(it->get()->version());
|
|
|
|
++ scriptInfo->set_file_path(it->get()->file_path());
|
|
|
|
++ scriptInfo->set_url_source(it->get()->url_source());
|
|
|
|
++ scriptInfo->set_parser_error(it->get()->parser_error());
|
|
|
|
++ scriptInfo->force_disabled = (it->get()->force_disabled());
|
|
|
|
++
|
|
|
|
++ PrefUpdate update(prefs_, key, kUserScriptsList);
|
|
|
|
++ base::DictionaryValue* script_dict = update.Get();
|
|
|
|
++
|
|
|
|
++ script_dict->SetString(kScriptName, scriptInfo->name());
|
|
|
|
++ script_dict->SetString(kScriptDescription, scriptInfo->description());
|
|
|
|
++ script_dict->SetBoolean(kScriptIsEnabled, scriptInfo->enabled);
|
|
|
|
++ script_dict->SetString(kScriptVersion, scriptInfo->version());
|
|
|
|
++ script_dict->SetString(kScriptFilePath, scriptInfo->file_path());
|
|
|
|
++ script_dict->SetString(kScriptUrlSource, scriptInfo->url_source());
|
|
|
|
++ script_dict->SetString(kScriptParserError, scriptInfo->parser_error());
|
|
|
|
++ script_dict->SetBoolean(kScriptForceDisabled, scriptInfo->force_disabled);
|
|
|
|
++
|
|
|
|
++ std::string install_time_str =
|
|
|
|
++ base::NumberToString(scriptInfo->install_time.ToInternalValue());
|
|
|
|
++ script_dict->SetString(kScriptInstallTime, install_time_str);
|
|
|
|
++
|
|
|
|
++ if (!scriptInfo->enabled) {
|
|
|
|
++ it = user_scripts.erase(it);
|
|
|
|
++ } else {
|
|
|
|
++ ++it;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // remove script from prefs if no more present
|
|
|
|
++ std::vector<std::string> all_scripts_to_remove;
|
|
|
|
++ const base::DictionaryValue* dict =
|
|
|
|
++ prefs_->GetDictionary(kUserScriptsList);
|
|
|
|
++ for (base::DictionaryValue::Iterator script_it(*dict); !script_it.IsAtEnd();
|
|
|
|
++ script_it.Advance()) {
|
|
|
|
++ const std::string& key = script_it.key();
|
|
|
|
++
|
|
|
|
++ if (std::find(all_scripts.begin(), all_scripts.end(), key) == all_scripts.end()) {
|
|
|
|
++ all_scripts_to_remove.push_back(key);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ DictionaryPrefUpdate update(prefs_, kUserScriptsList);
|
|
|
|
++ base::DictionaryValue* const update_dict = update.Get();
|
|
|
|
++ for (auto key : all_scripts_to_remove) {
|
|
|
|
++ update_dict->RemoveKey(key);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::string UserScriptsPrefs::GetScriptsInfo() {
|
|
|
|
++ std::string json_string;
|
|
|
|
++
|
|
|
|
++ const base::DictionaryValue* dict =
|
|
|
|
++ prefs_->GetDictionary(kUserScriptsList);
|
|
|
|
++
|
|
|
|
++ if (dict) {
|
|
|
|
++ base::JSONWriter::WriteWithOptions(
|
|
|
|
++ *dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
|
|
|
|
++ base::TrimWhitespaceASCII(json_string, base::TRIM_ALL, &json_string);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return json_string;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::unique_ptr<UserScriptsListPrefs::ScriptInfo> UserScriptsPrefs::CreateScriptInfoFromPrefs(
|
|
|
|
++ const std::string& script_id) const {
|
|
|
|
++
|
|
|
|
++ auto scriptInfo = std::make_unique<UserScriptsListPrefs::ScriptInfo>(
|
|
|
|
++ script_id, "", base::Time::Now(), false);
|
|
|
|
++
|
|
|
|
++ const base::DictionaryValue* scripts =
|
|
|
|
++ prefs_->GetDictionary(kUserScriptsList);
|
|
|
|
++ if (!scripts)
|
|
|
|
++ return scriptInfo;
|
|
|
|
++
|
|
|
|
++ const base::DictionaryValue* script = static_cast<const base::DictionaryValue*>(
|
|
|
|
++ scripts->FindDictKey(script_id));
|
|
|
|
++ if (!script)
|
|
|
|
++ return scriptInfo;
|
|
|
|
++
|
|
|
|
++ const std::string* name = script->FindStringKey(kScriptName);
|
|
|
|
++ const std::string* description = script->FindStringKey(kScriptDescription);
|
|
|
|
++ const std::string* version = script->FindStringKey(kScriptVersion);
|
|
|
|
++ const std::string* file_path = script->FindStringKey(kScriptFilePath);
|
|
|
|
++ const std::string* url_source = script->FindStringKey(kScriptUrlSource);
|
|
|
|
++ const std::string* parser_error = script->FindStringKey(kScriptParserError);
|
|
|
|
++
|
|
|
|
++ scriptInfo->set_name( name ? *name : "no name" );
|
|
|
|
++ scriptInfo->set_description( description ? *description : "no description" );
|
|
|
|
++ scriptInfo->set_version( version ? *version : "no version" );
|
|
|
|
++ scriptInfo->enabled = script->FindBoolKey(kScriptIsEnabled).value_or(false);
|
|
|
|
++ scriptInfo->set_file_path( file_path ? *file_path : "no file path" );
|
|
|
|
++ scriptInfo->set_url_source( url_source ? *url_source : "" );
|
|
|
|
++ scriptInfo->set_parser_error( parser_error ? *parser_error : "" );
|
|
|
|
++ scriptInfo->force_disabled = script->FindBoolKey(kScriptForceDisabled).value_or(false);
|
|
|
|
++
|
|
|
|
++ int64_t time_interval = 0;
|
|
|
|
++ if (GetInt64FromPref(script, kScriptInstallTime, &time_interval)) {
|
|
|
|
++ scriptInfo->install_time = base::Time::FromInternalValue(time_interval);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return scriptInfo;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsPrefs::RemoveScriptFromPrefs(const std::string& script_id) {
|
|
|
|
++ DictionaryPrefUpdate update(prefs_, kUserScriptsList);
|
|
|
|
++ base::DictionaryValue* const update_dict = update.Get();
|
|
|
|
++ update_dict->RemoveKey(script_id);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsPrefs::SetScriptEnabled(const std::string& script_id, bool is_enabled) {
|
|
|
|
++ PrefUpdate update(prefs_, script_id, kUserScriptsList);
|
|
|
|
++ base::DictionaryValue* script_dict = update.Get();
|
|
|
|
++ if (script_dict->FindBoolKey(kScriptForceDisabled).value_or(false))
|
|
|
|
++ is_enabled = true;
|
|
|
|
++ script_dict->SetBoolean(kScriptIsEnabled, is_enabled);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/browser/user_script_prefs.h b/components/user_scripts/browser/user_script_prefs.h
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/user_script_prefs.h
|
|
|
|
+@@ -0,0 +1,62 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_BROWSER_USERSCRIPT_PREFS_H_
|
|
|
|
++#define USERSCRIPTS_BROWSER_USERSCRIPT_PREFS_H_
|
|
|
|
++
|
|
|
|
++#include "content/public/browser/browser_context.h"
|
|
|
|
++#include "components/prefs/pref_service.h"
|
|
|
|
++#include "components/pref_registry/pref_registry_syncable.h"
|
|
|
|
++
|
|
|
|
++#include "user_script_pref_info.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace prefs {
|
|
|
|
++ extern const char kUserScriptsEnabled[];
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++class UserScriptsPrefs {
|
|
|
|
++ public:
|
|
|
|
++ UserScriptsPrefs(
|
|
|
|
++ PrefService* prefs);
|
|
|
|
++
|
|
|
|
++ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
|
|
|
|
++
|
|
|
|
++ bool IsEnabled();
|
|
|
|
++ void SetEnabled(bool enabled);
|
|
|
|
++
|
|
|
|
++ void StartupTryout(int number);
|
|
|
|
++ int GetCurrentStartupTryout();
|
|
|
|
++
|
|
|
|
++ void CompareWithPrefs(UserScriptList& user_scripts);
|
|
|
|
++
|
|
|
|
++ std::string GetScriptsInfo();
|
|
|
|
++ void RemoveScriptFromPrefs(const std::string& script_id);
|
|
|
|
++ void SetScriptEnabled(const std::string& script_id, bool is_enabled);
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<UserScriptsListPrefs::ScriptInfo> CreateScriptInfoFromPrefs(
|
|
|
|
++ const std::string& script_id) const;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ PrefService* prefs_;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_BROWSER_USERSCRIPT_PREFS_H_
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/browser/userscripts_browser_client.cc b/components/user_scripts/browser/userscripts_browser_client.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/userscripts_browser_client.cc
|
|
|
|
+@@ -0,0 +1,78 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include "userscripts_browser_client.h"
|
|
|
|
++
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++
|
|
|
|
++#include "chrome/browser/browser_process.h"
|
|
|
|
++
|
|
|
|
++#include "chrome/browser/profiles/profile.h"
|
|
|
|
++#include "chrome/browser/profiles/profile_manager.h"
|
|
|
|
++
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++#include "user_script_loader.h"
|
|
|
|
++#include "file_task_runner.h"
|
|
|
|
++#include "user_script_prefs.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++// remember: was ExtensionsBrowserClient
|
|
|
|
++UserScriptsBrowserClient* g_userscripts_browser_client = NULL;
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++UserScriptsBrowserClient::UserScriptsBrowserClient() {}
|
|
|
|
++
|
|
|
|
++UserScriptsBrowserClient::~UserScriptsBrowserClient() = default;
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++UserScriptsBrowserClient* UserScriptsBrowserClient::GetInstance() {
|
|
|
|
++ // only for browser process
|
|
|
|
++ if (!g_browser_process)
|
|
|
|
++ return NULL;
|
|
|
|
++
|
|
|
|
++ // singleton
|
|
|
|
++ if (g_userscripts_browser_client)
|
|
|
|
++ return g_userscripts_browser_client;
|
|
|
|
++
|
|
|
|
++ // make file task runner
|
|
|
|
++ GetUserScriptsFileTaskRunner().get();
|
|
|
|
++
|
|
|
|
++ // new instance singleton
|
|
|
|
++ g_userscripts_browser_client = new UserScriptsBrowserClient();
|
|
|
|
++
|
|
|
|
++ return g_userscripts_browser_client;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsBrowserClient::SetProfile(content::BrowserContext* context) {
|
|
|
|
++ browser_context_ = context;
|
|
|
|
++
|
|
|
|
++ prefs_ =
|
|
|
|
++ std::make_unique<user_scripts::UserScriptsPrefs>(
|
|
|
|
++ static_cast<Profile*>(context)->GetPrefs());
|
|
|
|
++
|
|
|
|
++ userscript_loader_ =
|
|
|
|
++ std::make_unique<user_scripts::UserScriptLoader>(browser_context_, prefs_.get());
|
|
|
|
++ if (prefs_->IsEnabled()) {
|
|
|
|
++ userscript_loader_->SetReady(true);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace user_scripts
|
|
|
|
+diff --git a/components/user_scripts/browser/userscripts_browser_client.h b/components/user_scripts/browser/userscripts_browser_client.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/browser/userscripts_browser_client.h
|
|
|
|
+@@ -0,0 +1,62 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_BROWSER_USERSCRIPTS_BROWSER_CLIENT_H_
|
|
|
|
++#define USERSCRIPTS_BROWSER_USERSCRIPTS_BROWSER_CLIENT_H_
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "content/public/browser/browser_context.h"
|
|
|
|
++
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "user_script_loader.h"
|
|
|
|
++#include "user_script_prefs.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class UserScriptsBrowserClient {
|
|
|
|
++ public:
|
|
|
|
++ UserScriptsBrowserClient();
|
|
|
|
++ UserScriptsBrowserClient(const UserScriptsBrowserClient&) = delete;
|
|
|
|
++ UserScriptsBrowserClient& operator=(const UserScriptsBrowserClient&) = delete;
|
|
|
|
++ virtual ~UserScriptsBrowserClient();
|
|
|
|
++
|
|
|
|
++ // Returns the single instance of |this|.
|
|
|
|
++ static UserScriptsBrowserClient* GetInstance();
|
|
|
|
++
|
|
|
|
++ void SetProfile(content::BrowserContext* context);
|
|
|
|
++
|
|
|
|
++ user_scripts::UserScriptsPrefs* GetPrefs() {
|
|
|
|
++ return prefs_.get();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ user_scripts::UserScriptLoader* GetLoader() {
|
|
|
|
++ return userscript_loader_.get();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ std::unique_ptr<UserScriptList> scripts_;
|
|
|
|
++ content::BrowserContext* browser_context_;
|
|
|
|
++ std::unique_ptr<user_scripts::UserScriptsPrefs> prefs_;
|
|
|
|
++ std::unique_ptr<user_scripts::UserScriptLoader> userscript_loader_;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_BROWSER_USERSCRIPTS_BROWSER_CLIENT_H_
|
|
|
|
+diff --git a/components/user_scripts/common/BUILD.gn b/components/user_scripts/common/BUILD.gn
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/BUILD.gn
|
|
|
|
+@@ -0,0 +1,49 @@
|
|
|
|
++# 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("//mojo/public/tools/bindings/mojom.gni")
|
|
|
|
++
|
|
|
|
++static_library("common") {
|
|
|
|
++ sources = [
|
|
|
|
++ "user_scripts_features.cc",
|
|
|
|
++ "user_scripts_features.h",
|
|
|
|
++ "constants.h",
|
|
|
|
++ "host_id.cc",
|
|
|
|
++ "host_id.h",
|
|
|
|
++ "script_constants.h",
|
|
|
|
++ "url_pattern_set.cc",
|
|
|
|
++ "url_pattern_set.h",
|
|
|
|
++ "url_pattern.cc",
|
|
|
|
++ "url_pattern.h",
|
|
|
|
++ "user_script.cc",
|
|
|
|
++ "user_script.h",
|
|
|
|
++ "view_type.cc",
|
|
|
|
++ "view_type.h",
|
|
|
|
++ "extension_messages.cc",
|
|
|
|
++ "extension_messages.h",
|
|
|
|
++ "extension_message_generator.cc",
|
|
|
|
++ "extension_message_generator.h",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ configs += [
|
|
|
|
++ "//build/config:precompiled_headers",
|
|
|
|
++ "//build/config/compiler:wexit_time_destructors",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ public_deps = [
|
|
|
|
++ "//components/services/app_service/public/cpp:app_file_handling",
|
|
|
|
++ "//content/public/common",
|
|
|
|
++ "//ipc",
|
|
|
|
++ "//skia",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ deps = [
|
|
|
|
++ "//base",
|
|
|
|
++ "//components/url_formatter",
|
|
|
|
++ "//components/url_matcher",
|
|
|
|
++ "//components/version_info",
|
|
|
|
++ "//crypto",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/common/constants.h b/components/user_scripts/common/constants.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/constants.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 USERSCRIPTS_COMMON_CONSTANTS_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_CONSTANTS_H_
|
|
|
|
++
|
|
|
|
++#include "base/files/file_path.h"
|
|
|
|
++#include "base/strings/string_piece_forward.h"
|
|
|
|
++#include "components/services/app_service/public/mojom/types.mojom.h"
|
|
|
|
++#include "components/version_info/channel.h"
|
|
|
|
++#include "ui/base/layout.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// The origin of injected CSS.
|
|
|
|
++enum CSSOrigin { /*CSS_ORIGIN_AUTHOR,*/ CSS_ORIGIN_USER };
|
|
|
|
++
|
|
|
|
++} // namespace user_scripts
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_CONSTANTS_H_
|
|
|
|
+diff --git a/components/user_scripts/common/error_utils.cc b/components/user_scripts/common/error_utils.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/error_utils.cc
|
|
|
|
+@@ -0,0 +1,54 @@
|
|
|
|
++// 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 "error_utils.h"
|
|
|
|
++
|
|
|
|
++#include <initializer_list>
|
|
|
|
++
|
|
|
|
++#include "base/check_op.h"
|
|
|
|
++#include "base/strings/string_tokenizer.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "base/strings/utf_string_conversions.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++std::string FormatErrorMessageInternal(
|
|
|
|
++ base::StringPiece format,
|
|
|
|
++ std::initializer_list<base::StringPiece> args) {
|
|
|
|
++ std::string format_str = format.as_string();
|
|
|
|
++ base::StringTokenizer tokenizer(format_str, "*");
|
|
|
|
++ tokenizer.set_options(base::StringTokenizer::RETURN_DELIMS);
|
|
|
|
++
|
|
|
|
++ std::vector<base::StringPiece> result_pieces;
|
|
|
|
++ auto* args_it = args.begin();
|
|
|
|
++ while (tokenizer.GetNext()) {
|
|
|
|
++ if (!tokenizer.token_is_delim()) {
|
|
|
|
++ result_pieces.push_back(tokenizer.token_piece());
|
|
|
|
++ continue;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ CHECK_NE(args_it, args.end())
|
|
|
|
++ << "More placeholders (*) than substitutions.";
|
|
|
|
++
|
|
|
|
++ // Substitute the argument.
|
|
|
|
++ result_pieces.push_back(*args_it);
|
|
|
|
++ args_it++;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Not all substitutions were consumed.
|
|
|
|
++ CHECK_EQ(args_it, args.end()) << "Fewer placeholders (*) than substitutions.";
|
|
|
|
++
|
|
|
|
++ return base::JoinString(result_pieces, "" /* separator */);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++std::string ErrorUtils::FormatErrorMessage(base::StringPiece format,
|
|
|
|
++ base::StringPiece s1) {
|
|
|
|
++ return FormatErrorMessageInternal(format, {s1});
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace user_scripts
|
|
|
|
+diff --git a/components/user_scripts/common/error_utils.h b/components/user_scripts/common/error_utils.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/error_utils.h
|
|
|
|
+@@ -0,0 +1,24 @@
|
|
|
|
++// 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 USERSCRIPTS_COMMON_ERROR_UTILS_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_ERROR_UTILS_H_
|
|
|
|
++
|
|
|
|
++#include <string>
|
|
|
|
++
|
|
|
|
++#include "base/strings/string_piece.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class ErrorUtils {
|
|
|
|
++ public:
|
|
|
|
++ // Creates an error messages from a pattern.
|
|
|
|
++ static std::string FormatErrorMessage(base::StringPiece format,
|
|
|
|
++ base::StringPiece s1);
|
|
|
|
++
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_ERROR_UTILS_H_
|
|
|
|
+diff --git a/components/user_scripts/common/extension_message_generator.cc b/components/user_scripts/common/extension_message_generator.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/extension_message_generator.cc
|
|
|
|
+@@ -0,0 +1,29 @@
|
|
|
|
++// 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.
|
|
|
|
++
|
|
|
|
++// Get basic type definitions.
|
|
|
|
++#define IPC_MESSAGE_IMPL
|
|
|
|
++#include "components/user_scripts/common/extension_message_generator.h"
|
|
|
|
++
|
|
|
|
++// Generate constructors.
|
|
|
|
++#include "ipc/struct_constructor_macros.h"
|
|
|
|
++#include "components/user_scripts/common/extension_message_generator.h"
|
|
|
|
++
|
|
|
|
++// Generate param traits write methods.
|
|
|
|
++#include "ipc/param_traits_write_macros.h"
|
|
|
|
++namespace IPC {
|
|
|
|
++#include "components/user_scripts/common/extension_message_generator.h"
|
|
|
|
++} // namespace IPC
|
|
|
|
++
|
|
|
|
++// Generate param traits read methods.
|
|
|
|
++#include "ipc/param_traits_read_macros.h"
|
|
|
|
++namespace IPC {
|
|
|
|
++#include "components/user_scripts/common/extension_message_generator.h"
|
|
|
|
++} // namespace IPC
|
|
|
|
++
|
|
|
|
++// Generate param traits log methods.
|
|
|
|
++#include "ipc/param_traits_log_macros.h"
|
|
|
|
++namespace IPC {
|
|
|
|
++#include "components/user_scripts/common/extension_message_generator.h"
|
|
|
|
++} // namespace IPC
|
|
|
|
+diff --git a/components/user_scripts/common/extension_message_generator.h b/components/user_scripts/common/extension_message_generator.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/extension_message_generator.h
|
|
|
|
+@@ -0,0 +1,11 @@
|
|
|
|
++// 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.
|
|
|
|
++
|
|
|
|
++// Multiply-included file, hence no include guard.
|
|
|
|
++
|
|
|
|
++#undef USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++#include "extension_messages.h"
|
|
|
|
++#ifndef USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++#error "Failed to include header extension_messages.h"
|
|
|
|
++#endif
|
|
|
|
+diff --git a/components/user_scripts/common/extension_messages.cc b/components/user_scripts/common/extension_messages.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/extension_messages.cc
|
|
|
|
+@@ -0,0 +1,40 @@
|
|
|
|
++// 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 "extension_messages.h"
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "content/public/common/common_param_traits.h"
|
|
|
|
++
|
|
|
|
++namespace IPC {
|
|
|
|
++
|
|
|
|
++void ParamTraits<HostID>::Write(base::Pickle* m, const param_type& p) {
|
|
|
|
++ WriteParam(m, p.type());
|
|
|
|
++ WriteParam(m, p.id());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool ParamTraits<HostID>::Read(const base::Pickle* m,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ param_type* r) {
|
|
|
|
++ HostID::HostType type;
|
|
|
|
++ std::string id;
|
|
|
|
++ if (!ReadParam(m, iter, &type))
|
|
|
|
++ return false;
|
|
|
|
++ if (!ReadParam(m, iter, &id))
|
|
|
|
++ return false;
|
|
|
|
++ *r = HostID(type, id);
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ParamTraits<HostID>::Log(
|
|
|
|
++ const param_type& p, std::string* l) {
|
|
|
|
++ LogParam(p.type(), l);
|
|
|
|
++ LogParam(p.id(), l);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace IPC
|
|
|
|
+diff --git a/components/user_scripts/common/extension_messages.h b/components/user_scripts/common/extension_messages.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/extension_messages.h
|
|
|
|
+@@ -0,0 +1,71 @@
|
|
|
|
++// 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.
|
|
|
|
++
|
|
|
|
++// IPC messages for extensions.
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++
|
|
|
|
++#include <stdint.h>
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <memory>
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/read_only_shared_memory_region.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "content/public/common/common_param_traits.h"
|
|
|
|
++#include "constants.h"
|
|
|
|
++#include "host_id.h"
|
|
|
|
++#include "ipc/ipc_message_start.h"
|
|
|
|
++#include "ipc/ipc_message_macros.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++#include "url/origin.h"
|
|
|
|
++
|
|
|
|
++#define IPC_MESSAGE_START ExtensionMsgStart
|
|
|
|
++
|
|
|
|
++IPC_ENUM_TRAITS_MAX_VALUE(HostID::HostType, HostID::HOST_TYPE_LAST)
|
|
|
|
++
|
|
|
|
++// Singly-included section for custom IPC traits.
|
|
|
|
++#ifndef INTERNAL_USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++#define INTERNAL_USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++
|
|
|
|
++namespace IPC {
|
|
|
|
++
|
|
|
|
++template <>
|
|
|
|
++struct ParamTraits<HostID> {
|
|
|
|
++ typedef HostID param_type;
|
|
|
|
++ static void Write(base::Pickle* m, const param_type& p);
|
|
|
|
++ static bool Read(const base::Pickle* m,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ param_type* r);
|
|
|
|
++ static void Log(const param_type& p, std::string* l);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++
|
|
|
|
++} // namespace IPC
|
|
|
|
++
|
|
|
|
++#endif // INTERNAL_USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
++
|
|
|
|
++// Notification that the user scripts have been updated. It has one
|
|
|
|
++// ReadOnlySharedMemoryRegion argument consisting of the pickled script data.
|
|
|
|
++// This memory region is valid in the context of the renderer.
|
|
|
|
++// If |owner| is not empty, then the shared memory handle refers to |owner|'s
|
|
|
|
++// programmatically-defined scripts. Otherwise, the handle refers to all
|
|
|
|
++// hosts' statically defined scripts. So far, only extension-hosts support
|
|
|
|
++// statically defined scripts; WebUI-hosts don't.
|
|
|
|
++// If |changed_hosts| is not empty, only the host in that set will
|
|
|
|
++// be updated. Otherwise, all hosts that have scripts in the shared memory
|
|
|
|
++// region will be updated. Note that the empty set => all hosts case is not
|
|
|
|
++// supported for per-extension programmatically-defined script regions; in such
|
|
|
|
++// regions, the owner is expected to list itself as the only changed host.
|
|
|
|
++// If |whitelisted_only| is true, this process should only run whitelisted
|
|
|
|
++// scripts and not all user scripts.
|
|
|
|
++IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdateUserScripts,
|
|
|
|
++ base::ReadOnlySharedMemoryRegion)
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_EXTENSION_MESSAGES_H_
|
|
|
|
+diff --git a/components/user_scripts/common/host_id.cc b/components/user_scripts/common/host_id.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/host_id.cc
|
|
|
|
+@@ -0,0 +1,31 @@
|
|
|
|
++// 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 "host_id.h"
|
|
|
|
++
|
|
|
|
++#include <tuple>
|
|
|
|
++
|
|
|
|
++HostID::HostID()
|
|
|
|
++ : type_(HostType::EXTENSIONS) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++HostID::HostID(HostType type, const std::string& id)
|
|
|
|
++ : type_(type), id_(id) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++HostID::HostID(const HostID& host_id)
|
|
|
|
++ : type_(host_id.type()),
|
|
|
|
++ id_(host_id.id()) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++HostID::~HostID() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool HostID::operator<(const HostID& host_id) const {
|
|
|
|
++ return std::tie(type_, id_) < std::tie(host_id.type_, host_id.id_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool HostID::operator==(const HostID& host_id) const {
|
|
|
|
++ return type_ == host_id.type_ && id_ == host_id.id_;
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/common/host_id.h b/components/user_scripts/common/host_id.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/host_id.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 USERSCRIPTS_COMMON_HOST_ID_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_HOST_ID_H_
|
|
|
|
++
|
|
|
|
++#include <string>
|
|
|
|
++
|
|
|
|
++// IDs of hosts who own user scripts.
|
|
|
|
++// A HostID is immutable after creation.
|
|
|
|
++struct HostID {
|
|
|
|
++ enum HostType { EXTENSIONS, WEBUI, HOST_TYPE_LAST = WEBUI };
|
|
|
|
++
|
|
|
|
++ HostID();
|
|
|
|
++ HostID(HostType type, const std::string& id);
|
|
|
|
++ HostID(const HostID& host_id);
|
|
|
|
++ ~HostID();
|
|
|
|
++
|
|
|
|
++ bool operator<(const HostID& host_id) const;
|
|
|
|
++ bool operator==(const HostID& host_id) const;
|
|
|
|
++
|
|
|
|
++ HostType type() const { return type_; }
|
|
|
|
++ const std::string& id() const { return id_; }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // The type of the host.
|
|
|
|
++ HostType type_;
|
|
|
|
++
|
|
|
|
++ // Similar to extension_id, host_id is a unique indentifier for a host,
|
|
|
|
++ // e.g., an Extension or WebUI.
|
|
|
|
++ std::string id_;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_HOST_ID_H_
|
|
|
|
+diff --git a/components/user_scripts/common/script_constants.h b/components/user_scripts/common/script_constants.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/script_constants.h
|
|
|
|
+@@ -0,0 +1,33 @@
|
|
|
|
++// Copyright 2020 The Chromium 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 USERSCRIPTS_COMMON_SCRIPT_CONSTANTS_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_SCRIPT_CONSTANTS_H_
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// Whether to fall back to matching the origin for frames where the URL
|
|
|
|
++// cannot be matched directly, such as those with about: or data: schemes.
|
|
|
|
++enum class MatchOriginAsFallbackBehavior {
|
|
|
|
++ // Never fall back on the origin; this means scripts will never match on
|
|
|
|
++ // these frames.
|
|
|
|
++ kNever,
|
|
|
|
++ // Match the origin only for about:-scheme frames, and then climb the frame
|
|
|
|
++ // tree to find an appropriate ancestor to get a full URL (including path).
|
|
|
|
++ // This is for supporting the "match_about_blank" key.
|
|
|
|
++ // TODO(devlin): I wonder if we could simplify this to be "MatchForAbout",
|
|
|
|
++ // and not worry about climbing the frame tree. It would be a behavior
|
|
|
|
++ // change, but I wonder how many extensions it would impact in practice.
|
|
|
|
++ kMatchForAboutSchemeAndClimbTree,
|
|
|
|
++ // Match the origin as a fallback whenever applicable. This won't have a
|
|
|
|
++ // corresponding path.
|
|
|
|
++ kAlways,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++// TODO(devlin): Move the other non-UserScript-specific constants like
|
|
|
|
++// RunLocation and InjectionType from UserScript into here.
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_SCRIPT_CONSTANTS_H_
|
|
|
|
+diff --git a/components/user_scripts/common/url_pattern.cc b/components/user_scripts/common/url_pattern.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/url_pattern.cc
|
|
|
|
+@@ -0,0 +1,803 @@
|
|
|
|
++// 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 "url_pattern.h"
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <ostream>
|
|
|
|
++
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "base/strings/pattern.h"
|
|
|
|
++#include "base/strings/strcat.h"
|
|
|
|
++#include "base/strings/string_number_conversions.h"
|
|
|
|
++#include "base/strings/string_piece.h"
|
|
|
|
++#include "base/strings/string_split.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "base/strings/stringprintf.h"
|
|
|
|
++#include "content/public/common/url_constants.h"
|
|
|
|
++#include "constants.h"
|
|
|
|
++#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
|
|
|
++#include "net/base/url_util.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++#include "url/url_util.h"
|
|
|
|
++
|
|
|
|
++const char URLPattern::kAllUrlsPattern[] = "<all_urls>";
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++// TODO(aa): What about more obscure schemes like javascript: ?
|
|
|
|
++// Note: keep this array in sync with kValidSchemeMasks.
|
|
|
|
++const char* const kValidSchemes[] = {
|
|
|
|
++ url::kHttpScheme, url::kHttpsScheme,
|
|
|
|
++ url::kFileScheme, url::kFtpScheme,
|
|
|
|
++ /*content::kChromeUIScheme,*/ /*extensions::kExtensionScheme,*/
|
|
|
|
++ url::kFileSystemScheme, url::kWsScheme,
|
|
|
|
++ url::kWssScheme, url::kDataScheme,
|
|
|
|
++ url::kUrnScheme,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++const int kValidSchemeMasks[] = {
|
|
|
|
++ URLPattern::SCHEME_HTTP, URLPattern::SCHEME_HTTPS,
|
|
|
|
++ URLPattern::SCHEME_FILE, URLPattern::SCHEME_FTP,
|
|
|
|
++ /*URLPattern::SCHEME_CHROMEUI,*/ /*URLPattern::SCHEME_EXTENSION,*/
|
|
|
|
++ URLPattern::SCHEME_FILESYSTEM, URLPattern::SCHEME_WS,
|
|
|
|
++ URLPattern::SCHEME_WSS, URLPattern::SCHEME_DATA,
|
|
|
|
++ URLPattern::SCHEME_URN,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++static_assert(base::size(kValidSchemes) == base::size(kValidSchemeMasks),
|
|
|
|
++ "must keep these arrays in sync");
|
|
|
|
++
|
|
|
|
++const char kParseSuccess[] = "Success.";
|
|
|
|
++const char kParseErrorMissingSchemeSeparator[] = "Missing scheme separator.";
|
|
|
|
++const char kParseErrorInvalidScheme[] = "Invalid scheme.";
|
|
|
|
++const char kParseErrorWrongSchemeType[] = "Wrong scheme type.";
|
|
|
|
++const char kParseErrorEmptyHost[] = "Host can not be empty.";
|
|
|
|
++const char kParseErrorInvalidHostWildcard[] = "Invalid host wildcard.";
|
|
|
|
++const char kParseErrorEmptyPath[] = "Empty path.";
|
|
|
|
++const char kParseErrorInvalidPort[] = "Invalid port.";
|
|
|
|
++const char kParseErrorInvalidHost[] = "Invalid host.";
|
|
|
|
++
|
|
|
|
++// Message explaining each URLPattern::ParseResult.
|
|
|
|
++const char* const kParseResultMessages[] = {
|
|
|
|
++ kParseSuccess,
|
|
|
|
++ kParseErrorMissingSchemeSeparator,
|
|
|
|
++ kParseErrorInvalidScheme,
|
|
|
|
++ kParseErrorWrongSchemeType,
|
|
|
|
++ kParseErrorEmptyHost,
|
|
|
|
++ kParseErrorInvalidHostWildcard,
|
|
|
|
++ kParseErrorEmptyPath,
|
|
|
|
++ kParseErrorInvalidPort,
|
|
|
|
++ kParseErrorInvalidHost,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++static_assert(static_cast<int>(URLPattern::ParseResult::kNumParseResults) ==
|
|
|
|
++ base::size(kParseResultMessages),
|
|
|
|
++ "must add message for each parse result");
|
|
|
|
++
|
|
|
|
++const char kPathSeparator[] = "/";
|
|
|
|
++
|
|
|
|
++bool IsStandardScheme(base::StringPiece scheme) {
|
|
|
|
++ // "*" gets the same treatment as a standard scheme.
|
|
|
|
++ if (scheme == "*")
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ return url::IsStandard(scheme.data(),
|
|
|
|
++ url::Component(0, static_cast<int>(scheme.length())));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool IsValidPortForScheme(base::StringPiece scheme, base::StringPiece port) {
|
|
|
|
++ if (port == "*")
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ // Only accept non-wildcard ports if the scheme uses ports.
|
|
|
|
++ if (url::DefaultPortForScheme(scheme.data(), scheme.length()) ==
|
|
|
|
++ url::PORT_UNSPECIFIED) {
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ int parsed_port = url::PORT_UNSPECIFIED;
|
|
|
|
++ if (!base::StringToInt(port, &parsed_port))
|
|
|
|
++ return false;
|
|
|
|
++ return (parsed_port >= 0) && (parsed_port < 65536);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// Returns |path| with the trailing wildcard stripped if one existed.
|
|
|
|
++//
|
|
|
|
++// The functions that rely on this (OverlapsWith and Contains) are only
|
|
|
|
++// called for the patterns inside URLPatternSet. In those cases, we know that
|
|
|
|
++// the path will have only a single wildcard at the end. This makes figuring
|
|
|
|
++// out overlap much easier. It seems like there is probably a computer-sciency
|
|
|
|
++// way to solve the general case, but we don't need that yet.
|
|
|
|
++base::StringPiece StripTrailingWildcard(base::StringPiece path) {
|
|
|
|
++ if (base::EndsWith(path, "*"))
|
|
|
|
++ path.remove_suffix(1);
|
|
|
|
++ return path;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// Removes trailing dot from |host_piece| if any.
|
|
|
|
++base::StringPiece CanonicalizeHostForMatching(base::StringPiece host_piece) {
|
|
|
|
++ if (base::EndsWith(host_piece, "."))
|
|
|
|
++ host_piece.remove_suffix(1);
|
|
|
|
++ return host_piece;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++bool URLPattern::IsValidSchemeForExtensions(base::StringPiece scheme) {
|
|
|
|
++ for (size_t i = 0; i < base::size(kValidSchemes); ++i) {
|
|
|
|
++ if (scheme == kValidSchemes[i])
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++int URLPattern::GetValidSchemeMaskForExtensions() {
|
|
|
|
++ int result = 0;
|
|
|
|
++ for (size_t i = 0; i < base::size(kValidSchemeMasks); ++i)
|
|
|
|
++ result |= kValidSchemeMasks[i];
|
|
|
|
++ return result;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++URLPattern::URLPattern()
|
|
|
|
++ : valid_schemes_(SCHEME_NONE),
|
|
|
|
++ match_all_urls_(false),
|
|
|
|
++ match_subdomains_(false),
|
|
|
|
++ port_("*") {}
|
|
|
|
++
|
|
|
|
++URLPattern::URLPattern(int valid_schemes)
|
|
|
|
++ : valid_schemes_(valid_schemes),
|
|
|
|
++ match_all_urls_(false),
|
|
|
|
++ match_subdomains_(false),
|
|
|
|
++ port_("*") {}
|
|
|
|
++
|
|
|
|
++URLPattern::URLPattern(int valid_schemes, base::StringPiece pattern)
|
|
|
|
++ // Strict error checking is used, because this constructor is only
|
|
|
|
++ // appropriate when we know |pattern| is valid.
|
|
|
|
++ : valid_schemes_(valid_schemes),
|
|
|
|
++ match_all_urls_(false),
|
|
|
|
++ match_subdomains_(false),
|
|
|
|
++ port_("*") {
|
|
|
|
++ ParseResult result = Parse(pattern);
|
|
|
|
++ DCHECK_EQ(ParseResult::kSuccess, result)
|
|
|
|
++ << "Parsing unexpectedly failed for pattern: " << pattern << ": "
|
|
|
|
++ << GetParseResultString(result);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++URLPattern::URLPattern(const URLPattern& other) = default;
|
|
|
|
++
|
|
|
|
++URLPattern::URLPattern(URLPattern&& other) = default;
|
|
|
|
++
|
|
|
|
++URLPattern::~URLPattern() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++URLPattern& URLPattern::operator=(const URLPattern& other) = default;
|
|
|
|
++
|
|
|
|
++URLPattern& URLPattern::operator=(URLPattern&& other) = default;
|
|
|
|
++
|
|
|
|
++bool URLPattern::operator<(const URLPattern& other) const {
|
|
|
|
++ return GetAsString() < other.GetAsString();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::operator>(const URLPattern& other) const {
|
|
|
|
++ return GetAsString() > other.GetAsString();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::operator==(const URLPattern& other) const {
|
|
|
|
++ return GetAsString() == other.GetAsString();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern) {
|
|
|
|
++ return out << '"' << url_pattern.GetAsString() << '"';
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++URLPattern::ParseResult URLPattern::Parse(base::StringPiece pattern) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ SetMatchAllURLs(false);
|
|
|
|
++ SetMatchSubdomains(false);
|
|
|
|
++ SetPort("*");
|
|
|
|
++
|
|
|
|
++ // Special case pattern to match every valid URL.
|
|
|
|
++ if (pattern == kAllUrlsPattern) {
|
|
|
|
++ SetMatchAllURLs(true);
|
|
|
|
++ return ParseResult::kSuccess;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Parse out the scheme.
|
|
|
|
++ size_t scheme_end_pos = pattern.find(url::kStandardSchemeSeparator);
|
|
|
|
++ bool has_standard_scheme_separator = true;
|
|
|
|
++
|
|
|
|
++ // Some urls also use ':' alone as the scheme separator.
|
|
|
|
++ if (scheme_end_pos == base::StringPiece::npos) {
|
|
|
|
++ scheme_end_pos = pattern.find(':');
|
|
|
|
++ has_standard_scheme_separator = false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (scheme_end_pos == base::StringPiece::npos)
|
|
|
|
++ return ParseResult::kMissingSchemeSeparator;
|
|
|
|
++
|
|
|
|
++ if (!SetScheme(pattern.substr(0, scheme_end_pos)))
|
|
|
|
++ return ParseResult::kInvalidScheme;
|
|
|
|
++
|
|
|
|
++ bool standard_scheme = IsStandardScheme(scheme_);
|
|
|
|
++ if (standard_scheme != has_standard_scheme_separator)
|
|
|
|
++ return ParseResult::kWrongSchemeSeparator;
|
|
|
|
++
|
|
|
|
++ // Advance past the scheme separator.
|
|
|
|
++ scheme_end_pos +=
|
|
|
|
++ (standard_scheme ? strlen(url::kStandardSchemeSeparator) : 1);
|
|
|
|
++ if (scheme_end_pos >= pattern.size())
|
|
|
|
++ return ParseResult::kEmptyHost;
|
|
|
|
++
|
|
|
|
++ // Parse out the host and path.
|
|
|
|
++ size_t host_start_pos = scheme_end_pos;
|
|
|
|
++ size_t path_start_pos = 0;
|
|
|
|
++
|
|
|
|
++ if (!standard_scheme) {
|
|
|
|
++ path_start_pos = host_start_pos;
|
|
|
|
++ } else if (scheme_ == url::kFileScheme) {
|
|
|
|
++ size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos);
|
|
|
|
++ if (host_end_pos == base::StringPiece::npos) {
|
|
|
|
++ // Allow hostname omission.
|
|
|
|
++ // e.g. file://* is interpreted as file:///*,
|
|
|
|
++ // file://foo* is interpreted as file:///foo*.
|
|
|
|
++ path_start_pos = host_start_pos - 1;
|
|
|
|
++ } else {
|
|
|
|
++ // Ignore hostname if scheme is file://.
|
|
|
|
++ // e.g. file://localhost/foo is equal to file:///foo.
|
|
|
|
++ path_start_pos = host_end_pos;
|
|
|
|
++ }
|
|
|
|
++ } else {
|
|
|
|
++ size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos);
|
|
|
|
++
|
|
|
|
++ // Host is required.
|
|
|
|
++ if (host_start_pos == host_end_pos)
|
|
|
|
++ return ParseResult::kEmptyHost;
|
|
|
|
++
|
|
|
|
++ if (host_end_pos == base::StringPiece::npos)
|
|
|
|
++ return ParseResult::kEmptyPath;
|
|
|
|
++
|
|
|
|
++ base::StringPiece host_and_port =
|
|
|
|
++ pattern.substr(host_start_pos, host_end_pos - host_start_pos);
|
|
|
|
++
|
|
|
|
++ size_t port_separator_pos = base::StringPiece::npos;
|
|
|
|
++ if (host_and_port[0] != '[') {
|
|
|
|
++ // Not IPv6 (either IPv4 or just a normal address).
|
|
|
|
++ port_separator_pos = host_and_port.find(':');
|
|
|
|
++ } else { // IPv6.
|
|
|
|
++ size_t host_end_pos = host_and_port.find(']');
|
|
|
|
++ if (host_end_pos == base::StringPiece::npos)
|
|
|
|
++ return ParseResult::kInvalidHost;
|
|
|
|
++ if (host_end_pos == 1)
|
|
|
|
++ return ParseResult::kEmptyHost;
|
|
|
|
++
|
|
|
|
++ if (host_end_pos < host_and_port.length() - 1) {
|
|
|
|
++ // The host isn't the only component. Check for a port. This would
|
|
|
|
++ // require a ':' to follow the closing ']' from the host.
|
|
|
|
++ if (host_and_port[host_end_pos + 1] != ':')
|
|
|
|
++ return ParseResult::kInvalidHost;
|
|
|
|
++
|
|
|
|
++ port_separator_pos = host_end_pos + 1;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (port_separator_pos != base::StringPiece::npos &&
|
|
|
|
++ !SetPort(host_and_port.substr(port_separator_pos + 1))) {
|
|
|
|
++ return ParseResult::kInvalidPort;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Note: this substr() will be the entire string if the port position
|
|
|
|
++ // wasn't found.
|
|
|
|
++ base::StringPiece host_piece = host_and_port.substr(0, port_separator_pos);
|
|
|
|
++
|
|
|
|
++ if (host_piece.empty())
|
|
|
|
++ return ParseResult::kEmptyHost;
|
|
|
|
++
|
|
|
|
++ if (host_piece == "*") {
|
|
|
|
++ match_subdomains_ = true;
|
|
|
|
++ host_piece = base::StringPiece();
|
|
|
|
++ } else if (base::StartsWith(host_piece, "*.")) {
|
|
|
|
++ if (host_piece.length() == 2) {
|
|
|
|
++ // We don't allow just '*.' as a host.
|
|
|
|
++ return ParseResult::kEmptyHost;
|
|
|
|
++ }
|
|
|
|
++ match_subdomains_ = true;
|
|
|
|
++ host_piece = host_piece.substr(2);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ host_ = std::string(host_piece);
|
|
|
|
++
|
|
|
|
++ path_start_pos = host_end_pos;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ SetPath(pattern.substr(path_start_pos));
|
|
|
|
++
|
|
|
|
++ // No other '*' can occur in the host, though. This isn't necessary, but is
|
|
|
|
++ // done as a convenience to developers who might otherwise be confused and
|
|
|
|
++ // think '*' works as a glob in the host.
|
|
|
|
++ if (host_.find('*') != std::string::npos)
|
|
|
|
++ return ParseResult::kInvalidHostWildcard;
|
|
|
|
++
|
|
|
|
++ if (!host_.empty()) {
|
|
|
|
++ // If |host_| is present (i.e., isn't a wildcard), we need to canonicalize
|
|
|
|
++ // it.
|
|
|
|
++ url::CanonHostInfo host_info;
|
|
|
|
++ host_ = net::CanonicalizeHost(host_, &host_info);
|
|
|
|
++ // net::CanonicalizeHost() returns an empty string on failure.
|
|
|
|
++ if (host_.empty())
|
|
|
|
++ return ParseResult::kInvalidHost;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Null characters are not allowed in hosts.
|
|
|
|
++ if (host_.find('\0') != std::string::npos)
|
|
|
|
++ return ParseResult::kInvalidHost;
|
|
|
|
++
|
|
|
|
++ return ParseResult::kSuccess;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPattern::SetValidSchemes(int valid_schemes) {
|
|
|
|
++ // TODO(devlin): Should we check that valid_schemes agrees with |scheme_|
|
|
|
|
++ // here? Otherwise, valid_schemes_ and schemes_ may stop agreeing with each
|
|
|
|
++ // other (e.g., in the case of `*://*/*`, where the scheme should only be
|
|
|
|
++ // http or https).
|
|
|
|
++ spec_.clear();
|
|
|
|
++ valid_schemes_ = valid_schemes;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPattern::SetHost(base::StringPiece host) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ host_.assign(host.data(), host.size());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPattern::SetMatchAllURLs(bool val) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ match_all_urls_ = val;
|
|
|
|
++
|
|
|
|
++ if (val) {
|
|
|
|
++ match_subdomains_ = true;
|
|
|
|
++ scheme_ = "*";
|
|
|
|
++ host_.clear();
|
|
|
|
++ SetPath("/*");
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPattern::SetMatchSubdomains(bool val) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ match_subdomains_ = val;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::SetScheme(base::StringPiece scheme) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ scheme_.assign(scheme.data(), scheme.size());
|
|
|
|
++ if (scheme_ == "*") {
|
|
|
|
++ valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS);
|
|
|
|
++ } else if (!IsValidScheme(scheme_)) {
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::IsValidScheme(base::StringPiece scheme) const {
|
|
|
|
++ if (valid_schemes_ == SCHEME_ALL)
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ for (size_t i = 0; i < base::size(kValidSchemes); ++i) {
|
|
|
|
++ if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i]))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPattern::SetPath(base::StringPiece path) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ path_.assign(path.data(), path.size());
|
|
|
|
++ path_escaped_ = path_;
|
|
|
|
++ base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\");
|
|
|
|
++ base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?");
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::SetPort(base::StringPiece port) {
|
|
|
|
++ spec_.clear();
|
|
|
|
++ if (IsValidPortForScheme(scheme_, port)) {
|
|
|
|
++ port_.assign(port.data(), port.size());
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesURL(const GURL& test) const {
|
|
|
|
++ // Invalid URLs can never match.
|
|
|
|
++ if (!test.is_valid())
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ const GURL* test_url = &test;
|
|
|
|
++ bool has_inner_url = test.inner_url() != nullptr;
|
|
|
|
++
|
|
|
|
++ if (has_inner_url) {
|
|
|
|
++ if (!test.SchemeIsFileSystem())
|
|
|
|
++ return false; // The only nested URLs we handle are filesystem URLs.
|
|
|
|
++ test_url = test.inner_url();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Ensure the scheme matches first, since <all_urls> may not match this URL if
|
|
|
|
++ // the scheme is excluded.
|
|
|
|
++ if (!MatchesScheme(test_url->scheme_piece()))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ if (match_all_urls_)
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ // Unless |match_all_urls_| is true, the grammar only permits matching
|
|
|
|
++ // URLs with nonempty paths.
|
|
|
|
++ if (!test.has_path())
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ std::string path_for_request = test.PathForRequest();
|
|
|
|
++ if (has_inner_url) {
|
|
|
|
++ path_for_request = base::StringPrintf("%s%s", test_url->path_piece().data(),
|
|
|
|
++ path_for_request.c_str());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return MatchesSecurityOriginHelper(*test_url) &&
|
|
|
|
++ MatchesPath(path_for_request);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesSecurityOrigin(const GURL& test) const {
|
|
|
|
++ const GURL* test_url = &test;
|
|
|
|
++ bool has_inner_url = test.inner_url() != NULL;
|
|
|
|
++
|
|
|
|
++ if (has_inner_url) {
|
|
|
|
++ if (!test.SchemeIsFileSystem())
|
|
|
|
++ return false; // The only nested URLs we handle are filesystem URLs.
|
|
|
|
++ test_url = test.inner_url();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!MatchesScheme(test_url->scheme()))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ if (match_all_urls_)
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ return MatchesSecurityOriginHelper(*test_url);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesScheme(base::StringPiece test) const {
|
|
|
|
++ if (!IsValidScheme(test))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ return scheme_ == "*" || test == scheme_;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesHost(base::StringPiece host) const {
|
|
|
|
++ // TODO(devlin): This is a bit sad. Parsing urls is expensive. However, it's
|
|
|
|
++ // important that we do this conversion to a GURL in order to canonicalize the
|
|
|
|
++ // host (the pattern's host_ already is canonicalized from Parse()). We can't
|
|
|
|
++ // just do string comparison.
|
|
|
|
++ return MatchesHost(
|
|
|
|
++ GURL(base::StringPrintf("%s%s%s/", url::kHttpScheme,
|
|
|
|
++ url::kStandardSchemeSeparator, host.data())));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesHost(const GURL& test) const {
|
|
|
|
++ base::StringPiece test_host(CanonicalizeHostForMatching(test.host_piece()));
|
|
|
|
++ const base::StringPiece pattern_host(CanonicalizeHostForMatching(host_));
|
|
|
|
++
|
|
|
|
++ // If the hosts are exactly equal, we have a match.
|
|
|
|
++ if (test_host == pattern_host)
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ // If we're matching subdomains, and we have no host in the match pattern,
|
|
|
|
++ // that means that we're matching all hosts, which means we have a match no
|
|
|
|
++ // matter what the test host is.
|
|
|
|
++ if (match_subdomains_ && pattern_host.empty())
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ // Otherwise, we can only match if our match pattern matches subdomains.
|
|
|
|
++ if (!match_subdomains_)
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ // We don't do subdomain matching against IP addresses, so we can give up now
|
|
|
|
++ // if the test host is an IP address.
|
|
|
|
++ if (test.HostIsIPAddress())
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ // Check if the test host is a subdomain of our host.
|
|
|
|
++ if (test_host.length() <= (pattern_host.length() + 1))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ if (!base::EndsWith(test_host, pattern_host))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ return test_host[test_host.length() - pattern_host.length() - 1] == '.';
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesEffectiveTld(
|
|
|
|
++ net::registry_controlled_domains::PrivateRegistryFilter private_filter,
|
|
|
|
++ net::registry_controlled_domains::UnknownRegistryFilter unknown_filter)
|
|
|
|
++ const {
|
|
|
|
++ // Check if it matches all urls or is a pattern like http://*/*.
|
|
|
|
++ if (match_all_urls_ || (match_subdomains_ && host_.empty()))
|
|
|
|
++ return true;
|
|
|
|
++
|
|
|
|
++ // If this doesn't even match subdomains, it can't possibly be a TLD wildcard.
|
|
|
|
++ if (!match_subdomains_)
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ // If there was more than just a TLD in the host (e.g., *.foobar.com), it
|
|
|
|
++ // doesn't match all hosts in an effective TLD.
|
|
|
|
++ if (net::registry_controlled_domains::HostHasRegistryControlledDomain(
|
|
|
|
++ host_, unknown_filter, private_filter)) {
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // At this point the host could either be just a TLD ("com") or some unknown
|
|
|
|
++ // TLD-like string ("notatld"). To disambiguate between them construct a
|
|
|
|
++ // fake URL, and check the registry.
|
|
|
|
++ //
|
|
|
|
++ // If we recognized this TLD, then this is a pattern like *.com, and it
|
|
|
|
++ // matches an effective TLD.
|
|
|
|
++ return net::registry_controlled_domains::HostHasRegistryControlledDomain(
|
|
|
|
++ "notatld." + host_, unknown_filter, private_filter);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesSingleOrigin() const {
|
|
|
|
++ // Strictly speaking, the port is part of the origin, but in URLPattern it
|
|
|
|
++ // defaults to *. It's not very interesting anyway, so leave it out.
|
|
|
|
++ return !MatchesEffectiveTld() && scheme_ != "*" && !match_subdomains_;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesPath(base::StringPiece test) const {
|
|
|
|
++ // Make the behaviour of OverlapsWith consistent with MatchesURL, which is
|
|
|
|
++ // need to match hosted apps on e.g. 'google.com' also run on 'google.com/'.
|
|
|
|
++ // The below if is a no-copy way of doing (test + "/*" == path_escaped_).
|
|
|
|
++ if (path_escaped_.length() == test.length() + 2 &&
|
|
|
|
++ base::StartsWith(path_escaped_.c_str(), test) &&
|
|
|
|
++ base::EndsWith(path_escaped_, "/*")) {
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return base::MatchPattern(test, path_escaped_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++const std::string& URLPattern::GetAsString() const {
|
|
|
|
++ if (!spec_.empty())
|
|
|
|
++ return spec_;
|
|
|
|
++
|
|
|
|
++ if (match_all_urls_) {
|
|
|
|
++ spec_ = kAllUrlsPattern;
|
|
|
|
++ return spec_;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ bool standard_scheme = IsStandardScheme(scheme_);
|
|
|
|
++
|
|
|
|
++ std::string spec = scheme_ +
|
|
|
|
++ (standard_scheme ? url::kStandardSchemeSeparator : ":");
|
|
|
|
++
|
|
|
|
++ if (scheme_ != url::kFileScheme && standard_scheme) {
|
|
|
|
++ if (match_subdomains_) {
|
|
|
|
++ spec += "*";
|
|
|
|
++ if (!host_.empty())
|
|
|
|
++ spec += ".";
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!host_.empty())
|
|
|
|
++ spec += host_;
|
|
|
|
++
|
|
|
|
++ if (port_ != "*") {
|
|
|
|
++ spec += ":";
|
|
|
|
++ spec += port_;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!path_.empty())
|
|
|
|
++ spec += path_;
|
|
|
|
++
|
|
|
|
++ spec_ = std::move(spec);
|
|
|
|
++ return spec_;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::OverlapsWith(const URLPattern& other) const {
|
|
|
|
++ if (match_all_urls() || other.match_all_urls())
|
|
|
|
++ return true;
|
|
|
|
++ return (MatchesAnyScheme(other.GetExplicitSchemes()) ||
|
|
|
|
++ other.MatchesAnyScheme(GetExplicitSchemes()))
|
|
|
|
++ && (MatchesHost(other.host()) || other.MatchesHost(host()))
|
|
|
|
++ && (MatchesPortPattern(other.port()) || other.MatchesPortPattern(port()))
|
|
|
|
++ && (MatchesPath(StripTrailingWildcard(other.path())) ||
|
|
|
|
++ other.MatchesPath(StripTrailingWildcard(path())));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::Contains(const URLPattern& other) const {
|
|
|
|
++ // Important: it's not enough to just check match_all_urls(); we also need to
|
|
|
|
++ // make sure that the schemes in this pattern are a superset of those in
|
|
|
|
++ // |other|.
|
|
|
|
++ if (match_all_urls() &&
|
|
|
|
++ (valid_schemes_ & other.valid_schemes_) == other.valid_schemes_) {
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return MatchesAllSchemes(other.GetExplicitSchemes()) &&
|
|
|
|
++ MatchesHost(other.host()) &&
|
|
|
|
++ (!other.match_subdomains_ || match_subdomains_) &&
|
|
|
|
++ MatchesPortPattern(other.port()) &&
|
|
|
|
++ MatchesPath(StripTrailingWildcard(other.path()));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++absl::optional<URLPattern> URLPattern::CreateIntersection(
|
|
|
|
++ const URLPattern& other) const {
|
|
|
|
++ // Easy case: Schemes don't overlap. Return nullopt.
|
|
|
|
++ int intersection_schemes = URLPattern::SCHEME_NONE;
|
|
|
|
++ if (valid_schemes_ == URLPattern::SCHEME_ALL)
|
|
|
|
++ intersection_schemes = other.valid_schemes_;
|
|
|
|
++ else if (other.valid_schemes_ == URLPattern::SCHEME_ALL)
|
|
|
|
++ intersection_schemes = valid_schemes_;
|
|
|
|
++ else
|
|
|
|
++ intersection_schemes = valid_schemes_ & other.valid_schemes_;
|
|
|
|
++
|
|
|
|
++ if (intersection_schemes == URLPattern::SCHEME_NONE)
|
|
|
|
++ return absl::nullopt;
|
|
|
|
++
|
|
|
|
++ {
|
|
|
|
++ // In a few cases, we can (mostly) return a copy of one of the patterns.
|
|
|
|
++ // This can happen when either:
|
|
|
|
++ // - The URLPattern's are identical (possibly excluding valid_schemes_)
|
|
|
|
++ // - One of the patterns has match_all_urls() equal to true.
|
|
|
|
++ // NOTE(devlin): Theoretically, we could use Contains() instead of
|
|
|
|
++ // match_all_urls() here. However, Contains() strips the trailing wildcard
|
|
|
|
++ // from the path, which could yield the incorrect result.
|
|
|
|
++ const URLPattern* copy_source = nullptr;
|
|
|
|
++ if (*this == other || other.match_all_urls())
|
|
|
|
++ copy_source = this;
|
|
|
|
++ else if (match_all_urls())
|
|
|
|
++ copy_source = &other;
|
|
|
|
++
|
|
|
|
++ if (copy_source) {
|
|
|
|
++ // NOTE: equality checks don't take into account valid_schemes_, and
|
|
|
|
++ // schemes can be different in the case of match_all_urls() as well, so
|
|
|
|
++ // we can't always just return *copy_source.
|
|
|
|
++ if (intersection_schemes == copy_source->valid_schemes_)
|
|
|
|
++ return *copy_source;
|
|
|
|
++ URLPattern result(intersection_schemes);
|
|
|
|
++ ParseResult parse_result = result.Parse(copy_source->GetAsString());
|
|
|
|
++ CHECK_EQ(ParseResult::kSuccess, parse_result);
|
|
|
|
++ return result;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // No more easy cases. Go through component by component to find the patterns
|
|
|
|
++ // that intersect.
|
|
|
|
++
|
|
|
|
++ // Note: Alias the function type (rather than using auto) because
|
|
|
|
++ // MatchesHost() is overloaded.
|
|
|
|
++ using match_function_type = bool (URLPattern::*)(base::StringPiece) const;
|
|
|
|
++
|
|
|
|
++ auto get_intersection = [this, &other](base::StringPiece own_str,
|
|
|
|
++ base::StringPiece other_str,
|
|
|
|
++ match_function_type match_function,
|
|
|
|
++ base::StringPiece* out) {
|
|
|
|
++ if ((this->*match_function)(other_str)) {
|
|
|
|
++ *out = other_str;
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ if ((other.*match_function)(own_str)) {
|
|
|
|
++ *out = own_str;
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ base::StringPiece scheme;
|
|
|
|
++ base::StringPiece host;
|
|
|
|
++ base::StringPiece port;
|
|
|
|
++ base::StringPiece path;
|
|
|
|
++ // If any pieces fail to overlap, then there is no intersection.
|
|
|
|
++ if (!get_intersection(scheme_, other.scheme_, &URLPattern::MatchesScheme,
|
|
|
|
++ &scheme) ||
|
|
|
|
++ !get_intersection(host_, other.host_, &URLPattern::MatchesHost, &host) ||
|
|
|
|
++ !get_intersection(port_, other.port_, &URLPattern::MatchesPortPattern,
|
|
|
|
++ &port) ||
|
|
|
|
++ !get_intersection(path_, other.path_, &URLPattern::MatchesPath, &path)) {
|
|
|
|
++ return absl::nullopt;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Only match subdomains if both patterns match subdomains.
|
|
|
|
++ base::StringPiece subdomains;
|
|
|
|
++ if (match_subdomains_ && other.match_subdomains_) {
|
|
|
|
++ // The host may be empty (e.g., in the case of *://*/* - in that case, only
|
|
|
|
++ // append '*' instead of '*.'.
|
|
|
|
++ subdomains = host.empty() ? "*" : "*.";
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ base::StringPiece scheme_separator =
|
|
|
|
++ IsStandardScheme(scheme) ? url::kStandardSchemeSeparator : ":";
|
|
|
|
++
|
|
|
|
++ std::string pattern_str = base::StrCat(
|
|
|
|
++ {scheme, scheme_separator, subdomains, host, ":", port, path});
|
|
|
|
++
|
|
|
|
++ URLPattern pattern(intersection_schemes);
|
|
|
|
++ ParseResult result = pattern.Parse(pattern_str);
|
|
|
|
++ // TODO(devlin): I don't think there's any way this should ever fail, but
|
|
|
|
++ // use a CHECK() to flush any cases out. If nothing crops up, downgrade this
|
|
|
|
++ // to a DCHECK in M72.
|
|
|
|
++ CHECK_EQ(ParseResult::kSuccess, result);
|
|
|
|
++
|
|
|
|
++ return pattern;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesAnyScheme(
|
|
|
|
++ const std::vector<std::string>& schemes) const {
|
|
|
|
++ for (auto i = schemes.cbegin(); i != schemes.cend(); ++i) {
|
|
|
|
++ if (MatchesScheme(*i))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesAllSchemes(
|
|
|
|
++ const std::vector<std::string>& schemes) const {
|
|
|
|
++ for (auto i = schemes.cbegin(); i != schemes.cend(); ++i) {
|
|
|
|
++ if (!MatchesScheme(*i))
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesSecurityOriginHelper(const GURL& test) const {
|
|
|
|
++ // Ignore hostname if scheme is file://.
|
|
|
|
++ if (scheme_ != url::kFileScheme && !MatchesHost(test))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ if (!MatchesPortPattern(base::NumberToString(test.EffectiveIntPort())))
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPattern::MatchesPortPattern(base::StringPiece port) const {
|
|
|
|
++ return port_ == "*" || port_ == port;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::vector<std::string> URLPattern::GetExplicitSchemes() const {
|
|
|
|
++ std::vector<std::string> result;
|
|
|
|
++
|
|
|
|
++ if (scheme_ != "*" && !match_all_urls_ && IsValidScheme(scheme_)) {
|
|
|
|
++ result.push_back(scheme_);
|
|
|
|
++ return result;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ for (size_t i = 0; i < base::size(kValidSchemes); ++i) {
|
|
|
|
++ if (MatchesScheme(kValidSchemes[i])) {
|
|
|
|
++ result.push_back(kValidSchemes[i]);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return result;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::vector<URLPattern> URLPattern::ConvertToExplicitSchemes() const {
|
|
|
|
++ std::vector<std::string> explicit_schemes = GetExplicitSchemes();
|
|
|
|
++ std::vector<URLPattern> result;
|
|
|
|
++
|
|
|
|
++ for (std::vector<std::string>::const_iterator i = explicit_schemes.begin();
|
|
|
|
++ i != explicit_schemes.end(); ++i) {
|
|
|
|
++ URLPattern temp = *this;
|
|
|
|
++ temp.SetScheme(*i);
|
|
|
|
++ temp.SetMatchAllURLs(false);
|
|
|
|
++ result.push_back(temp);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return result;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++const char* URLPattern::GetParseResultString(
|
|
|
|
++ URLPattern::ParseResult parse_result) {
|
|
|
|
++ return kParseResultMessages[static_cast<int>(parse_result)];
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/common/url_pattern.h b/components/user_scripts/common/url_pattern.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/url_pattern.h
|
|
|
|
+@@ -0,0 +1,302 @@
|
|
|
|
++// 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 USERSCRIPTS_COMMON_URL_PATTERN_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_URL_PATTERN_H_
|
|
|
|
++
|
|
|
|
++#include <functional>
|
|
|
|
++#include <iosfwd>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/strings/string_piece.h"
|
|
|
|
++#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
|
|
|
++
|
|
|
|
++class GURL;
|
|
|
|
++
|
|
|
|
++// A pattern that can be used to match URLs. A URLPattern is a very restricted
|
|
|
|
++// subset of URL syntax:
|
|
|
|
++//
|
|
|
|
++// <url-pattern> := <scheme>://<host><port><path> | '<all_urls>'
|
|
|
|
++// <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome' |
|
|
|
|
++// 'chrome-extension' | 'filesystem'
|
|
|
|
++// <host> := '*' | <IPv4 address> | [<IPv6 address>] |
|
|
|
|
++// '*.' <anychar except '/' and '*'>+
|
|
|
|
++// <port> := [':' ('*' | <port number between 0 and 65535>)]
|
|
|
|
++// <path> := '/' <any chars>
|
|
|
|
++//
|
|
|
|
++// * Host is not used when the scheme is 'file'.
|
|
|
|
++// * The path can have embedded '*' characters which act as glob wildcards.
|
|
|
|
++// * '<all_urls>' is a special pattern that matches any valid URL that contains
|
|
|
|
++// a valid scheme (as specified by valid_schemes_).
|
|
|
|
++// * The '*' scheme pattern excludes file URLs.
|
|
|
|
++//
|
|
|
|
++// Examples of valid patterns:
|
|
|
|
++// - http://*/*
|
|
|
|
++// - http://*/foo*
|
|
|
|
++// - https://*.google.com/foo*bar
|
|
|
|
++// - file://monkey*
|
|
|
|
++// - http://127.0.0.1/*
|
|
|
|
++// - http://[2607:f8b0:4005:805::200e]/*
|
|
|
|
++//
|
|
|
|
++// Examples of invalid patterns:
|
|
|
|
++// - http://* -- path not specified
|
|
|
|
++// - http://*foo/bar -- * not allowed as substring of host component
|
|
|
|
++// - http://foo.*.bar/baz -- * must be first component
|
|
|
|
++// - http:/bar -- scheme separator not found
|
|
|
|
++// - foo://* -- invalid scheme
|
|
|
|
++// - chrome:// -- we don't support chrome internal URLs
|
|
|
|
++class URLPattern {
|
|
|
|
++ public:
|
|
|
|
++ // A collection of scheme bitmasks for use with valid_schemes.
|
|
|
|
++ enum SchemeMasks {
|
|
|
|
++ SCHEME_NONE = 0,
|
|
|
|
++ SCHEME_HTTP = 1 << 0,
|
|
|
|
++ SCHEME_HTTPS = 1 << 1,
|
|
|
|
++ SCHEME_FILE = 1 << 2,
|
|
|
|
++ SCHEME_FTP = 1 << 3,
|
|
|
|
++ SCHEME_CHROMEUI = 1 << 4,
|
|
|
|
++ SCHEME_EXTENSION = 1 << 5,
|
|
|
|
++ SCHEME_FILESYSTEM = 1 << 6,
|
|
|
|
++ SCHEME_WS = 1 << 7,
|
|
|
|
++ SCHEME_WSS = 1 << 8,
|
|
|
|
++ SCHEME_DATA = 1 << 9,
|
|
|
|
++ SCHEME_URN = 1 << 10,
|
|
|
|
++
|
|
|
|
++ // IMPORTANT!
|
|
|
|
++ // SCHEME_ALL will match every scheme, including chrome://, chrome-
|
|
|
|
++ // extension://, about:, etc. Because this has lots of security
|
|
|
|
++ // implications, third-party extensions should usually not be able to get
|
|
|
|
++ // access to URL patterns initialized this way. If there is a reason
|
|
|
|
++ // for violating this general rule, document why this it safe.
|
|
|
|
++ SCHEME_ALL = -1,
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // Error codes returned from Parse().
|
|
|
|
++ enum class ParseResult {
|
|
|
|
++ kSuccess = 0,
|
|
|
|
++ kMissingSchemeSeparator,
|
|
|
|
++ kInvalidScheme,
|
|
|
|
++ kWrongSchemeSeparator,
|
|
|
|
++ kEmptyHost,
|
|
|
|
++ kInvalidHostWildcard,
|
|
|
|
++ kEmptyPath,
|
|
|
|
++ kInvalidPort,
|
|
|
|
++ kInvalidHost,
|
|
|
|
++ kNumParseResults,
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // The <all_urls> string pattern.
|
|
|
|
++ static const char kAllUrlsPattern[];
|
|
|
|
++
|
|
|
|
++ // Returns true if the given |scheme| is considered valid for extensions.
|
|
|
|
++ static bool IsValidSchemeForExtensions(base::StringPiece scheme);
|
|
|
|
++
|
|
|
|
++ // Returns the mask for all schemes considered valid for extensions.
|
|
|
|
++ static int GetValidSchemeMaskForExtensions();
|
|
|
|
++
|
|
|
|
++ explicit URLPattern(int valid_schemes);
|
|
|
|
++
|
|
|
|
++ // Convenience to construct a URLPattern from a string. If the string is not
|
|
|
|
++ // known ahead of time, use Parse() instead, which returns success or failure.
|
|
|
|
++ URLPattern(int valid_schemes, base::StringPiece pattern);
|
|
|
|
++
|
|
|
|
++ URLPattern();
|
|
|
|
++ URLPattern(const URLPattern& other);
|
|
|
|
++ URLPattern(URLPattern&& other);
|
|
|
|
++ ~URLPattern();
|
|
|
|
++
|
|
|
|
++ URLPattern& operator=(const URLPattern& other);
|
|
|
|
++ URLPattern& operator=(URLPattern&& other);
|
|
|
|
++
|
|
|
|
++ bool operator<(const URLPattern& other) const;
|
|
|
|
++ bool operator>(const URLPattern& other) const;
|
|
|
|
++ bool operator==(const URLPattern& other) const;
|
|
|
|
++
|
|
|
|
++ // Initializes this instance by parsing the provided string. Returns
|
|
|
|
++ // URLPattern::ParseResult::kSuccess on success, or an error code otherwise.
|
|
|
|
++ // On failure, this instance will have some intermediate values and is in an
|
|
|
|
++ // invalid state.
|
|
|
|
++ ParseResult Parse(base::StringPiece pattern_str);
|
|
|
|
++
|
|
|
|
++ // Gets the bitmask of valid schemes.
|
|
|
|
++ int valid_schemes() const { return valid_schemes_; }
|
|
|
|
++ void SetValidSchemes(int valid_schemes);
|
|
|
|
++
|
|
|
|
++ // Gets the host the pattern matches. This can be an empty string if the
|
|
|
|
++ // pattern matches all hosts (the input was <scheme>://*/<whatever>).
|
|
|
|
++ const std::string& host() const { return host_; }
|
|
|
|
++ void SetHost(base::StringPiece host);
|
|
|
|
++
|
|
|
|
++ // Gets whether to match subdomains of host().
|
|
|
|
++ bool match_subdomains() const { return match_subdomains_; }
|
|
|
|
++ void SetMatchSubdomains(bool val);
|
|
|
|
++
|
|
|
|
++ // Gets the path the pattern matches with the leading slash. This can have
|
|
|
|
++ // embedded asterisks which are interpreted using glob rules.
|
|
|
|
++ const std::string& path() const { return path_; }
|
|
|
|
++ void SetPath(base::StringPiece path);
|
|
|
|
++
|
|
|
|
++ // Returns true if this pattern matches all (valid) urls.
|
|
|
|
++ bool match_all_urls() const { return match_all_urls_; }
|
|
|
|
++ void SetMatchAllURLs(bool val);
|
|
|
|
++
|
|
|
|
++ // Sets the scheme for pattern matches. This can be a single '*' if the
|
|
|
|
++ // pattern matches all valid schemes (as defined by the valid_schemes_
|
|
|
|
++ // property). Returns false on failure (if the scheme is not valid).
|
|
|
|
++ bool SetScheme(base::StringPiece scheme);
|
|
|
|
++ // Note: You should use MatchesScheme() instead of this getter unless you
|
|
|
|
++ // absolutely need the exact scheme. This is exposed for testing.
|
|
|
|
++ const std::string& scheme() const { return scheme_; }
|
|
|
|
++
|
|
|
|
++ // Returns true if the specified scheme can be used in this URL pattern, and
|
|
|
|
++ // false otherwise. Uses valid_schemes_ to determine validity.
|
|
|
|
++ bool IsValidScheme(base::StringPiece scheme) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if this instance matches the specified URL. Always returns
|
|
|
|
++ // false for invalid URLs.
|
|
|
|
++ bool MatchesURL(const GURL& test) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if this instance matches the specified security origin.
|
|
|
|
++ bool MatchesSecurityOrigin(const GURL& test) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if |test| matches our scheme.
|
|
|
|
++ // Note that if test is "filesystem", this may fail whereas MatchesURL
|
|
|
|
++ // may succeed. MatchesURL is smart enough to look at the inner_url instead
|
|
|
|
++ // of the outer "filesystem:" part.
|
|
|
|
++ bool MatchesScheme(base::StringPiece test) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if |test| matches our host.
|
|
|
|
++ bool MatchesHost(base::StringPiece test) const;
|
|
|
|
++ bool MatchesHost(const GURL& test) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if |test| matches our path.
|
|
|
|
++ bool MatchesPath(base::StringPiece test) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if the pattern matches all patterns in an (e)TLD. This
|
|
|
|
++ // includes patterns like *://*.com/*, *://*.co.uk/*, etc. A pattern that
|
|
|
|
++ // matches all domains (e.g., *://*/*) will return true.
|
|
|
|
++ // |private_filter| specifies whether private registries (like appspot.com)
|
|
|
|
++ // should be considered; if included, patterns like *://*.appspot.com/* will
|
|
|
|
++ // return true. By default, we exclude private registries (so *.appspot.com
|
|
|
|
++ // returns false).
|
|
|
|
++ // Note: This is an expensive method, and should be used sparingly!
|
|
|
|
++ // You should probably use URLPatternSet::ShouldWarnAllHosts(), which is
|
|
|
|
++ // cached.
|
|
|
|
++ bool MatchesEffectiveTld(
|
|
|
|
++ net::registry_controlled_domains::PrivateRegistryFilter private_filter =
|
|
|
|
++ net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES,
|
|
|
|
++ net::registry_controlled_domains::UnknownRegistryFilter unknown_filter =
|
|
|
|
++ net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if the pattern only matches a single origin. The pattern may
|
|
|
|
++ // include a path.
|
|
|
|
++ bool MatchesSingleOrigin() const;
|
|
|
|
++
|
|
|
|
++ // Sets the port. Returns false if the port is invalid.
|
|
|
|
++ bool SetPort(base::StringPiece port);
|
|
|
|
++ const std::string& port() const { return port_; }
|
|
|
|
++
|
|
|
|
++ // Returns a string representing this instance.
|
|
|
|
++ const std::string& GetAsString() const;
|
|
|
|
++
|
|
|
|
++ // Determines whether there is a URL that would match this instance and
|
|
|
|
++ // another instance. This method is symmetrical: Calling
|
|
|
|
++ // other.OverlapsWith(this) would result in the same answer.
|
|
|
|
++ bool OverlapsWith(const URLPattern& other) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if this pattern matches all possible URLs that |other| can
|
|
|
|
++ // match. For example, http://*.google.com encompasses http://www.google.com.
|
|
|
|
++ bool Contains(const URLPattern& other) const;
|
|
|
|
++
|
|
|
|
++ // Creates a new URLPattern that represents the intersection of this
|
|
|
|
++ // URLPattern with the |other|, or base::nullopt if no intersection exists.
|
|
|
|
++ // For instance, given the patterns http://*.google.com/* and
|
|
|
|
++ // *://maps.google.com/*, the intersection is http://maps.google.com/*.
|
|
|
|
++ // NOTES:
|
|
|
|
++ // - Though scheme intersections are supported, the serialization of
|
|
|
|
++ // URLPatternSet does not record them. Be sure that this is safe for your
|
|
|
|
++ // use cases.
|
|
|
|
++ // - Path intersection is done on a best-effort basis. If one path clearly
|
|
|
|
++ // contains another, it will be handled correctly, but this method does not
|
|
|
|
++ // deal with cases like /*a* and /*b* (where technically the intersection
|
|
|
|
++ // is /*a*b*|/*b*a*); the intersection returned for that case will be empty.
|
|
|
|
++ absl::optional<URLPattern> CreateIntersection(const URLPattern& other) const;
|
|
|
|
++
|
|
|
|
++ // Converts this URLPattern into an equivalent set of URLPatterns that don't
|
|
|
|
++ // use a wildcard in the scheme component. If this URLPattern doesn't use a
|
|
|
|
++ // wildcard scheme, then the returned set will contain one element that is
|
|
|
|
++ // equivalent to this instance.
|
|
|
|
++ std::vector<URLPattern> ConvertToExplicitSchemes() const;
|
|
|
|
++
|
|
|
|
++ static bool EffectiveHostCompare(const URLPattern& a, const URLPattern& b) {
|
|
|
|
++ if (a.match_all_urls_ && b.match_all_urls_)
|
|
|
|
++ return false;
|
|
|
|
++ return a.host_.compare(b.host_) < 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Used for origin comparisons in a std::set.
|
|
|
|
++ class EffectiveHostCompareFunctor {
|
|
|
|
++ public:
|
|
|
|
++ bool operator()(const URLPattern& a, const URLPattern& b) const {
|
|
|
|
++ return EffectiveHostCompare(a, b);
|
|
|
|
++ }
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // Get an error string for a ParseResult.
|
|
|
|
++ static const char* GetParseResultString(URLPattern::ParseResult parse_result);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // Returns true if any of the |schemes| items matches our scheme.
|
|
|
|
++ bool MatchesAnyScheme(const std::vector<std::string>& schemes) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if all of the |schemes| items matches our scheme.
|
|
|
|
++ bool MatchesAllSchemes(const std::vector<std::string>& schemes) const;
|
|
|
|
++
|
|
|
|
++ bool MatchesSecurityOriginHelper(const GURL& test) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if our port matches the |port| pattern (it may be "*").
|
|
|
|
++ bool MatchesPortPattern(base::StringPiece port) const;
|
|
|
|
++
|
|
|
|
++ // If the URLPattern contains a wildcard scheme, returns a list of
|
|
|
|
++ // equivalent literal schemes, otherwise returns the current scheme.
|
|
|
|
++ std::vector<std::string> GetExplicitSchemes() const;
|
|
|
|
++
|
|
|
|
++ // A bitmask containing the schemes which are considered valid for this
|
|
|
|
++ // pattern. Parse() uses this to decide whether a pattern contains a valid
|
|
|
|
++ // scheme.
|
|
|
|
++ int valid_schemes_;
|
|
|
|
++
|
|
|
|
++ // True if this is a special-case "<all_urls>" pattern.
|
|
|
|
++ bool match_all_urls_;
|
|
|
|
++
|
|
|
|
++ // The scheme for the pattern.
|
|
|
|
++ std::string scheme_;
|
|
|
|
++
|
|
|
|
++ // The host without any leading "*" components.
|
|
|
|
++ std::string host_;
|
|
|
|
++
|
|
|
|
++ // Whether we should match subdomains of the host. This is true if the first
|
|
|
|
++ // component of the pattern's host was "*".
|
|
|
|
++ bool match_subdomains_;
|
|
|
|
++
|
|
|
|
++ // The port.
|
|
|
|
++ std::string port_;
|
|
|
|
++
|
|
|
|
++ // The path to match. This is everything after the host of the URL, or
|
|
|
|
++ // everything after the scheme in the case of file:// URLs.
|
|
|
|
++ std::string path_;
|
|
|
|
++
|
|
|
|
++ // The path with "?" and "\" characters escaped for use with the
|
|
|
|
++ // MatchPattern() function.
|
|
|
|
++ std::string path_escaped_;
|
|
|
|
++
|
|
|
|
++ // A string representing this URLPattern.
|
|
|
|
++ mutable std::string spec_;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern);
|
|
|
|
++
|
|
|
|
++typedef std::vector<URLPattern> URLPatternList;
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_URL_PATTERN_H_
|
|
|
|
+diff --git a/components/user_scripts/common/url_pattern_set.cc b/components/user_scripts/common/url_pattern_set.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/url_pattern_set.cc
|
|
|
|
+@@ -0,0 +1,334 @@
|
|
|
|
++// 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 "url_pattern_set.h"
|
|
|
|
++
|
|
|
|
++#include <iterator>
|
|
|
|
++#include <ostream>
|
|
|
|
++
|
|
|
|
++#include "base/containers/contains.h"
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "base/stl_util.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "error_utils.h"
|
|
|
|
++#include "url_pattern.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++#include "url/origin.h"
|
|
|
|
++#include "url/url_constants.h"
|
|
|
|
++#include "user_scripts_features.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++const char kInvalidURLPatternError[] = "Invalid url pattern '*'";
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++URLPatternSet URLPatternSet::CreateDifference(const URLPatternSet& set1,
|
|
|
|
++ const URLPatternSet& set2) {
|
|
|
|
++ return URLPatternSet(base::STLSetDifference<std::set<URLPattern>>(
|
|
|
|
++ set1.patterns_, set2.patterns_));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++URLPatternSet URLPatternSet::CreateIntersection(
|
|
|
|
++ const URLPatternSet& set1,
|
|
|
|
++ const URLPatternSet& set2,
|
|
|
|
++ IntersectionBehavior intersection_behavior) {
|
|
|
|
++ // Note: leverage return value optimization; always return the same object.
|
|
|
|
++ URLPatternSet result;
|
|
|
|
++
|
|
|
|
++ if (intersection_behavior == IntersectionBehavior::kStringComparison) {
|
|
|
|
++ // String comparison just relies on STL set behavior, which looks at the
|
|
|
|
++ // string representation.
|
|
|
|
++ result = URLPatternSet(base::STLSetIntersection<std::set<URLPattern>>(
|
|
|
|
++ set1.patterns_, set2.patterns_));
|
|
|
|
++ return result;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Look for a semantic intersection.
|
|
|
|
++
|
|
|
|
++ // Step 1: Iterate over each set. Find any patterns that are completely
|
|
|
|
++ // contained by the other (thus being necessarily present in any intersection)
|
|
|
|
++ // and add them, collecting the others in a set of unique items.
|
|
|
|
++ // Note: Use a collection of pointers for the uniques to avoid excessive
|
|
|
|
++ // copies. Since these are owned by the URLPatternSet passed in, which is
|
|
|
|
++ // const, this should be safe.
|
|
|
|
++ std::vector<const URLPattern*> unique_set1;
|
|
|
|
++ for (const URLPattern& pattern : set1) {
|
|
|
|
++ if (set2.ContainsPattern(pattern))
|
|
|
|
++ result.patterns_.insert(pattern);
|
|
|
|
++ else
|
|
|
|
++ unique_set1.push_back(&pattern);
|
|
|
|
++ }
|
|
|
|
++ std::vector<const URLPattern*> unique_set2;
|
|
|
|
++ for (const URLPattern& pattern : set2) {
|
|
|
|
++ if (set1.ContainsPattern(pattern))
|
|
|
|
++ result.patterns_.insert(pattern);
|
|
|
|
++ else
|
|
|
|
++ unique_set2.push_back(&pattern);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // If we're just looking for patterns contained by both, we're done.
|
|
|
|
++ if (intersection_behavior == IntersectionBehavior::kPatternsContainedByBoth)
|
|
|
|
++ return result;
|
|
|
|
++
|
|
|
|
++ DCHECK_EQ(IntersectionBehavior::kDetailed, intersection_behavior);
|
|
|
|
++
|
|
|
|
++ // Step 2: Iterate over all the unique patterns and find the intersections
|
|
|
|
++ // they have with the other patterns.
|
|
|
|
++ for (const auto* pattern : unique_set1) {
|
|
|
|
++ for (const auto* pattern2 : unique_set2) {
|
|
|
|
++ absl::optional<URLPattern> intersection =
|
|
|
|
++ pattern->CreateIntersection(*pattern2);
|
|
|
|
++ if (intersection)
|
|
|
|
++ result.patterns_.insert(std::move(*intersection));
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return result;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++URLPatternSet URLPatternSet::CreateUnion(const URLPatternSet& set1,
|
|
|
|
++ const URLPatternSet& set2) {
|
|
|
|
++ return URLPatternSet(
|
|
|
|
++ base::STLSetUnion<std::set<URLPattern>>(set1.patterns_, set2.patterns_));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++URLPatternSet URLPatternSet::CreateUnion(
|
|
|
|
++ const std::vector<URLPatternSet>& sets) {
|
|
|
|
++ URLPatternSet result;
|
|
|
|
++ if (sets.empty())
|
|
|
|
++ return result;
|
|
|
|
++
|
|
|
|
++ // N-way union algorithm is basic O(nlog(n)) merge algorithm.
|
|
|
|
++ //
|
|
|
|
++ // Do the first merge step into a working set so that we don't mutate any of
|
|
|
|
++ // the input.
|
|
|
|
++ // TODO(devlin): Looks like this creates a bunch of copies; we can probably
|
|
|
|
++ // clean that up.
|
|
|
|
++ std::vector<URLPatternSet> working;
|
|
|
|
++ for (size_t i = 0; i < sets.size(); i += 2) {
|
|
|
|
++ if (i + 1 < sets.size())
|
|
|
|
++ working.push_back(CreateUnion(sets[i], sets[i + 1]));
|
|
|
|
++ else
|
|
|
|
++ working.push_back(sets[i].Clone());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ for (size_t skip = 1; skip < working.size(); skip *= 2) {
|
|
|
|
++ for (size_t i = 0; i < (working.size() - skip); i += skip) {
|
|
|
|
++ URLPatternSet u = CreateUnion(working[i], working[i + skip]);
|
|
|
|
++ working[i].patterns_.swap(u.patterns_);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ result.patterns_.swap(working[0].patterns_);
|
|
|
|
++ return result;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++URLPatternSet::URLPatternSet() = default;
|
|
|
|
++
|
|
|
|
++URLPatternSet::URLPatternSet(URLPatternSet&& rhs) = default;
|
|
|
|
++
|
|
|
|
++URLPatternSet::URLPatternSet(const std::set<URLPattern>& patterns)
|
|
|
|
++ : patterns_(patterns) {}
|
|
|
|
++
|
|
|
|
++URLPatternSet::~URLPatternSet() = default;
|
|
|
|
++
|
|
|
|
++URLPatternSet& URLPatternSet::operator=(URLPatternSet&& rhs) = default;
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::operator==(const URLPatternSet& other) const {
|
|
|
|
++ return patterns_ == other.patterns_;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::ostream& operator<<(std::ostream& out,
|
|
|
|
++ const URLPatternSet& url_pattern_set) {
|
|
|
|
++ out << "{ ";
|
|
|
|
++
|
|
|
|
++ auto iter = url_pattern_set.patterns().cbegin();
|
|
|
|
++ if (!url_pattern_set.patterns().empty()) {
|
|
|
|
++ out << *iter;
|
|
|
|
++ ++iter;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ for (;iter != url_pattern_set.patterns().end(); ++iter)
|
|
|
|
++ out << ", " << *iter;
|
|
|
|
++
|
|
|
|
++ if (!url_pattern_set.patterns().empty())
|
|
|
|
++ out << " ";
|
|
|
|
++
|
|
|
|
++ out << "}";
|
|
|
|
++ return out;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++URLPatternSet URLPatternSet::Clone() const {
|
|
|
|
++ return URLPatternSet(patterns_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::is_empty() const {
|
|
|
|
++ return patterns_.empty();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++size_t URLPatternSet::size() const {
|
|
|
|
++ return patterns_.size();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::AddPattern(const URLPattern& pattern) {
|
|
|
|
++ return patterns_.insert(pattern).second;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPatternSet::AddPatterns(const URLPatternSet& set) {
|
|
|
|
++ patterns_.insert(set.patterns().begin(),
|
|
|
|
++ set.patterns().end());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void URLPatternSet::ClearPatterns() {
|
|
|
|
++ patterns_.clear();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::AddOrigin(int valid_schemes, const GURL& origin) {
|
|
|
|
++ if (origin.is_empty())
|
|
|
|
++ return false;
|
|
|
|
++ const url::Origin real_origin = url::Origin::Create(origin);
|
|
|
|
++ DCHECK(real_origin.IsSameOriginWith(url::Origin::Create(origin.GetOrigin())));
|
|
|
|
++ URLPattern origin_pattern(valid_schemes);
|
|
|
|
++ // Origin adding could fail if |origin| does not match |valid_schemes|.
|
|
|
|
++ if (origin_pattern.Parse(origin.spec()) !=
|
|
|
|
++ URLPattern::ParseResult::kSuccess) {
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ origin_pattern.SetPath("/*");
|
|
|
|
++ return AddPattern(origin_pattern);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::Contains(const URLPatternSet& other) const {
|
|
|
|
++ for (auto it = other.begin(); it != other.end(); ++it) {
|
|
|
|
++ if (!ContainsPattern(*it))
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::ContainsPattern(const URLPattern& pattern) const {
|
|
|
|
++ for (auto it = begin(); it != end(); ++it) {
|
|
|
|
++ if (it->Contains(pattern))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::MatchesURL(const GURL& url) const {
|
|
|
|
++ for (auto pattern = patterns_.cbegin(); pattern != patterns_.cend();
|
|
|
|
++ ++pattern) {
|
|
|
|
++ if (pattern->MatchesURL(url)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: URLPatternSet::MatchesURL true " << url.spec();
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: URLPatternSet::MatchesURL false " << url.spec();
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::MatchesAllURLs() const {
|
|
|
|
++ for (auto host = begin(); host != end(); ++host) {
|
|
|
|
++ if (host->match_all_urls() ||
|
|
|
|
++ (host->match_subdomains() && host->host().empty()))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::MatchesSecurityOrigin(const GURL& origin) const {
|
|
|
|
++ for (auto pattern = patterns_.begin(); pattern != patterns_.end();
|
|
|
|
++ ++pattern) {
|
|
|
|
++ if (pattern->MatchesSecurityOrigin(origin))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::OverlapsWith(const URLPatternSet& other) const {
|
|
|
|
++ // Two extension extents overlap if there is any one URL that would match at
|
|
|
|
++ // least one pattern in each of the extents.
|
|
|
|
++ for (auto i = patterns_.cbegin(); i != patterns_.cend(); ++i) {
|
|
|
|
++ for (auto j = other.patterns().cbegin(); j != other.patterns().cend();
|
|
|
|
++ ++j) {
|
|
|
|
++ if (i->OverlapsWith(*j))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::unique_ptr<base::ListValue> URLPatternSet::ToValue() const {
|
|
|
|
++ std::unique_ptr<base::ListValue> value(new base::ListValue);
|
|
|
|
++ for (auto i = patterns_.cbegin(); i != patterns_.cend(); ++i) {
|
|
|
|
++ base::Value pattern_str_value(i->GetAsString());
|
|
|
|
++ if (!base::Contains(value->GetList(), pattern_str_value))
|
|
|
|
++ value->Append(std::move(pattern_str_value));
|
|
|
|
++ }
|
|
|
|
++ return value;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::Populate(const std::vector<std::string>& patterns,
|
|
|
|
++ int valid_schemes,
|
|
|
|
++ bool allow_file_access,
|
|
|
|
++ std::string* error) {
|
|
|
|
++ ClearPatterns();
|
|
|
|
++ for (size_t i = 0; i < patterns.size(); ++i) {
|
|
|
|
++ URLPattern pattern(valid_schemes);
|
|
|
|
++ if (pattern.Parse(patterns[i]) != URLPattern::ParseResult::kSuccess) {
|
|
|
|
++ if (error) {
|
|
|
|
++ *error = ErrorUtils::FormatErrorMessage(kInvalidURLPatternError,
|
|
|
|
++ patterns[i]);
|
|
|
|
++ } else {
|
|
|
|
++ LOG(ERROR) << "Invalid url pattern: " << patterns[i];
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ if (!allow_file_access && pattern.MatchesScheme(url::kFileScheme)) {
|
|
|
|
++ pattern.SetValidSchemes(
|
|
|
|
++ pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
|
|
|
|
++ }
|
|
|
|
++ AddPattern(pattern);
|
|
|
|
++ }
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::unique_ptr<std::vector<std::string>> URLPatternSet::ToStringVector()
|
|
|
|
++ const {
|
|
|
|
++ std::unique_ptr<std::vector<std::string>> value(new std::vector<std::string>);
|
|
|
|
++ for (auto i = patterns_.cbegin(); i != patterns_.cend(); ++i) {
|
|
|
|
++ value->push_back(i->GetAsString());
|
|
|
|
++ }
|
|
|
|
++ return value;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool URLPatternSet::Populate(const base::ListValue& value,
|
|
|
|
++ int valid_schemes,
|
|
|
|
++ bool allow_file_access,
|
|
|
|
++ std::string* error) {
|
|
|
|
++ std::vector<std::string> patterns;
|
|
|
|
++ for (size_t i = 0; i < value.GetList().size(); ++i) {
|
|
|
|
++ std::string item;
|
|
|
|
++ if (!value.GetString(i, &item))
|
|
|
|
++ return false;
|
|
|
|
++ patterns.push_back(item);
|
|
|
|
++ }
|
|
|
|
++ return Populate(patterns, valid_schemes, allow_file_access, error);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/common/url_pattern_set.h b/components/user_scripts/common/url_pattern_set.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/url_pattern_set.h
|
|
|
|
+@@ -0,0 +1,161 @@
|
|
|
|
++// 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 USERSCRIPTS_COMMON_URL_PATTERN_SET_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_URL_PATTERN_SET_H_
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <iosfwd>
|
|
|
|
++#include <memory>
|
|
|
|
++#include <set>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "url_pattern.h"
|
|
|
|
++
|
|
|
|
++class GURL;
|
|
|
|
++
|
|
|
|
++namespace base {
|
|
|
|
++class ListValue;
|
|
|
|
++class Value;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// Represents the set of URLs an extension uses for web content.
|
|
|
|
++class URLPatternSet {
|
|
|
|
++ public:
|
|
|
|
++ typedef std::set<URLPattern>::const_iterator const_iterator;
|
|
|
|
++ typedef std::set<URLPattern>::iterator iterator;
|
|
|
|
++
|
|
|
|
++ // Returns |set1| - |set2|.
|
|
|
|
++ static URLPatternSet CreateDifference(const URLPatternSet& set1,
|
|
|
|
++ const URLPatternSet& set2);
|
|
|
|
++
|
|
|
|
++ enum class IntersectionBehavior {
|
|
|
|
++ // For the following descriptions, consider the two URLPatternSets:
|
|
|
|
++ // Set 1: {"https://example.com/*", "https://*.google.com/*", "http://*/*"}
|
|
|
|
++ // Set 2: {"https://example.com/*", "https://google.com/maps",
|
|
|
|
++ // "*://chromium.org/*"}
|
|
|
|
++
|
|
|
|
++ // Only includes patterns that are exactly in both sets. The intersection of
|
|
|
|
++ // the two sets above is {"https://example.com/*"}, since that is the only
|
|
|
|
++ // pattern that appears exactly in each.
|
|
|
|
++ kStringComparison,
|
|
|
|
++
|
|
|
|
++ // Includes patterns that are effectively contained by both sets. The
|
|
|
|
++ // intersection of the two sets above is
|
|
|
|
++ // {
|
|
|
|
++ // "https://example.com/*" (contained exactly by each set)
|
|
|
|
++ // "https://google.com/maps" (contained exactly by set 2 and a strict
|
|
|
|
++ // subset of https://*.google.com/* in set 1)
|
|
|
|
++ // }
|
|
|
|
++ kPatternsContainedByBoth,
|
|
|
|
++
|
|
|
|
++ // Includes patterns that are contained by both sets and creates new
|
|
|
|
++ // patterns to represent the intersection of any others. The intersection of
|
|
|
|
++ // the two sets above is
|
|
|
|
++ // {
|
|
|
|
++ // "https://example.com/*" (contained exactly by each set)
|
|
|
|
++ // "https://google.com/maps" (contained exactly by set 2 and a strict
|
|
|
|
++ // subset of https://*.google.com/* in set 1)
|
|
|
|
++ // "http://chromium.org/*" (the overlap between "http://*/*" in set 1 and
|
|
|
|
++ // *://chromium.org/*" in set 2).
|
|
|
|
++ // }
|
|
|
|
++ // Note that this is the most computationally expensive - potentially
|
|
|
|
++ // O(n^2) - since it can require comparing each pattern in one set to every
|
|
|
|
++ // pattern in the other set.
|
|
|
|
++ kDetailed,
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // Returns the intersection of |set1| and |set2| according to
|
|
|
|
++ // |intersection_behavior|.
|
|
|
|
++ static URLPatternSet CreateIntersection(
|
|
|
|
++ const URLPatternSet& set1,
|
|
|
|
++ const URLPatternSet& set2,
|
|
|
|
++ IntersectionBehavior intersection_behavior);
|
|
|
|
++
|
|
|
|
++ // Returns the union of |set1| and |set2|.
|
|
|
|
++ static URLPatternSet CreateUnion(const URLPatternSet& set1,
|
|
|
|
++ const URLPatternSet& set2);
|
|
|
|
++
|
|
|
|
++ // Returns the union of all sets in |sets|.
|
|
|
|
++ static URLPatternSet CreateUnion(const std::vector<URLPatternSet>& sets);
|
|
|
|
++
|
|
|
|
++ URLPatternSet();
|
|
|
|
++ URLPatternSet(URLPatternSet&& rhs);
|
|
|
|
++ explicit URLPatternSet(const std::set<URLPattern>& patterns);
|
|
|
|
++ ~URLPatternSet();
|
|
|
|
++
|
|
|
|
++ URLPatternSet& operator=(URLPatternSet&& rhs);
|
|
|
|
++ bool operator==(const URLPatternSet& rhs) const;
|
|
|
|
++
|
|
|
|
++ bool is_empty() const;
|
|
|
|
++ size_t size() const;
|
|
|
|
++ const std::set<URLPattern>& patterns() const { return patterns_; }
|
|
|
|
++ const_iterator begin() const { return patterns_.begin(); }
|
|
|
|
++ const_iterator end() const { return patterns_.end(); }
|
|
|
|
++ iterator erase(iterator iter) { return patterns_.erase(iter); }
|
|
|
|
++
|
|
|
|
++ // Returns a copy of this URLPatternSet; not instrumented as a copy
|
|
|
|
++ // constructor to avoid accidental/unnecessary copies.
|
|
|
|
++ URLPatternSet Clone() const;
|
|
|
|
++
|
|
|
|
++ // Adds a pattern to the set. Returns true if a new pattern was inserted,
|
|
|
|
++ // false if the pattern was already in the set.
|
|
|
|
++ bool AddPattern(const URLPattern& pattern);
|
|
|
|
++
|
|
|
|
++ // Adds all patterns from |set| into this.
|
|
|
|
++ void AddPatterns(const URLPatternSet& set);
|
|
|
|
++
|
|
|
|
++ void ClearPatterns();
|
|
|
|
++
|
|
|
|
++ // Adds a pattern based on |origin| to the set.
|
|
|
|
++ bool AddOrigin(int valid_schemes, const GURL& origin);
|
|
|
|
++
|
|
|
|
++ // Returns true if every URL that matches |set| is matched by this. In other
|
|
|
|
++ // words, if every pattern in |set| is encompassed by a pattern in this.
|
|
|
|
++ bool Contains(const URLPatternSet& set) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if any pattern in this set encompasses |pattern|.
|
|
|
|
++ bool ContainsPattern(const URLPattern& pattern) const;
|
|
|
|
++
|
|
|
|
++ // Test if the extent contains a URL.
|
|
|
|
++ bool MatchesURL(const GURL& url) const;
|
|
|
|
++
|
|
|
|
++ // Test if the extent matches all URLs (for example, <all_urls>).
|
|
|
|
++ bool MatchesAllURLs() const;
|
|
|
|
++
|
|
|
|
++ bool MatchesSecurityOrigin(const GURL& origin) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if there is a single URL that would be in two extents.
|
|
|
|
++ bool OverlapsWith(const URLPatternSet& other) const;
|
|
|
|
++
|
|
|
|
++ // Converts to and from Value for serialization to preferences.
|
|
|
|
++ std::unique_ptr<base::ListValue> ToValue() const;
|
|
|
|
++ bool Populate(const base::ListValue& value,
|
|
|
|
++ int valid_schemes,
|
|
|
|
++ bool allow_file_access,
|
|
|
|
++ std::string* error);
|
|
|
|
++
|
|
|
|
++ // Converts to and from a vector of strings.
|
|
|
|
++ std::unique_ptr<std::vector<std::string>> ToStringVector() const;
|
|
|
|
++ bool Populate(const std::vector<std::string>& patterns,
|
|
|
|
++ int valid_schemes,
|
|
|
|
++ bool allow_file_access,
|
|
|
|
++ std::string* error);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // The list of URL patterns that comprise the extent.
|
|
|
|
++ std::set<URLPattern> patterns_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(URLPatternSet);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++std::ostream& operator<<(std::ostream& out,
|
|
|
|
++ const URLPatternSet& url_pattern_set);
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_URL_PATTERN_SET_H_
|
|
|
|
+diff --git a/components/user_scripts/common/user_script.cc b/components/user_scripts/common/user_script.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/user_script.cc
|
|
|
|
+@@ -0,0 +1,317 @@
|
|
|
|
++// 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 "user_script.h"
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++#include <stdint.h>
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "base/atomic_sequence_num.h"
|
|
|
|
++#include "base/command_line.h"
|
|
|
|
++#include "base/pickle.h"
|
|
|
|
++#include "base/strings/pattern.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "user_scripts_features.h"
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++// This cannot be a plain int or int64_t because we need to generate unique IDs
|
|
|
|
++// from multiple threads.
|
|
|
|
++base::AtomicSequenceNumber g_user_script_id_generator;
|
|
|
|
++
|
|
|
|
++bool UrlMatchesGlobs(const std::vector<std::string>* globs,
|
|
|
|
++ const GURL& url) {
|
|
|
|
++ for (auto glob = globs->cbegin(); glob != globs->cend(); ++glob) {
|
|
|
|
++ if (base::MatchPattern(url.spec(), *glob))
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// The bitmask for valid user script injectable schemes used by URLPattern.
|
|
|
|
++enum {
|
|
|
|
++ kValidUserScriptSchemes = //URLPattern::SCHEME_CHROMEUI |
|
|
|
|
++ URLPattern::SCHEME_HTTP |
|
|
|
|
++ URLPattern::SCHEME_HTTPS
|
|
|
|
++ //| URLPattern::SCHEME_FILE |
|
|
|
|
++ //URLPattern::SCHEME_FTP
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++const char UserScript::kFileExtension[] = ".user.js";
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++int UserScript::GenerateUserScriptID() {
|
|
|
|
++ return g_user_script_id_generator.GetNext();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScript::IsURLUserScript(const GURL& url,
|
|
|
|
++ const std::string& mime_type) {
|
|
|
|
++ return base::EndsWith(url.ExtractFileName(), kFileExtension,
|
|
|
|
++ base::CompareCase::INSENSITIVE_ASCII) &&
|
|
|
|
++ mime_type != "text/html";
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++int UserScript::ValidUserScriptSchemes(bool canExecuteScriptEverywhere) {
|
|
|
|
++ if (canExecuteScriptEverywhere)
|
|
|
|
++ return URLPattern::SCHEME_ALL;
|
|
|
|
++ int valid_schemes = kValidUserScriptSchemes;
|
|
|
|
++ // if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
|
|
|
|
++ // switches::kExtensionsOnChromeURLs)) {
|
|
|
|
++ // valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
|
|
|
|
++ // }
|
|
|
|
++ return valid_schemes;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScript::File::File(const base::FilePath& extension_root,
|
|
|
|
++ const base::FilePath& relative_path,
|
|
|
|
++ const GURL& url)
|
|
|
|
++ : extension_root_(extension_root),
|
|
|
|
++ relative_path_(relative_path),
|
|
|
|
++ url_(url) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScript::File::File() {}
|
|
|
|
++
|
|
|
|
++UserScript::File::File(const File& other)
|
|
|
|
++ : extension_root_(other.extension_root_),
|
|
|
|
++ relative_path_(other.relative_path_),
|
|
|
|
++ url_(other.url_),
|
|
|
|
++ external_content_(other.external_content_),
|
|
|
|
++ content_(other.content_),
|
|
|
|
++ key_(other.key_) {}
|
|
|
|
++
|
|
|
|
++UserScript::File::~File() {}
|
|
|
|
++
|
|
|
|
++UserScript::UserScript() = default;
|
|
|
|
++UserScript::~UserScript() = default;
|
|
|
|
++
|
|
|
|
++void UserScript::add_url_pattern(const URLPattern& pattern) {
|
|
|
|
++ url_set_.AddPattern(pattern);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::add_exclude_url_pattern(const URLPattern& pattern) {
|
|
|
|
++ exclude_url_set_.AddPattern(pattern);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScript::MatchesURL(const GURL& url) const {
|
|
|
|
++ if (!url_set_.is_empty()) {
|
|
|
|
++ if (!url_set_.MatchesURL(url)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: No Match for url_set";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!exclude_url_set_.is_empty()) {
|
|
|
|
++ if (exclude_url_set_.MatchesURL(url)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: No Match for exclude_url_set";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!globs_.empty()) {
|
|
|
|
++ if (!UrlMatchesGlobs(&globs_, url)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: No Match for globs";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!exclude_globs_.empty()) {
|
|
|
|
++ if (UrlMatchesGlobs(&exclude_globs_, url)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: No Match for exclude_globs";
|
|
|
|
++ return false;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScript::MatchesDocument(const GURL& effective_document_url,
|
|
|
|
++ bool is_subframe) const {
|
|
|
|
++ if (is_subframe && !match_all_frames())
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ return MatchesURL(effective_document_url);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::File::Pickle(base::Pickle* pickle) const {
|
|
|
|
++ pickle->WriteString(url_.spec());
|
|
|
|
++ // Do not write path. It's not needed in the renderer.
|
|
|
|
++ // Do not write content. It will be serialized by other means.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::File::Unpickle(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter) {
|
|
|
|
++ // Read the url from the pickle.
|
|
|
|
++ std::string url;
|
|
|
|
++ CHECK(iter->ReadString(&url));
|
|
|
|
++ set_url(GURL(url));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::Pickle(base::Pickle* pickle) const {
|
|
|
|
++ // Write the simple types to the pickle.
|
|
|
|
++ pickle->WriteInt(run_location());
|
|
|
|
++ pickle->WriteInt(user_script_id_);
|
|
|
|
++ pickle->WriteBool(emulate_greasemonkey());
|
|
|
|
++ pickle->WriteBool(match_all_frames());
|
|
|
|
++ pickle->WriteInt(static_cast<int>(match_origin_as_fallback()));
|
|
|
|
++ pickle->WriteBool(is_incognito_enabled());
|
|
|
|
++
|
|
|
|
++ PickleHostID(pickle, host_id_);
|
|
|
|
++ pickle->WriteInt(consumer_instance_type());
|
|
|
|
++ PickleGlobs(pickle, globs_);
|
|
|
|
++ PickleGlobs(pickle, exclude_globs_);
|
|
|
|
++ PickleURLPatternSet(pickle, url_set_);
|
|
|
|
++ PickleURLPatternSet(pickle, exclude_url_set_);
|
|
|
|
++ PickleScripts(pickle, js_scripts_);
|
|
|
|
++ PickleScripts(pickle, css_scripts_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::PickleGlobs(base::Pickle* pickle,
|
|
|
|
++ const std::vector<std::string>& globs) const {
|
|
|
|
++ pickle->WriteUInt32(globs.size());
|
|
|
|
++ for (auto glob = globs.cbegin(); glob != globs.cend(); ++glob) {
|
|
|
|
++ pickle->WriteString(*glob);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::PickleHostID(base::Pickle* pickle,
|
|
|
|
++ const HostID& host_id) const {
|
|
|
|
++ pickle->WriteInt(host_id.type());
|
|
|
|
++ pickle->WriteString(host_id.id());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::PickleURLPatternSet(base::Pickle* pickle,
|
|
|
|
++ const URLPatternSet& pattern_list) const {
|
|
|
|
++ pickle->WriteUInt32(pattern_list.patterns().size());
|
|
|
|
++ for (auto pattern = pattern_list.begin(); pattern != pattern_list.end();
|
|
|
|
++ ++pattern) {
|
|
|
|
++ pickle->WriteInt(pattern->valid_schemes());
|
|
|
|
++ pickle->WriteString(pattern->GetAsString());
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::PickleScripts(base::Pickle* pickle,
|
|
|
|
++ const FileList& scripts) const {
|
|
|
|
++ pickle->WriteUInt32(scripts.size());
|
|
|
|
++ for (const std::unique_ptr<File>& file : scripts)
|
|
|
|
++ file->Pickle(pickle);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::Unpickle(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter) {
|
|
|
|
++ // Read the run location.
|
|
|
|
++ int run_location = 0;
|
|
|
|
++ CHECK(iter->ReadInt(&run_location));
|
|
|
|
++ CHECK(run_location >= 0 && run_location < RUN_LOCATION_LAST);
|
|
|
|
++ run_location_ = static_cast<RunLocation>(run_location);
|
|
|
|
++
|
|
|
|
++ CHECK(iter->ReadInt(&user_script_id_));
|
|
|
|
++ CHECK(iter->ReadBool(&emulate_greasemonkey_));
|
|
|
|
++ CHECK(iter->ReadBool(&match_all_frames_));
|
|
|
|
++ int match_origin_as_fallback_int = 0;
|
|
|
|
++ CHECK(iter->ReadInt(&match_origin_as_fallback_int));
|
|
|
|
++ match_origin_as_fallback_ =
|
|
|
|
++ static_cast<MatchOriginAsFallbackBehavior>(match_origin_as_fallback_int);
|
|
|
|
++ CHECK(iter->ReadBool(&incognito_enabled_));
|
|
|
|
++
|
|
|
|
++ UnpickleHostID(pickle, iter, &host_id_);
|
|
|
|
++
|
|
|
|
++ int consumer_instance_type = 0;
|
|
|
|
++ CHECK(iter->ReadInt(&consumer_instance_type));
|
|
|
|
++ consumer_instance_type_ =
|
|
|
|
++ static_cast<ConsumerInstanceType>(consumer_instance_type);
|
|
|
|
++
|
|
|
|
++ UnpickleGlobs(pickle, iter, &globs_);
|
|
|
|
++ UnpickleGlobs(pickle, iter, &exclude_globs_);
|
|
|
|
++ UnpickleURLPatternSet(pickle, iter, &url_set_);
|
|
|
|
++ UnpickleURLPatternSet(pickle, iter, &exclude_url_set_);
|
|
|
|
++ UnpickleScripts(pickle, iter, &js_scripts_);
|
|
|
|
++ UnpickleScripts(pickle, iter, &css_scripts_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::UnpickleGlobs(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ std::vector<std::string>* globs) {
|
|
|
|
++ uint32_t num_globs = 0;
|
|
|
|
++ CHECK(iter->ReadUInt32(&num_globs));
|
|
|
|
++ globs->clear();
|
|
|
|
++ for (uint32_t i = 0; i < num_globs; ++i) {
|
|
|
|
++ std::string glob;
|
|
|
|
++ CHECK(iter->ReadString(&glob));
|
|
|
|
++ globs->push_back(glob);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::UnpickleHostID(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ HostID* host_id) {
|
|
|
|
++ int type = 0;
|
|
|
|
++ std::string id;
|
|
|
|
++ CHECK(iter->ReadInt(&type));
|
|
|
|
++ CHECK(iter->ReadString(&id));
|
|
|
|
++ *host_id = HostID(static_cast<HostID::HostType>(type), id);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::UnpickleURLPatternSet(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ URLPatternSet* pattern_list) {
|
|
|
|
++ uint32_t num_patterns = 0;
|
|
|
|
++ CHECK(iter->ReadUInt32(&num_patterns));
|
|
|
|
++
|
|
|
|
++ pattern_list->ClearPatterns();
|
|
|
|
++ for (uint32_t i = 0; i < num_patterns; ++i) {
|
|
|
|
++ int valid_schemes;
|
|
|
|
++ CHECK(iter->ReadInt(&valid_schemes));
|
|
|
|
++
|
|
|
|
++ std::string pattern_str;
|
|
|
|
++ CHECK(iter->ReadString(&pattern_str));
|
|
|
|
++
|
|
|
|
++ URLPattern pattern(kValidUserScriptSchemes);
|
|
|
|
++ URLPattern::ParseResult result = pattern.Parse(pattern_str);
|
|
|
|
++ CHECK(URLPattern::ParseResult::kSuccess == result)
|
|
|
|
++ << URLPattern::GetParseResultString(result) << " "
|
|
|
|
++ << pattern_str.c_str();
|
|
|
|
++
|
|
|
|
++ pattern.SetValidSchemes(valid_schemes);
|
|
|
|
++ pattern_list->AddPattern(pattern);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScript::UnpickleScripts(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ FileList* scripts) {
|
|
|
|
++ uint32_t num_files = 0;
|
|
|
|
++ CHECK(iter->ReadUInt32(&num_files));
|
|
|
|
++ scripts->clear();
|
|
|
|
++ for (uint32_t i = 0; i < num_files; ++i) {
|
|
|
|
++ std::unique_ptr<File> file(new File());
|
|
|
|
++ file->Unpickle(pickle, iter);
|
|
|
|
++ scripts->push_back(std::move(file));
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptIDPair::UserScriptIDPair(int id, const HostID& host_id)
|
|
|
|
++ : id(id), host_id(host_id) {}
|
|
|
|
++
|
|
|
|
++UserScriptIDPair::UserScriptIDPair(int id) : id(id), host_id(HostID()) {}
|
|
|
|
++
|
|
|
|
++bool operator<(const UserScriptIDPair& a, const UserScriptIDPair& b) {
|
|
|
|
++ return a.id < b.id;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/common/user_script.h b/components/user_scripts/common/user_script.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/user_script.h
|
|
|
|
+@@ -0,0 +1,403 @@
|
|
|
|
++// 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 USERSCRIPTS_COMMON_USER_SCRIPT_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_USER_SCRIPT_H_
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/files/file_path.h"
|
|
|
|
++#include "base/strings/string_piece.h"
|
|
|
|
++#include "script_constants.h"
|
|
|
|
++#include "host_id.h"
|
|
|
|
++#include "url_pattern.h"
|
|
|
|
++#include "url_pattern_set.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++
|
|
|
|
++namespace base {
|
|
|
|
++class Pickle;
|
|
|
|
++class PickleIterator;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// Represents a user script, either a standalone one, or one that is part of an
|
|
|
|
++// extension.
|
|
|
|
++class UserScript {
|
|
|
|
++ public:
|
|
|
|
++ // The file extension for standalone user scripts.
|
|
|
|
++ static const char kFileExtension[];
|
|
|
|
++
|
|
|
|
++ static int GenerateUserScriptID();
|
|
|
|
++
|
|
|
|
++ // Check if a URL should be treated as a user script and converted to an
|
|
|
|
++ // extension.
|
|
|
|
++ static bool IsURLUserScript(const GURL& url, const std::string& mime_type);
|
|
|
|
++
|
|
|
|
++ // Get the valid user script schemes for the current process. If
|
|
|
|
++ // canExecuteScriptEverywhere is true, this will return ALL_SCHEMES.
|
|
|
|
++ static int ValidUserScriptSchemes(bool canExecuteScriptEverywhere = false);
|
|
|
|
++
|
|
|
|
++ // TODO(rdevlin.cronin) This and RunLocation don't really belong here, since
|
|
|
|
++ // they are used for more than UserScripts (e.g., tabs.executeScript()).
|
|
|
|
++ // The type of injected script.
|
|
|
|
++ enum InjectionType {
|
|
|
|
++ // A content script specified in the extension's manifest.
|
|
|
|
++ CONTENT_SCRIPT,
|
|
|
|
++ // A script injected via, e.g. tabs.executeScript().
|
|
|
|
++ //PROGRAMMATIC_SCRIPT
|
|
|
|
++ };
|
|
|
|
++ // The last type of injected script; used for enum verification in IPC.
|
|
|
|
++ // Update this if you add more injected script types!
|
|
|
|
++ static const InjectionType INJECTION_TYPE_LAST = CONTENT_SCRIPT/*PROGRAMMATIC_SCRIPT*/;
|
|
|
|
++
|
|
|
|
++ // Locations that user scripts can be run inside the document.
|
|
|
|
++ // The three run locations must strictly follow each other in both load order
|
|
|
|
++ // (i.e., start *always* comes before end) and numerically, as we use
|
|
|
|
++ // arithmetic checking (e.g., curr == last + 1). So, no bitmasks here!!
|
|
|
|
++ enum RunLocation {
|
|
|
|
++ UNDEFINED,
|
|
|
|
++ DOCUMENT_START, // After the documentElement is created, but before
|
|
|
|
++ // anything else happens.
|
|
|
|
++ DOCUMENT_END, // After the entire document is parsed. Same as
|
|
|
|
++ // DOMContentLoaded.
|
|
|
|
++ DOCUMENT_IDLE, // Sometime after DOMContentLoaded, as soon as the document
|
|
|
|
++ // is "idle". Currently this uses the simple heuristic of:
|
|
|
|
++ // min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no
|
|
|
|
++ // particular injection point is guaranteed.
|
|
|
|
++ RUN_DEFERRED, // The user script's injection was deferred for permissions
|
|
|
|
++ // reasons, and was executed at a later time.
|
|
|
|
++ BROWSER_DRIVEN, // The user script will be injected when triggered by an
|
|
|
|
++ // IPC in the browser process.
|
|
|
|
++ RUN_LOCATION_LAST // Leave this as the last item.
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // Holds script file info.
|
|
|
|
++ class File {
|
|
|
|
++ public:
|
|
|
|
++ File(const base::FilePath& extension_root,
|
|
|
|
++ const base::FilePath& relative_path,
|
|
|
|
++ const GURL& url);
|
|
|
|
++ File();
|
|
|
|
++ File(const File& other);
|
|
|
|
++ ~File();
|
|
|
|
++
|
|
|
|
++ const base::FilePath& extension_root() const { return extension_root_; }
|
|
|
|
++ const base::FilePath& relative_path() const { return relative_path_; }
|
|
|
|
++
|
|
|
|
++ const GURL& url() const { return url_; }
|
|
|
|
++ void set_url(const GURL& url) { url_ = url; }
|
|
|
|
++
|
|
|
|
++ // If external_content_ is set returns it as content otherwise it returns
|
|
|
|
++ // content_
|
|
|
|
++ const base::StringPiece GetContent() const {
|
|
|
|
++ if (external_content_.data())
|
|
|
|
++ return external_content_;
|
|
|
|
++ else
|
|
|
|
++ return content_;
|
|
|
|
++ }
|
|
|
|
++ void set_external_content(const base::StringPiece& content) {
|
|
|
|
++ external_content_ = content;
|
|
|
|
++ }
|
|
|
|
++ void set_content(const base::StringPiece& content) {
|
|
|
|
++ content_.assign(content.begin(), content.end());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& key() const { return key_; }
|
|
|
|
++ void set_key(const std::string& key) {
|
|
|
|
++ key_ = key;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Serialization support. The content and FilePath members will not be
|
|
|
|
++ // serialized!
|
|
|
|
++ void Pickle(base::Pickle* pickle) const;
|
|
|
|
++ void Unpickle(const base::Pickle& pickle, base::PickleIterator* iter);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // Where the script file lives on the disk. We keep the path split so that
|
|
|
|
++ // it can be localized at will.
|
|
|
|
++ base::FilePath extension_root_;
|
|
|
|
++ base::FilePath relative_path_;
|
|
|
|
++
|
|
|
|
++ // The url to this script file.
|
|
|
|
++ GURL url_;
|
|
|
|
++
|
|
|
|
++ // The script content. It can be set to either loaded_content_ or
|
|
|
|
++ // externally allocated string.
|
|
|
|
++ base::StringPiece external_content_;
|
|
|
|
++
|
|
|
|
++ // Set when the content is loaded by LoadContent
|
|
|
|
++ std::string content_;
|
|
|
|
++
|
|
|
|
++ std::string key_;
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ using FileList = std::vector<std::unique_ptr<File>>;
|
|
|
|
++
|
|
|
|
++ // Type of a API consumer instance that user scripts will be injected on.
|
|
|
|
++ enum ConsumerInstanceType { TAB, WEBVIEW };
|
|
|
|
++
|
|
|
|
++ // Constructor. Default the run location to document end, which is like
|
|
|
|
++ // Greasemonkey and probably more useful for typical scripts.
|
|
|
|
++ UserScript();
|
|
|
|
++ ~UserScript();
|
|
|
|
++
|
|
|
|
++ // Performs a copy of all fields except file contents.
|
|
|
|
++ // static std::unique_ptr<UserScript> CopyMetadataFrom(const UserScript& other);
|
|
|
|
++
|
|
|
|
++ const std::string& name_space() const { return name_space_; }
|
|
|
|
++ void set_name_space(const std::string& name_space) {
|
|
|
|
++ name_space_ = name_space;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& name() const { return name_; }
|
|
|
|
++ void set_name(const std::string& name) { name_ = name; }
|
|
|
|
++
|
|
|
|
++ const std::string& version() const { return version_; }
|
|
|
|
++ void set_version(const std::string& version) {
|
|
|
|
++ version_ = version;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& key() const { return key_; }
|
|
|
|
++ void set_key(const std::string& key) {
|
|
|
|
++ key_ = key;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& file_path() const { return file_path_; }
|
|
|
|
++ void set_file_path(const std::string& file_path) {
|
|
|
|
++ file_path_ = file_path;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& url_source() const { return url_source_; }
|
|
|
|
++ void set_url_source(const std::string& url_source) {
|
|
|
|
++ url_source_ = url_source;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& description() const { return description_; }
|
|
|
|
++ void set_description(const std::string& description) {
|
|
|
|
++ description_ = description;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ const std::string& parser_error() const { return parser_error_; }
|
|
|
|
++ void set_parser_error(const std::string& parser_error) {
|
|
|
|
++ parser_error_ = parser_error;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ bool force_disabled() const { return force_disabled_; }
|
|
|
|
++ void set_force_disabled() {
|
|
|
|
++ force_disabled_ = true;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // The place in the document to run the script.
|
|
|
|
++ RunLocation run_location() const { return run_location_; }
|
|
|
|
++ void set_run_location(RunLocation location) { run_location_ = location; }
|
|
|
|
++
|
|
|
|
++ // Whether to emulate greasemonkey when running this script.
|
|
|
|
++ bool emulate_greasemonkey() const { return emulate_greasemonkey_; }
|
|
|
|
++ void set_emulate_greasemonkey(bool val) { emulate_greasemonkey_ = val; }
|
|
|
|
++
|
|
|
|
++ // Whether to match all frames, or only the top one.
|
|
|
|
++ bool match_all_frames() const { return match_all_frames_; }
|
|
|
|
++ void set_match_all_frames(bool val) { match_all_frames_ = val; }
|
|
|
|
++
|
|
|
|
++ // Whether to match the origin as a fallback if the URL cannot be used
|
|
|
|
++ // directly.
|
|
|
|
++ MatchOriginAsFallbackBehavior match_origin_as_fallback() const {
|
|
|
|
++ return match_origin_as_fallback_;
|
|
|
|
++ }
|
|
|
|
++ void set_match_origin_as_fallback(MatchOriginAsFallbackBehavior val) {
|
|
|
|
++ match_origin_as_fallback_ = val;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // The globs, if any, that determine which pages this script runs against.
|
|
|
|
++ // These are only used with "standalone" Greasemonkey-like user scripts.
|
|
|
|
++ const std::vector<std::string>& globs() const { return globs_; }
|
|
|
|
++ void add_glob(const std::string& glob) { globs_.push_back(glob); }
|
|
|
|
++ void clear_globs() { globs_.clear(); }
|
|
|
|
++ const std::vector<std::string>& exclude_globs() const {
|
|
|
|
++ return exclude_globs_;
|
|
|
|
++ }
|
|
|
|
++ void add_exclude_glob(const std::string& glob) {
|
|
|
|
++ exclude_globs_.push_back(glob);
|
|
|
|
++ }
|
|
|
|
++ void clear_exclude_globs() { exclude_globs_.clear(); }
|
|
|
|
++
|
|
|
|
++ // The URLPatterns, if any, that determine which pages this script runs
|
|
|
|
++ // against.
|
|
|
|
++ const URLPatternSet& url_patterns() const { return url_set_; }
|
|
|
|
++ void add_url_pattern(const URLPattern& pattern);
|
|
|
|
++ const URLPatternSet& exclude_url_patterns() const {
|
|
|
|
++ return exclude_url_set_;
|
|
|
|
++ }
|
|
|
|
++ void add_exclude_url_pattern(const URLPattern& pattern);
|
|
|
|
++
|
|
|
|
++ // List of js scripts for this user script
|
|
|
|
++ FileList& js_scripts() { return js_scripts_; }
|
|
|
|
++ const FileList& js_scripts() const { return js_scripts_; }
|
|
|
|
++
|
|
|
|
++ // List of css scripts for this user script
|
|
|
|
++ FileList& css_scripts() { return css_scripts_; }
|
|
|
|
++ const FileList& css_scripts() const { return css_scripts_; }
|
|
|
|
++
|
|
|
|
++ const std::string& extension_id() const { return host_id_.id(); }
|
|
|
|
++
|
|
|
|
++ const HostID& host_id() const { return host_id_; }
|
|
|
|
++ void set_host_id(const HostID& host_id) { host_id_ = host_id; }
|
|
|
|
++
|
|
|
|
++ const ConsumerInstanceType& consumer_instance_type() const {
|
|
|
|
++ return consumer_instance_type_;
|
|
|
|
++ }
|
|
|
|
++ void set_consumer_instance_type(
|
|
|
|
++ const ConsumerInstanceType& consumer_instance_type) {
|
|
|
|
++ consumer_instance_type_ = consumer_instance_type;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ int id() const { return user_script_id_; }
|
|
|
|
++ void set_id(int id) { user_script_id_ = id; }
|
|
|
|
++
|
|
|
|
++ // TODO(lazyboy): Incognito information is extension specific, it doesn't
|
|
|
|
++ // belong here. We should be able to determine this in the renderer/ where it
|
|
|
|
++ // is used.
|
|
|
|
++ bool is_incognito_enabled() const { return incognito_enabled_; }
|
|
|
|
++ void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; }
|
|
|
|
++
|
|
|
|
++ // Returns true if the script should be applied to the specified URL, false
|
|
|
|
++ // otherwise.
|
|
|
|
++ bool MatchesURL(const GURL& url) const;
|
|
|
|
++
|
|
|
|
++ // Returns true if the script should be applied to the given
|
|
|
|
++ // |effective_document_url|. It is the caller's responsibility to calculate
|
|
|
|
++ // |effective_document_url| based on match_origin_as_fallback().
|
|
|
|
++ bool MatchesDocument(const GURL& effective_document_url,
|
|
|
|
++ bool is_subframe) const;
|
|
|
|
++
|
|
|
|
++ // Serializes the UserScript into a pickle. The content of the scripts and
|
|
|
|
++ // paths to UserScript::Files will not be serialized!
|
|
|
|
++ void Pickle(base::Pickle* pickle) const;
|
|
|
|
++
|
|
|
|
++ // Deserializes the script from a pickle. Note that this always succeeds
|
|
|
|
++ // because presumably we were the one that pickled it, and we did it
|
|
|
|
++ // correctly.
|
|
|
|
++ void Unpickle(const base::Pickle& pickle, base::PickleIterator* iter);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // base::Pickle helper functions used to pickle the individual types of
|
|
|
|
++ // components.
|
|
|
|
++ void PickleGlobs(base::Pickle* pickle,
|
|
|
|
++ const std::vector<std::string>& globs) const;
|
|
|
|
++ void PickleHostID(base::Pickle* pickle, const HostID& host_id) const;
|
|
|
|
++ void PickleURLPatternSet(base::Pickle* pickle,
|
|
|
|
++ const URLPatternSet& pattern_list) const;
|
|
|
|
++ void PickleScripts(base::Pickle* pickle, const FileList& scripts) const;
|
|
|
|
++
|
|
|
|
++ // Unpickle helper functions used to unpickle individual types of components.
|
|
|
|
++ void UnpickleGlobs(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ std::vector<std::string>* globs);
|
|
|
|
++ void UnpickleHostID(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ HostID* host_id);
|
|
|
|
++ void UnpickleURLPatternSet(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ URLPatternSet* pattern_list);
|
|
|
|
++ void UnpickleScripts(const base::Pickle& pickle,
|
|
|
|
++ base::PickleIterator* iter,
|
|
|
|
++ FileList* scripts);
|
|
|
|
++
|
|
|
|
++ // The location to run the script inside the document.
|
|
|
|
++ RunLocation run_location_ = DOCUMENT_IDLE;
|
|
|
|
++
|
|
|
|
++ // The namespace of the script. This is used by Greasemonkey in the same way
|
|
|
|
++ // as XML namespaces. Only used when parsing Greasemonkey-style scripts.
|
|
|
|
++ std::string name_space_;
|
|
|
|
++
|
|
|
|
++ // The script's name. Only used when parsing Greasemonkey-style scripts.
|
|
|
|
++ std::string name_;
|
|
|
|
++
|
|
|
|
++ // A longer description. Only used when parsing Greasemonkey-style scripts.
|
|
|
|
++ std::string description_;
|
|
|
|
++
|
|
|
|
++ // Parser error to show to user
|
|
|
|
++ std::string parser_error_;
|
|
|
|
++
|
|
|
|
++ // A version number of the script. Only used when parsing Greasemonkey-style
|
|
|
|
++ // scripts.
|
|
|
|
++ std::string version_;
|
|
|
|
++
|
|
|
|
++ // Greasemonkey-style globs that determine pages to inject the script into.
|
|
|
|
++ // These are only used with standalone scripts.
|
|
|
|
++ std::vector<std::string> globs_;
|
|
|
|
++ std::vector<std::string> exclude_globs_;
|
|
|
|
++
|
|
|
|
++ // URLPatterns that determine pages to inject the script into. These are
|
|
|
|
++ // only used with scripts that are part of extensions.
|
|
|
|
++ URLPatternSet url_set_;
|
|
|
|
++ URLPatternSet exclude_url_set_;
|
|
|
|
++
|
|
|
|
++ // List of js scripts defined in content_scripts
|
|
|
|
++ FileList js_scripts_;
|
|
|
|
++
|
|
|
|
++ // List of css scripts defined in content_scripts
|
|
|
|
++ FileList css_scripts_;
|
|
|
|
++
|
|
|
|
++ // internal key of scripts
|
|
|
|
++ std::string key_;
|
|
|
|
++
|
|
|
|
++ std::string file_path_;
|
|
|
|
++
|
|
|
|
++ // url source of script
|
|
|
|
++ std::string url_source_;
|
|
|
|
++
|
|
|
|
++ // The ID of the host this script is a part of. The |ID| of the
|
|
|
|
++ // |host_id| can be empty if the script is a "standlone" user script.
|
|
|
|
++ HostID host_id_;
|
|
|
|
++
|
|
|
|
++ // The type of the consumer instance that the script will be injected.
|
|
|
|
++ ConsumerInstanceType consumer_instance_type_ = TAB;
|
|
|
|
++
|
|
|
|
++ // The globally-unique id associated with this user script. -1 indicates
|
|
|
|
++ // "invalid".
|
|
|
|
++ int user_script_id_ = -1;
|
|
|
|
++
|
|
|
|
++ // Whether we should try to emulate Greasemonkey's APIs when running this
|
|
|
|
++ // script.
|
|
|
|
++ bool emulate_greasemonkey_ = false;
|
|
|
|
++
|
|
|
|
++ // Whether the user script should run in all frames, or only just the top one.
|
|
|
|
++ bool match_all_frames_ = false;
|
|
|
|
++
|
|
|
|
++ // Whether the user script should run in frames whose initiator / precursor
|
|
|
|
++ // origin matches a match pattern, if an appropriate URL cannot be found for
|
|
|
|
++ // the frame for matching purposes, such as in the case of about:, data:, and
|
|
|
|
++ // other schemes.
|
|
|
|
++ MatchOriginAsFallbackBehavior match_origin_as_fallback_ =
|
|
|
|
++ MatchOriginAsFallbackBehavior::kNever;
|
|
|
|
++
|
|
|
|
++ // True if the script should be injected into an incognito tab.
|
|
|
|
++ bool incognito_enabled_ = false;
|
|
|
|
++
|
|
|
|
++ // Script cannot be enabled
|
|
|
|
++ bool force_disabled_ = false;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(UserScript);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++// Information we need while removing scripts from a UserScriptLoader.
|
|
|
|
++struct UserScriptIDPair {
|
|
|
|
++ UserScriptIDPair(int id, const HostID& host_id);
|
|
|
|
++ explicit UserScriptIDPair(int id);
|
|
|
|
++
|
|
|
|
++ int id;
|
|
|
|
++ HostID host_id;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++bool operator<(const UserScriptIDPair& a, const UserScriptIDPair& b);
|
|
|
|
++
|
|
|
|
++using UserScriptList = std::vector<std::unique_ptr<UserScript>>;
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_USER_SCRIPT_H_
|
|
|
|
+diff --git a/components/user_scripts/common/user_scripts_features.cc b/components/user_scripts/common/user_scripts_features.cc
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/user_scripts_features.cc
|
|
|
|
+@@ -0,0 +1,32 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#include "user_scripts_features.h"
|
|
|
|
++
|
|
|
|
++#include "build/build_config.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace features {
|
|
|
|
++
|
|
|
|
++const base::Feature kEnableLoggingUserScripts =
|
|
|
|
++ {"EnableLoggingUserScripts",
|
|
|
|
++ base::FEATURE_DISABLED_BY_DEFAULT};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/common/user_scripts_features.h b/components/user_scripts/common/user_scripts_features.h
|
|
|
|
+new file mode 100644
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/user_scripts_features.h
|
|
|
|
+@@ -0,0 +1,34 @@
|
|
|
|
++/*
|
|
|
|
++ This file is part of Bromite.
|
|
|
|
++
|
|
|
|
++ Bromite is free software: you can redistribute it and/or modify
|
|
|
|
++ it under the terms of the GNU General Public License as published by
|
|
|
|
++ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
++ (at your option) any later version.
|
|
|
|
++
|
|
|
|
++ Bromite is distributed in the hope that it will be useful,
|
|
|
|
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
++ GNU General Public License for more details.
|
|
|
|
++
|
|
|
|
++ You should have received a copy of the GNU General Public License
|
|
|
|
++ along with Bromite. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
++*/
|
|
|
|
++
|
|
|
|
++#ifndef USERSCRIPTS_COMMON_USERSCRIPTS_FEATURES_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_USERSCRIPTS_FEATURES_H_
|
|
|
|
++
|
|
|
|
++// This file defines all the base::FeatureList features for the Password Manager
|
|
|
|
++// module.
|
|
|
|
++
|
|
|
|
++#include "base/feature_list.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace features {
|
|
|
|
++ extern const base::Feature kEnableLoggingUserScripts;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/common/view_type.cc b/components/user_scripts/common/view_type.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/view_type.cc
|
|
|
|
+@@ -0,0 +1,39 @@
|
|
|
|
++// 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 "view_type.h"
|
|
|
|
++
|
|
|
|
++#include "base/strings/string_piece.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++bool GetViewTypeFromString(const std::string& view_type,
|
|
|
|
++ ViewType* view_type_out) {
|
|
|
|
++ // TODO(devlin): This map doesn't contain the following values:
|
|
|
|
++ // - VIEW_TYPE_BACKGROUND_CONTENTS
|
|
|
|
++ // - VIEW_TYPE_COMPONENT
|
|
|
|
++ // - VIEW_TYPE_EXTENSION_GUEST
|
|
|
|
++ // Why? Is it just because we don't expose those types to JS?
|
|
|
|
++ static const struct {
|
|
|
|
++ ViewType type;
|
|
|
|
++ base::StringPiece name;
|
|
|
|
++ } constexpr kTypeMap[] = {
|
|
|
|
++ // {VIEW_TYPE_APP_WINDOW, "APP_WINDOW"},
|
|
|
|
++ // {VIEW_TYPE_EXTENSION_BACKGROUND_PAGE, "BACKGROUND"},
|
|
|
|
++ // {VIEW_TYPE_EXTENSION_DIALOG, "EXTENSION_DIALOG"},
|
|
|
|
++ // {VIEW_TYPE_EXTENSION_POPUP, "POPUP"},
|
|
|
|
++ {VIEW_TYPE_TAB_CONTENTS, "TAB"},
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ for (const auto& entry : kTypeMap) {
|
|
|
|
++ if (entry.name == view_type) {
|
|
|
|
++ *view_type_out = entry.type;
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/common/view_type.h b/components/user_scripts/common/view_type.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/common/view_type.h
|
|
|
|
+@@ -0,0 +1,48 @@
|
|
|
|
++// 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 USERSCRIPTS_COMMON_VIEW_TYPE_H_
|
|
|
|
++#define USERSCRIPTS_COMMON_VIEW_TYPE_H_
|
|
|
|
++
|
|
|
|
++#include <string>
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// Icky RTTI used by a few systems to distinguish the host type of a given
|
|
|
|
++// WebContents.
|
|
|
|
++//
|
|
|
|
++// Do not change or reuse the the entry values in this list as this is used in
|
|
|
|
++// ExtensionViewType enum in tools/metrics/histograms/enums.xml.
|
|
|
|
++//
|
|
|
|
++// TODO(aa): Remove this and teach those systems to keep track of their own
|
|
|
|
++// data.
|
|
|
|
++enum ViewType {
|
|
|
|
++ VIEW_TYPE_INVALID = 0,
|
|
|
|
++ // VIEW_TYPE_APP_WINDOW = 1,
|
|
|
|
++ // VIEW_TYPE_BACKGROUND_CONTENTS = 2,
|
|
|
|
++
|
|
|
|
++ // // For custom parts of Chrome if no other type applies.
|
|
|
|
++ // VIEW_TYPE_COMPONENT = 3,
|
|
|
|
++
|
|
|
|
++ // VIEW_TYPE_EXTENSION_BACKGROUND_PAGE = 4,
|
|
|
|
++ // VIEW_TYPE_EXTENSION_DIALOG = 5,
|
|
|
|
++ // VIEW_TYPE_EXTENSION_GUEST = 6,
|
|
|
|
++ // VIEW_TYPE_EXTENSION_POPUP = 7,
|
|
|
|
++
|
|
|
|
++ // Panels were removed in https://crbug.com/571511.
|
|
|
|
++ // DEPRECATED_VIEW_TYPE_PANEL = 8,
|
|
|
|
++
|
|
|
|
++ VIEW_TYPE_TAB_CONTENTS = 9,
|
|
|
|
++
|
|
|
|
++ VIEW_TYPE_LAST = VIEW_TYPE_TAB_CONTENTS
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++// Matches the |view_type| to the corresponding ViewType, and populates
|
|
|
|
++// |view_type_out|. Returns true if a match is found.
|
|
|
|
++bool GetViewTypeFromString(const std::string& view_type,
|
|
|
|
++ ViewType* view_type_out);
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_COMMON_VIEW_TYPE_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/BUILD.gn b/components/user_scripts/renderer/BUILD.gn
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/BUILD.gn
|
|
|
|
+@@ -0,0 +1,67 @@
|
|
|
|
++# 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("//tools/grit/grit_rule.gni")
|
|
|
|
++import("//tools/grit/repack.gni")
|
|
|
|
++
|
|
|
|
++group("user_scripts_resources") {
|
|
|
|
++ public_deps = [
|
|
|
|
++ ":user_scripts_renderer_resources",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++grit("user_scripts_renderer_resources") {
|
|
|
|
++ source = "resources/user_scripts_renderer_resources.grd"
|
|
|
|
++ outputs = [
|
|
|
|
++ "grit/user_scripts_renderer_resources.h",
|
|
|
|
++ "user_scripts_renderer_resources.pak",
|
|
|
|
++ ]
|
|
|
|
++ grit_flags = [
|
|
|
|
++ "-E",
|
|
|
|
++ "mojom_root=" + rebase_path(root_gen_dir, root_build_dir),
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static_library("renderer") {
|
|
|
|
++ sources = [
|
|
|
|
++ "extension_frame_helper.cc",
|
|
|
|
++ "extension_frame_helper.h",
|
|
|
|
++ "injection_host.cc",
|
|
|
|
++ "injection_host.h",
|
|
|
|
++ "script_injection_manager.cc",
|
|
|
|
++ "script_injection_manager.h",
|
|
|
|
++ "script_injection_callback.cc",
|
|
|
|
++ "script_injection_callback.h",
|
|
|
|
++ "script_injection.cc",
|
|
|
|
++ "script_injection.h",
|
|
|
|
++ "script_injector.h",
|
|
|
|
++ "script_context.cc",
|
|
|
|
++ "script_context.h",
|
|
|
|
++ "scripts_run_info.cc",
|
|
|
|
++ "scripts_run_info.h",
|
|
|
|
++ "user_script_injector.cc",
|
|
|
|
++ "user_script_injector.h",
|
|
|
|
++ "user_script_set_manager.cc",
|
|
|
|
++ "user_script_set_manager.h",
|
|
|
|
++ "user_script_set.cc",
|
|
|
|
++ "user_script_set.h",
|
|
|
|
++ "user_scripts_dispatcher.cc",
|
|
|
|
++ "user_scripts_dispatcher.h",
|
|
|
|
++ "user_scripts_renderer_client.cc",
|
|
|
|
++ "user_scripts_renderer_client.h",
|
|
|
|
++ "web_ui_injection_host.cc",
|
|
|
|
++ "web_ui_injection_host.h",
|
|
|
|
++ ]
|
|
|
|
++
|
|
|
|
++ deps = [
|
|
|
|
++ ":user_scripts_resources",
|
|
|
|
++ "//base",
|
|
|
|
++ "//content/public/common",
|
|
|
|
++ "//content/public/renderer",
|
|
|
|
++ "//components/user_scripts/common",
|
|
|
|
++ "//mojo/public/cpp/bindings",
|
|
|
|
++ "//third_party/blink/public:blink_headers",
|
|
|
|
++ "//v8",
|
|
|
|
++ ]
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/renderer/extension_frame_helper.cc b/components/user_scripts/renderer/extension_frame_helper.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/extension_frame_helper.cc
|
|
|
|
+@@ -0,0 +1,96 @@
|
|
|
|
++// 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 "extension_frame_helper.h"
|
|
|
|
++
|
|
|
|
++#include <set>
|
|
|
|
++
|
|
|
|
++#include "base/metrics/histogram_macros.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "base/timer/elapsed_timer.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_view.h"
|
|
|
|
++#include "../common/constants.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_security_origin.h"
|
|
|
|
++#include "third_party/blink/public/web/web_console_message.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document_loader.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++#include "third_party/blink/public/web/web_settings.h"
|
|
|
|
++#include "third_party/blink/public/web/web_view.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++base::LazyInstance<std::set<const ExtensionFrameHelper*>>::DestructorAtExit
|
|
|
|
++ g_frame_helpers = LAZY_INSTANCE_INITIALIZER;
|
|
|
|
++
|
|
|
|
++// Runs every callback in |callbacks_to_be_run_and_cleared| while |frame_helper|
|
|
|
|
++// is valid, and clears |callbacks_to_be_run_and_cleared|.
|
|
|
|
++void RunCallbacksWhileFrameIsValid(
|
|
|
|
++ base::WeakPtr<ExtensionFrameHelper> frame_helper,
|
|
|
|
++ std::vector<base::OnceClosure>* callbacks_to_be_run_and_cleared) {
|
|
|
|
++ // The JavaScript code can cause re-entrancy. To avoid a deadlock, don't run
|
|
|
|
++ // callbacks that are added during the iteration.
|
|
|
|
++ std::vector<base::OnceClosure> callbacks;
|
|
|
|
++ callbacks_to_be_run_and_cleared->swap(callbacks);
|
|
|
|
++ for (auto& callback : callbacks) {
|
|
|
|
++ std::move(callback).Run();
|
|
|
|
++ if (!frame_helper.get())
|
|
|
|
++ return; // Frame and ExtensionFrameHelper invalidated by callback.
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++ExtensionFrameHelper::ExtensionFrameHelper(content::RenderFrame* render_frame)
|
|
|
|
++ : content::RenderFrameObserver(render_frame),
|
|
|
|
++ content::RenderFrameObserverTracker<ExtensionFrameHelper>(render_frame),
|
|
|
|
++ tab_id_(-1) {
|
|
|
|
++ g_frame_helpers.Get().insert(this);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ExtensionFrameHelper::~ExtensionFrameHelper() {
|
|
|
|
++ g_frame_helpers.Get().erase(this);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::ScheduleAtDocumentStart(
|
|
|
|
++ base::OnceClosure callback) {
|
|
|
|
++ document_element_created_callbacks_.push_back(std::move(callback));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::ScheduleAtDocumentEnd(
|
|
|
|
++ base::OnceClosure callback) {
|
|
|
|
++ document_load_finished_callbacks_.push_back(std::move(callback));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::ScheduleAtDocumentIdle(
|
|
|
|
++ base::OnceClosure callback) {
|
|
|
|
++ document_idle_callbacks_.push_back(std::move(callback));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::RunScriptsAtDocumentStart() {
|
|
|
|
++ RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
|
|
|
|
++ &document_element_created_callbacks_);
|
|
|
|
++ // |this| might be dead by now.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::RunScriptsAtDocumentEnd() {
|
|
|
|
++ RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
|
|
|
|
++ &document_load_finished_callbacks_);
|
|
|
|
++ // |this| might be dead by now.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::RunScriptsAtDocumentIdle() {
|
|
|
|
++ RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
|
|
|
|
++ &document_idle_callbacks_);
|
|
|
|
++ // |this| might be dead by now.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ExtensionFrameHelper::OnDestruct() {
|
|
|
|
++ delete this;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace user_scripts
|
|
|
|
+diff --git a/components/user_scripts/renderer/extension_frame_helper.h b/components/user_scripts/renderer/extension_frame_helper.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/extension_frame_helper.h
|
|
|
|
+@@ -0,0 +1,92 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_EXTENSION_FRAME_HELPER_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_EXTENSION_FRAME_HELPER_H_
|
|
|
|
++
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/callback_forward.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/weak_ptr.h"
|
|
|
|
++#include "content/public/renderer/render_frame_observer.h"
|
|
|
|
++#include "content/public/renderer/render_frame_observer_tracker.h"
|
|
|
|
++#include "../common/view_type.h"
|
|
|
|
++#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
|
|
|
|
++#include "v8/include/v8.h"
|
|
|
|
++
|
|
|
|
++struct ExtensionMsg_ExternalConnectionInfo;
|
|
|
|
++struct ExtensionMsg_TabConnectionInfo;
|
|
|
|
++
|
|
|
|
++namespace base {
|
|
|
|
++class ListValue;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class Dispatcher;
|
|
|
|
++struct Message;
|
|
|
|
++struct PortId;
|
|
|
|
++class ScriptContext;
|
|
|
|
++
|
|
|
|
++// RenderFrame-level plumbing for extension features.
|
|
|
|
++class ExtensionFrameHelper
|
|
|
|
++ : public content::RenderFrameObserver,
|
|
|
|
++ public content::RenderFrameObserverTracker<ExtensionFrameHelper> {
|
|
|
|
++ public:
|
|
|
|
++ ExtensionFrameHelper(content::RenderFrame* render_frame /*,
|
|
|
|
++ Dispatcher* extension_dispatcher*/);
|
|
|
|
++ ~ExtensionFrameHelper() override;
|
|
|
|
++
|
|
|
|
++ int tab_id() const { return tab_id_; }
|
|
|
|
++
|
|
|
|
++ // Called when the document element has been inserted in this frame. This
|
|
|
|
++ // method may invoke untrusted JavaScript code that invalidate the frame and
|
|
|
|
++ // this ExtensionFrameHelper.
|
|
|
|
++ void RunScriptsAtDocumentStart();
|
|
|
|
++
|
|
|
|
++ // Called after the DOMContentLoaded event has fired.
|
|
|
|
++ void RunScriptsAtDocumentEnd();
|
|
|
|
++
|
|
|
|
++ // Called before the window.onload event is fired.
|
|
|
|
++ void RunScriptsAtDocumentIdle();
|
|
|
|
++
|
|
|
|
++ // Schedule a callback, to be run at the next RunScriptsAtDocumentStart
|
|
|
|
++ // notification. Only call this when you are certain that there will be such a
|
|
|
|
++ // notification, e.g. from RenderFrameObserver::DidCreateDocumentElement.
|
|
|
|
++ // Otherwise the callback is never invoked, or invoked for a document that you
|
|
|
|
++ // were not expecting.
|
|
|
|
++ void ScheduleAtDocumentStart(base::OnceClosure callback);
|
|
|
|
++
|
|
|
|
++ // Schedule a callback, to be run at the next RunScriptsAtDocumentEnd call.
|
|
|
|
++ void ScheduleAtDocumentEnd(base::OnceClosure callback);
|
|
|
|
++
|
|
|
|
++ // Schedule a callback, to be run at the next RunScriptsAtDocumentIdle call.
|
|
|
|
++ void ScheduleAtDocumentIdle(base::OnceClosure callback);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++
|
|
|
|
++ void OnDestruct() override;
|
|
|
|
++
|
|
|
|
++ // The id of the tab the render frame is attached to.
|
|
|
|
++ int tab_id_;
|
|
|
|
++
|
|
|
|
++ // Callbacks to be run at the next RunScriptsAtDocumentStart notification.
|
|
|
|
++ std::vector<base::OnceClosure> document_element_created_callbacks_;
|
|
|
|
++
|
|
|
|
++ // Callbacks to be run at the next RunScriptsAtDocumentEnd notification.
|
|
|
|
++ std::vector<base::OnceClosure> document_load_finished_callbacks_;
|
|
|
|
++
|
|
|
|
++ // Callbacks to be run at the next RunScriptsAtDocumentIdle notification.
|
|
|
|
++ std::vector<base::OnceClosure> document_idle_callbacks_;
|
|
|
|
++
|
|
|
|
++ base::WeakPtrFactory<ExtensionFrameHelper> weak_ptr_factory_{this};
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(ExtensionFrameHelper);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_EXTENSION_FRAME_HELPER_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/injection_host.cc b/components/user_scripts/renderer/injection_host.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/injection_host.cc
|
|
|
|
+@@ -0,0 +1,12 @@
|
|
|
|
++// 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 "injection_host.h"
|
|
|
|
++
|
|
|
|
++InjectionHost::InjectionHost(const HostID& host_id) :
|
|
|
|
++ id_(host_id) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++InjectionHost::~InjectionHost() {
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/renderer/injection_host.h b/components/user_scripts/renderer/injection_host.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/injection_host.h
|
|
|
|
+@@ -0,0 +1,42 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_INJECTION_HOST_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_INJECTION_HOST_H_
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++
|
|
|
|
++namespace content {
|
|
|
|
++class RenderFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// An interface for all kinds of hosts who own user scripts.
|
|
|
|
++class InjectionHost {
|
|
|
|
++ public:
|
|
|
|
++ InjectionHost(const HostID& host_id);
|
|
|
|
++ virtual ~InjectionHost();
|
|
|
|
++
|
|
|
|
++ // Returns the CSP to be used for the isolated world. Currently this only
|
|
|
|
++ // bypasses the main world CSP. If null is returned, the main world CSP is not
|
|
|
|
++ // bypassed.
|
|
|
|
++ virtual const std::string* GetContentSecurityPolicy() const = 0;
|
|
|
|
++
|
|
|
|
++ // The base url for the host.
|
|
|
|
++ virtual const GURL& url() const = 0;
|
|
|
|
++
|
|
|
|
++ // The human-readable name of the host.
|
|
|
|
++ virtual const std::string& name() const = 0;
|
|
|
|
++
|
|
|
|
++ const HostID& id() const { return id_; }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // The ID of the host.
|
|
|
|
++ HostID id_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(InjectionHost);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_INJECTION_HOST_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/resources/greasemonkey_api.js b/components/user_scripts/renderer/resources/greasemonkey_api.js
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/resources/greasemonkey_api.js
|
|
|
|
+@@ -0,0 +1,82 @@
|
|
|
|
++// 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.
|
|
|
|
++
|
|
|
|
++// -----------------------------------------------------------------------------
|
|
|
|
++// NOTE: If you change this file you need to touch renderer_resources.grd to
|
|
|
|
++// have your change take effect.
|
|
|
|
++// -----------------------------------------------------------------------------
|
|
|
|
++
|
|
|
|
++// Partial implementation of the Greasemonkey API, see:
|
|
|
|
++// http://wiki.greasespot.net/Greasemonkey_Manual:APIs
|
|
|
|
++
|
|
|
|
++function GM_addStyle(css) {
|
|
|
|
++ var parent = document.getElementsByTagName("head")[0];
|
|
|
|
++ if (!parent) {
|
|
|
|
++ parent = document.documentElement;
|
|
|
|
++ }
|
|
|
|
++ var style = document.createElement("style");
|
|
|
|
++ style.type = "text/css";
|
|
|
|
++ var textNode = document.createTextNode(css);
|
|
|
|
++ style.appendChild(textNode);
|
|
|
|
++ parent.appendChild(style);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++function GM_xmlhttpRequest(details) {
|
|
|
|
++ function setupEvent(xhr, url, eventName, callback) {
|
|
|
|
++ xhr[eventName] = function () {
|
|
|
|
++ var isComplete = xhr.readyState == 4;
|
|
|
|
++ var responseState = {
|
|
|
|
++ responseText: xhr.responseText,
|
|
|
|
++ readyState: xhr.readyState,
|
|
|
|
++ responseHeaders: isComplete ? xhr.getAllResponseHeaders() : "",
|
|
|
|
++ status: isComplete ? xhr.status : 0,
|
|
|
|
++ statusText: isComplete ? xhr.statusText : "",
|
|
|
|
++ finalUrl: isComplete ? url : ""
|
|
|
|
++ };
|
|
|
|
++ callback(responseState);
|
|
|
|
++ };
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ var xhr = new XMLHttpRequest();
|
|
|
|
++ var eventNames = ["onload", "onerror", "onreadystatechange"];
|
|
|
|
++ for (var i = 0; i < eventNames.length; i++ ) {
|
|
|
|
++ var eventName = eventNames[i];
|
|
|
|
++ if (eventName in details) {
|
|
|
|
++ setupEvent(xhr, details.url, eventName, details[eventName]);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ xhr.open(details.method, details.url);
|
|
|
|
++
|
|
|
|
++ if (details.overrideMimeType) {
|
|
|
|
++ xhr.overrideMimeType(details.overrideMimeType);
|
|
|
|
++ }
|
|
|
|
++ if (details.headers) {
|
|
|
|
++ for (var header in details.headers) {
|
|
|
|
++ xhr.setRequestHeader(header, details.headers[header]);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ xhr.send(details.data ? details.data : null);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++function GM_openInTab(url) {
|
|
|
|
++ window.open(url, "");
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++function GM_log(message) {
|
|
|
|
++ window.console.log(message);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++(function() {
|
|
|
|
++ function generateGreasemonkeyStub(name) {
|
|
|
|
++ return function() {
|
|
|
|
++ console.log("%s is not supported.", name);
|
|
|
|
++ };
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ var apis = ["GM_getValue", "GM_setValue", "GM_registerMenuCommand"];
|
|
|
|
++ for (var i = 0, api; api = apis[i]; i++) {
|
|
|
|
++ window[api] = generateGreasemonkeyStub(api);
|
|
|
|
++ }
|
|
|
|
++})();
|
|
|
|
+diff --git a/components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd b/components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd
|
|
|
|
+@@ -0,0 +1,14 @@
|
|
|
|
++<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
++<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
|
|
|
|
++ <outputs>
|
|
|
|
++ <output filename="grit/user_scripts_renderer_resources.h" type="rc_header">
|
|
|
|
++ <emit emit_type='prepend'></emit>
|
|
|
|
++ </output>
|
|
|
|
++ <output filename="user_scripts_renderer_resources.pak" type="data_package" />
|
|
|
|
++ </outputs>
|
|
|
|
++ <release seq="1">
|
|
|
|
++ <includes>
|
|
|
|
++ <include name="IDR_GREASEMONKEY_API_JS" file="greasemonkey_api.js" type="BINDATA" />
|
|
|
|
++ </includes>
|
|
|
|
++ </release>
|
|
|
|
++</grit>
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_context.cc b/components/user_scripts/renderer/script_context.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_context.cc
|
|
|
|
+@@ -0,0 +1,215 @@
|
|
|
|
++// 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 "script_context.h"
|
|
|
|
++
|
|
|
|
++#include "base/command_line.h"
|
|
|
|
++#include "base/containers/flat_set.h"
|
|
|
|
++#include "base/containers/contains.h"
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "base/no_destructor.h"
|
|
|
|
++#include "base/stl_util.h"
|
|
|
|
++#include "base/strings/string_util.h"
|
|
|
|
++#include "base/strings/stringprintf.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "content/public/common/content_switches.h"
|
|
|
|
++#include "content/public/common/url_constants.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "../common/constants.h"
|
|
|
|
++#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_security_origin.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document_loader.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++#include "third_party/blink/public/web/web_navigation_params.h"
|
|
|
|
++#include "v8/include/v8.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++GURL GetEffectiveDocumentURL(
|
|
|
|
++ blink::WebLocalFrame* frame,
|
|
|
|
++ const GURL& document_url,
|
|
|
|
++ MatchOriginAsFallbackBehavior match_origin_as_fallback,
|
|
|
|
++ bool allow_inaccessible_parents) {
|
|
|
|
++ auto should_consider_origin = [document_url, match_origin_as_fallback]() {
|
|
|
|
++ switch (match_origin_as_fallback) {
|
|
|
|
++ case MatchOriginAsFallbackBehavior::kNever:
|
|
|
|
++ return false;
|
|
|
|
++ case MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree:
|
|
|
|
++ return document_url.SchemeIs(url::kAboutScheme);
|
|
|
|
++ case MatchOriginAsFallbackBehavior::kAlways:
|
|
|
|
++ // TODO(devlin): Add more schemes here - blob, filesystem, etc.
|
|
|
|
++ return document_url.SchemeIs(url::kAboutScheme) ||
|
|
|
|
++ document_url.SchemeIs(url::kDataScheme);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ NOTREACHED();
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ // If we don't need to consider the origin, we're done.
|
|
|
|
++ if (!should_consider_origin())
|
|
|
|
++ return document_url;
|
|
|
|
++
|
|
|
|
++ // Get the "security origin" for the frame. For about: frames, this is the
|
|
|
|
++ // origin of that of the controlling frame - e.g., an about:blank frame on
|
|
|
|
++ // https://example.com will have the security origin of https://example.com.
|
|
|
|
++ // Other frames, like data: frames, will have an opaque origin. For these,
|
|
|
|
++ // we can get the precursor origin.
|
|
|
|
++ const blink::WebSecurityOrigin web_frame_origin = frame->GetSecurityOrigin();
|
|
|
|
++ const url::Origin frame_origin = web_frame_origin;
|
|
|
|
++ const url::SchemeHostPort& tuple_or_precursor_tuple =
|
|
|
|
++ frame_origin.GetTupleOrPrecursorTupleIfOpaque();
|
|
|
|
++
|
|
|
|
++ // When there's no valid tuple (which can happen in the case of e.g. a
|
|
|
|
++ // browser-initiated navigation to an opaque URL), there's no origin to
|
|
|
|
++ // fallback to. Bail.
|
|
|
|
++ if (!tuple_or_precursor_tuple.IsValid())
|
|
|
|
++ return document_url;
|
|
|
|
++
|
|
|
|
++ const url::Origin origin_or_precursor_origin =
|
|
|
|
++ url::Origin::Create(tuple_or_precursor_tuple.GetURL());
|
|
|
|
++
|
|
|
|
++ if (!allow_inaccessible_parents &&
|
|
|
|
++ !web_frame_origin.CanAccess(
|
|
|
|
++ blink::WebSecurityOrigin(origin_or_precursor_origin))) {
|
|
|
|
++ // The frame can't access its precursor. Bail.
|
|
|
|
++ return document_url;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Looks like the initiator origin is an appropriate fallback!
|
|
|
|
++
|
|
|
|
++ if (match_origin_as_fallback == MatchOriginAsFallbackBehavior::kAlways) {
|
|
|
|
++ // The easy case! We use the origin directly. We're done.
|
|
|
|
++ return origin_or_precursor_origin.GetURL();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ DCHECK_EQ(MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree,
|
|
|
|
++ match_origin_as_fallback);
|
|
|
|
++
|
|
|
|
++ // Unfortunately, in this case, we have to climb the frame tree. This is for
|
|
|
|
++ // match patterns that are associated with paths as well, not just origins.
|
|
|
|
++ // For instance, if an extension wants to run on google.com/maps/* with
|
|
|
|
++ // match_about_blank true, then it should run on about:-scheme frames created
|
|
|
|
++ // by google.com/maps, but not about:-scheme frames created by google.com
|
|
|
|
++ // (which is what the precursor tuple origin would be).
|
|
|
|
++
|
|
|
|
++ // Traverse the frame/window hierarchy to find the closest non-about:-page
|
|
|
|
++ // with the same origin as the precursor and return its URL.
|
|
|
|
++ // Note: This can return the incorrect result, e.g. if a parent frame
|
|
|
|
++ // navigates a grandchild frame.
|
|
|
|
++ blink::WebFrame* parent = frame;
|
|
|
|
++ GURL parent_url;
|
|
|
|
++ blink::WebDocument parent_document;
|
|
|
|
++ base::flat_set<blink::WebFrame*> already_visited_frames;
|
|
|
|
++ do {
|
|
|
|
++ already_visited_frames.insert(parent);
|
|
|
|
++ if (parent->Parent())
|
|
|
|
++ parent = parent->Parent();
|
|
|
|
++ else
|
|
|
|
++ parent = parent->Opener();
|
|
|
|
++
|
|
|
|
++ // Avoid an infinite loop - see https://crbug.com/568432 and
|
|
|
|
++ // https://crbug.com/883526.
|
|
|
|
++ if (base::Contains(already_visited_frames, parent))
|
|
|
|
++ return document_url;
|
|
|
|
++
|
|
|
|
++ parent_document = parent && parent->IsWebLocalFrame()
|
|
|
|
++ ? parent->ToWebLocalFrame()->GetDocument()
|
|
|
|
++ : blink::WebDocument();
|
|
|
|
++
|
|
|
|
++ // We reached the end of the ancestral chain without finding a valid parent,
|
|
|
|
++ // or found a remote web frame (in which case, it's a different origin).
|
|
|
|
++ // Bail and use the original URL.
|
|
|
|
++ if (parent_document.IsNull())
|
|
|
|
++ return document_url;
|
|
|
|
++
|
|
|
|
++ url::SchemeHostPort parent_tuple_or_precursor_tuple =
|
|
|
|
++ url::Origin(parent->GetSecurityOrigin())
|
|
|
|
++ .GetTupleOrPrecursorTupleIfOpaque();
|
|
|
|
++ if (!parent_tuple_or_precursor_tuple.IsValid() ||
|
|
|
|
++ parent_tuple_or_precursor_tuple != tuple_or_precursor_tuple) {
|
|
|
|
++ // The parent has a different tuple origin than frame; this could happen
|
|
|
|
++ // in edge cases where a parent navigates an iframe or popup of a child
|
|
|
|
++ // frame at a different origin. [1] In this case, bail, since we can't
|
|
|
|
++ // find a full URL (i.e., one including the path) with the same security
|
|
|
|
++ // origin to use for the frame in question.
|
|
|
|
++ // [1] Consider a frame tree like:
|
|
|
|
++ // <html> <!--example.com-->
|
|
|
|
++ // <iframe id="a" src="a.com">
|
|
|
|
++ // <iframe id="b" src="b.com"></iframe>
|
|
|
|
++ // </iframe>
|
|
|
|
++ // </html>
|
|
|
|
++ // Frame "a" is cross-origin from the top-level frame, and so the
|
|
|
|
++ // example.com top-level frame can't directly access frame "b". However,
|
|
|
|
++ // it can navigate it through
|
|
|
|
++ // window.frames[0].frames[0].location.href = 'about:blank';
|
|
|
|
++ // In that case, the precursor origin tuple origin of frame "b" would be
|
|
|
|
++ // example.com, but the parent tuple origin is a.com.
|
|
|
|
++ // Note that usually, this would have bailed earlier with a remote frame,
|
|
|
|
++ // but it may not if we're at the process limit.
|
|
|
|
++ return document_url;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ parent_url = GURL(parent_document.Url());
|
|
|
|
++ } while (parent_url.SchemeIs(url::kAboutScheme));
|
|
|
|
++
|
|
|
|
++ DCHECK(!parent_url.is_empty());
|
|
|
|
++ DCHECK(!parent_document.IsNull());
|
|
|
|
++
|
|
|
|
++ // We should know that the frame can access the parent document (unless we
|
|
|
|
++ // explicitly allow it not to), since it has the same tuple origin as the
|
|
|
|
++ // frame, and we checked the frame access above.
|
|
|
|
++ DCHECK(allow_inaccessible_parents ||
|
|
|
|
++ web_frame_origin.CanAccess(parent_document.GetSecurityOrigin()));
|
|
|
|
++ return parent_url;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++using FrameToDocumentLoader =
|
|
|
|
++ base::flat_map<blink::WebLocalFrame*, blink::WebDocumentLoader*>;
|
|
|
|
++
|
|
|
|
++FrameToDocumentLoader& FrameDocumentLoaderMap() {
|
|
|
|
++ static base::NoDestructor<FrameToDocumentLoader> map;
|
|
|
|
++ return *map;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++blink::WebDocumentLoader* CurrentDocumentLoader(
|
|
|
|
++ const blink::WebLocalFrame* frame) {
|
|
|
|
++ auto& map = FrameDocumentLoaderMap();
|
|
|
|
++ auto it = map.find(frame);
|
|
|
|
++ return it == map.end() ? frame->GetDocumentLoader() : it->second;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++GURL ScriptContext::GetDocumentLoaderURLForFrame(
|
|
|
|
++ const blink::WebLocalFrame* frame) {
|
|
|
|
++ // Normally we would use frame->document().url() to determine the document's
|
|
|
|
++ // URL, but to decide whether to inject a content script, we use the URL from
|
|
|
|
++ // the data source. This "quirk" helps prevents content scripts from
|
|
|
|
++ // inadvertently adding DOM elements to the compose iframe in Gmail because
|
|
|
|
++ // the compose iframe's dataSource URL is about:blank, but the document URL
|
|
|
|
++ // changes to match the parent document after Gmail document.writes into
|
|
|
|
++ // it to create the editor.
|
|
|
|
++ // http://code.google.com/p/chromium/issues/detail?id=86742
|
|
|
|
++ blink::WebDocumentLoader* document_loader = CurrentDocumentLoader(frame);
|
|
|
|
++ return document_loader ? GURL(document_loader->GetUrl()) : GURL();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++GURL ScriptContext::GetEffectiveDocumentURLForInjection(
|
|
|
|
++ blink::WebLocalFrame* frame,
|
|
|
|
++ const GURL& document_url,
|
|
|
|
++ MatchOriginAsFallbackBehavior match_origin_as_fallback) {
|
|
|
|
++ // We explicitly allow inaccessible parents here. Extensions should still be
|
|
|
|
++ // able to inject into a sandboxed iframe if it has access to the embedding
|
|
|
|
++ // origin.
|
|
|
|
++ constexpr bool allow_inaccessible_parents = true;
|
|
|
|
++ return GetEffectiveDocumentURL(frame, document_url, match_origin_as_fallback,
|
|
|
|
++ allow_inaccessible_parents);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_context.h b/components/user_scripts/renderer/script_context.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_context.h
|
|
|
|
+@@ -0,0 +1,70 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_SCRIPT_CONTEXT_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_SCRIPT_CONTEXT_H_
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <string>
|
|
|
|
++#include <utility>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/callback.h"
|
|
|
|
++#include "base/compiler_specific.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/threading/thread_checker.h"
|
|
|
|
++#include "base/unguessable_token.h"
|
|
|
|
++#include "../common/script_constants.h"
|
|
|
|
++#include "script_injection_callback.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++#include "v8/include/v8.h"
|
|
|
|
++
|
|
|
|
++namespace blink {
|
|
|
|
++class WebDocumentLoader;
|
|
|
|
++class WebLocalFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace content {
|
|
|
|
++class RenderFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// Extensions wrapper for a v8::Context.
|
|
|
|
++//
|
|
|
|
++// v8::Contexts can be constructed on any thread, and must only be accessed or
|
|
|
|
++// destroyed that thread.
|
|
|
|
++//
|
|
|
|
++// Note that ScriptContexts bound to worker threads will not have the full
|
|
|
|
++// functionality as those bound to the main RenderThread.
|
|
|
|
++class ScriptContext {
|
|
|
|
++ public:
|
|
|
|
++ // TODO(devlin): Move all these Get*URL*() methods out of here? While they are
|
|
|
|
++ // vaguely ScriptContext related, there's enough here that they probably
|
|
|
|
++ // warrant another class or utility file.
|
|
|
|
++
|
|
|
|
++ // Utility to get the URL we will match against for a frame. If the frame has
|
|
|
|
++ // committed, this is the commited URL. Otherwise it is the provisional URL.
|
|
|
|
++ // The returned URL may be invalid.
|
|
|
|
++ static GURL GetDocumentLoaderURLForFrame(const blink::WebLocalFrame* frame);
|
|
|
|
++
|
|
|
|
++ // Used to determine the "effective" URL for extension script injection.
|
|
|
|
++ // If |document_url| is an about: or data: URL, returns the URL of the first
|
|
|
|
++ // frame without an about: or data: URL that matches the initiator origin.
|
|
|
|
++ // This may not be the immediate parent. Returns |document_url| if it is not
|
|
|
|
++ // an about: or data: URL, if |match_origin_as_fallback| is set to not match,
|
|
|
|
++ // or if a suitable parent cannot be found.
|
|
|
|
++ // Considers parent contexts that cannot be accessed (as is the case for
|
|
|
|
++ // sandboxed frames).
|
|
|
|
++ static GURL GetEffectiveDocumentURLForInjection(
|
|
|
|
++ blink::WebLocalFrame* frame,
|
|
|
|
++ const GURL& document_url,
|
|
|
|
++ MatchOriginAsFallbackBehavior match_origin_as_fallback);
|
|
|
|
++
|
|
|
|
++// DISALLOW_COPY_AND_ASSIGN(ScriptContext);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_SCRIPT_CONTEXT_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injection.cc b/components/user_scripts/renderer/script_injection.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injection.cc
|
|
|
|
+@@ -0,0 +1,343 @@
|
|
|
|
++// 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 "script_injection.h"
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "base/bind.h"
|
|
|
|
++#include "base/feature_list.h"
|
|
|
|
++#include "base/lazy_instance.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/metrics/histogram_macros.h"
|
|
|
|
++#include "base/timer/elapsed_timer.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_frame_observer.h"
|
|
|
|
++#include "content/public/renderer/v8_value_converter.h"
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "script_injection_callback.h"
|
|
|
|
++#include "scripts_run_info.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_isolated_world_info.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_security_origin.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_string.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++#include "third_party/blink/public/web/web_script_source.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++using IsolatedWorldMap = std::map<std::string, int>;
|
|
|
|
++base::LazyInstance<IsolatedWorldMap>::DestructorAtExit g_isolated_worlds =
|
|
|
|
++ LAZY_INSTANCE_INITIALIZER;
|
|
|
|
++
|
|
|
|
++const int64_t kInvalidRequestId = -1;
|
|
|
|
++
|
|
|
|
++// Gets the isolated world ID to use for the given |injection_host|. If no
|
|
|
|
++// isolated world has been created for that |injection_host| one will be created
|
|
|
|
++// and initialized.
|
|
|
|
++int GetIsolatedWorldIdForInstance(const InjectionHost* injection_host) {
|
|
|
|
++ static int g_next_isolated_world_id = 1; // Embedder isolated worlds can use IDs in [1, 1<<29).
|
|
|
|
++
|
|
|
|
++ IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
|
|
|
|
++
|
|
|
|
++ int id = 0;
|
|
|
|
++ const std::string& key = injection_host->id().id();
|
|
|
|
++ auto iter = isolated_worlds.find(key);
|
|
|
|
++ if (iter != isolated_worlds.end()) {
|
|
|
|
++ id = iter->second;
|
|
|
|
++ } else {
|
|
|
|
++ id = g_next_isolated_world_id++;
|
|
|
|
++ // This map will tend to pile up over time, but realistically, you're never
|
|
|
|
++ // going to have enough injection hosts for it to matter.
|
|
|
|
++ isolated_worlds[key] = id;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ blink::WebIsolatedWorldInfo info;
|
|
|
|
++ info.security_origin =
|
|
|
|
++ blink::WebSecurityOrigin::Create(injection_host->url());
|
|
|
|
++ info.human_readable_name = blink::WebString::FromUTF8(injection_host->name());
|
|
|
|
++ info.stable_id = blink::WebString::FromUTF8(key);
|
|
|
|
++
|
|
|
|
++ const std::string* csp = injection_host->GetContentSecurityPolicy();
|
|
|
|
++ if (csp)
|
|
|
|
++ info.content_security_policy = blink::WebString::FromUTF8(*csp);
|
|
|
|
++
|
|
|
|
++ // Even though there may be an existing world for this |injection_host|'s key,
|
|
|
|
++ // the properties may have changed (e.g. due to an extension update).
|
|
|
|
++ // Overwrite any existing entries.
|
|
|
|
++ blink::SetIsolatedWorldInfo(id, info);
|
|
|
|
++
|
|
|
|
++ return id;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// This class manages its own lifetime.
|
|
|
|
++class TimedScriptInjectionCallback : public ScriptInjectionCallback {
|
|
|
|
++ public:
|
|
|
|
++ TimedScriptInjectionCallback(base::WeakPtr<ScriptInjection> injection)
|
|
|
|
++ : ScriptInjectionCallback(
|
|
|
|
++ base::BindOnce(&TimedScriptInjectionCallback::OnCompleted,
|
|
|
|
++ base::Unretained(this))),
|
|
|
|
++ injection_(injection) {}
|
|
|
|
++ ~TimedScriptInjectionCallback() override {}
|
|
|
|
++
|
|
|
|
++ void OnCompleted(const std::vector<v8::Local<v8::Value>>& result) {
|
|
|
|
++ if (injection_) {
|
|
|
|
++ base::TimeTicks timestamp(base::TimeTicks::Now());
|
|
|
|
++ absl::optional<base::TimeDelta> elapsed;
|
|
|
|
++ // If the script will never execute (such as if the context is destroyed),
|
|
|
|
++ // willExecute() will not be called, but OnCompleted() will. Only log a
|
|
|
|
++ // time for execution if the script, in fact, executed.
|
|
|
|
++ if (!start_time_.is_null())
|
|
|
|
++ elapsed = timestamp - start_time_;
|
|
|
|
++ injection_->OnJsInjectionCompleted(result, elapsed);
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ void WillExecute() override {
|
|
|
|
++ start_time_ = base::TimeTicks::Now();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ base::WeakPtr<ScriptInjection> injection_;
|
|
|
|
++ base::TimeTicks start_time_;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++// Watches for the deletion of a RenderFrame, after which is_valid will return
|
|
|
|
++// false.
|
|
|
|
++class ScriptInjection::FrameWatcher : public content::RenderFrameObserver {
|
|
|
|
++ public:
|
|
|
|
++ FrameWatcher(content::RenderFrame* render_frame,
|
|
|
|
++ ScriptInjection* injection)
|
|
|
|
++ : content::RenderFrameObserver(render_frame),
|
|
|
|
++ injection_(injection) {}
|
|
|
|
++ ~FrameWatcher() override {}
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ void WillDetach() override { injection_->invalidate_render_frame(); }
|
|
|
|
++ void OnDestruct() override { injection_->invalidate_render_frame(); }
|
|
|
|
++
|
|
|
|
++ ScriptInjection* injection_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(FrameWatcher);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++std::string ScriptInjection::GetHostIdForIsolatedWorld(int isolated_world_id) {
|
|
|
|
++ const IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
|
|
|
|
++
|
|
|
|
++ for (const auto& iter : isolated_worlds) {
|
|
|
|
++ if (iter.second == isolated_world_id)
|
|
|
|
++ return iter.first;
|
|
|
|
++ }
|
|
|
|
++ return std::string();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++void ScriptInjection::RemoveIsolatedWorld(const std::string& host_id) {
|
|
|
|
++ g_isolated_worlds.Get().erase(host_id);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjection::ScriptInjection(
|
|
|
|
++ std::unique_ptr<ScriptInjector> injector,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ std::unique_ptr<const InjectionHost> injection_host,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ bool log_activity)
|
|
|
|
++ : injector_(std::move(injector)),
|
|
|
|
++ render_frame_(render_frame),
|
|
|
|
++ injection_host_(std::move(injection_host)),
|
|
|
|
++ run_location_(run_location),
|
|
|
|
++ request_id_(kInvalidRequestId),
|
|
|
|
++ complete_(false),
|
|
|
|
++ did_inject_js_(false),
|
|
|
|
++ log_activity_(log_activity),
|
|
|
|
++ frame_watcher_(new FrameWatcher(render_frame, this)) {
|
|
|
|
++ CHECK(injection_host_.get());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjection::~ScriptInjection() {
|
|
|
|
++ if (!complete_)
|
|
|
|
++ NotifyWillNotInject(ScriptInjector::WONT_INJECT);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjection::InjectionResult ScriptInjection::TryToInject(
|
|
|
|
++ UserScript::RunLocation current_location,
|
|
|
|
++ ScriptsRunInfo* scripts_run_info,
|
|
|
|
++ CompletionCallback async_completion_callback) {
|
|
|
|
++ if (current_location < run_location_)
|
|
|
|
++ return INJECTION_WAITING; // Wait for the right location.
|
|
|
|
++
|
|
|
|
++ if (request_id_ != kInvalidRequestId) {
|
|
|
|
++ // We're waiting for permission right now, try again later.
|
|
|
|
++ return INJECTION_WAITING;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!injection_host_) {
|
|
|
|
++ NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
|
|
|
|
++ return INJECTION_FINISHED; // We're done.
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ InjectionResult result = Inject(scripts_run_info);
|
|
|
|
++ // If the injection is blocked, we need to set the manager so we can
|
|
|
|
++ // notify it upon completion.
|
|
|
|
++ if (result == INJECTION_BLOCKED)
|
|
|
|
++ async_completion_callback_ = std::move(async_completion_callback);
|
|
|
|
++ return result;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjection::InjectionResult ScriptInjection::OnPermissionGranted(
|
|
|
|
++ ScriptsRunInfo* scripts_run_info) {
|
|
|
|
++ if (!injection_host_) {
|
|
|
|
++ NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
|
|
|
|
++ return INJECTION_FINISHED;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return Inject(scripts_run_info);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjection::OnHostRemoved() {
|
|
|
|
++ injection_host_.reset(nullptr);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjection::NotifyWillNotInject(
|
|
|
|
++ ScriptInjector::InjectFailureReason reason) {
|
|
|
|
++ complete_ = true;
|
|
|
|
++ injector_->OnWillNotInject(reason, render_frame_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjection::InjectionResult ScriptInjection::Inject(
|
|
|
|
++ ScriptsRunInfo* scripts_run_info) {
|
|
|
|
++ DCHECK(injection_host_);
|
|
|
|
++ //DCHECK(scripts_run_info);
|
|
|
|
++ DCHECK(!complete_);
|
|
|
|
++ bool should_inject_js = injector_->ShouldInjectJs(
|
|
|
|
++ run_location_, scripts_run_info->executing_scripts[host_id().id()]);
|
|
|
|
++ bool should_inject_css = injector_->ShouldInjectCss(
|
|
|
|
++ run_location_, scripts_run_info->injected_stylesheets[host_id().id()]);
|
|
|
|
++
|
|
|
|
++ // This can happen if the extension specified a script to
|
|
|
|
++ // be run in multiple rules, and the script has already run.
|
|
|
|
++ // See crbug.com/631247.
|
|
|
|
++ if (!should_inject_js && !should_inject_css) {
|
|
|
|
++ return INJECTION_FINISHED;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (should_inject_js)
|
|
|
|
++ InjectJs(&(scripts_run_info->executing_scripts[host_id().id()]),
|
|
|
|
++ &(scripts_run_info->num_js));
|
|
|
|
++ if (should_inject_css)
|
|
|
|
++ InjectCss(&(scripts_run_info->injected_stylesheets[host_id().id()]),
|
|
|
|
++ &(scripts_run_info->num_css));
|
|
|
|
++
|
|
|
|
++ complete_ = did_inject_js_ || !should_inject_js;
|
|
|
|
++
|
|
|
|
++ if (complete_) {
|
|
|
|
++ injector_->OnInjectionComplete(std::move(execution_result_), run_location_,
|
|
|
|
++ render_frame_);
|
|
|
|
++ } else {
|
|
|
|
++ ++scripts_run_info->num_blocking_js;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjection::InjectJs(std::set<std::string>* executing_scripts,
|
|
|
|
++ size_t* num_injected_js_scripts) {
|
|
|
|
++ DCHECK(!did_inject_js_);
|
|
|
|
++ std::vector<blink::WebScriptSource> sources = injector_->GetJsSources(
|
|
|
|
++ run_location_, executing_scripts, num_injected_js_scripts);
|
|
|
|
++ DCHECK(!sources.empty());
|
|
|
|
++ int world_id = GetIsolatedWorldIdForInstance(injection_host_.get());
|
|
|
|
++ bool is_user_gesture = injector_->IsUserGesture();
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<blink::WebScriptExecutionCallback> callback(
|
|
|
|
++ new TimedScriptInjectionCallback(weak_ptr_factory_.GetWeakPtr()));
|
|
|
|
++
|
|
|
|
++ base::ElapsedTimer exec_timer;
|
|
|
|
++
|
|
|
|
++ // For content scripts executing during page load, we run them asynchronously
|
|
|
|
++ // in order to reduce UI jank experienced by the user. (We don't do this for
|
|
|
|
++ // DOCUMENT_START scripts, because there's no UI to jank until after those
|
|
|
|
++ // run, so we run them as soon as we can.)
|
|
|
|
++ // Note: We could potentially also run deferred and browser-driven scripts
|
|
|
|
++ // asynchronously; however, these are rare enough that there probably isn't
|
|
|
|
++ // UI jank. If this changes, we can update this.
|
|
|
|
++ bool should_execute_asynchronously =
|
|
|
|
++ injector_->script_type() == UserScript::CONTENT_SCRIPT &&
|
|
|
|
++ (run_location_ == UserScript::DOCUMENT_END ||
|
|
|
|
++ run_location_ == UserScript::DOCUMENT_IDLE);
|
|
|
|
++ blink::WebLocalFrame::ScriptExecutionType execution_option =
|
|
|
|
++ should_execute_asynchronously
|
|
|
|
++ ? blink::WebLocalFrame::kAsynchronousBlockingOnload
|
|
|
|
++ : blink::WebLocalFrame::kSynchronous;
|
|
|
|
++
|
|
|
|
++ render_frame_->GetWebFrame()->RequestExecuteScript(
|
|
|
|
++ world_id, sources, is_user_gesture,
|
|
|
|
++ execution_option, callback.release(),
|
|
|
|
++ blink::BackForwardCacheAware::kPossiblyDisallow);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjection::OnJsInjectionCompleted(
|
|
|
|
++ const std::vector<v8::Local<v8::Value>>& results,
|
|
|
|
++ absl::optional<base::TimeDelta> elapsed) {
|
|
|
|
++ DCHECK(!did_inject_js_);
|
|
|
|
++
|
|
|
|
++ bool expects_results = injector_->ExpectsResults();
|
|
|
|
++ if (expects_results) {
|
|
|
|
++ if (!results.empty() && !results[0].IsEmpty()) {
|
|
|
|
++ // Right now, we only support returning single results (per frame).
|
|
|
|
++ // It's safe to always use the main world context when converting
|
|
|
|
++ // here. V8ValueConverterImpl shouldn't actually care about the
|
|
|
|
++ // context scope, and it switches to v8::Object's creation context
|
|
|
|
++ // when encountered.
|
|
|
|
++ v8::Local<v8::Context> context =
|
|
|
|
++ render_frame_->GetWebFrame()->MainWorldScriptContext();
|
|
|
|
++ execution_result_ =
|
|
|
|
++ content::V8ValueConverter::Create()->FromV8Value(results[0], context);
|
|
|
|
++ }
|
|
|
|
++ if (!execution_result_.get())
|
|
|
|
++ execution_result_ = std::make_unique<base::Value>();
|
|
|
|
++ }
|
|
|
|
++ did_inject_js_ = true;
|
|
|
|
++
|
|
|
|
++ // If |async_completion_callback_| is set, it means the script finished
|
|
|
|
++ // asynchronously, and we should run it.
|
|
|
|
++ if (!async_completion_callback_.is_null()) {
|
|
|
|
++ complete_ = true;
|
|
|
|
++ injector_->OnInjectionComplete(std::move(execution_result_), run_location_,
|
|
|
|
++ render_frame_);
|
|
|
|
++ // Warning: this object can be destroyed after this line!
|
|
|
|
++ std::move(async_completion_callback_).Run(this);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjection::InjectCss(std::set<std::string>* injected_stylesheets,
|
|
|
|
++ size_t* num_injected_stylesheets) {
|
|
|
|
++ std::vector<blink::WebString> css_sources = injector_->GetCssSources(
|
|
|
|
++ run_location_, injected_stylesheets, num_injected_stylesheets);
|
|
|
|
++ blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
|
|
|
|
++ // Default CSS origin is "author", but can be overridden to "user" by scripts.
|
|
|
|
++ absl::optional<CSSOrigin> css_origin = injector_->GetCssOrigin();
|
|
|
|
++ blink::WebDocument::CSSOrigin blink_css_origin =
|
|
|
|
++ css_origin && *css_origin == CSS_ORIGIN_USER
|
|
|
|
++ ? blink::WebDocument::kUserOrigin
|
|
|
|
++ : blink::WebDocument::kAuthorOrigin;
|
|
|
|
++ blink::WebStyleSheetKey style_sheet_key;
|
|
|
|
++ if (const absl::optional<std::string>& injection_key =
|
|
|
|
++ injector_->GetInjectionKey())
|
|
|
|
++ style_sheet_key = blink::WebString::FromASCII(*injection_key);
|
|
|
|
++ for (const blink::WebString& css : css_sources)
|
|
|
|
++ web_frame->GetDocument().InsertStyleSheet(css, &style_sheet_key,
|
|
|
|
++ blink_css_origin);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injection.h b/components/user_scripts/renderer/script_injection.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injection.h
|
|
|
|
+@@ -0,0 +1,155 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_SCRIPT_INJECTION_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_SCRIPT_INJECTION_H_
|
|
|
|
++
|
|
|
|
++#include <stdint.h>
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/callback.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/weak_ptr.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "injection_host.h"
|
|
|
|
++#include "script_injector.h"
|
|
|
|
++
|
|
|
|
++struct HostID;
|
|
|
|
++
|
|
|
|
++namespace content {
|
|
|
|
++class RenderFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace v8 {
|
|
|
|
++class Value;
|
|
|
|
++template <class T> class Local;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++struct ScriptsRunInfo;
|
|
|
|
++
|
|
|
|
++// A script wrapper which is aware of whether or not it is allowed to execute,
|
|
|
|
++// and contains the implementation to do so.
|
|
|
|
++class ScriptInjection {
|
|
|
|
++ public:
|
|
|
|
++ enum InjectionResult {
|
|
|
|
++ INJECTION_FINISHED,
|
|
|
|
++ INJECTION_BLOCKED,
|
|
|
|
++ INJECTION_WAITING
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ using CompletionCallback = base::OnceCallback<void(ScriptInjection*)>;
|
|
|
|
++
|
|
|
|
++ // Return the id of the injection host associated with the given world.
|
|
|
|
++ static std::string GetHostIdForIsolatedWorld(int world_id);
|
|
|
|
++
|
|
|
|
++ // Remove the isolated world associated with the given injection host.
|
|
|
|
++ static void RemoveIsolatedWorld(const std::string& host_id);
|
|
|
|
++
|
|
|
|
++ ScriptInjection(std::unique_ptr<ScriptInjector> injector,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ std::unique_ptr<const InjectionHost> injection_host,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ bool log_activity);
|
|
|
|
++ ~ScriptInjection();
|
|
|
|
++
|
|
|
|
++ // Try to inject the script at the |current_location|. This returns
|
|
|
|
++ // INJECTION_FINISHED if injection has injected or will never inject, returns
|
|
|
|
++ // INJECTION_BLOCKED if injection is running asynchronously and has not
|
|
|
|
++ // finished yet, returns INJECTION_WAITING if injections is delayed (either
|
|
|
|
++ // for permission purposes or because |current_location| is not the designated
|
|
|
|
++ // |run_location_|).
|
|
|
|
++ // If INJECTION_BLOCKED is returned, |async_completion_callback| will be
|
|
|
|
++ // called upon completion.
|
|
|
|
++ InjectionResult TryToInject(
|
|
|
|
++ UserScript::RunLocation current_location,
|
|
|
|
++ ScriptsRunInfo* scripts_run_info,
|
|
|
|
++ CompletionCallback async_completion_callback);
|
|
|
|
++
|
|
|
|
++ // Called when permission for the given injection has been granted.
|
|
|
|
++ // Returns INJECTION_FINISHED if injection has injected or will never inject,
|
|
|
|
++ // returns INJECTION_BLOCKED if injection is ran asynchronously.
|
|
|
|
++ InjectionResult OnPermissionGranted(ScriptsRunInfo* scripts_run_info);
|
|
|
|
++
|
|
|
|
++ // Resets the pointer of the injection host when the host is gone.
|
|
|
|
++ void OnHostRemoved();
|
|
|
|
++
|
|
|
|
++ void invalidate_render_frame() { render_frame_ = nullptr; }
|
|
|
|
++
|
|
|
|
++ // Accessors.
|
|
|
|
++ content::RenderFrame* render_frame() const { return render_frame_; }
|
|
|
|
++ const HostID& host_id() const { return injection_host_->id(); }
|
|
|
|
++ int64_t request_id() const { return request_id_; }
|
|
|
|
++
|
|
|
|
++ // Called when JS injection for the given frame has been completed or
|
|
|
|
++ // cancelled.
|
|
|
|
++ void OnJsInjectionCompleted(const std::vector<v8::Local<v8::Value>>& results,
|
|
|
|
++ absl::optional<base::TimeDelta> elapsed);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ class FrameWatcher;
|
|
|
|
++
|
|
|
|
++ // Sends a message to the browser to request permission to inject.
|
|
|
|
++ void RequestPermissionFromBrowser();
|
|
|
|
++
|
|
|
|
++ // Injects the script. Returns INJECTION_FINISHED if injection has finished,
|
|
|
|
++ // otherwise INJECTION_BLOCKED.
|
|
|
|
++ InjectionResult Inject(ScriptsRunInfo* scripts_run_info);
|
|
|
|
++
|
|
|
|
++ // Inject any JS scripts into the frame for the injection.
|
|
|
|
++ void InjectJs(std::set<std::string>* executing_scripts,
|
|
|
|
++ size_t* num_injected_js_scripts);
|
|
|
|
++
|
|
|
|
++ // Inject any CSS source into the frame for the injection.
|
|
|
|
++ void InjectCss(std::set<std::string>* injected_stylesheets,
|
|
|
|
++ size_t* num_injected_stylesheets);
|
|
|
|
++
|
|
|
|
++ // Notify that we will not inject, and mark it as acknowledged.
|
|
|
|
++ void NotifyWillNotInject(ScriptInjector::InjectFailureReason reason);
|
|
|
|
++
|
|
|
|
++ // The injector for this injection.
|
|
|
|
++ std::unique_ptr<ScriptInjector> injector_;
|
|
|
|
++
|
|
|
|
++ // The RenderFrame into which this should inject the script.
|
|
|
|
++ content::RenderFrame* render_frame_;
|
|
|
|
++
|
|
|
|
++ // The associated injection host.
|
|
|
|
++ std::unique_ptr<const InjectionHost> injection_host_;
|
|
|
|
++
|
|
|
|
++ // The location in the document load at which we inject the script.
|
|
|
|
++ UserScript::RunLocation run_location_;
|
|
|
|
++
|
|
|
|
++ // This injection's request id. This will be -1 unless the injection is
|
|
|
|
++ // currently waiting on permission.
|
|
|
|
++ int64_t request_id_;
|
|
|
|
++
|
|
|
|
++ // Whether or not the injection is complete, either via injecting the script
|
|
|
|
++ // or because it will never complete.
|
|
|
|
++ bool complete_;
|
|
|
|
++
|
|
|
|
++ // Whether or not the injection successfully injected JS.
|
|
|
|
++ bool did_inject_js_;
|
|
|
|
++
|
|
|
|
++ // Whether or not we should log dom activity for this injection.
|
|
|
|
++ bool log_activity_;
|
|
|
|
++
|
|
|
|
++ // Results storage.
|
|
|
|
++ std::unique_ptr<base::Value> execution_result_;
|
|
|
|
++
|
|
|
|
++ // The callback to run upon completing asynchronously.
|
|
|
|
++ CompletionCallback async_completion_callback_;
|
|
|
|
++
|
|
|
|
++ // A helper class to hold the render frame and watch for its deletion.
|
|
|
|
++ std::unique_ptr<FrameWatcher> frame_watcher_;
|
|
|
|
++
|
|
|
|
++ base::WeakPtrFactory<ScriptInjection> weak_ptr_factory_{this};
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(ScriptInjection);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_SCRIPT_INJECTION_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injection_callback.cc b/components/user_scripts/renderer/script_injection_callback.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injection_callback.cc
|
|
|
|
+@@ -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.
|
|
|
|
++
|
|
|
|
++#include "script_injection_callback.h"
|
|
|
|
++
|
|
|
|
++#include "third_party/blink/public/platform/web_vector.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++ScriptInjectionCallback::ScriptInjectionCallback(
|
|
|
|
++ CompleteCallback injection_completed_callback)
|
|
|
|
++ : injection_completed_callback_(std::move(injection_completed_callback)) {}
|
|
|
|
++
|
|
|
|
++ScriptInjectionCallback::~ScriptInjectionCallback() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionCallback::Completed(
|
|
|
|
++ const blink::WebVector<v8::Local<v8::Value>>& result) {
|
|
|
|
++ std::vector<v8::Local<v8::Value>> stl_result(result.begin(), result.end());
|
|
|
|
++ std::move(injection_completed_callback_).Run(stl_result);
|
|
|
|
++ delete this;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injection_callback.h b/components/user_scripts/renderer/script_injection_callback.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injection_callback.h
|
|
|
|
+@@ -0,0 +1,39 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_SCRIPT_INJECTION_CALLBACK_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_SCRIPT_INJECTION_CALLBACK_H_
|
|
|
|
++
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/callback.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "third_party/blink/public/web/web_script_execution_callback.h"
|
|
|
|
++#include "v8/include/v8.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// A wrapper around a callback to notify a script injection when injection
|
|
|
|
++// completes.
|
|
|
|
++// This class manages its own lifetime.
|
|
|
|
++class ScriptInjectionCallback : public blink::WebScriptExecutionCallback {
|
|
|
|
++ public:
|
|
|
|
++ using CompleteCallback =
|
|
|
|
++ base::OnceCallback<void(const std::vector<v8::Local<v8::Value>>& result)>;
|
|
|
|
++
|
|
|
|
++ explicit ScriptInjectionCallback(
|
|
|
|
++ CompleteCallback injection_completed_callback);
|
|
|
|
++ ~ScriptInjectionCallback() override;
|
|
|
|
++
|
|
|
|
++ void Completed(const blink::WebVector<v8::Local<v8::Value>>& result) override;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ CompleteCallback injection_completed_callback_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(ScriptInjectionCallback);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_SCRIPT_INJECTION_CALLBACK_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injection_manager.cc b/components/user_scripts/renderer/script_injection_manager.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injection_manager.cc
|
|
|
|
+@@ -0,0 +1,417 @@
|
|
|
|
++// 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 "script_injection_manager.h"
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "base/auto_reset.h"
|
|
|
|
++#include "base/bind.h"
|
|
|
|
++#include "base/feature_list.h"
|
|
|
|
++#include "base/memory/weak_ptr.h"
|
|
|
|
++#include "base/threading/thread_task_runner_handle.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_frame_observer.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "extension_frame_helper.h"
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "script_injection.h"
|
|
|
|
++#include "scripts_run_info.h"
|
|
|
|
++#include "web_ui_injection_host.h"
|
|
|
|
++#include "ipc/ipc_message_macros.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_url_error.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document.h"
|
|
|
|
++#include "third_party/blink/public/web/web_frame.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++#include "third_party/blink/public/web/web_view.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++// The length of time to wait after the DOM is complete to try and run user
|
|
|
|
++// scripts.
|
|
|
|
++const int kScriptIdleTimeoutInMs = 200;
|
|
|
|
++
|
|
|
|
++// Returns the RunLocation that follows |run_location|.
|
|
|
|
++UserScript::RunLocation NextRunLocation(UserScript::RunLocation run_location) {
|
|
|
|
++ switch (run_location) {
|
|
|
|
++ case UserScript::DOCUMENT_START:
|
|
|
|
++ return UserScript::DOCUMENT_END;
|
|
|
|
++ case UserScript::DOCUMENT_END:
|
|
|
|
++ return UserScript::DOCUMENT_IDLE;
|
|
|
|
++ case UserScript::DOCUMENT_IDLE:
|
|
|
|
++ return UserScript::RUN_LOCATION_LAST;
|
|
|
|
++ case UserScript::UNDEFINED:
|
|
|
|
++ case UserScript::RUN_DEFERRED:
|
|
|
|
++ case UserScript::BROWSER_DRIVEN:
|
|
|
|
++ case UserScript::RUN_LOCATION_LAST:
|
|
|
|
++ break;
|
|
|
|
++ }
|
|
|
|
++ NOTREACHED();
|
|
|
|
++ return UserScript::RUN_LOCATION_LAST;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++class ScriptInjectionManager::RFOHelper : public content::RenderFrameObserver {
|
|
|
|
++ public:
|
|
|
|
++ RFOHelper(content::RenderFrame* render_frame,
|
|
|
|
++ ScriptInjectionManager* manager);
|
|
|
|
++ ~RFOHelper() override;
|
|
|
|
++
|
|
|
|
++ // commit @9f2aac4
|
|
|
|
++ void Initialize();
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // RenderFrameObserver implementation.
|
|
|
|
++ void DidCreateNewDocument() override;
|
|
|
|
++ void DidCreateDocumentElement() override;
|
|
|
|
++ void DidFailProvisionalLoad() override;
|
|
|
|
++ void DidDispatchDOMContentLoadedEvent() override;
|
|
|
|
++ void WillDetach() override;
|
|
|
|
++ void OnDestruct() override;
|
|
|
|
++ void OnStop() override;
|
|
|
|
++
|
|
|
|
++ // Tells the ScriptInjectionManager to run tasks associated with
|
|
|
|
++ // document_idle.
|
|
|
|
++ void RunIdle();
|
|
|
|
++
|
|
|
|
++ void StartInjectScripts(UserScript::RunLocation run_location);
|
|
|
|
++
|
|
|
|
++ // Indicate that the frame is no longer valid because it is starting
|
|
|
|
++ // a new load or closing.
|
|
|
|
++ void InvalidateAndResetFrame(bool force_reset);
|
|
|
|
++
|
|
|
|
++ // The owning ScriptInjectionManager.
|
|
|
|
++ ScriptInjectionManager* manager_;
|
|
|
|
++
|
|
|
|
++ bool should_run_idle_ = true; // commit @9f2aac4
|
|
|
|
++
|
|
|
|
++ base::WeakPtrFactory<RFOHelper> weak_factory_{this};
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++ScriptInjectionManager::RFOHelper::RFOHelper(content::RenderFrame* render_frame,
|
|
|
|
++ ScriptInjectionManager* manager)
|
|
|
|
++ : content::RenderFrameObserver(render_frame),
|
|
|
|
++ manager_(manager),
|
|
|
|
++ should_run_idle_(true) {}
|
|
|
|
++
|
|
|
|
++ScriptInjectionManager::RFOHelper::~RFOHelper() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::Initialize() {
|
|
|
|
++ // Set up for the initial empty document, for which the Document created
|
|
|
|
++ // events do not happen as it's already present.
|
|
|
|
++ DidCreateNewDocument();
|
|
|
|
++ // The initial empty document for a main frame may have scripts attached to it
|
|
|
|
++ // but we do not want to invalidate the frame and lose them when the next
|
|
|
|
++ // document loads. For example the IncognitoApiTest.IncognitoSplitMode test
|
|
|
|
++ // does `chrome.tabs.create()` with a script to be run, which is added to the
|
|
|
|
++ // frame before it navigates, so it needs to be preserved. However scripts in
|
|
|
|
++ // child frames are expected to be run inside the initial empty document. For
|
|
|
|
++ // example the ExecuteScriptApiTest.FrameWithHttp204 test creates a child
|
|
|
|
++ // frame at about:blank and expects to run injected scripts inside it.
|
|
|
|
++ // This is all quite inconsistent however tests both depend on us queuing and
|
|
|
|
++ // not queueing the DOCUMENT_START events in the initial empty document.
|
|
|
|
++ if (!render_frame()->IsMainFrame()) {
|
|
|
|
++ DidCreateDocumentElement();
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::DidCreateNewDocument() {
|
|
|
|
++ // A new document is going to be shown, so invalidate the old document state.
|
|
|
|
++ // Don't force-reset the frame, because it is possible that a script injection
|
|
|
|
++ // was scheduled before the page was loaded, e.g. by navigating to a
|
|
|
|
++ // javascript: URL before the page has loaded.
|
|
|
|
++ constexpr bool kForceReset = false;
|
|
|
|
++ InvalidateAndResetFrame(kForceReset);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::DidCreateDocumentElement() {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: DidCreateDocumentElement -> DOCUMENT_START";
|
|
|
|
++
|
|
|
|
++ ExtensionFrameHelper::Get(render_frame())
|
|
|
|
++ ->ScheduleAtDocumentStart(
|
|
|
|
++ base::BindOnce(&ScriptInjectionManager::RFOHelper::StartInjectScripts,
|
|
|
|
++ weak_factory_.GetWeakPtr(), UserScript::DOCUMENT_START));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::DidFailProvisionalLoad() {
|
|
|
|
++ auto it = manager_->frame_statuses_.find(render_frame());
|
|
|
|
++ if (it != manager_->frame_statuses_.end() &&
|
|
|
|
++ it->second == UserScript::DOCUMENT_START) {
|
|
|
|
++ // Since the provisional load failed, the frame stays at its previous loaded
|
|
|
|
++ // state and origin (or the parent's origin for new/about:blank frames).
|
|
|
|
++ // Reset the frame to DOCUMENT_IDLE in order to reflect that the frame is
|
|
|
|
++ // done loading, and avoid any deadlock in the system.
|
|
|
|
++ //
|
|
|
|
++ // We skip injection of DOCUMENT_END and DOCUMENT_IDLE scripts, because the
|
|
|
|
++ // injections closely follow the DOMContentLoaded (and onload) events, which
|
|
|
|
++ // are not triggered after a failed provisional load.
|
|
|
|
++ // This assumption is verified in the checkDOMContentLoadedEvent subtest of
|
|
|
|
++ // ExecuteScriptApiTest.FrameWithHttp204 (browser_tests).
|
|
|
|
++ constexpr bool kForceReset = true;
|
|
|
|
++ InvalidateAndResetFrame(kForceReset);
|
|
|
|
++ should_run_idle_ = false;
|
|
|
|
++ manager_->frame_statuses_[render_frame()] = UserScript::DOCUMENT_IDLE;
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::DidDispatchDOMContentLoadedEvent() {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: DidDispatchDOMContentLoadedEvent -> DOCUMENT_END";
|
|
|
|
++
|
|
|
|
++ DCHECK(content::RenderThread::Get());
|
|
|
|
++ ExtensionFrameHelper::Get(render_frame())
|
|
|
|
++ ->ScheduleAtDocumentEnd(
|
|
|
|
++ base::BindOnce(&ScriptInjectionManager::RFOHelper::StartInjectScripts,
|
|
|
|
++ weak_factory_.GetWeakPtr(), UserScript::DOCUMENT_END));
|
|
|
|
++
|
|
|
|
++ // We try to run idle in two places: a delayed task here and in response to
|
|
|
|
++ // ContentRendererClient::RunScriptsAtDocumentIdle(). DidDispatchDOMContentLoadedEvent()
|
|
|
|
++ // corresponds to completing the document's load, whereas
|
|
|
|
++ // RunScriptsAtDocumentIdle() corresponds to completing the document and all
|
|
|
|
++ // subresources' load (but before the window.onload event). We don't want to
|
|
|
|
++ // hold up script injection for a particularly slow subresource, so we set a
|
|
|
|
++ // delayed task from here - but if we finish everything before that point
|
|
|
|
++ // (i.e., RunScriptsAtDocumentIdle() is triggered), then there's no reason to
|
|
|
|
++ // keep waiting.
|
|
|
|
++ render_frame()
|
|
|
|
++ ->GetTaskRunner(blink::TaskType::kInternalDefault)
|
|
|
|
++ ->PostDelayedTask(
|
|
|
|
++ FROM_HERE,
|
|
|
|
++ base::BindOnce(&ScriptInjectionManager::RFOHelper::RunIdle,
|
|
|
|
++ weak_factory_.GetWeakPtr()),
|
|
|
|
++ base::TimeDelta::FromMilliseconds(kScriptIdleTimeoutInMs));
|
|
|
|
++
|
|
|
|
++ ExtensionFrameHelper::Get(render_frame())
|
|
|
|
++ ->ScheduleAtDocumentIdle(
|
|
|
|
++ base::BindOnce(&ScriptInjectionManager::RFOHelper::RunIdle,
|
|
|
|
++ weak_factory_.GetWeakPtr()));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::WillDetach() {
|
|
|
|
++ // The frame is closing - invalidate.
|
|
|
|
++ constexpr bool kForceReset = true;
|
|
|
|
++ InvalidateAndResetFrame(kForceReset);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::OnDestruct() {
|
|
|
|
++ manager_->RemoveObserver(this);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::OnStop() {
|
|
|
|
++ // If the navigation request fails (e.g. 204/205/downloads), notify the
|
|
|
|
++ // extension to avoid keeping the frame in a START state indefinitely which
|
|
|
|
++ // leads to deadlocks.
|
|
|
|
++ DidFailProvisionalLoad();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::RunIdle() {
|
|
|
|
++ // Only notify the manager if the frame hasn't already had idle run since the
|
|
|
|
++ // task to RunIdle() was posted.
|
|
|
|
++ if (should_run_idle_) {
|
|
|
|
++ should_run_idle_ = false;
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: RunIdle -> DOCUMENT_IDLE";
|
|
|
|
++ manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_IDLE);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::StartInjectScripts(
|
|
|
|
++ UserScript::RunLocation run_location) {
|
|
|
|
++ manager_->StartInjectScripts(render_frame(), run_location);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RFOHelper::InvalidateAndResetFrame(
|
|
|
|
++ bool force_reset) {
|
|
|
|
++ // Invalidate any pending idle injections, and reset the frame inject on idle.
|
|
|
|
++ weak_factory_.InvalidateWeakPtrs();
|
|
|
|
++ // We reset to inject on idle, because the frame can be reused (in the case of
|
|
|
|
++ // navigation).
|
|
|
|
++ should_run_idle_ = true;
|
|
|
|
++
|
|
|
|
++ // Reset the frame if either |force_reset| is true, or if the manager is
|
|
|
|
++ // keeping track of the state of the frame (in which case we need to clean it
|
|
|
|
++ // up).
|
|
|
|
++ if (force_reset || manager_->frame_statuses_.count(render_frame()) != 0)
|
|
|
|
++ manager_->InvalidateForFrame(render_frame());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjectionManager::ScriptInjectionManager(
|
|
|
|
++ UserScriptSetManager* user_script_set_manager)
|
|
|
|
++ : user_script_set_manager_(user_script_set_manager),
|
|
|
|
++ user_script_set_manager_observation_(this) {
|
|
|
|
++ user_script_set_manager_observation_.Observe(user_script_set_manager_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++ScriptInjectionManager::~ScriptInjectionManager() {
|
|
|
|
++ for (const auto& injection : pending_injections_)
|
|
|
|
++ injection->invalidate_render_frame();
|
|
|
|
++ for (const auto& injection : running_injections_)
|
|
|
|
++ injection->invalidate_render_frame();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::OnRenderFrameCreated(
|
|
|
|
++ content::RenderFrame* render_frame) {
|
|
|
|
++ rfo_helpers_.push_back(std::make_unique<RFOHelper>(render_frame, this));
|
|
|
|
++ rfo_helpers_.back()->Initialize(); // commit @9f2aac4
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::OnInjectionFinished(
|
|
|
|
++ ScriptInjection* injection) {
|
|
|
|
++ auto iter =
|
|
|
|
++ std::find_if(running_injections_.begin(), running_injections_.end(),
|
|
|
|
++ [injection](const std::unique_ptr<ScriptInjection>& mode) {
|
|
|
|
++ return injection == mode.get();
|
|
|
|
++ });
|
|
|
|
++ if (iter != running_injections_.end())
|
|
|
|
++ running_injections_.erase(iter);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::OnUserScriptsUpdated(
|
|
|
|
++ const std::set<HostID>& changed_hosts) {
|
|
|
|
++ for (auto iter = pending_injections_.begin();
|
|
|
|
++ iter != pending_injections_.end();) {
|
|
|
|
++ if (changed_hosts.count((*iter)->host_id()) > 0)
|
|
|
|
++ iter = pending_injections_.erase(iter);
|
|
|
|
++ else
|
|
|
|
++ ++iter;
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::RemoveObserver(RFOHelper* helper) {
|
|
|
|
++ for (auto iter = rfo_helpers_.begin(); iter != rfo_helpers_.end(); ++iter) {
|
|
|
|
++ if (iter->get() == helper) {
|
|
|
|
++ rfo_helpers_.erase(iter);
|
|
|
|
++ break;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) {
|
|
|
|
++ // If the frame invalidated is the frame being injected into, we need to
|
|
|
|
++ // note it.
|
|
|
|
++ active_injection_frames_.erase(frame);
|
|
|
|
++
|
|
|
|
++ for (auto iter = pending_injections_.begin();
|
|
|
|
++ iter != pending_injections_.end();) {
|
|
|
|
++ if ((*iter)->render_frame() == frame)
|
|
|
|
++ iter = pending_injections_.erase(iter);
|
|
|
|
++ else
|
|
|
|
++ ++iter;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ frame_statuses_.erase(frame);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::StartInjectScripts(
|
|
|
|
++ content::RenderFrame* frame,
|
|
|
|
++ UserScript::RunLocation run_location) {
|
|
|
|
++ auto iter = frame_statuses_.find(frame);
|
|
|
|
++ // We also don't execute if we detect that the run location is somehow out of
|
|
|
|
++ // order. This can happen if:
|
|
|
|
++ // - The first run location reported for the frame isn't DOCUMENT_START, or
|
|
|
|
++ // - The run location reported doesn't immediately follow the previous
|
|
|
|
++ // reported run location.
|
|
|
|
++ // We don't want to run because extensions may have requirements that scripts
|
|
|
|
++ // running in an earlier run location have run by the time a later script
|
|
|
|
++ // runs. Better to just not run.
|
|
|
|
++ // Note that we check run_location > NextRunLocation() in the second clause
|
|
|
|
++ // (as opposed to !=) because earlier signals (like DidCreateDocumentElement)
|
|
|
|
++ // can happen multiple times, so we can receive earlier/equal run locations.
|
|
|
|
++ if ((iter == frame_statuses_.end() &&
|
|
|
|
++ run_location != UserScript::DOCUMENT_START) ||
|
|
|
|
++ (iter != frame_statuses_.end() &&
|
|
|
|
++ run_location > NextRunLocation(iter->second))) {
|
|
|
|
++ // We also invalidate the frame, because the run order of pending injections
|
|
|
|
++ // may also be bad.
|
|
|
|
++ InvalidateForFrame(frame);
|
|
|
|
++ return;
|
|
|
|
++ } else if (iter != frame_statuses_.end() && iter->second >= run_location) {
|
|
|
|
++ // Certain run location signals (like DidCreateDocumentElement) can happen
|
|
|
|
++ // multiple times. Ignore the subsequent signals.
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Otherwise, all is right in the world, and we can get on with the
|
|
|
|
++ // injections!
|
|
|
|
++ frame_statuses_[frame] = run_location;
|
|
|
|
++ InjectScripts(frame, run_location);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::InjectScripts(
|
|
|
|
++ content::RenderFrame* frame,
|
|
|
|
++ UserScript::RunLocation run_location) {
|
|
|
|
++ // Find any injections that want to run on the given frame.
|
|
|
|
++ ScriptInjectionVector frame_injections;
|
|
|
|
++ for (auto iter = pending_injections_.begin();
|
|
|
|
++ iter != pending_injections_.end();) {
|
|
|
|
++ if ((*iter)->render_frame() == frame) {
|
|
|
|
++ frame_injections.push_back(std::move(*iter));
|
|
|
|
++ iter = pending_injections_.erase(iter);
|
|
|
|
++ } else {
|
|
|
|
++ ++iter;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // Add any injections for user scripts.
|
|
|
|
++ int tab_id = ExtensionFrameHelper::Get(frame)->tab_id();
|
|
|
|
++ user_script_set_manager_->GetAllInjections(&frame_injections, frame, tab_id,
|
|
|
|
++ run_location);
|
|
|
|
++
|
|
|
|
++ // Note that we are running in |frame|.
|
|
|
|
++ active_injection_frames_.insert(frame);
|
|
|
|
++
|
|
|
|
++ ScriptsRunInfo scripts_run_info(frame, run_location);
|
|
|
|
++
|
|
|
|
++ for (auto iter = frame_injections.begin(); iter != frame_injections.end();) {
|
|
|
|
++ // It's possible for thScriptsRunInfoe frame to be invalidated in the course of injection
|
|
|
|
++ // (if a script removes its own frame, for example). If this happens, abort.
|
|
|
|
++ if (!active_injection_frames_.count(frame))
|
|
|
|
++ break;
|
|
|
|
++ std::unique_ptr<ScriptInjection> injection(std::move(*iter));
|
|
|
|
++ iter = frame_injections.erase(iter);
|
|
|
|
++ TryToInject(std::move(injection), run_location, &scripts_run_info);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ // We are done running in the frame.
|
|
|
|
++ active_injection_frames_.erase(frame);
|
|
|
|
++
|
|
|
|
++ scripts_run_info.LogRun(activity_logging_enabled_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptInjectionManager::TryToInject(
|
|
|
|
++ std::unique_ptr<ScriptInjection> injection,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ ScriptsRunInfo* scripts_run_info) {
|
|
|
|
++ // Try to inject the script. If the injection is waiting (i.e., for
|
|
|
|
++ // permission), add it to the list of pending injections. If the injection
|
|
|
|
++ // has blocked, add it to the list of running injections.
|
|
|
|
++ // The Unretained below is safe because this object owns all the
|
|
|
|
++ // ScriptInjections, so is guaranteed to outlive them.
|
|
|
|
++ switch (injection->TryToInject(
|
|
|
|
++ run_location, scripts_run_info,
|
|
|
|
++ base::BindOnce(&ScriptInjectionManager::OnInjectionFinished,
|
|
|
|
++ base::Unretained(this)))) {
|
|
|
|
++ case ScriptInjection::INJECTION_WAITING:
|
|
|
|
++ pending_injections_.push_back(std::move(injection));
|
|
|
|
++ break;
|
|
|
|
++ case ScriptInjection::INJECTION_BLOCKED:
|
|
|
|
++ running_injections_.push_back(std::move(injection));
|
|
|
|
++ break;
|
|
|
|
++ case ScriptInjection::INJECTION_FINISHED:
|
|
|
|
++ break;
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injection_manager.h b/components/user_scripts/renderer/script_injection_manager.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injection_manager.h
|
|
|
|
+@@ -0,0 +1,102 @@
|
|
|
|
++#include <stdint.h>
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/callback.h"
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/scoped_observation.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "script_injection.h"
|
|
|
|
++#include "user_script_set_manager.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// The ScriptInjectionManager manages extensions injecting scripts into frames
|
|
|
|
++// via both content/user scripts and tabs.executeScript(). It is responsible for
|
|
|
|
++// maintaining any pending injections awaiting permission or the appropriate
|
|
|
|
++// load point, and injecting them when ready.
|
|
|
|
++class ScriptInjectionManager : public UserScriptSetManager::Observer {
|
|
|
|
++ public:
|
|
|
|
++ explicit ScriptInjectionManager(
|
|
|
|
++ UserScriptSetManager* user_script_set_manager);
|
|
|
|
++ virtual ~ScriptInjectionManager();
|
|
|
|
++
|
|
|
|
++ // Notifies that a new render view has been created.
|
|
|
|
++ void OnRenderFrameCreated(content::RenderFrame* render_frame);
|
|
|
|
++
|
|
|
|
++ // Removes pending injections of the unloaded extension.
|
|
|
|
++ //void OnExtensionUnloaded(const std::string& extension_id);
|
|
|
|
++
|
|
|
|
++ void set_activity_logging_enabled(bool enabled) {
|
|
|
|
++ activity_logging_enabled_ = enabled;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // A RenderFrameObserver implementation which watches the various render
|
|
|
|
++ // frames in order to notify the ScriptInjectionManager of different
|
|
|
|
++ // document load states and IPCs.
|
|
|
|
++ class RFOHelper;
|
|
|
|
++
|
|
|
|
++ using FrameStatusMap =
|
|
|
|
++ std::map<content::RenderFrame*, UserScript::RunLocation>;
|
|
|
|
++
|
|
|
|
++ using ScriptInjectionVector = std::vector<std::unique_ptr<ScriptInjection>>;
|
|
|
|
++
|
|
|
|
++ // Notifies that an injection has been finished.
|
|
|
|
++ void OnInjectionFinished(ScriptInjection* injection);
|
|
|
|
++
|
|
|
|
++ // UserScriptSetManager::Observer implementation.
|
|
|
|
++ void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) override;
|
|
|
|
++
|
|
|
|
++ // Notifies that an RFOHelper should be removed.
|
|
|
|
++ void RemoveObserver(RFOHelper* helper);
|
|
|
|
++
|
|
|
|
++ // Invalidate any pending tasks associated with |frame|.
|
|
|
|
++ void InvalidateForFrame(content::RenderFrame* frame);
|
|
|
|
++
|
|
|
|
++ // Starts the process to inject appropriate scripts into |frame|.
|
|
|
|
++ void StartInjectScripts(content::RenderFrame* frame,
|
|
|
|
++ UserScript::RunLocation run_location);
|
|
|
|
++
|
|
|
|
++ // Actually injects the scripts into |frame|.
|
|
|
|
++ void InjectScripts(content::RenderFrame* frame,
|
|
|
|
++ UserScript::RunLocation run_location);
|
|
|
|
++
|
|
|
|
++ // Try to inject and store injection if it has not finished.
|
|
|
|
++ void TryToInject(std::unique_ptr<ScriptInjection> injection,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ ScriptsRunInfo* scripts_run_info);
|
|
|
|
++
|
|
|
|
++ // The map of active web frames to their corresponding statuses. The
|
|
|
|
++ // RunLocation of the frame corresponds to the last location that has ran.
|
|
|
|
++ FrameStatusMap frame_statuses_;
|
|
|
|
++
|
|
|
|
++ // The frames currently being injected into, so long as that frame is valid.
|
|
|
|
++ std::set<content::RenderFrame*> active_injection_frames_;
|
|
|
|
++
|
|
|
|
++ // The collection of RFOHelpers.
|
|
|
|
++ std::vector<std::unique_ptr<RFOHelper>> rfo_helpers_;
|
|
|
|
++
|
|
|
|
++ // The set of UserScripts associated with extensions. Owned by the Dispatcher.
|
|
|
|
++ UserScriptSetManager* user_script_set_manager_;
|
|
|
|
++
|
|
|
|
++ // Pending injections which are waiting for either the proper run location or
|
|
|
|
++ // user consent.
|
|
|
|
++ ScriptInjectionVector pending_injections_;
|
|
|
|
++
|
|
|
|
++ // Running injections which are waiting for async callbacks from blink.
|
|
|
|
++ ScriptInjectionVector running_injections_;
|
|
|
|
++
|
|
|
|
++ // Whether or not dom activity should be logged for scripts injected.
|
|
|
|
++ bool activity_logging_enabled_ = false;
|
|
|
|
++
|
|
|
|
++ base::ScopedObservation<UserScriptSetManager, UserScriptSetManager::Observer>
|
|
|
|
++ user_script_set_manager_observation_{this};
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(ScriptInjectionManager);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/renderer/script_injector.h b/components/user_scripts/renderer/script_injector.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/script_injector.h
|
|
|
|
+@@ -0,0 +1,96 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_SCRIPT_INJECTOR_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_SCRIPT_INJECTOR_H_
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "../common/constants.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "third_party/blink/public/web/web_script_source.h"
|
|
|
|
++
|
|
|
|
++class InjectionHost;
|
|
|
|
++
|
|
|
|
++namespace blink {
|
|
|
|
++class WebLocalFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// The pseudo-delegate class for a ScriptInjection that provides all necessary
|
|
|
|
++// information about how to inject the script, including what code to inject and
|
|
|
|
++// when (run location), but without any injection logic.
|
|
|
|
++class ScriptInjector {
|
|
|
|
++ public:
|
|
|
|
++ // The possible reasons for not injecting the script.
|
|
|
|
++ enum InjectFailureReason {
|
|
|
|
++ EXTENSION_REMOVED, // The extension was removed before injection.
|
|
|
|
++ NOT_ALLOWED, // The script is not allowed to inject.
|
|
|
|
++ WONT_INJECT // The injection won't inject because the user rejected
|
|
|
|
++ // (or just did not accept) the injection.
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ virtual ~ScriptInjector() {}
|
|
|
|
++
|
|
|
|
++ // Returns the script type of this particular injection.
|
|
|
|
++ virtual UserScript::InjectionType script_type() const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns true if the script is running inside a user gesture.
|
|
|
|
++ virtual bool IsUserGesture() const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns the CSS origin of this injection.
|
|
|
|
++ virtual absl::optional<CSSOrigin> GetCssOrigin() const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns the key for this injection, if it's a CSS injection.
|
|
|
|
++ virtual const absl::optional<std::string> GetInjectionKey() const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns true if the script expects results.
|
|
|
|
++ virtual bool ExpectsResults() const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns true if the script should inject JS source at the given
|
|
|
|
++ // |run_location|.
|
|
|
|
++ virtual bool ShouldInjectJs(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const std::set<std::string>& executing_scripts) const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns true if the script should inject CSS at the given |run_location|.
|
|
|
|
++ virtual bool ShouldInjectCss(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const std::set<std::string>& injected_stylesheets) const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns the javascript sources to inject at the given |run_location|.
|
|
|
|
++ // Only called if ShouldInjectJs() is true.
|
|
|
|
++ virtual std::vector<blink::WebScriptSource> GetJsSources(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ std::set<std::string>* executing_scripts,
|
|
|
|
++ size_t* num_injected_js_scripts) const = 0;
|
|
|
|
++
|
|
|
|
++ // Returns the css to inject at the given |run_location|.
|
|
|
|
++ // Only called if ShouldInjectCss() is true.
|
|
|
|
++ virtual std::vector<blink::WebString> GetCssSources(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ std::set<std::string>* injected_stylesheets,
|
|
|
|
++ size_t* num_injected_stylesheets) const = 0;
|
|
|
|
++
|
|
|
|
++ // Notifies the script that injection has completed, with a possibly-populated
|
|
|
|
++ // list of results (depending on whether or not ExpectsResults() was true).
|
|
|
|
++ // |render_frame| contains the render frame, or null if the frame was
|
|
|
|
++ // invalidated.
|
|
|
|
++ virtual void OnInjectionComplete(
|
|
|
|
++ std::unique_ptr<base::Value> execution_result,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ content::RenderFrame* render_frame) = 0;
|
|
|
|
++
|
|
|
|
++ // Notifies the script that injection will never occur.
|
|
|
|
++ // |render_frame| contains the render frame, or null if the frame was
|
|
|
|
++ // invalidated.
|
|
|
|
++ virtual void OnWillNotInject(InjectFailureReason reason,
|
|
|
|
++ content::RenderFrame* render_frame) = 0;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_SCRIPT_INJECTOR_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/scripts_run_info.cc b/components/user_scripts/renderer/scripts_run_info.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/scripts_run_info.cc
|
|
|
|
+@@ -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.
|
|
|
|
++
|
|
|
|
++#include "scripts_run_info.h"
|
|
|
|
++
|
|
|
|
++#include "base/metrics/histogram_macros.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "script_context.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++ScriptsRunInfo::ScriptsRunInfo(content::RenderFrame* render_frame,
|
|
|
|
++ UserScript::RunLocation location)
|
|
|
|
++ : num_css(0u),
|
|
|
|
++ num_js(0u),
|
|
|
|
++ num_blocking_js(0u),
|
|
|
|
++ routing_id_(render_frame->GetRoutingID()),
|
|
|
|
++ run_location_(location),
|
|
|
|
++ frame_url_(ScriptContext::GetDocumentLoaderURLForFrame(
|
|
|
|
++ render_frame->GetWebFrame())) {}
|
|
|
|
++
|
|
|
|
++ScriptsRunInfo::~ScriptsRunInfo() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void ScriptsRunInfo::LogRun(bool send_script_activity) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/scripts_run_info.h b/components/user_scripts/renderer/scripts_run_info.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/scripts_run_info.h
|
|
|
|
+@@ -0,0 +1,70 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_SCRIPTS_RUN_INFO_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_SCRIPTS_RUN_INFO_H_
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/timer/elapsed_timer.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++
|
|
|
|
++namespace content {
|
|
|
|
++class RenderFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// A struct containing information about a script run.
|
|
|
|
++struct ScriptsRunInfo {
|
|
|
|
++ // Map of extensions IDs to the executing script paths.
|
|
|
|
++ typedef std::map<std::string, std::set<std::string> > ExecutingScriptsMap;
|
|
|
|
++
|
|
|
|
++ ScriptsRunInfo(content::RenderFrame* render_frame,
|
|
|
|
++ UserScript::RunLocation location);
|
|
|
|
++ ~ScriptsRunInfo();
|
|
|
|
++
|
|
|
|
++ // The number of CSS scripts injected.
|
|
|
|
++ size_t num_css;
|
|
|
|
++ // The number of JS scripts injected.
|
|
|
|
++ size_t num_js;
|
|
|
|
++ // The number of blocked JS scripts injected.
|
|
|
|
++ size_t num_blocking_js;
|
|
|
|
++ // A map of extension ids to executing script paths.
|
|
|
|
++ ExecutingScriptsMap executing_scripts;
|
|
|
|
++ // A map of extension ids to injected stylesheet paths.
|
|
|
|
++ ExecutingScriptsMap injected_stylesheets;
|
|
|
|
++ // The elapsed time since the ScriptsRunInfo was constructed.
|
|
|
|
++ base::ElapsedTimer timer;
|
|
|
|
++
|
|
|
|
++ // Log information about a given script run. If |send_script_activity| is
|
|
|
|
++ // true, this also informs the browser of the script run.
|
|
|
|
++ void LogRun(bool send_script_activity);
|
|
|
|
++
|
|
|
|
++ static void LogLongInjectionTaskTime(UserScript::RunLocation run_location,
|
|
|
|
++ const base::TimeDelta& elapsed);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // The routinig id to use to notify the browser of any injections. Since the
|
|
|
|
++ // frame may be deleted in injection, we don't hold on to a reference to it
|
|
|
|
++ // directly.
|
|
|
|
++ int routing_id_;
|
|
|
|
++
|
|
|
|
++ // The run location at which injection is happening.
|
|
|
|
++ UserScript::RunLocation run_location_;
|
|
|
|
++
|
|
|
|
++ // The url of the frame, preserved for the same reason as the routing id.
|
|
|
|
++ GURL frame_url_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(ScriptsRunInfo);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_SCRIPTS_RUN_INFO_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_script_injector.cc b/components/user_scripts/renderer/user_script_injector.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_script_injector.cc
|
|
|
|
+@@ -0,0 +1,228 @@
|
|
|
|
++// 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 "user_script_injector.h"
|
|
|
|
++
|
|
|
|
++#include <tuple>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "base/lazy_instance.h"
|
|
|
|
++#include "content/public/common/url_constants.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "content/public/renderer/render_view.h"
|
|
|
|
++#include "components/user_scripts/renderer/grit/user_scripts_renderer_resources.h"
|
|
|
|
++#include "injection_host.h"
|
|
|
|
++#include "script_context.h"
|
|
|
|
++#include "scripts_run_info.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++#include "third_party/blink/public/web/web_script_source.h"
|
|
|
|
++#include "ui/base/resource/resource_bundle.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++struct RoutingInfoKey {
|
|
|
|
++ int routing_id;
|
|
|
|
++ int script_id;
|
|
|
|
++
|
|
|
|
++ RoutingInfoKey(int routing_id, int script_id)
|
|
|
|
++ : routing_id(routing_id), script_id(script_id) {}
|
|
|
|
++
|
|
|
|
++ bool operator<(const RoutingInfoKey& other) const {
|
|
|
|
++ return std::tie(routing_id, script_id) <
|
|
|
|
++ std::tie(other.routing_id, other.script_id);
|
|
|
|
++ }
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++using RoutingInfoMap = std::map<RoutingInfoKey, bool>;
|
|
|
|
++
|
|
|
|
++// A map records whether a given |script_id| from a webview-added user script
|
|
|
|
++// is allowed to inject on the render of given |routing_id|.
|
|
|
|
++// Once a script is added, the decision of whether or not allowed to inject
|
|
|
|
++// won't be changed.
|
|
|
|
++// After removed by the webview, the user scipt will also be removed
|
|
|
|
++// from the render. Therefore, there won't be any query from the same
|
|
|
|
++// |script_id| and |routing_id| pair.
|
|
|
|
++// base::LazyInstance<RoutingInfoMap>::DestructorAtExit g_routing_info_map =
|
|
|
|
++// LAZY_INSTANCE_INITIALIZER;
|
|
|
|
++
|
|
|
|
++// Greasemonkey API source that is injected with the scripts.
|
|
|
|
++struct GreasemonkeyApiJsString {
|
|
|
|
++ GreasemonkeyApiJsString();
|
|
|
|
++ blink::WebScriptSource GetSource() const;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ blink::WebString source_;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++// The below constructor, monstrous as it is, just makes a WebScriptSource from
|
|
|
|
++// the GreasemonkeyApiJs resource.
|
|
|
|
++GreasemonkeyApiJsString::GreasemonkeyApiJsString() {
|
|
|
|
++ std::string greasemonky_api_js(
|
|
|
|
++ ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
|
|
|
|
++ IDR_GREASEMONKEY_API_JS));
|
|
|
|
++ source_ = blink::WebString::FromUTF8(greasemonky_api_js);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
|
|
|
|
++ return blink::WebScriptSource(source_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++base::LazyInstance<GreasemonkeyApiJsString>::Leaky g_greasemonkey_api =
|
|
|
|
++ LAZY_INSTANCE_INITIALIZER;
|
|
|
|
++
|
|
|
|
++bool ShouldInjectScripts(const UserScript::FileList& scripts,
|
|
|
|
++ const std::set<std::string>& injected_files) {
|
|
|
|
++ for (const std::unique_ptr<UserScript::File>& file : scripts) {
|
|
|
|
++ // Check if the script is already injected.
|
|
|
|
++ if (injected_files.count(file->url().path()) == 0) {
|
|
|
|
++ return true;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++UserScriptInjector::UserScriptInjector(const UserScript* script,
|
|
|
|
++ UserScriptSet* script_list)
|
|
|
|
++ : script_(script),
|
|
|
|
++ user_script_set_(script_list),
|
|
|
|
++ script_id_(script_->id()),
|
|
|
|
++ user_script_set_observer_(this) {
|
|
|
|
++ user_script_set_observer_.Observe(script_list);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptInjector::~UserScriptInjector() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptInjector::OnUserScriptsUpdated(
|
|
|
|
++ const std::set<HostID>& changed_hosts,
|
|
|
|
++ const UserScriptList& scripts) {
|
|
|
|
++ // When user scripts are updated, all the old script pointers are invalidated.
|
|
|
|
++ script_ = nullptr;
|
|
|
|
++ // If the host causing this injection changed, then this injection
|
|
|
|
++ // will be removed, and there's no guarantee the backing script still exists.
|
|
|
|
++ // if (changed_hosts.count(host_id_) > 0)
|
|
|
|
++ // return;
|
|
|
|
++
|
|
|
|
++ for (const std::unique_ptr<UserScript>& script : scripts) {
|
|
|
|
++ if (script->id() == script_id_) {
|
|
|
|
++ script_ = script.get();
|
|
|
|
++ break;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ // If |host_id_| wasn't in |changed_hosts|, then the script for this injection
|
|
|
|
++ // should be guaranteed to exist.
|
|
|
|
++ DCHECK(script_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScript::InjectionType UserScriptInjector::script_type() const {
|
|
|
|
++ return UserScript::CONTENT_SCRIPT;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptInjector::IsUserGesture() const {
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptInjector::ExpectsResults() const {
|
|
|
|
++ return false;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++absl::optional<CSSOrigin> UserScriptInjector::GetCssOrigin() const {
|
|
|
|
++ return absl::nullopt;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++const absl::optional<std::string> UserScriptInjector::GetInjectionKey() const {
|
|
|
|
++ return absl::nullopt;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptInjector::ShouldInjectJs(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const std::set<std::string>& executing_scripts) const {
|
|
|
|
++ return script_ && script_->run_location() == run_location &&
|
|
|
|
++ !script_->js_scripts().empty() &&
|
|
|
|
++ ShouldInjectScripts(script_->js_scripts(), executing_scripts);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptInjector::ShouldInjectCss(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const std::set<std::string>& injected_stylesheets) const {
|
|
|
|
++ return script_ && run_location == UserScript::DOCUMENT_START &&
|
|
|
|
++ !script_->css_scripts().empty() &&
|
|
|
|
++ ShouldInjectScripts(script_->css_scripts(), injected_stylesheets);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ std::set<std::string>* executing_scripts,
|
|
|
|
++ size_t* num_injected_js_scripts) const {
|
|
|
|
++ DCHECK(script_);
|
|
|
|
++ std::vector<blink::WebScriptSource> sources;
|
|
|
|
++
|
|
|
|
++ DCHECK_EQ(script_->run_location(), run_location);
|
|
|
|
++
|
|
|
|
++ const UserScript::FileList& js_scripts = script_->js_scripts();
|
|
|
|
++ sources.reserve(js_scripts.size() +
|
|
|
|
++ (script_->emulate_greasemonkey() ? 1 : 0));
|
|
|
|
++ // Emulate Greasemonkey API for scripts that were converted to extension
|
|
|
|
++ // user scripts.
|
|
|
|
++ if (script_->emulate_greasemonkey())
|
|
|
|
++ sources.push_back(g_greasemonkey_api.Get().GetSource());
|
|
|
|
++ for (const std::unique_ptr<UserScript::File>& file : js_scripts) {
|
|
|
|
++ const GURL& script_url = file->url();
|
|
|
|
++ // Check if the script is already injected.
|
|
|
|
++ if (executing_scripts->count(script_url.path()) != 0)
|
|
|
|
++ continue;
|
|
|
|
++
|
|
|
|
++ sources.push_back(blink::WebScriptSource(
|
|
|
|
++ user_script_set_->GetJsSource(*file, script_->emulate_greasemonkey()),
|
|
|
|
++ script_url));
|
|
|
|
++
|
|
|
|
++ (*num_injected_js_scripts) += 1;
|
|
|
|
++ executing_scripts->insert(script_url.path());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return sources;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::vector<blink::WebString> UserScriptInjector::GetCssSources(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ std::set<std::string>* injected_stylesheets,
|
|
|
|
++ size_t* num_injected_stylesheets) const {
|
|
|
|
++ DCHECK(script_);
|
|
|
|
++ DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
|
|
|
|
++
|
|
|
|
++ std::vector<blink::WebString> sources;
|
|
|
|
++
|
|
|
|
++ const UserScript::FileList& css_scripts = script_->css_scripts();
|
|
|
|
++ sources.reserve(css_scripts.size());
|
|
|
|
++ for (const std::unique_ptr<UserScript::File>& file : script_->css_scripts()) {
|
|
|
|
++ const std::string& stylesheet_path = file->url().path();
|
|
|
|
++ // Check if the stylesheet is already injected.
|
|
|
|
++ if (injected_stylesheets->count(stylesheet_path) != 0)
|
|
|
|
++ continue;
|
|
|
|
++
|
|
|
|
++ sources.push_back(user_script_set_->GetCssSource(*file));
|
|
|
|
++ (*num_injected_stylesheets) += 1;
|
|
|
|
++ injected_stylesheets->insert(stylesheet_path);
|
|
|
|
++ }
|
|
|
|
++ return sources;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptInjector::OnInjectionComplete(
|
|
|
|
++ std::unique_ptr<base::Value> execution_result,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ content::RenderFrame* render_frame) {}
|
|
|
|
++
|
|
|
|
++void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
|
|
|
|
++ content::RenderFrame* render_frame) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_script_injector.h b/components/user_scripts/renderer/user_script_injector.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_script_injector.h
|
|
|
|
+@@ -0,0 +1,87 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_USER_SCRIPT_INJECTOR_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_USER_SCRIPT_INJECTOR_H_
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <string>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/values.h"
|
|
|
|
++#include "base/scoped_observation.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "script_injection.h"
|
|
|
|
++#include "user_script_set.h"
|
|
|
|
++
|
|
|
|
++class InjectionHost;
|
|
|
|
++
|
|
|
|
++namespace blink {
|
|
|
|
++class WebLocalFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// A ScriptInjector for UserScripts.
|
|
|
|
++class UserScriptInjector : public ScriptInjector,
|
|
|
|
++ public UserScriptSet::Observer {
|
|
|
|
++ public:
|
|
|
|
++ UserScriptInjector(const UserScript* user_script,
|
|
|
|
++ UserScriptSet* user_script_set);
|
|
|
|
++ ~UserScriptInjector() override;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // UserScriptSet::Observer implementation.
|
|
|
|
++ void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts,
|
|
|
|
++ const UserScriptList& scripts) override;
|
|
|
|
++
|
|
|
|
++ // ScriptInjector implementation.
|
|
|
|
++ UserScript::InjectionType script_type() const override;
|
|
|
|
++ bool IsUserGesture() const override;
|
|
|
|
++ absl::optional<CSSOrigin> GetCssOrigin() const override;
|
|
|
|
++ const absl::optional<std::string> GetInjectionKey() const override;
|
|
|
|
++ bool ExpectsResults() const override;
|
|
|
|
++ bool ShouldInjectJs(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const std::set<std::string>& executing_scripts) const override;
|
|
|
|
++ bool ShouldInjectCss(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const std::set<std::string>& injected_stylesheets) const override;
|
|
|
|
++ std::vector<blink::WebScriptSource> GetJsSources(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ std::set<std::string>* executing_scripts,
|
|
|
|
++ size_t* num_injected_js_scripts) const override;
|
|
|
|
++ std::vector<blink::WebString> GetCssSources(
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ std::set<std::string>* injected_stylesheets,
|
|
|
|
++ size_t* num_injected_stylesheets) const override;
|
|
|
|
++ void OnInjectionComplete(std::unique_ptr<base::Value> execution_result,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ content::RenderFrame* render_frame) override;
|
|
|
|
++ void OnWillNotInject(InjectFailureReason reason,
|
|
|
|
++ content::RenderFrame* render_frame) override;
|
|
|
|
++
|
|
|
|
++ // The associated user script. Owned by the UserScriptInjector that created
|
|
|
|
++ // this object.
|
|
|
|
++ const UserScript* script_;
|
|
|
|
++
|
|
|
|
++ // The UserScriptSet that eventually owns the UserScript this
|
|
|
|
++ // UserScriptInjector points to.
|
|
|
|
++ // Outlives |this|.
|
|
|
|
++ UserScriptSet* const user_script_set_;
|
|
|
|
++
|
|
|
|
++ // The id of the associated user script. We cache this because when we update
|
|
|
|
++ // the |script_| associated with this injection, the old referance may be
|
|
|
|
++ // deleted.
|
|
|
|
++ int script_id_;
|
|
|
|
++
|
|
|
|
++ base::ScopedObservation<UserScriptSet, UserScriptSet::Observer>
|
|
|
|
++ user_script_set_observer_{this};
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(UserScriptInjector);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_USER_SCRIPT_INJECTOR_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_script_set.cc b/components/user_scripts/renderer/user_script_set.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_script_set.cc
|
|
|
|
+@@ -0,0 +1,259 @@
|
|
|
|
++// 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 "user_script_set.h"
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "base/debug/alias.h"
|
|
|
|
++#include "base/memory/ref_counted.h"
|
|
|
|
++#include "base/strings/strcat.h"
|
|
|
|
++#include "content/public/common/url_constants.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "injection_host.h"
|
|
|
|
++#include "script_context.h"
|
|
|
|
++#include "script_injection.h"
|
|
|
|
++#include "user_script_injector.h"
|
|
|
|
++#include "web_ui_injection_host.h"
|
|
|
|
++#include "third_party/blink/public/web/web_document.h"
|
|
|
|
++#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
++#include "url/gurl.h"
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++// These two strings are injected before and after the Greasemonkey API and
|
|
|
|
++// user script to wrap it in an anonymous scope.
|
|
|
|
++const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
|
|
|
|
++const char kUserScriptTail[] = "\n})(window);";
|
|
|
|
++// Maximum number of total content scripts we allow (across all extensions).
|
|
|
|
++// The limit exists to diagnose https://crbug.com/723381. The number is
|
|
|
|
++// arbitrarily chosen.
|
|
|
|
++// TODO(lazyboy): Remove when the bug is fixed.
|
|
|
|
++const uint32_t kNumScriptsArbitraryMax = 100000u;
|
|
|
|
++
|
|
|
|
++GURL GetDocumentUrlForFrame(blink::WebLocalFrame* frame) {
|
|
|
|
++ GURL data_source_url = ScriptContext::GetDocumentLoaderURLForFrame(frame);
|
|
|
|
++ if (!data_source_url.is_empty() && frame->IsViewSourceModeEnabled()) {
|
|
|
|
++ data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
|
|
|
|
++ data_source_url.spec());
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return data_source_url;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace
|
|
|
|
++
|
|
|
|
++UserScriptSet::UserScriptSet() {}
|
|
|
|
++
|
|
|
|
++UserScriptSet::~UserScriptSet() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSet::AddObserver(Observer* observer) {
|
|
|
|
++ observers_.AddObserver(observer);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSet::RemoveObserver(Observer* observer) {
|
|
|
|
++ observers_.RemoveObserver(observer);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSet::GetInjections(
|
|
|
|
++ std::vector<std::unique_ptr<ScriptInjection>>* injections,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ int tab_id,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ bool log_activity) {
|
|
|
|
++ GURL document_url = GetDocumentUrlForFrame(render_frame->GetWebFrame());
|
|
|
|
++ for (const std::unique_ptr<UserScript>& script : scripts_) {
|
|
|
|
++ std::unique_ptr<ScriptInjection> injection = GetInjectionForScript(
|
|
|
|
++ script.get(), render_frame, tab_id, run_location, document_url,
|
|
|
|
++ /* is_declarative, */ log_activity);
|
|
|
|
++ if (injection.get())
|
|
|
|
++ injections->push_back(std::move(injection));
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptSet::UpdateUserScripts(
|
|
|
|
++ base::ReadOnlySharedMemoryRegion shared_memory,
|
|
|
|
++ const std::set<HostID>& changed_hosts,
|
|
|
|
++ bool whitelisted_only) {
|
|
|
|
++ bool only_inject_incognito = false;
|
|
|
|
++ //ExtensionsRendererClient::Get()->IsIncognitoProcess();
|
|
|
|
++
|
|
|
|
++ // Create the shared memory mapping.
|
|
|
|
++ shared_memory_mapping_ = shared_memory.Map();
|
|
|
|
++ if (!shared_memory.IsValid())
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ // First get the size of the memory block.
|
|
|
|
++ const base::Pickle::Header* pickle_header =
|
|
|
|
++ shared_memory_mapping_.GetMemoryAs<base::Pickle::Header>();
|
|
|
|
++ if (!pickle_header)
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ // Now read in the rest of the block.
|
|
|
|
++ size_t pickle_size =
|
|
|
|
++ sizeof(base::Pickle::Header) + pickle_header->payload_size;
|
|
|
|
++
|
|
|
|
++ // Unpickle scripts.
|
|
|
|
++ uint32_t num_scripts = 0;
|
|
|
|
++ auto memory = shared_memory_mapping_.GetMemoryAsSpan<char>(pickle_size);
|
|
|
|
++ if (!memory.size())
|
|
|
|
++ return false;
|
|
|
|
++
|
|
|
|
++ base::Pickle pickle(memory.data(), pickle_size);
|
|
|
|
++ base::PickleIterator iter(pickle);
|
|
|
|
++ base::debug::Alias(&pickle_size);
|
|
|
|
++ CHECK(iter.ReadUInt32(&num_scripts));
|
|
|
|
++
|
|
|
|
++ // Sometimes the shared memory contents seem to be corrupted
|
|
|
|
++ // (https://crbug.com/723381). Set an arbitrary max limit to the number of
|
|
|
|
++ // scripts so that we don't add OOM noise to crash reports.
|
|
|
|
++ CHECK_LT(num_scripts, kNumScriptsArbitraryMax);
|
|
|
|
++
|
|
|
|
++ scripts_.clear();
|
|
|
|
++ script_sources_.clear();
|
|
|
|
++ scripts_.reserve(num_scripts);
|
|
|
|
++ for (uint32_t i = 0; i < num_scripts; ++i) {
|
|
|
|
++ std::unique_ptr<UserScript> script(new UserScript());
|
|
|
|
++ script->Unpickle(pickle, &iter);
|
|
|
|
++
|
|
|
|
++ // Note that this is a pointer into shared memory. We don't own it. It gets
|
|
|
|
++ // cleared up when the last renderer or browser process drops their
|
|
|
|
++ // reference to the shared memory.
|
|
|
|
++ for (size_t j = 0; j < script->js_scripts().size(); ++j) {
|
|
|
|
++ const char* body = NULL;
|
|
|
|
++ int body_length = 0;
|
|
|
|
++ CHECK(iter.ReadData(&body, &body_length));
|
|
|
|
++ script->js_scripts()[j]->set_external_content(
|
|
|
|
++ base::StringPiece(body, body_length));
|
|
|
|
++ }
|
|
|
|
++ for (size_t j = 0; j < script->css_scripts().size(); ++j) {
|
|
|
|
++ const char* body = NULL;
|
|
|
|
++ int body_length = 0;
|
|
|
|
++ CHECK(iter.ReadData(&body, &body_length));
|
|
|
|
++ script->css_scripts()[j]->set_external_content(
|
|
|
|
++ base::StringPiece(body, body_length));
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (only_inject_incognito && !script->is_incognito_enabled())
|
|
|
|
++ continue; // This script shouldn't run in an incognito tab.
|
|
|
|
++
|
|
|
|
++ scripts_.push_back(std::move(script));
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ for (auto& observer : observers_)
|
|
|
|
++ observer.OnUserScriptsUpdated(changed_hosts, scripts_);
|
|
|
|
++ return true;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSet::AddScript(std::unique_ptr<UserScript> script) {
|
|
|
|
++ scripts_.push_back(std::move(script));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++std::unique_ptr<ScriptInjection> UserScriptSet::GetInjectionForScript(
|
|
|
|
++ const UserScript* script,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ int tab_id,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const GURL& document_url,
|
|
|
|
++ //bool is_declarative,
|
|
|
|
++ bool log_activity) {
|
|
|
|
++ std::unique_ptr<ScriptInjection> injection;
|
|
|
|
++ std::unique_ptr<const InjectionHost> injection_host;
|
|
|
|
++ blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
|
|
|
|
++
|
|
|
|
++ const HostID& host_id = script->host_id();
|
|
|
|
++ injection_host.reset(new WebUIInjectionHost(host_id));
|
|
|
|
++
|
|
|
|
++ GURL effective_document_url =
|
|
|
|
++ ScriptContext::GetEffectiveDocumentURLForInjection(
|
|
|
|
++ web_frame, document_url, script->match_origin_as_fallback());
|
|
|
|
++
|
|
|
|
++ bool is_subframe = web_frame->Parent();
|
|
|
|
++ if (!script->MatchesDocument(effective_document_url, is_subframe)) {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: No Match name=" << script->name() << " " <<
|
|
|
|
++ "url=" << effective_document_url.spec();
|
|
|
|
++ return injection;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<ScriptInjector> injector(
|
|
|
|
++ new UserScriptInjector(script, this));
|
|
|
|
++
|
|
|
|
++ bool inject_css = !script->css_scripts().empty() &&
|
|
|
|
++ run_location == UserScript::DOCUMENT_START;
|
|
|
|
++ bool inject_js =
|
|
|
|
++ !script->js_scripts().empty() && script->run_location() == run_location;
|
|
|
|
++ if (inject_css || inject_js) {
|
|
|
|
++ injection.reset(new ScriptInjection(std::move(injector), render_frame,
|
|
|
|
++ std::move(injection_host), run_location,
|
|
|
|
++ log_activity));
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: Match name=" << script->name() << " " <<
|
|
|
|
++ "url=" << effective_document_url.spec();
|
|
|
|
++ } else {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts)) {
|
|
|
|
++ if (script->run_location() != run_location)
|
|
|
|
++ LOG(INFO) << "UserScripts: run location for name=" << script->name() <<
|
|
|
|
++ "current " << run_location << " " <<
|
|
|
|
++ "need " << script->run_location();
|
|
|
|
++ else
|
|
|
|
++ LOG(INFO) << "UserScripts: Match but no run name=" << script->name() << " " <<
|
|
|
|
++ "url=" << effective_document_url.spec();
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++ return injection;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++blink::WebString UserScriptSet::GetJsSource(const UserScript::File& file,
|
|
|
|
++ bool emulate_greasemonkey) {
|
|
|
|
++ const GURL& url = file.url();
|
|
|
|
++ auto iter = script_sources_.find(url);
|
|
|
|
++ if (iter != script_sources_.end()) {
|
|
|
|
++ return iter->second;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ base::StringPiece script_content = file.GetContent();
|
|
|
|
++ blink::WebString source;
|
|
|
|
++ if (emulate_greasemonkey) {
|
|
|
|
++ // We add this dumb function wrapper for user scripts to emulate what
|
|
|
|
++ // Greasemonkey does. |script_content| becomes:
|
|
|
|
++ // concat(kUserScriptHead, script_content, kUserScriptTail).
|
|
|
|
++ std::string content =
|
|
|
|
++ base::StrCat({kUserScriptHead, script_content, kUserScriptTail});
|
|
|
|
++ source = blink::WebString::FromUTF8(content);
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: Injecting w/greasemonkey " << file.url();
|
|
|
|
++ } else {
|
|
|
|
++ source = blink::WebString::FromUTF8(script_content.data(),
|
|
|
|
++ script_content.length());
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: Injecting " << file.url();
|
|
|
|
++ }
|
|
|
|
++ script_sources_[url] = source;
|
|
|
|
++ return source;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++blink::WebString UserScriptSet::GetCssSource(const UserScript::File& file) {
|
|
|
|
++ const GURL& url = file.url();
|
|
|
|
++ auto iter = script_sources_.find(url);
|
|
|
|
++ if (iter != script_sources_.end())
|
|
|
|
++ return iter->second;
|
|
|
|
++
|
|
|
|
++ base::StringPiece script_content = file.GetContent();
|
|
|
|
++ return script_sources_
|
|
|
|
++ .insert(std::make_pair(
|
|
|
|
++ url, blink::WebString::FromUTF8(script_content.data(),
|
|
|
|
++ script_content.length())))
|
|
|
|
++ .first->second;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_script_set.h b/components/user_scripts/renderer/user_script_set.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_script_set.h
|
|
|
|
+@@ -0,0 +1,102 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_USER_SCRIPT_SET_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_USER_SCRIPT_SET_H_
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <memory>
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/read_only_shared_memory_region.h"
|
|
|
|
++#include "base/observer_list.h"
|
|
|
|
++#include "../common/user_script.h"
|
|
|
|
++#include "third_party/blink/public/platform/web_string.h"
|
|
|
|
++
|
|
|
|
++class GURL;
|
|
|
|
++
|
|
|
|
++namespace content {
|
|
|
|
++class RenderFrame;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++class ScriptInjection;
|
|
|
|
++
|
|
|
|
++// The UserScriptSet is a collection of UserScripts which knows how to update
|
|
|
|
++// itself from SharedMemory and create ScriptInjections for UserScripts to
|
|
|
|
++// inject on a page.
|
|
|
|
++class UserScriptSet {
|
|
|
|
++ public:
|
|
|
|
++ class Observer {
|
|
|
|
++ public:
|
|
|
|
++ // Called when the set of user scripts is updated. |changed_hosts| contains
|
|
|
|
++ // the hosts whose scripts have been altered. Note that *all* script objects
|
|
|
|
++ // are invalidated, even if they aren't in |changed_hosts|.
|
|
|
|
++ virtual void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts,
|
|
|
|
++ const UserScriptList& scripts) = 0;
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ UserScriptSet();
|
|
|
|
++ ~UserScriptSet();
|
|
|
|
++
|
|
|
|
++ // Adds or removes observers.
|
|
|
|
++ void AddObserver(Observer* observer);
|
|
|
|
++ void RemoveObserver(Observer* observer);
|
|
|
|
++ void AddScript(std::unique_ptr<UserScript> script);
|
|
|
|
++
|
|
|
|
++ // Append any ScriptInjections that should run on the given |render_frame| and
|
|
|
|
++ // |tab_id|, at the given |run_location|, to |injections|.
|
|
|
|
++ // |extensions| is passed in to verify the corresponding extension is still
|
|
|
|
++ // valid.
|
|
|
|
++ void GetInjections(std::vector<std::unique_ptr<ScriptInjection>>* injections,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ int tab_id,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ bool log_activity);
|
|
|
|
++
|
|
|
|
++ // Updates scripts given the shared memory region containing user scripts.
|
|
|
|
++ // Returns true if the scripts were successfully updated.
|
|
|
|
++ bool UpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory,
|
|
|
|
++ const std::set<HostID>& changed_hosts,
|
|
|
|
++ bool whitelisted_only);
|
|
|
|
++
|
|
|
|
++ // Returns the contents of a script file.
|
|
|
|
++ // Note that copying is cheap as this uses WebString.
|
|
|
|
++ blink::WebString GetJsSource(const UserScript::File& file,
|
|
|
|
++ bool emulate_greasemonkey);
|
|
|
|
++ blink::WebString GetCssSource(const UserScript::File& file);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // Returns a new ScriptInjection for the given |script| to execute in the
|
|
|
|
++ // |render_frame|, or NULL if the script should not execute.
|
|
|
|
++ std::unique_ptr<ScriptInjection> GetInjectionForScript(
|
|
|
|
++ const UserScript* script,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ int tab_id,
|
|
|
|
++ UserScript::RunLocation run_location,
|
|
|
|
++ const GURL& document_url,
|
|
|
|
++ //bool is_declarative,
|
|
|
|
++ bool log_activity);
|
|
|
|
++
|
|
|
|
++ // Shared memory mapping containing raw script data.
|
|
|
|
++ base::ReadOnlySharedMemoryMapping shared_memory_mapping_;
|
|
|
|
++
|
|
|
|
++ // The UserScripts this injector manages.
|
|
|
|
++ UserScriptList scripts_;
|
|
|
|
++
|
|
|
|
++ // Map of user script file url -> source.
|
|
|
|
++ std::map<GURL, blink::WebString> script_sources_;
|
|
|
|
++
|
|
|
|
++ // The associated observers.
|
|
|
|
++ base::ObserverList<Observer>::Unchecked observers_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(UserScriptSet);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++} // namespace extensions
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_USER_SCRIPT_SET_H_
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_script_set_manager.cc b/components/user_scripts/renderer/user_script_set_manager.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_script_set_manager.cc
|
|
|
|
+@@ -0,0 +1,77 @@
|
|
|
|
++#include "user_script_set_manager.h"
|
|
|
|
++
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "../common/extension_messages.h"
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++#include "user_script_set.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++UserScriptSetManager::UserScriptSetManager() {
|
|
|
|
++ content::RenderThread::Get()->AddObserver(this);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptSetManager::~UserScriptSetManager() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSetManager::AddObserver(Observer* observer) {
|
|
|
|
++ observers_.AddObserver(observer);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSetManager::RemoveObserver(Observer* observer) {
|
|
|
|
++ observers_.RemoveObserver(observer);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++bool UserScriptSetManager::OnControlMessageReceived(
|
|
|
|
++ const IPC::Message& message) {
|
|
|
|
++ bool handled = true;
|
|
|
|
++ IPC_BEGIN_MESSAGE_MAP(UserScriptSetManager, message)
|
|
|
|
++ IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
|
|
|
|
++ IPC_MESSAGE_UNHANDLED(handled = false)
|
|
|
|
++ IPC_END_MESSAGE_MAP()
|
|
|
|
++ return handled;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSetManager::GetAllInjections(
|
|
|
|
++ std::vector<std::unique_ptr<ScriptInjection>>* injections,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ int tab_id,
|
|
|
|
++ UserScript::RunLocation run_location) {
|
|
|
|
++
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: GetAllInjections";
|
|
|
|
++
|
|
|
|
++ // static_scripts_ is UserScriptSet
|
|
|
|
++ static_scripts_.GetInjections(injections, render_frame, tab_id, run_location,
|
|
|
|
++ activity_logging_enabled_);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptSetManager::OnUpdateUserScripts(
|
|
|
|
++ base::ReadOnlySharedMemoryRegion shared_memory) {
|
|
|
|
++ if (!shared_memory.IsValid()) {
|
|
|
|
++ NOTREACHED() << "Bad scripts handle";
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ UserScriptSet* scripts = NULL;
|
|
|
|
++ scripts = &static_scripts_;
|
|
|
|
++
|
|
|
|
++ DCHECK(scripts);
|
|
|
|
++
|
|
|
|
++ // If no hosts are included in the set, that indicates that all
|
|
|
|
++ // hosts were updated. Add them all to the set so that observers and
|
|
|
|
++ // individual UserScriptSets don't need to know this detail.
|
|
|
|
++ //const std::set<HostID>* effective_hosts = &changed_hosts;
|
|
|
|
++ std::set<HostID> all_hosts;
|
|
|
|
++ const std::set<HostID>* effective_hosts = &all_hosts;
|
|
|
|
++
|
|
|
|
++ if (scripts->UpdateUserScripts(std::move(shared_memory), *effective_hosts,
|
|
|
|
++ false /*whitelisted_only*/)) {
|
|
|
|
++ for (auto& observer : observers_)
|
|
|
|
++ observer.OnUserScriptsUpdated(all_hosts /* *effective_hosts*/);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_script_set_manager.h b/components/user_scripts/renderer/user_script_set_manager.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_script_set_manager.h
|
|
|
|
+@@ -0,0 +1,62 @@
|
|
|
|
++#ifndef USERSCRIPTS_RENDER_SET_MANAGER_H_
|
|
|
|
++#define USERSCRIPTS_RENDER_SET_MANAGER_H_
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/memory/read_only_shared_memory_region.h"
|
|
|
|
++#include "base/observer_list.h"
|
|
|
|
++#include "content/public/renderer/render_thread_observer.h"
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "user_script_set.h"
|
|
|
|
++#include "script_injection.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class UserScriptSetManager : public content::RenderThreadObserver {
|
|
|
|
++ public:
|
|
|
|
++ class Observer {
|
|
|
|
++ public:
|
|
|
|
++ virtual void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) = 0;
|
|
|
|
++ };
|
|
|
|
++
|
|
|
|
++ UserScriptSetManager();
|
|
|
|
++
|
|
|
|
++ ~UserScriptSetManager() override;
|
|
|
|
++
|
|
|
|
++ void AddObserver(Observer* observer);
|
|
|
|
++ void RemoveObserver(Observer* observer);
|
|
|
|
++
|
|
|
|
++ // Append all injections from |static_scripts| and each of
|
|
|
|
++ // |programmatic_scripts_| to |injections|.
|
|
|
|
++ void GetAllInjections(
|
|
|
|
++ std::vector<std::unique_ptr<ScriptInjection>>* injections,
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ int tab_id,
|
|
|
|
++ UserScript::RunLocation run_location);
|
|
|
|
++
|
|
|
|
++private:
|
|
|
|
++ // content::RenderThreadObserver implementation.
|
|
|
|
++ bool OnControlMessageReceived(const IPC::Message& message) override;
|
|
|
|
++
|
|
|
|
++ base::ObserverList<Observer>::Unchecked observers_;
|
|
|
|
++
|
|
|
|
++ // Handle the UpdateUserScripts extension message.
|
|
|
|
++ void OnUpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory);
|
|
|
|
++ //, const HostID& host_id,
|
|
|
|
++ //const std::set<HostID>& changed_hosts,
|
|
|
|
++ //bool whitelisted_only);
|
|
|
|
++
|
|
|
|
++ // Scripts statically defined in extension manifests.
|
|
|
|
++ UserScriptSet static_scripts_;
|
|
|
|
++
|
|
|
|
++ // Whether or not dom activity should be logged for scripts injected.
|
|
|
|
++ bool activity_logging_enabled_ = false;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_scripts_dispatcher.cc b/components/user_scripts/renderer/user_scripts_dispatcher.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_scripts_dispatcher.cc
|
|
|
|
+@@ -0,0 +1,36 @@
|
|
|
|
++#include "user_scripts_dispatcher.h"
|
|
|
|
++
|
|
|
|
++#include <stddef.h>
|
|
|
|
++
|
|
|
|
++#include <algorithm>
|
|
|
|
++#include <memory>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "extension_frame_helper.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// ex ChromeExtensionsDispatcherDelegate
|
|
|
|
++UserScriptsDispatcher::UserScriptsDispatcher()
|
|
|
|
++ : user_script_set_manager_observer_(this) {
|
|
|
|
++ user_script_set_manager_.reset(new UserScriptSetManager());
|
|
|
|
++ script_injection_manager_.reset(
|
|
|
|
++ new ScriptInjectionManager(user_script_set_manager_.get()));
|
|
|
|
++ user_script_set_manager_observer_.Observe(user_script_set_manager_.get());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++UserScriptsDispatcher::~UserScriptsDispatcher() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsDispatcher::OnRenderThreadStarted(content::RenderThread* thread) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsDispatcher::OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsDispatcher::OnRenderFrameCreated(content::RenderFrame* render_frame) {
|
|
|
|
++ script_injection_manager_->OnRenderFrameCreated(render_frame);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_scripts_dispatcher.h b/components/user_scripts/renderer/user_scripts_dispatcher.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_scripts_dispatcher.h
|
|
|
|
+@@ -0,0 +1,48 @@
|
|
|
|
++#ifndef USERSCRIPTS_RENDER_DISPATCHER_H_
|
|
|
|
++#define USERSCRIPTS_RENDER_DISPATCHER_H_
|
|
|
|
++
|
|
|
|
++#include "user_script_set_manager.h"
|
|
|
|
++#include "script_injection_manager.h"
|
|
|
|
++
|
|
|
|
++#include <stdint.h>
|
|
|
|
++
|
|
|
|
++#include <map>
|
|
|
|
++#include <memory>
|
|
|
|
++#include <set>
|
|
|
|
++#include <string>
|
|
|
|
++#include <utility>
|
|
|
|
++#include <vector>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "base/scoped_observation.h"
|
|
|
|
++#include "content/public/renderer/render_thread_observer.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "../common/host_id.h"
|
|
|
|
++#include "user_script_set_manager.h"
|
|
|
|
++#include "script_injection.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class UserScriptsDispatcher : public content::RenderThreadObserver,
|
|
|
|
++ public UserScriptSetManager::Observer {
|
|
|
|
++
|
|
|
|
++ public:
|
|
|
|
++ explicit UserScriptsDispatcher();
|
|
|
|
++ ~UserScriptsDispatcher() override;
|
|
|
|
++
|
|
|
|
++ void OnRenderThreadStarted(content::RenderThread* thread);
|
|
|
|
++ void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts) override;
|
|
|
|
++ void OnRenderFrameCreated(content::RenderFrame* render_frame);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ std::unique_ptr<UserScriptSetManager> user_script_set_manager_;
|
|
|
|
++
|
|
|
|
++ std::unique_ptr<ScriptInjectionManager> script_injection_manager_;
|
|
|
|
++
|
|
|
|
++ base::ScopedObservation<UserScriptSetManager, UserScriptSetManager::Observer>
|
|
|
|
++ user_script_set_manager_observer_{this};
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_scripts_renderer_client.cc b/components/user_scripts/renderer/user_scripts_renderer_client.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_scripts_renderer_client.cc
|
|
|
|
+@@ -0,0 +1,105 @@
|
|
|
|
++#include "user_scripts_renderer_client.h"
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <utility>
|
|
|
|
++
|
|
|
|
++#include "base/logging.h"
|
|
|
|
++#include "base/lazy_instance.h"
|
|
|
|
++#include "content/public/renderer/render_frame.h"
|
|
|
|
++#include "content/public/renderer/render_thread.h"
|
|
|
|
++#include "content/public/renderer/render_frame_visitor.h"
|
|
|
|
++#include "chrome/renderer/chrome_render_thread_observer.h"
|
|
|
|
++
|
|
|
|
++#include "../common/user_scripts_features.h"
|
|
|
|
++#include "user_scripts_dispatcher.h"
|
|
|
|
++#include "extension_frame_helper.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++// was ChromeExtensionsRendererClient
|
|
|
|
++UserScriptsRendererClient::UserScriptsRendererClient() {}
|
|
|
|
++
|
|
|
|
++UserScriptsRendererClient::~UserScriptsRendererClient() {}
|
|
|
|
++
|
|
|
|
++// static
|
|
|
|
++UserScriptsRendererClient* UserScriptsRendererClient::GetInstance() {
|
|
|
|
++ static base::LazyInstance<UserScriptsRendererClient>::Leaky client =
|
|
|
|
++ LAZY_INSTANCE_INITIALIZER;
|
|
|
|
++ return client.Pointer();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsRendererClient::RenderThreadStarted() {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: RenderThreadStarted";
|
|
|
|
++
|
|
|
|
++ content::RenderThread* thread = content::RenderThread::Get();
|
|
|
|
++ dispatcher_ = std::make_unique<UserScriptsDispatcher>();
|
|
|
|
++
|
|
|
|
++ dispatcher_->OnRenderThreadStarted(thread);
|
|
|
|
++ thread->AddObserver(dispatcher_.get());
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsRendererClient::ConfigurationUpdated() {
|
|
|
|
++ if (base::FeatureList::IsEnabled(features::kEnableLoggingUserScripts))
|
|
|
|
++ LOG(INFO) << "UserScripts: Configuration Updated";
|
|
|
|
++
|
|
|
|
++ struct WatchFrame : public content::RenderFrameVisitor {
|
|
|
|
++ bool Visit(content::RenderFrame* frame) override {
|
|
|
|
++ if (frame)
|
|
|
|
++ UserScriptsRendererClient::GetInstance()->RenderFrameCreated(frame, NULL);
|
|
|
|
++ return true; // Continue visiting.
|
|
|
|
++ }
|
|
|
|
++ };
|
|
|
|
++ WatchFrame visitor = {};
|
|
|
|
++ content::RenderFrame::ForEach(&visitor);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsRendererClient::RenderFrameCreated(
|
|
|
|
++ content::RenderFrame* render_frame,
|
|
|
|
++ service_manager::BinderRegistry* registry) {
|
|
|
|
++
|
|
|
|
++ auto params = ChromeRenderThreadObserver::GetDynamicParams();
|
|
|
|
++ enabled_ = params.allow_userscript;
|
|
|
|
++ if (!enabled_) return;
|
|
|
|
++
|
|
|
|
++ if (loaded_ == false) {
|
|
|
|
++ loaded_ = true;
|
|
|
|
++ new user_scripts::ExtensionFrameHelper(render_frame);
|
|
|
|
++ dispatcher_->OnRenderFrameCreated(render_frame);
|
|
|
|
++ }
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsRendererClient::RunScriptsAtDocumentStart(content::RenderFrame* render_frame) {
|
|
|
|
++ if (!enabled_ || !loaded_) return;
|
|
|
|
++
|
|
|
|
++ ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
|
|
|
|
++ if (!frame_helper)
|
|
|
|
++ return; // The frame is invisible to user scripts.
|
|
|
|
++
|
|
|
|
++ frame_helper->RunScriptsAtDocumentStart();
|
|
|
|
++ // |frame_helper| and |render_frame| might be dead by now.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsRendererClient::RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) {
|
|
|
|
++ if (!enabled_ || !loaded_) return;
|
|
|
|
++
|
|
|
|
++ ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
|
|
|
|
++ if (!frame_helper)
|
|
|
|
++ return; // The frame is invisible to user scripts.
|
|
|
|
++
|
|
|
|
++ frame_helper->RunScriptsAtDocumentEnd();
|
|
|
|
++ // |frame_helper| and |render_frame| might be dead by now.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++void UserScriptsRendererClient::RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) {
|
|
|
|
++ if (!enabled_ || !loaded_) return;
|
|
|
|
++
|
|
|
|
++ ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
|
|
|
|
++ if (!frame_helper)
|
|
|
|
++ return; // The frame is invisible to user scripts.
|
|
|
|
++
|
|
|
|
++ frame_helper->RunScriptsAtDocumentIdle();
|
|
|
|
++ // |frame_helper| and |render_frame| might be dead by now.
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/renderer/user_scripts_renderer_client.h b/components/user_scripts/renderer/user_scripts_renderer_client.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/user_scripts_renderer_client.h
|
|
|
|
+@@ -0,0 +1,36 @@
|
|
|
|
++#ifndef USERSCRIPTS_RENDER_CLIENT_H_
|
|
|
|
++#define USERSCRIPTS_RENDER_CLIENT_H_
|
|
|
|
++
|
|
|
|
++#include <memory>
|
|
|
|
++#include <string>
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "user_scripts_dispatcher.h"
|
|
|
|
++#include "services/service_manager/public/cpp/binder_registry.h"
|
|
|
|
++
|
|
|
|
++namespace user_scripts {
|
|
|
|
++
|
|
|
|
++class UserScriptsRendererClient {
|
|
|
|
++ public:
|
|
|
|
++ UserScriptsRendererClient();
|
|
|
|
++ ~UserScriptsRendererClient();
|
|
|
|
++
|
|
|
|
++ static UserScriptsRendererClient* GetInstance();
|
|
|
|
++
|
|
|
|
++ void RenderThreadStarted();
|
|
|
|
++ void ConfigurationUpdated();
|
|
|
|
++ void RenderFrameCreated(content::RenderFrame* render_frame,
|
|
|
|
++ service_manager::BinderRegistry* registry);
|
|
|
|
++ void RunScriptsAtDocumentStart(content::RenderFrame* render_frame);
|
|
|
|
++ void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame);
|
|
|
|
++ void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame);
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ std::unique_ptr<UserScriptsDispatcher> dispatcher_;
|
|
|
|
++ bool enabled_ = false;
|
|
|
|
++ bool loaded_ = false;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++#endif
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/components/user_scripts/renderer/web_ui_injection_host.cc b/components/user_scripts/renderer/web_ui_injection_host.cc
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/web_ui_injection_host.cc
|
|
|
|
+@@ -0,0 +1,40 @@
|
|
|
|
++// 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 "web_ui_injection_host.h"
|
|
|
|
++#include "base/no_destructor.h"
|
|
|
|
++
|
|
|
|
++namespace {
|
|
|
|
++
|
|
|
|
++// The default secure CSP to be used in order to prevent remote scripts.
|
|
|
|
++const char kDefaultSecureCSP[] = "script-src 'self'; object-src 'self';";
|
|
|
|
++
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++WebUIInjectionHost::WebUIInjectionHost(const HostID& host_id)
|
|
|
|
++ : InjectionHost(host_id),
|
|
|
|
++ url_(host_id.id()) {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++WebUIInjectionHost::~WebUIInjectionHost() {
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++const std::string* WebUIInjectionHost::GetContentSecurityPolicy() const {
|
|
|
|
++ // Use the main world CSP.
|
|
|
|
++ // return nullptr;
|
|
|
|
++
|
|
|
|
++ // The isolated world will use its own CSP which blocks remotely hosted
|
|
|
|
++ // code.
|
|
|
|
++ static const base::NoDestructor<std::string> default_isolated_world_csp(
|
|
|
|
++ kDefaultSecureCSP);
|
|
|
|
++ return default_isolated_world_csp.get();
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++const GURL& WebUIInjectionHost::url() const {
|
|
|
|
++ return url_;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++const std::string& WebUIInjectionHost::name() const {
|
|
|
|
++ return id().id();
|
|
|
|
++}
|
|
|
|
+diff --git a/components/user_scripts/renderer/web_ui_injection_host.h b/components/user_scripts/renderer/web_ui_injection_host.h
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/renderer/web_ui_injection_host.h
|
|
|
|
+@@ -0,0 +1,28 @@
|
|
|
|
++// 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 USERSCRIPTS_RENDERER_WEB_UI_INJECTION_HOST_H_
|
|
|
|
++#define USERSCRIPTS_RENDERER_WEB_UI_INJECTION_HOST_H_
|
|
|
|
++
|
|
|
|
++#include "base/macros.h"
|
|
|
|
++#include "injection_host.h"
|
|
|
|
++
|
|
|
|
++class WebUIInjectionHost : public InjectionHost {
|
|
|
|
++ public:
|
|
|
|
++ WebUIInjectionHost(const HostID& host_id);
|
|
|
|
++ ~WebUIInjectionHost() override;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ // InjectionHost:
|
|
|
|
++ const std::string* GetContentSecurityPolicy() const override;
|
|
|
|
++ const GURL& url() const override;
|
|
|
|
++ const std::string& name() const override;
|
|
|
|
++
|
|
|
|
++ private:
|
|
|
|
++ GURL url_;
|
|
|
|
++
|
|
|
|
++ DISALLOW_COPY_AND_ASSIGN(WebUIInjectionHost);
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++#endif // USERSCRIPTS_RENDERER_WEB_UI_INJECTION_HOST_H_
|
|
|
|
+diff --git a/components/user_scripts/strings/userscripts_strings.grdp b/components/user_scripts/strings/userscripts_strings.grdp
|
|
|
|
+new file mode 100755
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/components/user_scripts/strings/userscripts_strings.grdp
|
|
|
|
+@@ -0,0 +1,55 @@
|
|
|
|
++<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
++<grit-part>
|
|
|
|
++
|
|
|
|
++ <!-- Preferences -->
|
|
|
|
++ <message name="IDS_PREFS_USERSCRIPTS_SETTINGS"
|
|
|
|
++ desc="."
|
|
|
|
++ formatter_data="android_java">
|
|
|
|
++ User Scripts
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++ <message name="IDS_OPTION_USERSCRIPT_FLAG" desc="." formatter_data="android_java">
|
|
|
|
++ Activate User Scripts
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++ <message name="IDS_OPTION_USERSCRIPT_FLAG_ON" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ ON
|
|
|
|
++ </message>
|
|
|
|
++ <message name="IDS_OPTION_USERSCRIPT_FLAG_OFF" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ OFF
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++ <message name="IDS_ADD_SCRIPT" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ Add script
|
|
|
|
++ </message>
|
|
|
|
++ <message name="IDS_SCRIPTS_LIST_DESCRIPTION" desc="." formatter_data="android_java">
|
|
|
|
++ Experimental support for Greasemonkey-style user scripts.
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++ <message name="IDS_SCRIPTS_ITEM_VERSION" desc="." formatter_data="android_java">
|
|
|
|
++ Version:
|
|
|
|
++ </message>
|
|
|
|
++ <message name="IDS_SCRIPTS_ITEM_FILENAME" desc="." formatter_data="android_java">
|
|
|
|
++ File:
|
|
|
|
++ </message>
|
|
|
|
++ <message name="IDS_SCRIPTS_ITEM_URL" desc="." formatter_data="android_java">
|
|
|
|
++ Url:
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++ <message name="IDS_SCRIPTS_VIEW_SOURCE" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ View source
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++ <message name="IDS_ASK_TO_INSTALL" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ Only install user scripts that you have verified are secure, user scripts can steal your credentials and data.
|
|
|
|
++
|
|
|
|
++Do you want to install <ph name="FILE">%s</ph>?
|
|
|
|
++ </message>
|
|
|
|
++ <message name="IDS_YES" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ Yes
|
|
|
|
++ </message>
|
|
|
|
++ <message name="IDS_NO" desc=". [CHAR-LIMIT=32]" formatter_data="android_java">
|
|
|
|
++ No
|
|
|
|
++ </message>
|
|
|
|
++
|
|
|
|
++</grit-part>
|
|
|
|
+\ No newline at end of file
|
|
|
|
+diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
|
|
|
|
+--- a/tools/gritsettings/resource_ids.spec
|
|
|
|
++++ b/tools/gritsettings/resource_ids.spec
|
|
|
|
+@@ -585,6 +585,12 @@
|
|
|
|
+ "components/autofill/core/browser/autofill_address_rewriter_resources.grd":{
|
|
|
|
+ "includes": [3720]
|
|
|
|
+ },
|
|
|
|
++ "components/user_scripts/renderer/resources/user_scripts_renderer_resources.grd": {
|
|
|
|
++ "includes": [6000],
|
|
|
|
++ },
|
|
|
|
++ "components/user_scripts/browser/resources/browser_resources.grd": {
|
|
|
|
++ "includes": [6020],
|
|
|
|
++ },
|
|
|
|
+ # END components/ section.
|
|
|
|
+
|
|
|
|
+ # START ios/ section.
|
|
|
|
+--
|
|
|
|
+2.20.1
|
|
|
|
+
|