diff --git a/DEPS b/DEPS index f5580a8..521af94 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'c43559e6e6bd403c5f3abc46a33e996498eef63f', + 'skia_revision': '0168e0442e00a42d8367422542033bc8a1ac9efe', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'abb293d2c26943d89c113d3f23baa88ff9830e3f', + 'v8_revision': '0f46fd7ac5cebbfac599ed99b4ea094a173f298e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -92,11 +92,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype-android # and whatever else without interference from each other. - 'freetype_android_revision': 'c38be52bf8de3b1699d74932b849bf150265819e', + 'freetype_android_revision': '66725768cdf758cfb3f9abf03cbf5e5a77f42088', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'fadc168f513f08690b97aea9fa132464bf1648f7', + 'catapult_revision': 'fc25e6f948ca9973f27d812a8cc7086738b279a1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/android_webview/common/crash_reporter/aw_microdump_crash_reporter.cc b/android_webview/common/crash_reporter/aw_microdump_crash_reporter.cc index 3cf45d6bd..6d5e33d9 100644 --- a/android_webview/common/crash_reporter/aw_microdump_crash_reporter.cc +++ b/android_webview/common/crash_reporter/aw_microdump_crash_reporter.cc
@@ -38,7 +38,19 @@ size_t RegisterCrashKeys() override; bool IsRunningUnattended() override { return false; } - bool GetCollectStatsConsent() override { return false; } + bool GetCollectStatsConsent() override { +#if defined(GOOGLE_CHROME_BUILD) + // TODO(gsennton): Enabling minidump-generation unconditionally means we + // will generate minidumps even if the user doesn't consent to minidump + // uploads. However, we will check user-consent before uploading any + // minidumps, if we do not have user consent we will delete the minidumps. + // We should investigate whether we can avoid generating minidumps + // altogether if we don't have user consent, see crbug.com/692485 + return true; +#else + return false; +#endif // defined(GOOGLE_CHROME_BUILD) + } void GetProductNameAndVersion(const char** product_name, const char** version) override {
diff --git a/android_webview/java/src/org/chromium/android_webview/command_line/CommandLineUtil.java b/android_webview/java/src/org/chromium/android_webview/command_line/CommandLineUtil.java index e05c309..01ec647 100644 --- a/android_webview/java/src/org/chromium/android_webview/command_line/CommandLineUtil.java +++ b/android_webview/java/src/org/chromium/android_webview/command_line/CommandLineUtil.java
@@ -42,9 +42,4 @@ CommandLine.init(null); } } - - public static void initCommandLineIfNotInitialized() { - if (CommandLine.isInitialized()) return; - initCommandLine(); - } }
diff --git a/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploadJobService.java b/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploadJobService.java index 841ae36d..88735ea3 100644 --- a/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploadJobService.java +++ b/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploadJobService.java
@@ -8,6 +8,7 @@ import android.app.job.JobService; import android.os.Build; +import org.chromium.android_webview.command_line.CommandLineUtil; import org.chromium.base.ContextUtils; /** @@ -23,6 +24,10 @@ @Override public void onCreate() { super.onCreate(); + // This overwrites the command line set by ChromeApplication.onCreate() to use a + // WebView-specific command line file. This is okay since this Service is not running in the + // same process as the main Chrome process. + CommandLineUtil.initCommandLine(); } @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploaderImpl.java b/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploaderImpl.java index e47cd268..7b49540 100644 --- a/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploaderImpl.java +++ b/android_webview/java/src/org/chromium/android_webview/crash/MinidumpUploaderImpl.java
@@ -129,9 +129,9 @@ } @Override public boolean isUploadEnabledForTests() { - // Note that CommandLine/CommandLineUtil are not thread safe (so only use them from - // one thread!). - CommandLineUtil.initCommandLineIfNotInitialized(); + // Note that CommandLine/CommandLineUtil are not thread safe. They are initialized + // on the main thread, but before the current worker thread started - so this thread + // will have seen the initialization of the CommandLine. return CommandLine.getInstance().hasSwitch( CommandLineUtil.CRASH_UPLOADS_ENABLED_FOR_TESTING_SWITCH); }
diff --git a/chrome/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 0000000..dbf731b5 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 0000000..656d022 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_fast_forward.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_fast_forward.png deleted file mode 100644 index 5df6c0e..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_fast_forward.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_fast_rewind.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_fast_rewind.png deleted file mode 100644 index 3b81844..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_fast_rewind.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_pause.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_pause.png deleted file mode 100644 index 454d3087..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_pause.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_play.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_play.png deleted file mode 100644 index bdc889e..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_play.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_skip_next.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_skip_next.png deleted file mode 100644 index 80664ab..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_skip_next.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_skip_previous.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_skip_previous.png deleted file mode 100644 index 286e254..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_skip_previous.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_media_control_stop.png b/chrome/android/java/res/drawable-hdpi/ic_media_control_stop.png deleted file mode 100644 index 96fab3a..0000000 --- a/chrome/android/java/res/drawable-hdpi/ic_media_control_stop.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_pause_white_24dp.png b/chrome/android/java/res/drawable-hdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000..4d2ea05 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_pause_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_pause_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_pause_white_36dp.png new file mode 100644 index 0000000..1d02439 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_pause_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png index 35f2e7f..57c9fa5 100644 --- a/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png +++ b/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 0000000..29adeed --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png new file mode 100644 index 0000000..9cc4bf9 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 0000000..da1c1c95 --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_stop_white_36dp.png b/chrome/android/java/res/drawable-hdpi/ic_stop_white_36dp.png new file mode 100644 index 0000000..266214d --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/ic_stop_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 0000000..cf15f0c --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 0000000..3d754e2 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_fast_forward.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_fast_forward.png deleted file mode 100644 index 708c6118..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_fast_forward.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_fast_rewind.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_fast_rewind.png deleted file mode 100644 index 5b64055..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_fast_rewind.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_pause.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_pause.png deleted file mode 100644 index df5ee23..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_pause.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_play.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_play.png deleted file mode 100644 index 4ac92eb..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_play.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_skip_next.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_skip_next.png deleted file mode 100644 index 76828b60..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_skip_next.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_skip_previous.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_skip_previous.png deleted file mode 100644 index 15a24f3..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_skip_previous.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_media_control_stop.png b/chrome/android/java/res/drawable-mdpi/ic_media_control_stop.png deleted file mode 100644 index a785bb1..0000000 --- a/chrome/android/java/res/drawable-mdpi/ic_media_control_stop.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_pause_white_24dp.png b/chrome/android/java/res/drawable-mdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000..2272d47 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_pause_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_pause_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_pause_white_36dp.png new file mode 100644 index 0000000..4d2ea05 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_pause_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png b/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png index cc060f1a..c61e948 100644 --- a/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png +++ b/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 0000000..57c9fa5 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png new file mode 100644 index 0000000..044b6b0 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 0000000..23faeeb --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_stop_white_36dp.png b/chrome/android/java/res/drawable-mdpi/ic_stop_white_36dp.png new file mode 100644 index 0000000..dfff26c --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/ic_stop_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 0000000..ad9097d --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 0000000..12ff39a --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_fast_forward.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_fast_forward.png deleted file mode 100644 index 7e23977..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_fast_forward.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_fast_rewind.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_fast_rewind.png deleted file mode 100644 index 9310a29..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_fast_rewind.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_pause.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_pause.png deleted file mode 100644 index cc331b49..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_pause.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_play.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_play.png deleted file mode 100644 index cec780d..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_play.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_skip_next.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_skip_next.png deleted file mode 100644 index 0452fd87..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_skip_next.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_skip_previous.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_skip_previous.png deleted file mode 100644 index 6d56e18ac..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_skip_previous.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_media_control_stop.png b/chrome/android/java/res/drawable-xhdpi/ic_media_control_stop.png deleted file mode 100644 index 393550b..0000000 --- a/chrome/android/java/res/drawable-xhdpi/ic_media_control_stop.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png b/chrome/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000..f49aed7 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png new file mode 100644 index 0000000..7192ad4 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 0000000..547ef30 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png new file mode 100644 index 0000000..3ee6d75 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 0000000..1181ec9 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_stop_white_36dp.png b/chrome/android/java/res/drawable-xhdpi/ic_stop_white_36dp.png new file mode 100644 index 0000000..801d341 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/ic_stop_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 0000000..9c401ccd --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 0000000..253833b --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_fast_forward.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_fast_forward.png deleted file mode 100644 index 05aff43..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_fast_forward.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_fast_rewind.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_fast_rewind.png deleted file mode 100644 index 9771276..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_fast_rewind.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_pause.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_pause.png deleted file mode 100644 index f60546d..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_pause.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_play.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_play.png deleted file mode 100644 index 950fced9..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_play.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_skip_next.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_skip_next.png deleted file mode 100644 index 86325d9f..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_skip_next.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_skip_previous.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_skip_previous.png deleted file mode 100644 index 3a23003..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_skip_previous.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_stop.png b/chrome/android/java/res/drawable-xxhdpi/ic_media_control_stop.png deleted file mode 100644 index e1c37ea..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/ic_media_control_stop.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000..7192ad4 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png new file mode 100644 index 0000000..a03bad2 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 0000000..23bb1ba9 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png new file mode 100644 index 0000000..4652215 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 0000000..d663064 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_stop_white_36dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_stop_white_36dp.png new file mode 100644 index 0000000..adef631 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/ic_stop_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 0000000..133aa856 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 0000000..e1baaa3 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_fast_forward.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_fast_forward.png deleted file mode 100644 index bfc9040..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_fast_forward.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_fast_rewind.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_fast_rewind.png deleted file mode 100644 index 9b50e89..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_fast_rewind.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_pause.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_pause.png deleted file mode 100644 index ce48a2f..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_pause.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_play.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_play.png deleted file mode 100644 index fc61289..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_play.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_skip_next.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_skip_next.png deleted file mode 100644 index 377a21eb..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_skip_next.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_skip_previous.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_skip_previous.png deleted file mode 100644 index 7e24d13a..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_skip_previous.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_stop.png b/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_stop.png deleted file mode 100644 index 3d634d67..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/ic_media_control_stop.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png new file mode 100644 index 0000000..660ac658 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png new file mode 100644 index 0000000..3ea7e03 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 0000000..2745c3a --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png new file mode 100644 index 0000000..2928116 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 0000000..73e30477 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_stop_white_36dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_stop_white_36dp.png new file mode 100644 index 0000000..035ca181 --- /dev/null +++ b/chrome/android/java/res/drawable-xxxhdpi/ic_stop_white_36dp.png Binary files differ
diff --git a/chrome/android/java/res/layout/download_item_view.xml b/chrome/android/java/res/layout/download_item_view.xml index fec501d..e52746e2 100644 --- a/chrome/android/java/res/layout/download_item_view.xml +++ b/chrome/android/java/res/layout/download_item_view.xml
@@ -3,14 +3,14 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<!-- Represents a single item in the DownloadHistoryAdapterView. --> +<!-- Represents a single item in the DownloadHistoryAdapterView. --> <view class="org.chromium.chrome.browser.download.ui.DownloadItemView" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:chrome="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" > - <!-- The end margin is not assigned because the cancel button overlaps it. --> + <!-- The end margin is not assigned because the cancel button overlaps it. --> <LinearLayout android:id="@+id/layout_container" android:layout_width="match_parent" @@ -36,7 +36,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" > - + <TextView android:id="@+id/filename_completed_view" android:layout_width="wrap_content" @@ -48,7 +48,7 @@ android:singleLine="true" android:textColor="@color/default_text_color" android:textSize="16sp" /> - + <TextView android:id="@+id/hostname_view" android:layout_width="0dp" @@ -63,7 +63,7 @@ android:textColor="@color/google_grey_600" android:ellipsize="start" android:singleLine="true" /> - + <TextView android:id="@+id/filesize_view" android:layout_width="wrap_content" @@ -77,7 +77,7 @@ android:textColor="@color/google_grey_600" android:singleLine="true" /> </RelativeLayout> - + <!-- Shown for downloads that haven't been completed. --> <RelativeLayout android:id="@+id/progress_layout" @@ -140,7 +140,7 @@ android:padding="8dp" android:background="?attr/selectableItemBackground" android:contentDescription="@string/download_notification_pause_button" - android:src="@drawable/ic_media_control_pause" + android:src="@drawable/ic_pause_white_24dp" chrome:tint="@color/google_grey_600" /> <org.chromium.chrome.browser.widget.TintedImageButton @@ -157,4 +157,4 @@ chrome:tint="@color/google_grey_600" /> </RelativeLayout> </LinearLayout> -</view> \ No newline at end of file +</view>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java index 5c50882..c0430c98c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -328,7 +328,7 @@ Intent pauseIntent = buildActionIntent( mContext, ACTION_DOWNLOAD_PAUSE, downloadGuid, isOffTheRecord, isOfflinePage); - builder.addAction(R.drawable.ic_media_control_pause, + builder.addAction(R.drawable.ic_pause_white_24dp, mContext.getResources().getString(R.string.download_notification_pause_button), buildPendingIntent(pauseIntent, notificationId));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java index 511aa92..a2e05b93 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
@@ -184,12 +184,12 @@ boolean isIndeterminate = item.isIndeterminate(); if (item.isPaused()) { - mPauseResumeButton.setImageResource(R.drawable.ic_media_control_play); + mPauseResumeButton.setImageResource(R.drawable.ic_play_arrow_white_24dp); mPauseResumeButton.setContentDescription( getContext().getString(R.string.download_notification_resume_button)); mProgressView.setIndeterminate(false); } else { - mPauseResumeButton.setImageResource(R.drawable.ic_media_control_pause); + mPauseResumeButton.setImageResource(R.drawable.ic_pause_white_24dp); mPauseResumeButton.setContentDescription( getContext().getString(R.string.download_notification_pause_button)); mProgressView.setIndeterminate(isIndeterminate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java index dad837b..e2649e6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
@@ -180,7 +180,7 @@ // into a high priority one. builder.setPriority(Notification.PRIORITY_HIGH); builder.setVibrate(new long[0]); - builder.addAction(R.drawable.ic_media_control_stop, + builder.addAction(R.drawable.ic_stop_white_36dp, mContext.getResources().getString(R.string.accessibility_stop), buildStopCapturePendingIntent(notificationId)); } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java index 11431f7be..a83e0f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -585,26 +585,26 @@ mActionToButtonInfo = new SparseArray<>(); mActionToButtonInfo.put(MediaSessionAction.PLAY, - new MediaButtonInfo(R.drawable.ic_media_control_play, R.string.accessibility_play, - ListenerService.ACTION_PLAY)); + new MediaButtonInfo(R.drawable.ic_play_arrow_white_36dp, + R.string.accessibility_play, ListenerService.ACTION_PLAY)); mActionToButtonInfo.put(MediaSessionAction.PAUSE, - new MediaButtonInfo(R.drawable.ic_media_control_pause, R.string.accessibility_pause, + new MediaButtonInfo(R.drawable.ic_pause_white_36dp, R.string.accessibility_pause, ListenerService.ACTION_PAUSE)); mActionToButtonInfo.put(CUSTOM_MEDIA_SESSION_ACTION_STOP, - new MediaButtonInfo(R.drawable.ic_media_control_stop, R.string.accessibility_stop, + new MediaButtonInfo(R.drawable.ic_stop_white_36dp, R.string.accessibility_stop, ListenerService.ACTION_STOP)); mActionToButtonInfo.put(MediaSessionAction.PREVIOUS_TRACK, - new MediaButtonInfo(R.drawable.ic_media_control_skip_previous, + new MediaButtonInfo(R.drawable.ic_skip_previous_white_36dp, R.string.accessibility_previous_track, ListenerService.ACTION_PREVIOUS_TRACK)); mActionToButtonInfo.put(MediaSessionAction.NEXT_TRACK, - new MediaButtonInfo(R.drawable.ic_media_control_skip_next, + new MediaButtonInfo(R.drawable.ic_skip_next_white_36dp, R.string.accessibility_next_track, ListenerService.ACTION_NEXT_TRACK)); mActionToButtonInfo.put(MediaSessionAction.SEEK_FORWARD, - new MediaButtonInfo(R.drawable.ic_media_control_fast_forward, + new MediaButtonInfo(R.drawable.ic_fast_forward_white_36dp, R.string.accessibility_seek_forward, ListenerService.ACTION_SEEK_FORWARD)); mActionToButtonInfo.put(MediaSessionAction.SEEK_BACKWARD, - new MediaButtonInfo(R.drawable.ic_media_control_fast_rewind, + new MediaButtonInfo(R.drawable.ic_fast_rewind_white_36dp, R.string.accessibility_seek_backward, ListenerService.ACTION_SEEK_BACKWARD)); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/ntp/OWNERS index 50959dc0..a94fe8c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/OWNERS
@@ -2,3 +2,6 @@ mvanouwerkerk@chromium.org peconn@chromium.org tedchoc@chromium.org + +# TEAM: ntp-dev@chromium.org +# COMPONENT: UI>Browser>NewTabPage
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java index f3d6805..6b35c340 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java
@@ -36,6 +36,12 @@ /** The score expressing relative quality of the article for the user. */ public final float mScore; + /** + * The time when the article was fetched from the server. This field is only used for remote + * suggestions. + */ + public final long mFetchTimestampMilliseconds; + /** The rank of this article within its section. */ private int mPerSectionRank = -1; @@ -67,15 +73,17 @@ * Creates a SnippetArticleListItem object that will hold the data. */ public SnippetArticle(int category, String idWithinCategory, String title, String publisher, - String previewText, String url, long timestamp, float score) { + String previewText, String url, long publishTimestamp, float score, + long fetchTimestamp) { mCategory = category; mIdWithinCategory = idWithinCategory; mTitle = title; mPublisher = publisher; mPreviewText = previewText; mUrl = url; - mPublishTimestampMilliseconds = timestamp; + mPublishTimestampMilliseconds = publishTimestamp; mScore = score; + mFetchTimestampMilliseconds = fetchTimestamp; } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java index eb97251..87dee41 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
@@ -154,7 +154,8 @@ assert mNativeSnippetsBridge != 0; nativeOnSuggestionShown(mNativeSnippetsBridge, suggestion.getGlobalRank(), suggestion.mCategory, suggestion.getPerSectionRank(), - suggestion.mPublishTimestampMilliseconds, suggestion.mScore); + suggestion.mPublishTimestampMilliseconds, suggestion.mScore, + suggestion.mFetchTimestampMilliseconds); } @Override @@ -262,10 +263,10 @@ @CalledByNative private static SnippetArticle addSuggestion(List<SnippetArticle> suggestions, int category, String id, String title, String publisher, String previewText, String url, - long timestamp, float score) { + long timestamp, float score, long fetchTime) { int position = suggestions.size(); suggestions.add(new SnippetArticle( - category, id, title, publisher, previewText, url, timestamp, score)); + category, id, title, publisher, previewText, url, timestamp, score, fetchTime)); return suggestions.get(position); } @@ -343,7 +344,8 @@ private native void nativeOnPageShown( long nativeNTPSnippetsBridge, int[] categories, int[] suggestionsPerCategory); private native void nativeOnSuggestionShown(long nativeNTPSnippetsBridge, int globalPosition, - int category, int positionInCategory, long publishTimestampMs, float score); + int category, int positionInCategory, long publishTimestampMs, float score, + long fetchTimestampMs); private native void nativeOnSuggestionOpened(long nativeNTPSnippetsBridge, int globalPosition, int category, int categoryIndex, int positionInCategory, long publishTimestampMs, float score, int windowOpenDisposition);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java index 3990052..79a6a43 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -49,6 +49,9 @@ private static final String[] FAKE_MOST_VISITED_TITLES = new String[] {"Simple"}; private static final String[] FAKE_MOST_VISITED_WHITELIST_ICON_PATHS = new String[] {""}; private static final int[] FAKE_MOST_VISITED_SOURCES = new int[] {NTPTileSource.TOP_SITES}; + private static final long FAKE_PUBLISH_TIMESTAMP = 1466614774; + private static final long FAKE_FETCH_TIMESTAMP = 1466634774; + private static final float FAKE_SNIPPET_SCORE = 10f; // TODO(dgn): Properly bypass the native code when testing with a fake suggestions source. // We currently mix the fake and the snippets bridge, resulting in crashes with unregistered @@ -303,7 +306,8 @@ for (int i = 0; i < suggestionsCount; i++) { String url = mTestServer.getURL(TEST_PAGE) + "#" + i; suggestions.add(new SnippetArticle(TEST_CATEGORY, "id" + i, "title" + i, - "publisher" + i, "previewText" + i, url, 1466614774 + i, 10f)); + "publisher" + i, "previewText" + i, url, FAKE_PUBLISH_TIMESTAMP + i, + FAKE_SNIPPET_SCORE, FAKE_FETCH_TIMESTAMP)); } return suggestions; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java index 573c40e5..cc7666e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -134,8 +134,9 @@ int minimalCategory = 1; SnippetArticle shortSnippet = new SnippetArticle(fullCategory, "id1", "Snippet", "Publisher", "Preview Text", "www.google.com", - 1466614774, // Timestamp - 10f); // Score + 1466614774, // Publish timestamp + 10f, // Score + 1466634774); // Fetch timestamp shortSnippet.setThumbnailBitmap(BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.signin_promo_illustration)); @@ -143,19 +144,22 @@ new String(new char[20]).replace("\0", "Snippet "), new String(new char[20]).replace("\0", "Publisher "), new String(new char[80]).replace("\0", "Preview Text "), "www.google.com", - 1466614074, // Timestamp - 20f); // Score + 1466614074, // Publish timestamp + 20f, // Score + 1466634774); // Fetch timestamp SnippetArticle minimalSnippet = new SnippetArticle(minimalCategory, "id3", new String(new char[20]).replace("\0", "Bookmark "), "Publisher", "This should not be displayed", "www.google.com", - 1466614774, // Timestamp - 10f); // Score + 1466614774, // Publish timestamp + 10f, // Score + 1466634774); // Fetch timestamp SnippetArticle minimalSnippet2 = new SnippetArticle(minimalCategory, "id4", "Bookmark", "Publisher", "This should not be displayed", "www.google.com", - 1466614774, // Timestamp - 10f); // Score + 1466614774, // Publish timestamp + 10f, // Score + 1466634774); // Fetch timestamp mSnippetsSource.setInfoForCategory( fullCategory, new SuggestionsCategoryInfo(fullCategory, "Section Title",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java index f12e4af..1726bfd 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -304,7 +304,7 @@ // Add another suggestion. suggestions.add(new SnippetArticle(TEST_CATEGORY, "https://site.com/url1", "title1", "pub1", - "txt1", "https://site.com/url1", 0, 0)); + "txt1", "https://site.com/url1", 0, 0, 0)); // When suggestion are disabled, we should not be able to load them. mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.SIGNED_OUT);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 5bd05a1..68ff602 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1117,6 +1117,8 @@ "search/most_visited_iframe_source.h", "search/search.cc", "search/search.h", + "search/search_engine_base_url_tracker.cc", + "search/search_engine_base_url_tracker.h", "search/suggestions/image_decoder_impl.cc", "search/suggestions/image_decoder_impl.h", "search/suggestions/suggestions_service_factory.cc", @@ -2207,8 +2209,8 @@ "download/notification/download_notification_manager.h", "media/public_session_media_access_handler.cc", "media/public_session_media_access_handler.h", - "media/webrtc/public_session_tab_capture_access_handler.cc", - "media/webrtc/public_session_tab_capture_access_handler.h", + "media/public_session_tab_capture_access_handler.cc", + "media/public_session_tab_capture_access_handler.h", "memory/memory_kills_histogram.h", "memory/memory_kills_monitor.cc", "memory/memory_kills_monitor.h",
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.cc b/chrome/browser/android/ntp/ntp_snippets_bridge.cc index 3389af1..b5d38e19 100644 --- a/chrome/browser/android/ntp/ntp_snippets_bridge.cc +++ b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
@@ -22,10 +22,8 @@ #include "components/history/core/browser/history_service.h" #include "components/ntp_snippets/content_suggestion.h" #include "components/ntp_snippets/content_suggestions_metrics.h" -#include "components/ntp_snippets/pref_names.h" #include "components/ntp_snippets/remote/remote_suggestions_provider.h" #include "components/ntp_snippets/remote/remote_suggestions_scheduler.h" -#include "components/prefs/pref_service.h" #include "jni/SnippetsBridge_jni.h" #include "ui/base/window_open_disposition.h" #include "ui/gfx/android/java_bitmap.h" @@ -65,7 +63,8 @@ ConvertUTF16ToJavaString(env, suggestion.publisher_name()), ConvertUTF16ToJavaString(env, suggestion.snippet_text()), ConvertUTF8ToJavaString(env, suggestion.url().spec()), - suggestion.publish_date().ToJavaTime(), suggestion.score()); + suggestion.publish_date().ToJavaTime(), suggestion.score(), + suggestion.fetch_date().ToJavaTime()); if (suggestion.id().category().IsKnownCategory( KnownCategories::DOWNLOADS) && suggestion.download_suggestion_extra() != nullptr) { @@ -325,16 +324,12 @@ jint j_category_id, jint position_in_category, jlong publish_timestamp_ms, - jfloat score) { - PrefService* pref_service = ProfileManager::GetLastUsedProfile()->GetPrefs(); - base::Time last_background_fetch_time = - base::Time::FromInternalValue(pref_service->GetInt64( - ntp_snippets::prefs::kLastSuccessfulBackgroundFetchTime)); - + jfloat score, + jlong fetch_timestamp_ms) { ntp_snippets::metrics::OnSuggestionShown( global_position, Category::FromIDValue(j_category_id), position_in_category, base::Time::FromJavaTime(publish_timestamp_ms), - last_background_fetch_time, score); + score, base::Time::FromJavaTime(fetch_timestamp_ms)); if (global_position == 0) { content_suggestions_service_->user_classifier()->OnEvent( ntp_snippets::UserClassifier::Metric::SUGGESTIONS_SHOWN);
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.h b/chrome/browser/android/ntp/ntp_snippets_bridge.h index f7af988..78dc3ff 100644 --- a/chrome/browser/android/ntp/ntp_snippets_bridge.h +++ b/chrome/browser/android/ntp/ntp_snippets_bridge.h
@@ -99,7 +99,8 @@ jint j_category_id, jint position_in_category, jlong publish_timestamp_ms, - jfloat score); + jfloat score, + jlong fetch_timestamp_ms); void OnSuggestionOpened(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index be9be32c..1e3d9ea 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -453,6 +453,8 @@ "extensions/launcher_search_provider.h", "extensions/media_player_event_router.cc", "extensions/media_player_event_router.h", + "extensions/public_session_permission_helper.cc", + "extensions/public_session_permission_helper.h", "extensions/signin_screen_policy_provider.cc", "extensions/signin_screen_policy_provider.h", "external_metrics.cc", @@ -1528,6 +1530,7 @@ "extensions/file_manager/device_event_router_unittest.cc", "extensions/file_manager/job_event_router_unittest.cc", "extensions/gfx_utils_unittest.cc", + "extensions/public_session_permission_helper_unittest.cc", "extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc", "extensions/signin_screen_policy_provider_unittest.cc", "extensions/wallpaper_private_api_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc index 3ad17bd..df090969 100644 --- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc +++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
@@ -91,7 +91,7 @@ if (!policy_value) return; if (!policy_value->IsType(base::Value::Type::BOOLEAN)) { - LOG(ERROR) << "Policy " << policy_name << " is not a boolean."; + NOTREACHED() << "Policy " << policy_name << " is not a boolean."; return; } bool bool_value; @@ -111,7 +111,7 @@ if (!policy_value) return; if (!policy_value->IsType(base::Value::Type::INTEGER)) { - LOG(ERROR) << "Policy " << policy_name << " is not an integer."; + NOTREACHED() << "Policy " << policy_name << " is not an integer."; return; } int int_value; @@ -119,6 +119,28 @@ filtered_policies->SetBoolean(arc_policy_name, int_value == int_true); } +// Checks whether |policy_name| is present as an object and has all |fields|, +// Sets |arc_policy_name| to true only if the condition above is satisfied. +void MapObjectToPresenceBool(const std::string& arc_policy_name, + const std::string& policy_name, + const policy::PolicyMap& policy_map, + base::DictionaryValue* filtered_policies, + const std::vector<std::string>& fields) { + const base::Value* const policy_value = policy_map.GetValue(policy_name); + if (!policy_value) + return; + const base::DictionaryValue* dict = nullptr; + if (!policy_value->GetAsDictionary(&dict)) { + NOTREACHED() << "Policy " << policy_name << " is not an object."; + return; + } + for (const auto& field : fields) { + if (!dict->HasKey(field)) + return; + } + filtered_policies->SetBoolean(arc_policy_name, true); +} + void AddGlobalAppRestriction(const std::string& arc_app_restriction_name, const std::string& policy_name, const policy::PolicyMap& policy_map, @@ -264,6 +286,8 @@ MapBoolToBool("mountPhysicalMediaDisabled", policy::key::kExternalStorageDisabled, policy_map, false, &filtered_policies); + MapObjectToPresenceBool("setWallpaperDisabled", policy::key::kWallpaperImage, + policy_map, &filtered_policies, {"url", "hash"}); // Add global app restrictions. AddGlobalAppRestriction("com.android.browser:URLBlacklist",
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc index 59b9f68c..d2c2acf 100644 --- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc +++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge_unittest.cc
@@ -274,6 +274,27 @@ PolicyStringCallback("{\"mountPhysicalMediaDisabled\":true}")); } +TEST_F(ArcPolicyBridgeTest, WallpaperImageSetTest) { + base::DictionaryValue dict; + dict.SetString("url", "https://example.com/wallpaper.jpg"); + dict.SetString("hash", "somehash"); + policy_map().Set(policy::key::kWallpaperImage, policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, + dict.CreateDeepCopy(), nullptr); + policy_bridge()->GetPolicies( + PolicyStringCallback("{\"setWallpaperDisabled\":true}")); +} + +TEST_F(ArcPolicyBridgeTest, WallpaperImageSet_NotCompletePolicyTest) { + base::DictionaryValue dict; + dict.SetString("url", "https://example.com/wallpaper.jpg"); + // "hash" attribute is missing, so the policy shouldn't be set + policy_map().Set(policy::key::kWallpaperImage, policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, + dict.CreateDeepCopy(), nullptr); + policy_bridge()->GetPolicies(PolicyStringCallback("{}")); +} + TEST_F(ArcPolicyBridgeTest, URLBlacklistTest) { base::ListValue blacklist; blacklist.AppendString("www.blacklist1.com");
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc index d73b3878..0938b0d 100644 --- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc +++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -565,9 +565,10 @@ // icon and won't extract any data. "notifications", - // Captures page content, so block. Alternatively: Allow, but either (1) - // prompt user or (2) return blank content. - // "pageCapture", + // User is prompted (allow/deny) when an extension requests pageCapture for + // the first time in a session. The request is made via + // chrome.pageCapture.saveAsMHTML call. + "pageCapture", // Allows to use machine crypto keys - these would be provisioned by the // admin anyways.
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper.cc b/chrome/browser/chromeos/extensions/public_session_permission_helper.cc new file mode 100644 index 0000000..c54a197 --- /dev/null +++ b/chrome/browser/chromeos/extensions/public_session_permission_helper.cc
@@ -0,0 +1,227 @@ +// 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. + +#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/lazy_instance.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "chrome/browser/extensions/extension_install_prompt.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_id.h" +#include "extensions/common/permissions/manifest_permission_set.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/url_pattern_set.h" + +namespace extensions { +namespace permission_helper { + +namespace { + +std::unique_ptr<ExtensionInstallPrompt> CreateExtensionInstallPrompt( + content::WebContents* web_contents) { + return base::MakeUnique<ExtensionInstallPrompt>(web_contents); +} + +// This class is the internal implementation of HandlePermissionRequest(). It +// contains the actual prompt showing and resolving logic, and it caches the +// user choices. +class PublicSessionPermissionHelper { + public: + PublicSessionPermissionHelper(); + PublicSessionPermissionHelper(PublicSessionPermissionHelper&& other); + ~PublicSessionPermissionHelper(); + + void HandlePermissionRequestImpl(const Extension& extension, + const PermissionIDSet& requested_permissions, + content::WebContents* web_contents, + const RequestResolvedCallback& callback, + const PromptFactory& prompt_factory); + + private: + void ResolvePermissionPrompt(const ExtensionInstallPrompt* prompt, + const PermissionIDSet& unprompted_permissions, + ExtensionInstallPrompt::Result prompt_result); + + PermissionIDSet FilterAllowedPermissions(const PermissionIDSet& permissions); + + struct RequestCallback { + RequestCallback(const RequestResolvedCallback& callback, + const PermissionIDSet& permission_list); + RequestCallback(const RequestCallback& other); + ~RequestCallback(); + RequestResolvedCallback callback; + PermissionIDSet permission_list; + }; + using RequestCallbackList = std::vector<RequestCallback>; + + std::set<std::unique_ptr<ExtensionInstallPrompt>> prompts_; + PermissionIDSet prompted_permission_set_; + PermissionIDSet allowed_permission_set_; + PermissionIDSet denied_permission_set_; + RequestCallbackList callbacks_; + + DISALLOW_COPY_AND_ASSIGN(PublicSessionPermissionHelper); +}; + +PublicSessionPermissionHelper::PublicSessionPermissionHelper() {} + +PublicSessionPermissionHelper::PublicSessionPermissionHelper( + PublicSessionPermissionHelper&& other) = default; + +PublicSessionPermissionHelper::~PublicSessionPermissionHelper() {} + +void PublicSessionPermissionHelper::HandlePermissionRequestImpl( + const Extension& extension, + const PermissionIDSet& requested_permissions, + content::WebContents* web_contents, + const RequestResolvedCallback& callback, + const PromptFactory& prompt_factory) { + CHECK(web_contents); + + PermissionIDSet unresolved_permissions = PermissionIDSet::Difference( + requested_permissions, allowed_permission_set_); + unresolved_permissions = PermissionIDSet::Difference( + unresolved_permissions, denied_permission_set_); + if (unresolved_permissions.empty()) { + // All requested permissions are already resolved. + callback.Run(FilterAllowedPermissions(requested_permissions)); + return; + } + + // Since not all permissions are resolved yet, queue the callback to be called + // when all of them are resolved. + callbacks_.push_back(RequestCallback(callback, requested_permissions)); + + PermissionIDSet unprompted_permissions = PermissionIDSet::Difference( + unresolved_permissions, prompted_permission_set_); + if (unprompted_permissions.empty()) { + // Some permissions aren't resolved yet, but they are currently being + // prompted for, so no need to show a prompt. + return; + } + + // Some permissions need prompting, setup the prompt and show it. + APIPermissionSet new_apis; + for (const auto& permission : unprompted_permissions) { + prompted_permission_set_.insert(permission.id()); + new_apis.insert(permission.id()); + } + auto permission_set = base::MakeUnique<PermissionSet>( + new_apis, ManifestPermissionSet(), URLPatternSet(), URLPatternSet()); + auto prompt = prompt_factory.Run(web_contents); + // This Unretained is safe because the lifetime of this object is until + // process exit. + prompt->ShowDialog( + base::Bind(&PublicSessionPermissionHelper::ResolvePermissionPrompt, + base::Unretained(this), prompt.get(), + std::move(unprompted_permissions)), + &extension, + nullptr, // Use the extension icon. + base::MakeUnique<ExtensionInstallPrompt::Prompt>( + ExtensionInstallPrompt::PERMISSIONS_PROMPT), + std::move(permission_set), + ExtensionInstallPrompt::GetDefaultShowDialogCallback()); + prompts_.insert(std::move(prompt)); +} + +void PublicSessionPermissionHelper::ResolvePermissionPrompt( + const ExtensionInstallPrompt* prompt, + const PermissionIDSet& unprompted_permissions, + ExtensionInstallPrompt::Result prompt_result) { + PermissionIDSet& add_to_set = + prompt_result == ExtensionInstallPrompt::Result::ACCEPTED ? + allowed_permission_set_ : denied_permission_set_; + for (const auto& permission : unprompted_permissions) { + prompted_permission_set_.erase(permission.id()); + add_to_set.insert(permission.id()); + } + + // Here a list of callbacks to be invoked is created first from callbacks_, + // then those callbacks are invoked later because a callback can change + // callbacks_ while we're traversing them. + RequestCallbackList callbacks_to_invoke; + for (auto callback = callbacks_.begin(); callback != callbacks_.end(); ) { + if (prompted_permission_set_.ContainsAnyID(callback->permission_list)) { + // The request is still waiting on other permissions to be resolved - wait + // until all of them are resolved before calling the callback. + callback++; + continue; + } + callbacks_to_invoke.push_back(std::move(*callback)); + callback = callbacks_.erase(callback); + } + for (auto callback = callbacks_to_invoke.begin(); + callback != callbacks_to_invoke.end(); callback++) { + callback->callback.Run(FilterAllowedPermissions(callback->permission_list)); + } + + // Dispose of the prompt as it's not needed anymore. + auto iter = std::find_if( + prompts_.begin(), prompts_.end(), + [prompt](const std::unique_ptr<ExtensionInstallPrompt>& check) { + return check.get() == prompt; + }); + DCHECK(iter != prompts_.end()); + prompts_.erase(iter); +} + +PermissionIDSet PublicSessionPermissionHelper::FilterAllowedPermissions( + const PermissionIDSet& permissions) { + PermissionIDSet allowed_permissions; + for (auto iter = permissions.begin(); iter != permissions.end(); iter++) { + if (allowed_permission_set_.ContainsID(*iter)) { + allowed_permissions.insert(iter->id()); + } + } + return allowed_permissions; +} + +PublicSessionPermissionHelper::RequestCallback::RequestCallback( + const RequestResolvedCallback& callback, + const PermissionIDSet& permission_list) + : callback(callback), permission_list(permission_list) {} + +PublicSessionPermissionHelper::RequestCallback::RequestCallback( + const RequestCallback& other) = default; + +PublicSessionPermissionHelper::RequestCallback::~RequestCallback() {} + +base::LazyInstance<std::map<ExtensionId, PublicSessionPermissionHelper>>::Leaky + g_helpers = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +void HandlePermissionRequest(const Extension& extension, + const PermissionIDSet& requested_permissions, + content::WebContents* web_contents, + const RequestResolvedCallback& callback, + const PromptFactory& prompt_factory) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + const PromptFactory& factory = prompt_factory.is_null() + ? base::Bind(&CreateExtensionInstallPrompt) + : prompt_factory; + return g_helpers.Get()[extension.id()].HandlePermissionRequestImpl( + extension, requested_permissions, web_contents, callback, factory); +} + +void ResetPermissionsForTesting() { + // Clear out the std::map between tests. Just setting g_helpers to + // LAZY_INSTANCE_INITIALIZER again causes a memory leak (because of the + // LazyInstance::Leaky). + g_helpers.Get().clear(); +} + +} // namespace permission_helper +} // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper.h b/chrome/browser/chromeos/extensions/public_session_permission_helper.h new file mode 100644 index 0000000..445f879 --- /dev/null +++ b/chrome/browser/chromeos/extensions/public_session_permission_helper.h
@@ -0,0 +1,64 @@ +// 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. + +#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_PUBLIC_SESSION_PERMISSION_HELPER_H_ +#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_PUBLIC_SESSION_PERMISSION_HELPER_H_ + +#include <memory> +#include <set> + +#include "base/callback_forward.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/api_permission_set.h" + +class ExtensionInstallPrompt; + +namespace content { +class WebContents; +} + +namespace extensions { + +class Extension; + +namespace permission_helper { + +using RequestResolvedCallback = base::Callback<void(const PermissionIDSet&)>; +using PromptFactory = base::Callback<std::unique_ptr<ExtensionInstallPrompt>( + content::WebContents*)>; + +// In Public Sessions, extensions (and apps) are force-installed by admin policy +// so the user does not get a chance to review the permissions for these +// extensions. This is not acceptable from a security/privacy standpoint, so +// when an extension uses one of the sensitive APIs for the first time, we show +// the user a dialog where they can choose whether to allow the extension access +// to the API. +// +// This function sets up the prompt asking the user for additional +// permission(s), handles the result, caches it, and then runs the callback with +// the allowed permissions as the argument. +// +// The user will be prompted about a certain permission only once, and that +// choice will be cached and used in any subsequent requests that use the same +// permission. If a request comes for a permission that is currently being +// prompted, its callback will be queued up to be invoked when the prompt is +// resolved. +// +// Caller must ensure that web_contents is valid. Must be called on UI thread. +// +// Passing in a null prompt_factory (permission_helper::PromptFactory()) +// callback gets the default behaviour (ie. it is is used only for tests). +void HandlePermissionRequest(const Extension& extension, + const PermissionIDSet& requested_permissions, + content::WebContents* web_contents, + const RequestResolvedCallback& callback, + const PromptFactory& prompt_factory); + +// Used to completely reset state in between tests. +void ResetPermissionsForTesting(); + +} // namespace permission_helper +} // namespace extensions + +#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_PUBLIC_SESSION_PERMISSION_HELPER_H_
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc b/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc new file mode 100644 index 0000000..ba370db --- /dev/null +++ b/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
@@ -0,0 +1,255 @@ +// 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. + +#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "base/run_loop.h" +#include "chrome/browser/extensions/extension_install_prompt.h" +#include "chrome/common/extensions/extension_test_util.h" +#include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_dialog_auto_confirm.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest.h" +#include "extensions/common/permissions/api_permission.h" +#include "testing/gtest/include/gtest/gtest.h" + +using extension_test_util::LoadManifestUnchecked; +using content::WebContents; +using extensions::APIPermission; +using extensions::Extension; +using extensions::Manifest; +using Result = ExtensionInstallPrompt::Result; + +namespace { + +auto permission_a = APIPermission::kAudio; +auto permission_b = APIPermission::kBookmark; +bool did_show_dialog; + +scoped_refptr<Extension> LoadManifestHelper(const std::string& id) { + std::string error; + scoped_refptr<Extension> extension = LoadManifestUnchecked( + "common/background_page", "manifest.json", Manifest::INVALID_LOCATION, + Extension::NO_FLAGS, id, &error); + EXPECT_TRUE(extension.get()) << error; + return extension; +} + +bool get_did_show_dialog_and_reset() { + bool tmp = did_show_dialog; + did_show_dialog = false; + return tmp; +} + +class ProgrammableInstallPrompt + : public ExtensionInstallPrompt, + public base::SupportsWeakPtr<ProgrammableInstallPrompt> { + public: + explicit ProgrammableInstallPrompt(WebContents* contents) + : ExtensionInstallPrompt(contents) {} + + ~ProgrammableInstallPrompt() override {} + + void ShowDialog( + const DoneCallback& done_callback, + const extensions::Extension* extension, + const SkBitmap* icon, + std::unique_ptr<Prompt> prompt, + std::unique_ptr<const extensions::PermissionSet> custom_permissions, + const ShowDialogCallback& show_dialog_callback) override { + done_callback_ = done_callback; + did_show_dialog = true; + } + + void Resolve(ExtensionInstallPrompt::Result result) { + done_callback_.Run(result); + } + + private: + ExtensionInstallPrompt::DoneCallback done_callback_; + + DISALLOW_COPY_AND_ASSIGN(ProgrammableInstallPrompt); +}; + +} // namespace + +namespace extensions { +namespace permission_helper { + +class PublicSessionPermissionHelperTest + : public ChromeRenderViewHostTestHarness { + public: + PublicSessionPermissionHelperTest() {} + ~PublicSessionPermissionHelperTest() override {} + + // testing::Test + void SetUp() override; + void TearDown() override; + + // Class helpers. + void RequestResolved(const PermissionIDSet& allowed_permissions); + std::unique_ptr<ExtensionInstallPrompt> ReturnPrompt( + std::unique_ptr<ExtensionInstallPrompt> prompt, + WebContents* web_contents); + base::WeakPtr<ProgrammableInstallPrompt> CallHandlePermissionRequest( + const scoped_refptr<Extension>& extension, + const PermissionIDSet& permissions); + + protected: + scoped_refptr<Extension> extension_a_; + scoped_refptr<Extension> extension_b_; + + std::vector<PermissionIDSet> allowed_permissions_; + + private: + DISALLOW_COPY_AND_ASSIGN(PublicSessionPermissionHelperTest); +}; + +void PublicSessionPermissionHelperTest::SetUp() { + ChromeRenderViewHostTestHarness::SetUp(); + extension_a_ = LoadManifestHelper("extension_a"); + extension_b_ = LoadManifestHelper("extension_b"); +} + +void PublicSessionPermissionHelperTest::TearDown() { + ResetPermissionsForTesting(); + ChromeRenderViewHostTestHarness::TearDown(); +} + +void PublicSessionPermissionHelperTest::RequestResolved( + const PermissionIDSet& allowed_permissions) { + allowed_permissions_.push_back(allowed_permissions); +} + +std::unique_ptr<ExtensionInstallPrompt> +PublicSessionPermissionHelperTest::ReturnPrompt( + std::unique_ptr<ExtensionInstallPrompt> prompt, + WebContents* web_contents) { + return prompt; +} + +base::WeakPtr<ProgrammableInstallPrompt> +PublicSessionPermissionHelperTest::CallHandlePermissionRequest( + const scoped_refptr<Extension>& extension, + const PermissionIDSet& permissions) { + auto prompt = new ProgrammableInstallPrompt(web_contents()); + auto prompt_weak_ptr = prompt->AsWeakPtr(); + auto factory_callback = base::Bind( + &PublicSessionPermissionHelperTest::ReturnPrompt, base::Unretained(this), + base::Passed(base::WrapUnique<ExtensionInstallPrompt>(prompt))); + HandlePermissionRequest( + *extension.get(), permissions, web_contents(), + base::Bind(&PublicSessionPermissionHelperTest::RequestResolved, + base::Unretained(this)), + factory_callback); + // In case all permissions were already prompted, ReturnPrompt isn't called + // because of an early return in HandlePermissionRequest, and in that case the + // prompt is free'd as soon as HandlePermissionRequest returns (because it's + // owned by a unique_ptr). Using a weak ptr we can detect when this happens. + return prompt_weak_ptr; +} + +TEST_F(PublicSessionPermissionHelperTest, TestPermissionAllowed) { + // Allow permission_a for extension_a. + auto prompt = CallHandlePermissionRequest(extension_a_, {permission_a}); + EXPECT_TRUE(prompt); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + prompt->Resolve(Result::ACCEPTED); + EXPECT_TRUE(allowed_permissions_.at(0).Equals({permission_a})); + + // permission_a was already allowed for extension_a hence no prompt is being + // shown, and the ProgrammableInstallPrompt that is passed in is already + // free'd after CallHandlePermissionRequest returns so the returned weak + // pointer should evaluate to false. + EXPECT_FALSE(CallHandlePermissionRequest(extension_a_, {permission_a})); + EXPECT_FALSE(get_did_show_dialog_and_reset()); + EXPECT_TRUE(allowed_permissions_.at(1).Equals({permission_a})); + + // permission_a was allowed only for extension_a. + prompt = CallHandlePermissionRequest(extension_b_, {permission_a}); + EXPECT_TRUE(prompt); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + prompt->Resolve(Result::USER_CANCELED); + EXPECT_TRUE(allowed_permissions_.at(2).Equals({})); +} + +TEST_F(PublicSessionPermissionHelperTest, TestPermissionDenied) { + // Deny permission_a for extension_a. + auto prompt = CallHandlePermissionRequest(extension_a_, {permission_a}); + EXPECT_TRUE(prompt); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + prompt->Resolve(Result::USER_CANCELED); + EXPECT_TRUE(allowed_permissions_.at(0).Equals({})); + + // Still denied (previous choice is remembered). + EXPECT_FALSE(CallHandlePermissionRequest(extension_a_, {permission_a})); + EXPECT_FALSE(get_did_show_dialog_and_reset()); + EXPECT_TRUE(allowed_permissions_.at(1).Equals({})); + + // permission_a was denied only for extension_a. + prompt = CallHandlePermissionRequest(extension_b_, {permission_a}); + EXPECT_TRUE(prompt); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + prompt->Resolve(Result::ACCEPTED); + EXPECT_TRUE(allowed_permissions_.at(2).Equals({permission_a})); +} + +TEST_F(PublicSessionPermissionHelperTest, TestTwoPromptsA) { + // Open two permission prompts. + auto prompt1 = + CallHandlePermissionRequest(extension_a_, {permission_a, permission_b}); + EXPECT_TRUE(prompt1); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + EXPECT_FALSE(CallHandlePermissionRequest(extension_a_, {permission_b})); + EXPECT_FALSE(get_did_show_dialog_and_reset()); + // prompt1 resolves both permission requests (second permission request + // doesn't show a prompt as permission_b is already prompted by first + // permission request). + prompt1->Resolve(Result::ACCEPTED); + EXPECT_TRUE(allowed_permissions_.at(0).Equals({permission_a, permission_b})); + EXPECT_TRUE(allowed_permissions_.at(1).Equals({permission_b})); +} + +TEST_F(PublicSessionPermissionHelperTest, TestTwoPromptsB) { + auto prompt1 = CallHandlePermissionRequest(extension_a_, {permission_a}); + EXPECT_TRUE(prompt1); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + auto prompt2 = + CallHandlePermissionRequest(extension_a_, {permission_a, permission_b}); + EXPECT_TRUE(prompt2); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + // prompt2 resolves only permission_b because prompt1 already prompted for + // permission_a. + prompt2->Resolve(Result::ACCEPTED); + EXPECT_EQ(allowed_permissions_.size(), 0u); + prompt1->Resolve(Result::ACCEPTED); + EXPECT_TRUE(allowed_permissions_.at(0).Equals({permission_a})); + EXPECT_TRUE(allowed_permissions_.at(1).Equals({permission_a, permission_b})); +} + +TEST_F(PublicSessionPermissionHelperTest, TestTwoPromptsDeny) { + auto prompt1 = CallHandlePermissionRequest(extension_a_, {permission_a}); + EXPECT_TRUE(prompt1); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + auto prompt2 = + CallHandlePermissionRequest(extension_a_, {permission_a, permission_b}); + EXPECT_TRUE(prompt2); + EXPECT_TRUE(get_did_show_dialog_and_reset()); + prompt1->Resolve(Result::USER_CANCELED); + EXPECT_TRUE(allowed_permissions_.at(0).Equals({})); + prompt2->Resolve(Result::ACCEPTED); + EXPECT_TRUE(allowed_permissions_.at(1).Equals({permission_b})); +} + +} // namespace permission_helper +} // namespace extensions
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chrome/browser/extensions/api/page_capture/page_capture_api.cc index c6f19a7..5a26fd8 100644 --- a/chrome/browser/extensions/api/page_capture/page_capture_api.cc +++ b/chrome/browser/extensions/api/page_capture/page_capture_api.cc
@@ -8,10 +8,13 @@ #include <memory> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/files/file_util.h" +#include "base/memory/ptr_util.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profiles_state.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" @@ -22,6 +25,10 @@ #include "content/public/common/mhtml_generation_params.h" #include "extensions/common/extension_messages.h" +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h" +#endif + using content::BrowserThread; using content::ChildProcessSecurityPolicy; using content::WebContents; @@ -36,6 +43,9 @@ const char kMHTMLGenerationFailedError[] = "Failed to generate MHTML."; const char kTemporaryFileError[] = "Failed to create a temporary file."; const char kTabClosedError[] = "Cannot find the tab for this request."; +#if defined(OS_CHROMEOS) +const char kUserDenied[] = "User denied request."; +#endif void ClearFileReferenceOnIOThread( scoped_refptr<storage::ShareableFileReference>) {} @@ -65,6 +75,31 @@ AddRef(); // Balanced in ReturnFailure/ReturnSuccess() + // In Public Sessions, extensions (and apps) are force-installed by admin + // policy so the user does not get a chance to review the permissions for + // these extensions. This is not acceptable from a security/privacy + // standpoint, so when an extension uses the PageCapture API for the first + // time, we show the user a dialog where they can choose whether to allow the + // extension access to the API. +#if defined(OS_CHROMEOS) + if (profiles::IsPublicSession()) { + WebContents* web_contents = GetWebContents(); + if (!web_contents) { + ReturnFailure(kTabClosedError); + return true; + } + // This Unretained is safe because this object is Released() in + // OnMessageReceived which gets called at some point after callback is run. + auto callback = + base::Bind(&PageCaptureSaveAsMHTMLFunction::ResolvePermissionRequest, + base::Unretained(this)); + permission_helper::HandlePermissionRequest( + *extension(), {APIPermission::kPageCapture}, web_contents, callback, + permission_helper::PromptFactory()); + return true; + } +#endif + BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile, this)); @@ -93,6 +128,19 @@ return true; } +#if defined(OS_CHROMEOS) +void PageCaptureSaveAsMHTMLFunction::ResolvePermissionRequest( + const PermissionIDSet& allowed_permissions) { + if (allowed_permissions.ContainsID(APIPermission::kPageCapture)) { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile, this)); + } else { + ReturnFailure(kUserDenied); + } +} +#endif + void PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); bool success = base::CreateTemporaryFile(&mhtml_path_);
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.h b/chrome/browser/extensions/api/page_capture/page_capture_api.h index f72ea84..1f4f76d 100644 --- a/chrome/browser/extensions/api/page_capture/page_capture_api.h +++ b/chrome/browser/extensions/api/page_capture/page_capture_api.h
@@ -42,6 +42,11 @@ bool RunAsync() override; bool OnMessageReceived(const IPC::Message& message) override; +#if defined(OS_CHROMEOS) + // Resolves the API permission request in Public Sessions. + void ResolvePermissionRequest(const PermissionIDSet& allowed_permissions); +#endif + // Called on the file thread. void CreateTemporaryFile();
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc b/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc index 556e4be..3cdf16b 100644 --- a/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc +++ b/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
@@ -7,11 +7,14 @@ #include "chrome/browser/extensions/api/page_capture/page_capture_api.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" +#include "chromeos/login/login_state.h" #include "content/public/common/content_switches.h" #include "content/public/test/test_utils.h" +#include "extensions/browser/extension_dialog_auto_confirm.h" #include "net/dns/mock_host_resolver.h" using extensions::PageCaptureSaveAsMHTMLFunction; +using extensions::ScopedTestDialogAutoConfirm; class ExtensionPageCaptureApiTest : public ExtensionApiTest { public: @@ -44,9 +47,46 @@ ASSERT_TRUE(StartEmbeddedTestServer()); PageCaptureSaveAsMHTMLDelegate delegate; ASSERT_TRUE(RunExtensionTest("page_capture")) << message_; + // Make sure the MHTML data gets written to the temporary file. ASSERT_FALSE(delegate.temp_file_.empty()); // Flush the message loops to make sure the delete happens. content::RunAllPendingInMessageLoop(content::BrowserThread::FILE); content::RunAllPendingInMessageLoop(content::BrowserThread::IO); + // Make sure the temporary file is destroyed once the javascript side reads + // the contents. ASSERT_FALSE(base::PathExists(delegate.temp_file_)); } + +#if defined(OS_CHROMEOS) +IN_PROC_BROWSER_TEST_F(ExtensionPageCaptureApiTest, + PublicSessionRequestAllowed) { + host_resolver()->AddRule("www.a.com", "127.0.0.1"); + ASSERT_TRUE(StartEmbeddedTestServer()); + PageCaptureSaveAsMHTMLDelegate delegate; + // Set Public Session state. + chromeos::LoginState::Get()->SetLoggedInState( + chromeos::LoginState::LOGGED_IN_ACTIVE, + chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT); + // Resolve Permission dialog with Allow. + ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::ACCEPT); + ASSERT_TRUE(RunExtensionTest("page_capture")) << message_; + ASSERT_FALSE(delegate.temp_file_.empty()); + content::RunAllPendingInMessageLoop(content::BrowserThread::FILE); + content::RunAllPendingInMessageLoop(content::BrowserThread::IO); + ASSERT_FALSE(base::PathExists(delegate.temp_file_)); +} + +IN_PROC_BROWSER_TEST_F(ExtensionPageCaptureApiTest, + PublicSessionRequestDenied) { + host_resolver()->AddRule("www.a.com", "127.0.0.1"); + ASSERT_TRUE(StartEmbeddedTestServer()); + // Set Public Session state. + chromeos::LoginState::Get()->SetLoggedInState( + chromeos::LoginState::LOGGED_IN_ACTIVE, + chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT); + // Resolve Permission dialog with Deny. + ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::CANCEL); + ASSERT_TRUE(RunExtensionTestWithArg("page_capture", "REQUEST_DENIED")) + << message_; +} +#endif // defined(OS_CHROMEOS)
diff --git a/chrome/browser/media/public_session_media_access_handler.cc b/chrome/browser/media/public_session_media_access_handler.cc index 73a9f31..150e3ad 100644 --- a/chrome/browser/media/public_session_media_access_handler.cc +++ b/chrome/browser/media/public_session_media_access_handler.cc
@@ -4,11 +4,14 @@ #include "chrome/browser/media/public_session_media_access_handler.h" +#include <set> #include <utility> #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/ptr_util.h" +#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h" +#include "chrome/browser/profiles/profiles_state.h" #include "chromeos/login/login_state.h" #include "content/public/browser/web_contents.h" #include "extensions/common/extension.h" @@ -16,16 +19,6 @@ #include "extensions/common/permissions/permission_set.h" #include "extensions/common/url_pattern_set.h" -namespace { - -// Returns true if we're in a Public Session. -bool IsPublicSession() { - return chromeos::LoginState::IsInitialized() && - chromeos::LoginState::Get()->IsPublicSessionUser(); -} - -} // namespace - PublicSessionMediaAccessHandler::PublicSessionMediaAccessHandler() {} PublicSessionMediaAccessHandler::~PublicSessionMediaAccessHandler() {} @@ -52,140 +45,44 @@ const extensions::Extension* extension) { // This class handles requests for Public Sessions only, outside of them just // pass the request through to the original class. - if (!IsPublicSession() || !extension->is_platform_app()) { + if (!profiles::IsPublicSession() || !extension->is_platform_app()) { return extension_media_access_handler_.HandleRequest(web_contents, request, callback, extension); } - bool needs_prompt = false; - extensions::APIPermissionSet new_apis; - UserChoice& user_choice = user_choice_cache_[extension->id()]; + // This Unretained is safe because the lifetime of this object is until + // process exit (living inside a base::Singleton object). + auto prompt_resolved_callback = base::Bind( + &PublicSessionMediaAccessHandler::ChainHandleRequest, + base::Unretained(this), web_contents, request, callback, extension); - if (user_choice.NeedsPrompting(request.audio_type)) { - new_apis.insert(extensions::APIPermission::kAudioCapture); - user_choice.SetPrompted(content::MEDIA_DEVICE_AUDIO_CAPTURE); - needs_prompt = true; - } - if (user_choice.NeedsPrompting(request.video_type)) { - new_apis.insert(extensions::APIPermission::kVideoCapture); - user_choice.SetPrompted(content::MEDIA_DEVICE_VIDEO_CAPTURE); - needs_prompt = true; - } + extensions::PermissionIDSet requested_permissions; + if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) + requested_permissions.insert(extensions::APIPermission::kAudioCapture); + if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) + requested_permissions.insert(extensions::APIPermission::kVideoCapture); - if (!needs_prompt) - return ChainHandleRequest(web_contents, request, callback, extension); - - auto permission_set = base::MakeUnique<extensions::PermissionSet>( - new_apis, extensions::ManifestPermissionSet(), - extensions::URLPatternSet(), extensions::URLPatternSet()); - - auto prompt = base::MakeUnique<ExtensionInstallPrompt>(web_contents); - - prompt->ShowDialog( - base::Bind(&PublicSessionMediaAccessHandler::ResolvePermissionPrompt, - base::Unretained(this), web_contents, request, callback, - extension), - extension, - nullptr, // Uses the extension icon. - base::MakeUnique<ExtensionInstallPrompt::Prompt>( - ExtensionInstallPrompt::PERMISSIONS_PROMPT), - std::move(permission_set), - ExtensionInstallPrompt::GetDefaultShowDialogCallback()); - - extension_install_prompt_map_[extension->id()] = std::move(prompt); + extensions::permission_helper::HandlePermissionRequest( + *extension, requested_permissions, web_contents, prompt_resolved_callback, + extensions::permission_helper::PromptFactory()); } void PublicSessionMediaAccessHandler::ChainHandleRequest( content::WebContents* web_contents, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback, - const extensions::Extension* extension) { - DCHECK(IsPublicSession() && extension && extension->is_platform_app()); - const UserChoice& user_choice = user_choice_cache_[extension->id()]; + const extensions::Extension* extension, + const extensions::PermissionIDSet& allowed_permissions) { content::MediaStreamRequest request_copy(request); // If the user denies audio or video capture, here it gets filtered out from // the request before being passed on to the actual implementation. - if (!user_choice.IsAllowed(content::MEDIA_DEVICE_AUDIO_CAPTURE)) + if (!allowed_permissions.ContainsID(extensions::APIPermission::kAudioCapture)) request_copy.audio_type = content::MEDIA_NO_SERVICE; - if (!user_choice.IsAllowed(content::MEDIA_DEVICE_VIDEO_CAPTURE)) + if (!allowed_permissions.ContainsID(extensions::APIPermission::kVideoCapture)) request_copy.video_type = content::MEDIA_NO_SERVICE; // Pass the request through to the original class. extension_media_access_handler_.HandleRequest(web_contents, request_copy, callback, extension); } - -void PublicSessionMediaAccessHandler::ResolvePermissionPrompt( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension, - ExtensionInstallPrompt::Result prompt_result) { - // Dispose of the prompt as it's not needed anymore. - extension_install_prompt_map_.erase(extension->id()); - - bool allowed = prompt_result == ExtensionInstallPrompt::Result::ACCEPTED; - UserChoice& user_choice = user_choice_cache_[extension->id()]; - - if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) - user_choice.Set(content::MEDIA_DEVICE_AUDIO_CAPTURE, allowed); - if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) - user_choice.Set(content::MEDIA_DEVICE_VIDEO_CAPTURE, allowed); - - ChainHandleRequest(web_contents, request, callback, extension); -} - -bool PublicSessionMediaAccessHandler::UserChoice::IsAllowed( - content::MediaStreamType type) const { - switch (type) { - case content::MEDIA_DEVICE_AUDIO_CAPTURE: - return !audio_prompted_ || audio_allowed_; - case content::MEDIA_DEVICE_VIDEO_CAPTURE: - return !video_prompted_ || video_allowed_; - default: - NOTREACHED(); - return false; - } -} - -bool PublicSessionMediaAccessHandler::UserChoice::NeedsPrompting( - content::MediaStreamType type) const { - switch (type) { - case content::MEDIA_DEVICE_AUDIO_CAPTURE: - return !audio_prompted_; - case content::MEDIA_DEVICE_VIDEO_CAPTURE: - return !video_prompted_; - default: - return false; - } -} - -void PublicSessionMediaAccessHandler::UserChoice::Set( - content::MediaStreamType type, - bool allowed) { - switch (type) { - case content::MEDIA_DEVICE_AUDIO_CAPTURE: - audio_allowed_ = allowed; - break; - case content::MEDIA_DEVICE_VIDEO_CAPTURE: - video_allowed_ = allowed; - break; - default: - NOTREACHED(); - } -} - -void PublicSessionMediaAccessHandler::UserChoice::SetPrompted( - content::MediaStreamType type) { - switch (type) { - case content::MEDIA_DEVICE_AUDIO_CAPTURE: - audio_prompted_ = true; - break; - case content::MEDIA_DEVICE_VIDEO_CAPTURE: - video_prompted_ = true; - break; - default: - NOTREACHED(); - } -}
diff --git a/chrome/browser/media/public_session_media_access_handler.h b/chrome/browser/media/public_session_media_access_handler.h index 70e7aac..e809957 100644 --- a/chrome/browser/media/public_session_media_access_handler.h +++ b/chrome/browser/media/public_session_media_access_handler.h
@@ -11,6 +11,7 @@ #include "chrome/browser/media/media_access_handler.h" #include "content/public/common/media_stream_request.h" #include "extensions/common/extension_id.h" +#include "extensions/common/permissions/api_permission_set.h" // MediaAccessHandler for extension capturing requests in Public Sessions. This // class is implemented as a wrapper around ExtensionMediaAccessHandler. It @@ -45,38 +46,13 @@ private: // Helper function used to chain the HandleRequest call into the original // inside of ExtensionMediaAccessHandler. - void ChainHandleRequest(content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension); + void ChainHandleRequest( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const extensions::Extension* extension, + const extensions::PermissionIDSet& allowed_permissions); - // Function used to resolve user decision regarding allowing audio/video. - void ResolvePermissionPrompt(content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension, - ExtensionInstallPrompt::Result prompt_result); - - // Class used to cache user choice regarding allowing audio/video capture. - class UserChoice { - public: - // Helper function for checking if audio/video is allowed by user choice. - bool IsAllowed(content::MediaStreamType type) const; - // Helper function which returns true if audio/video wasn't prompted yet. - bool NeedsPrompting(content::MediaStreamType type) const; - void Set(content::MediaStreamType type, bool allowed); - void SetPrompted(content::MediaStreamType type); - - private: - bool audio_prompted_ = false; - bool audio_allowed_ = false; - bool video_prompted_ = false; - bool video_allowed_ = false; - }; - - std::map<extensions::ExtensionId, UserChoice> user_choice_cache_; - std::map<extensions::ExtensionId, std::unique_ptr<ExtensionInstallPrompt>> - extension_install_prompt_map_; ExtensionMediaAccessHandler extension_media_access_handler_; DISALLOW_COPY_AND_ASSIGN(PublicSessionMediaAccessHandler);
diff --git a/chrome/browser/media/public_session_tab_capture_access_handler.cc b/chrome/browser/media/public_session_tab_capture_access_handler.cc new file mode 100644 index 0000000..ff2a8367 --- /dev/null +++ b/chrome/browser/media/public_session_tab_capture_access_handler.cc
@@ -0,0 +1,83 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/media/public_session_tab_capture_access_handler.h" + +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/memory/ptr_util.h" +#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h" +#include "chrome/browser/profiles/profiles_state.h" +#include "chromeos/login/login_state.h" +#include "content/public/browser/web_contents.h" +#include "extensions/common/extension.h" +#include "extensions/common/permissions/manifest_permission_set.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/url_pattern_set.h" + +PublicSessionTabCaptureAccessHandler::PublicSessionTabCaptureAccessHandler() {} + +PublicSessionTabCaptureAccessHandler::~PublicSessionTabCaptureAccessHandler() {} + +bool PublicSessionTabCaptureAccessHandler::SupportsStreamType( + const content::MediaStreamType type, + const extensions::Extension* extension) { + return tab_capture_access_handler_.SupportsStreamType(type, extension); +} + +bool PublicSessionTabCaptureAccessHandler::CheckMediaAccessPermission( + content::WebContents* web_contents, + const GURL& security_origin, + content::MediaStreamType type, + const extensions::Extension* extension) { + return tab_capture_access_handler_.CheckMediaAccessPermission( + web_contents, security_origin, type, extension); +} + +void PublicSessionTabCaptureAccessHandler::HandleRequest( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const extensions::Extension* extension) { + // This class handles requests for Public Sessions only, outside of them just + // pass the request through to the original class. + if (!profiles::IsPublicSession() || !extension || + (request.audio_type != content::MEDIA_TAB_AUDIO_CAPTURE && + request.video_type != content::MEDIA_TAB_VIDEO_CAPTURE)) { + return tab_capture_access_handler_.HandleRequest(web_contents, request, + callback, extension); + } + + // This Unretained is safe because the lifetime of this object is until + // process exit (living inside a base::Singleton object). + auto prompt_resolved_callback = base::Bind( + &PublicSessionTabCaptureAccessHandler::ChainHandleRequest, + base::Unretained(this), web_contents, request, callback, extension); + + extensions::permission_helper::HandlePermissionRequest( + *extension, {extensions::APIPermission::kTabCapture}, web_contents, + prompt_resolved_callback, extensions::permission_helper::PromptFactory()); +} + +void PublicSessionTabCaptureAccessHandler::ChainHandleRequest( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const extensions::Extension* extension, + const extensions::PermissionIDSet& allowed_permissions) { + content::MediaStreamRequest request_copy(request); + + // If the user denied tab capture, here the request gets filtered out before + // being passed on to the actual implementation. + if (!allowed_permissions.ContainsID(extensions::APIPermission::kTabCapture)) { + request_copy.audio_type = content::MEDIA_NO_SERVICE; + request_copy.video_type = content::MEDIA_NO_SERVICE; + } + + // Pass the request through to the original class. + tab_capture_access_handler_.HandleRequest(web_contents, request_copy, + callback, extension); +}
diff --git a/chrome/browser/media/public_session_tab_capture_access_handler.h b/chrome/browser/media/public_session_tab_capture_access_handler.h new file mode 100644 index 0000000..08446fcc --- /dev/null +++ b/chrome/browser/media/public_session_tab_capture_access_handler.h
@@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_MEDIA_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_ +#define CHROME_BROWSER_MEDIA_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_ + +#include "base/macros.h" +#include "chrome/browser/extensions/extension_install_prompt.h" +#include "chrome/browser/media/capture_access_handler_base.h" +#include "chrome/browser/media/webrtc/tab_capture_access_handler.h" +#include "content/public/common/media_stream_request.h" +#include "extensions/common/extension_id.h" +#include "extensions/common/permissions/api_permission_set.h" + +// MediaAccessHandler for TabCapture API in Public Sessions. This class is +// implemented as a wrapper around TabCaptureAccessHandler. It allows for finer +// access control to the TabCapture manifest permission feature inside of Public +// Sessions. +// +// In Public Sessions, extensions (and apps) are force-installed by admin policy +// so the user does not get a chance to review the permissions for these +// extensions. This is not acceptable from a security/privacy standpoint, so +// when an extension uses the TabCapture API for the first time, we show the +// user a dialog where they can choose whether to allow the extension access to +// the API. +class PublicSessionTabCaptureAccessHandler : public CaptureAccessHandlerBase { + public: + PublicSessionTabCaptureAccessHandler(); + ~PublicSessionTabCaptureAccessHandler() override; + + // MediaAccessHandler implementation. + bool SupportsStreamType(const content::MediaStreamType type, + const extensions::Extension* extension) override; + bool CheckMediaAccessPermission( + content::WebContents* web_contents, + const GURL& security_origin, + content::MediaStreamType type, + const extensions::Extension* extension) override; + void HandleRequest(content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const extensions::Extension* extension) override; + + private: + // Helper function used to chain the HandleRequest call into the original + // inside of TabCaptureAccessHandler. + void ChainHandleRequest( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const extensions::Extension* extension, + const extensions::PermissionIDSet& allowed_permissions); + + TabCaptureAccessHandler tab_capture_access_handler_; + + DISALLOW_COPY_AND_ASSIGN(PublicSessionTabCaptureAccessHandler); +}; + +#endif // CHROME_BROWSER_MEDIA_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc index 662dba9..34c419f 100644 --- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc +++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -37,7 +37,7 @@ #if defined(OS_CHROMEOS) #include "ash/shell.h" #include "chrome/browser/media/public_session_media_access_handler.h" -#include "chrome/browser/media/webrtc/public_session_tab_capture_access_handler.h" +#include "chrome/browser/media/public_session_tab_capture_access_handler.h" #endif // defined(OS_CHROMEOS) #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/media/webrtc/public_session_tab_capture_access_handler.cc b/chrome/browser/media/webrtc/public_session_tab_capture_access_handler.cc deleted file mode 100644 index 65e2d33..0000000 --- a/chrome/browser/media/webrtc/public_session_tab_capture_access_handler.cc +++ /dev/null
@@ -1,143 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/media/webrtc/public_session_tab_capture_access_handler.h" - -#include <utility> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/memory/ptr_util.h" -#include "chromeos/login/login_state.h" -#include "content/public/browser/web_contents.h" -#include "extensions/common/extension.h" -#include "extensions/common/permissions/manifest_permission_set.h" -#include "extensions/common/permissions/permission_set.h" -#include "extensions/common/url_pattern_set.h" - -namespace { - -// Returns true if we're in a Public Session. -bool IsPublicSession() { - return chromeos::LoginState::IsInitialized() && - chromeos::LoginState::Get()->IsPublicSessionUser(); -} - -} // namespace - -PublicSessionTabCaptureAccessHandler::PublicSessionTabCaptureAccessHandler() {} - -PublicSessionTabCaptureAccessHandler::~PublicSessionTabCaptureAccessHandler() {} - -bool PublicSessionTabCaptureAccessHandler::SupportsStreamType( - const content::MediaStreamType type, - const extensions::Extension* extension) { - return tab_capture_access_handler_.SupportsStreamType(type, extension); -} - -bool PublicSessionTabCaptureAccessHandler::CheckMediaAccessPermission( - content::WebContents* web_contents, - const GURL& security_origin, - content::MediaStreamType type, - const extensions::Extension* extension) { - return tab_capture_access_handler_.CheckMediaAccessPermission( - web_contents, security_origin, type, extension); -} - -void PublicSessionTabCaptureAccessHandler::HandleRequest( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension) { - // This class handles requests for Public Sessions only, outside of them just - // pass the request through to the original class. - if (!IsPublicSession() || !extension) { - return tab_capture_access_handler_.HandleRequest(web_contents, request, - callback, extension); - } - - UserChoice& user_choice = user_choice_cache_[extension->id()]; - - if ((request.audio_type != content::MEDIA_TAB_AUDIO_CAPTURE && - request.video_type != content::MEDIA_TAB_VIDEO_CAPTURE) || - !user_choice.NeedsPrompting()) { - return ChainHandleRequest(web_contents, request, callback, extension); - } - - user_choice.SetPrompted(); - - extensions::APIPermissionSet new_apis; - new_apis.insert(extensions::APIPermission::kTabCapture); - auto permission_set = base::MakeUnique<extensions::PermissionSet>( - new_apis, extensions::ManifestPermissionSet(), - extensions::URLPatternSet(), extensions::URLPatternSet()); - auto prompt = base::MakeUnique<ExtensionInstallPrompt>(web_contents); - - prompt->ShowDialog( - base::Bind(&PublicSessionTabCaptureAccessHandler::ResolvePermissionPrompt, - base::Unretained(this), web_contents, request, callback, - extension), - extension, - nullptr, // Uses the extension icon. - base::MakeUnique<ExtensionInstallPrompt::Prompt>( - ExtensionInstallPrompt::PERMISSIONS_PROMPT), - std::move(permission_set), - ExtensionInstallPrompt::GetDefaultShowDialogCallback()); - - extension_install_prompt_map_[extension->id()] = std::move(prompt); -} - -void PublicSessionTabCaptureAccessHandler::ChainHandleRequest( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension) { - DCHECK(IsPublicSession() && extension); - const UserChoice& user_choice = user_choice_cache_[extension->id()]; - content::MediaStreamRequest request_copy(request); - - // If the user denied tab capture, here the request gets filtered out before - // being passed on to the actual implementation. - if (!user_choice.IsAllowed()) { - request_copy.audio_type = content::MEDIA_NO_SERVICE; - request_copy.video_type = content::MEDIA_NO_SERVICE; - } - - // Pass the request through to the original class. - tab_capture_access_handler_.HandleRequest(web_contents, request_copy, - callback, extension); -} - -void PublicSessionTabCaptureAccessHandler::ResolvePermissionPrompt( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension, - ExtensionInstallPrompt::Result prompt_result) { - // Dispose of the prompt as it's not needed anymore. - extension_install_prompt_map_.erase(extension->id()); - - bool allowed = prompt_result == ExtensionInstallPrompt::Result::ACCEPTED; - UserChoice& user_choice = user_choice_cache_[extension->id()]; - - user_choice.Set(allowed); - - ChainHandleRequest(web_contents, request, callback, extension); -} - -bool PublicSessionTabCaptureAccessHandler::UserChoice::IsAllowed() const { - return tab_capture_allowed_; -} - -bool PublicSessionTabCaptureAccessHandler::UserChoice::NeedsPrompting() const { - return !tab_capture_prompted_; -} - -void PublicSessionTabCaptureAccessHandler::UserChoice::Set(bool allowed) { - tab_capture_allowed_ = allowed; -} - -void PublicSessionTabCaptureAccessHandler::UserChoice::SetPrompted() { - tab_capture_prompted_ = true; -}
diff --git a/chrome/browser/media/webrtc/public_session_tab_capture_access_handler.h b/chrome/browser/media/webrtc/public_session_tab_capture_access_handler.h deleted file mode 100644 index a22dfad..0000000 --- a/chrome/browser/media/webrtc/public_session_tab_capture_access_handler.h +++ /dev/null
@@ -1,85 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_MEDIA_WEBRTC_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_ -#define CHROME_BROWSER_MEDIA_WEBRTC_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_ - -#include "base/macros.h" -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/media/capture_access_handler_base.h" -#include "chrome/browser/media/webrtc/tab_capture_access_handler.h" -#include "content/public/common/media_stream_request.h" -#include "extensions/common/extension_id.h" - -// MediaAccessHandler for TabCapture API in Public Sessions. This class is -// implemented as a wrapper around TabCaptureAccessHandler. It allows for finer -// access control to the TabCapture manifest permission feature inside of Public -// Sessions. -// -// In Public Sessions, extensions (and apps) are force-installed by admin policy -// so the user does not get a chance to review the permissions for these -// extensions. This is not acceptable from a security/privacy standpoint, so -// when an extension uses the TabCapture API for the first time, we show the -// user a dialog where they can choose whether to allow the extension access to -// the API. -// -// TODO(isandrk): Refactor common code out of this class and -// PublicSessionMediaAccessHandler (crbug.com/672620). -class PublicSessionTabCaptureAccessHandler : public CaptureAccessHandlerBase { - public: - PublicSessionTabCaptureAccessHandler(); - ~PublicSessionTabCaptureAccessHandler() override; - - // MediaAccessHandler implementation. - bool SupportsStreamType(const content::MediaStreamType type, - const extensions::Extension* extension) override; - bool CheckMediaAccessPermission( - content::WebContents* web_contents, - const GURL& security_origin, - content::MediaStreamType type, - const extensions::Extension* extension) override; - void HandleRequest(content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension) override; - - private: - // Helper function used to chain the HandleRequest call into the original - // inside of TabCaptureAccessHandler. - void ChainHandleRequest(content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension); - - // Function used to resolve user decision regarding allowing tab capture. - void ResolvePermissionPrompt(content::WebContents* web_contents, - const content::MediaStreamRequest& request, - const content::MediaResponseCallback& callback, - const extensions::Extension* extension, - ExtensionInstallPrompt::Result prompt_result); - - // Class used to cache user choice regarding allowing tab capture. - class UserChoice { - public: - // Helper function for checking if tab capture is allowed by user choice. - bool IsAllowed() const; - // Helper function which returns true if user choice wasn't prompted yet. - bool NeedsPrompting() const; - void Set(bool allowed); - void SetPrompted(); - - private: - bool tab_capture_prompted_ = false; - bool tab_capture_allowed_ = false; - }; - - std::map<extensions::ExtensionId, UserChoice> user_choice_cache_; - std::map<extensions::ExtensionId, std::unique_ptr<ExtensionInstallPrompt>> - extension_install_prompt_map_; - TabCaptureAccessHandler tab_capture_access_handler_; - - DISALLOW_COPY_AND_ASSIGN(PublicSessionTabCaptureAccessHandler); -}; - -#endif // CHROME_BROWSER_MEDIA_WEBRTC_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc index b36ae428..dbd8bf81 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -119,8 +119,8 @@ ~MockSubresourceFilterDriver() override = default; - MOCK_METHOD3(ActivateForProvisionalLoad, - void(subresource_filter::ActivationLevel, const GURL&, bool)); + MOCK_METHOD2(ActivateForNextCommittedLoad, + void(subresource_filter::ActivationLevel, bool)); private: DISALLOW_COPY_AND_ASSIGN(MockSubresourceFilterDriver); @@ -941,11 +941,11 @@ SetupResponseForUrl(bad_url, malware_full_hash); WebContents* main_contents = - browser()->tab_strip_model()->GetActiveWebContents(); + browser()->tab_strip_model()->GetActiveWebContents(); EXPECT_CALL(observer_, OnSafeBrowsingHit(IsUnsafeResourceFor(bad_url))) .Times(1); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(0); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(0); ui_test_utils::NavigateToURL(browser(), bad_url); Mock::VerifyAndClearExpectations(&observer_); ASSERT_TRUE(got_hit_report()); @@ -953,7 +953,7 @@ content::WaitForInterstitialAttach(main_contents); EXPECT_TRUE(ShowingInterstitialPage()); testing::Mock::VerifyAndClearExpectations(driver()); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(1); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(1); InterstitialPage* interstitial_page = main_contents->GetInterstitialPage(); ASSERT_TRUE(interstitial_page); interstitial_page->Proceed(); @@ -979,11 +979,11 @@ SetupResponseForUrl(bad_url, malware_full_hash); WebContents* main_contents = - browser()->tab_strip_model()->GetActiveWebContents(); + browser()->tab_strip_model()->GetActiveWebContents(); EXPECT_CALL(observer_, OnSafeBrowsingHit(IsUnsafeResourceFor(bad_url))) .Times(1); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(0); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(0); ui_test_utils::NavigateToURL(browser(), bad_url); testing::Mock::VerifyAndClearExpectations(driver()); ASSERT_TRUE(got_hit_report()); @@ -991,7 +991,7 @@ content::WaitForInterstitialAttach(main_contents); EXPECT_TRUE(ShowingInterstitialPage()); testing::Mock::VerifyAndClearExpectations(driver()); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(0); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(0); InterstitialPage* interstitial_page = main_contents->GetInterstitialPage(); ASSERT_TRUE(interstitial_page); interstitial_page->Proceed(); @@ -2154,7 +2154,7 @@ EXPECT_CALL(observer_, OnSafeBrowsingHit(IsUnsafeResourceFor(bad_url))) .Times(1); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(0); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(0); ui_test_utils::NavigateToURL(browser(), bad_url); Mock::VerifyAndClearExpectations(&observer_); ASSERT_TRUE(got_hit_report()); @@ -2162,7 +2162,7 @@ content::WaitForInterstitialAttach(main_contents); EXPECT_TRUE(ShowingInterstitialPage()); testing::Mock::VerifyAndClearExpectations(driver()); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(1); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(1); InterstitialPage* interstitial_page = main_contents->GetInterstitialPage(); ASSERT_TRUE(interstitial_page); interstitial_page->Proceed(); @@ -2190,7 +2190,7 @@ EXPECT_CALL(observer_, OnSafeBrowsingHit(IsUnsafeResourceFor(bad_url))) .Times(1); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(0); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(0); ui_test_utils::NavigateToURL(browser(), bad_url); testing::Mock::VerifyAndClearExpectations(driver()); ASSERT_TRUE(got_hit_report()); @@ -2198,7 +2198,7 @@ content::WaitForInterstitialAttach(main_contents); EXPECT_TRUE(ShowingInterstitialPage()); testing::Mock::VerifyAndClearExpectations(driver()); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad(_, _, _)).Times(0); + EXPECT_CALL(*driver(), ActivateForNextCommittedLoad(_, _)).Times(0); InterstitialPage* interstitial_page = main_contents->GetInterstitialPage(); ASSERT_TRUE(interstitial_page); interstitial_page->Proceed();
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc index e313d56..54e3265 100644 --- a/chrome/browser/search/instant_service.cc +++ b/chrome/browser/search/instant_service.cc
@@ -40,7 +40,6 @@ #include "components/keyed_service/core/service_access_type.h" #include "components/ntp_tiles/icon_cacher.h" #include "components/search/search.h" -#include "components/search_engines/template_url_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" @@ -66,7 +65,6 @@ InstantService::InstantService(Profile* profile) : profile_(profile), - template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)), weak_ptr_factory_(this) { // The initialization below depends on a typical set of browser threads. Skip // it if we are running in a unit test without the full suite. @@ -77,18 +75,16 @@ // is only instantiated here (after the check for a UI thread above). instant_io_context_ = new InstantIOContext(); - previous_google_base_url_ = - GURL(UIThreadSearchTermsData(profile).GoogleBaseURLValue()); - - // TemplateURLService is NULL by default in tests. - if (template_url_service_) { - template_url_service_->AddObserver(this); - const TemplateURL* default_search_provider = - template_url_service_->GetDefaultSearchProvider(); - if (default_search_provider) { - previous_default_search_provider_.reset( - new TemplateURLData(default_search_provider->data())); - } + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(profile_); + // TemplateURLService can be null in tests. + if (template_url_service) { + search_engine_base_url_tracker_ = + base::MakeUnique<SearchEngineBaseURLTracker>( + template_url_service, + base::MakeUnique<UIThreadSearchTermsData>(profile_), + base::Bind(&InstantService::OnSearchEngineBaseURLChanged, + base::Unretained(this))); } ResetInstantSearchPrerendererIfNecessary(); @@ -151,10 +147,7 @@ content::URLDataSource::Add(profile_, new MostVisitedIframeSource()); } -InstantService::~InstantService() { - if (template_url_service_) - template_url_service_->RemoveObserver(this); -} +InstantService::~InstantService() = default; void InstantService::AddInstantProcess(int process_id) { process_ids_.insert(process_id); @@ -452,39 +445,6 @@ } #endif // !defined(OS_ANDROID) -void InstantService::OnTemplateURLServiceChanged() { - // Check whether the default search provider was changed. - const TemplateURL* template_url = - template_url_service_->GetDefaultSearchProvider(); - bool default_search_provider_changed = !TemplateURL::MatchesData( - template_url, previous_default_search_provider_.get(), - UIThreadSearchTermsData(profile_)); - if (default_search_provider_changed) { - previous_default_search_provider_.reset( - template_url ? new TemplateURLData(template_url->data()) : NULL); - } - - // Note that, even if the TemplateURL for the Default Search Provider has not - // changed, the effective URLs might change if they reference the Google base - // URL. The TemplateURLService will notify us when the effective URL changes - // in this way but it's up to us to do the work to check both. - bool google_base_url_domain_changed = false; - GURL google_base_url(UIThreadSearchTermsData(profile_).GoogleBaseURLValue()); - if (google_base_url != previous_google_base_url_) { - previous_google_base_url_ = google_base_url; - if (template_url && - template_url->HasGoogleBaseURLs(UIThreadSearchTermsData(profile_))) { - google_base_url_domain_changed = true; - } - } - - if (default_search_provider_changed || google_base_url_domain_changed) { - ResetInstantSearchPrerendererIfNecessary(); - for (InstantServiceObserver& observer : observers_) - observer.DefaultSearchProviderChanged(google_base_url_domain_changed); - } -} - void InstantService::TopSitesLoaded(history::TopSites* top_sites) { DCHECK(!most_visited_sites_); DCHECK_EQ(top_sites_.get(), top_sites); @@ -502,6 +462,16 @@ false); } +void InstantService::OnSearchEngineBaseURLChanged( + SearchEngineBaseURLTracker::ChangeReason change_reason) { + ResetInstantSearchPrerendererIfNecessary(); + bool google_base_url_changed = + change_reason == + SearchEngineBaseURLTracker::ChangeReason::GOOGLE_BASE_URL; + for (InstantServiceObserver& observer : observers_) + observer.DefaultSearchProviderChanged(google_base_url_changed); +} + void InstantService::ResetInstantSearchPrerendererIfNecessary() { if (!search::IsInstantExtendedAPIEnabled()) return;
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h index 064ca1db..db8e7a07 100644 --- a/chrome/browser/search/instant_service.h +++ b/chrome/browser/search/instant_service.h
@@ -15,23 +15,21 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "build/build_config.h" +#include "chrome/browser/search/search_engine_base_url_tracker.h" #include "components/history/core/browser/history_types.h" #include "components/history/core/browser/top_sites_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "components/ntp_tiles/most_visited_sites.h" #include "components/ntp_tiles/ntp_tile.h" -#include "components/search_engines/template_url_service_observer.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "url/gurl.h" class InstantIOContext; -struct InstantMostVisitedItem; class InstantSearchPrerenderer; class InstantServiceObserver; class Profile; -struct TemplateURLData; -class TemplateURLService; +struct InstantMostVisitedItem; struct ThemeBackgroundInfo; namespace content { @@ -45,7 +43,6 @@ // Tracks render process host IDs that are associated with Instant. class InstantService : public KeyedService, public content::NotificationObserver, - public TemplateURLServiceObserver, public history::TopSitesObserver, public ntp_tiles::MostVisitedSites::Observer { public: @@ -119,17 +116,14 @@ const content::NotificationSource& source, const content::NotificationDetails& details) override; - // TemplateURLServiceObserver: - // Caches the previous value of the Default Search Provider and the Google - // base URL to filter out changes other than those affecting the Default - // Search Provider. - void OnTemplateURLServiceChanged() override; - // TopSitesObserver: void TopSitesLoaded(history::TopSites* top_sites) override; void TopSitesChanged(history::TopSites* top_sites, ChangeReason change_reason) override; + void OnSearchEngineBaseURLChanged( + SearchEngineBaseURLTracker::ChangeReason change_reason); + // Called when a renderer process is terminated. void OnRendererProcessTerminated(int process_id); @@ -155,9 +149,7 @@ Profile* const profile_; - // The TemplateURLService that we are observing. It will outlive this - // InstantService due to the dependency declared in InstantServiceFactory. - TemplateURLService* template_url_service_; + std::unique_ptr<SearchEngineBaseURLTracker> search_engine_base_url_tracker_; // The process ids associated with Instant processes. std::set<int> process_ids_; @@ -177,11 +169,6 @@ // Set to NULL if the default search provider does not support Instant. std::unique_ptr<InstantSearchPrerenderer> instant_prerenderer_; - // Used to check whether notifications from TemplateURLService indicate a - // change that affects the default search provider. - std::unique_ptr<TemplateURLData> previous_default_search_provider_; - GURL previous_google_base_url_; - // Data sources for NTP tiles (aka Most Visited tiles). Only one of these will // be non-null. std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites_;
diff --git a/chrome/browser/search/search_engine_base_url_tracker.cc b/chrome/browser/search/search_engine_base_url_tracker.cc new file mode 100644 index 0000000..64ec1a5 --- /dev/null +++ b/chrome/browser/search/search_engine_base_url_tracker.cc
@@ -0,0 +1,77 @@ +// 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. + +#include "chrome/browser/search/search_engine_base_url_tracker.h" + +#include "base/memory/ptr_util.h" + +#include "components/search_engines/search_terms_data.h" +#include "components/search_engines/template_url_service.h" + +SearchEngineBaseURLTracker::SearchEngineBaseURLTracker( + TemplateURLService* template_url_service, + std::unique_ptr<SearchTermsData> search_terms_data, + const BaseURLChangedCallback& base_url_changed_callback) + : template_url_service_(template_url_service), + search_terms_data_(std::move(search_terms_data)), + base_url_changed_callback_(base_url_changed_callback), + observer_(this), + previous_google_base_url_(search_terms_data_->GoogleBaseURLValue()) { + DCHECK(template_url_service_); + + observer_.Add(template_url_service_); + + const TemplateURL* default_search_provider = + template_url_service_->GetDefaultSearchProvider(); + if (default_search_provider) + previous_default_search_provider_data_ = default_search_provider->data(); +} + +SearchEngineBaseURLTracker::~SearchEngineBaseURLTracker() = default; + +void SearchEngineBaseURLTracker::OnTemplateURLServiceChanged() { + GURL google_base_url; + if (HasGoogleBaseURL()) + google_base_url = GURL(search_terms_data_->GoogleBaseURLValue()); + + // Check whether the default search provider was changed. + const TemplateURL* template_url = + template_url_service_->GetDefaultSearchProvider(); + const TemplateURLData* previous_data = + previous_default_search_provider_data_.has_value() + ? &previous_default_search_provider_data_.value() + : nullptr; + bool default_search_provider_changed = !TemplateURL::MatchesData( + template_url, previous_data, *search_terms_data_); + if (default_search_provider_changed) { + if (template_url) + previous_default_search_provider_data_ = template_url->data(); + else + previous_default_search_provider_data_ = base::nullopt; + + // Also update the cached Google base URL, without separately notifying. + previous_google_base_url_ = google_base_url; + + base_url_changed_callback_.Run(ChangeReason::DEFAULT_SEARCH_PROVIDER); + return; + } + + // Check whether the Google base URL has changed. + // Note that, even if the TemplateURL for the Default Search Provider has not + // changed, the effective URLs might change if they reference the Google base + // URL. The TemplateURLService will notify us when the effective URL changes + // in this way but it's up to us to do the work to check both. + if (google_base_url != previous_google_base_url_) { + previous_google_base_url_ = google_base_url; + if (HasGoogleBaseURL()) + base_url_changed_callback_.Run(ChangeReason::GOOGLE_BASE_URL); + } +} + +bool SearchEngineBaseURLTracker::HasGoogleBaseURL() { + const TemplateURL* template_url = + template_url_service_->GetDefaultSearchProvider(); + + return template_url && template_url->HasGoogleBaseURLs(*search_terms_data_); +}
diff --git a/chrome/browser/search/search_engine_base_url_tracker.h b/chrome/browser/search/search_engine_base_url_tracker.h new file mode 100644 index 0000000..81845969 --- /dev/null +++ b/chrome/browser/search/search_engine_base_url_tracker.h
@@ -0,0 +1,61 @@ +// 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. + +#ifndef CHROME_BROWSER_SEARCH_SEARCH_ENGINE_BASE_URL_TRACKER_H_ +#define CHROME_BROWSER_SEARCH_SEARCH_ENGINE_BASE_URL_TRACKER_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/scoped_observer.h" +#include "components/search_engines/template_url_data.h" +#include "components/search_engines/template_url_service_observer.h" +#include "url/gurl.h" + +class SearchTermsData; +class TemplateURLService; + +// A helper class that watches for changes to the base URL of the default search +// engine. Typically this changes when a different DSE is selected. For Google, +// it can also change without changing the DSE, when the Google base URL is +// updated. This can happen in the case of country (i.e. TLD) changes. +class SearchEngineBaseURLTracker : public TemplateURLServiceObserver { + public: + enum class ChangeReason { + DEFAULT_SEARCH_PROVIDER, + GOOGLE_BASE_URL, + }; + + using BaseURLChangedCallback = base::Callback<void(ChangeReason)>; + + SearchEngineBaseURLTracker( + TemplateURLService* template_url_service, + std::unique_ptr<SearchTermsData> search_terms_data, + const BaseURLChangedCallback& base_url_changed_callback); + ~SearchEngineBaseURLTracker() override; + + private: + // TemplateURLServiceObserver implementation. + void OnTemplateURLServiceChanged() override; + + // Returns true if the base URL of the current search engine is Google. + bool HasGoogleBaseURL(); + + TemplateURLService* template_url_service_; + std::unique_ptr<SearchTermsData> search_terms_data_; + BaseURLChangedCallback base_url_changed_callback_; + + ScopedObserver<TemplateURLService, TemplateURLServiceObserver> observer_; + + // Used to check whether notifications from TemplateURLService indicate a + // change that affects the default search provider. + GURL previous_google_base_url_; + base::Optional<TemplateURLData> previous_default_search_provider_data_; + + DISALLOW_COPY_AND_ASSIGN(SearchEngineBaseURLTracker); +}; + +#endif // CHROME_BROWSER_SEARCH_SEARCH_ENGINE_BASE_URL_TRACKER_H_
diff --git a/chrome/browser/ui/search/instant_test_utils.cc b/chrome/browser/ui/search/instant_test_utils.cc index 7f04880..6ab4a5f 100644 --- a/chrome/browser/ui/search/instant_test_utils.cc +++ b/chrome/browser/ui/search/instant_test_utils.cc
@@ -118,6 +118,13 @@ contents, WrapScript(script), result); } +bool InstantTestBase::GetDoubleFromJS(content::WebContents* contents, + const std::string& script, + double* result) { + return content::ExecuteScriptAndExtractDouble(contents, WrapScript(script), + result); +} + bool InstantTestBase::GetStringFromJS(content::WebContents* contents, const std::string& script, std::string* result) {
diff --git a/chrome/browser/ui/search/instant_test_utils.h b/chrome/browser/ui/search/instant_test_utils.h index 4c83d37..e688e22 100644 --- a/chrome/browser/ui/search/instant_test_utils.h +++ b/chrome/browser/ui/search/instant_test_utils.h
@@ -61,6 +61,9 @@ bool GetIntFromJS(content::WebContents* contents, const std::string& script, int* result) WARN_UNUSED_RESULT; + bool GetDoubleFromJS(content::WebContents* contents, + const std::string& script, + double* result) WARN_UNUSED_RESULT; bool GetStringFromJS(content::WebContents* contents, const std::string& script, std::string* result) WARN_UNUSED_RESULT;
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc index 61fcae05..97cd8e9c 100644 --- a/chrome/browser/ui/search/local_ntp_browsertest.cc +++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -4,17 +4,27 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/search/search.h" #include "chrome/browser/ui/search/instant_test_utils.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/omnibox/browser/omnibox_edit_model.h" +#include "components/omnibox/browser/omnibox_view.h" +#include "components/omnibox/common/omnibox_focus_state.h" #include "components/prefs/pref_service.h" +#include "content/public/browser/notification_service.h" #include "content/public/browser/web_contents.h" +#include "content/public/test/test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/vector2d.h" class LocalNTPTest : public InProcessBrowserTest, public InstantTestBase { @@ -32,7 +42,9 @@ } }; -IN_PROC_BROWSER_TEST_F(LocalNTPTest, LocalNTPJavascriptTest) { +// This runs a bunch of pure JS-side tests, i.e. those that don't require any +// interaction from the native side. +IN_PROC_BROWSER_TEST_F(LocalNTPTest, SimpleJavascriptTests) { ASSERT_NO_FATAL_FAILURE(SetupInstant(browser())); FocusOmnibox(); @@ -43,11 +55,86 @@ content::WebContents* active_tab = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(search::IsInstantNTP(active_tab)); + bool success = false; - ASSERT_TRUE(GetBoolFromJS(active_tab, "!!runTests()", &success)); + ASSERT_TRUE(GetBoolFromJS(active_tab, "!!runSimpleTests()", &success)); EXPECT_TRUE(success); } +IN_PROC_BROWSER_TEST_F(LocalNTPTest, FakeboxRedirectsToOmnibox) { + ASSERT_NO_FATAL_FAILURE(SetupInstant(browser())); + FocusOmnibox(); + + ui_test_utils::NavigateToURLWithDisposition( + browser(), ntp_url(), WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + content::WebContents* active_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(search::IsInstantNTP(active_tab)); + + // This is required to make the mouse events we send below arrive at the right + // place. It *should* be the default for all interactive_ui_tests anyway, but + // on Mac it isn't; see crbug.com/641969. + ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); + + bool result = false; + ASSERT_TRUE(GetBoolFromJS(active_tab, "!!setupAdvancedTest()", &result)); + ASSERT_TRUE(result); + + // Get the position of the fakebox on the page. + double fakebox_x = 0; + ASSERT_TRUE(GetDoubleFromJS(active_tab, "getFakeboxPositionX()", &fakebox_x)); + double fakebox_y = 0; + ASSERT_TRUE(GetDoubleFromJS(active_tab, "getFakeboxPositionY()", &fakebox_y)); + + // Move the mouse over the fakebox. + gfx::Vector2d fakebox_pos(static_cast<int>(std::ceil(fakebox_x)), + static_cast<int>(std::ceil(fakebox_y))); + gfx::Point origin = active_tab->GetContainerBounds().origin(); + ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(origin + fakebox_pos + + gfx::Vector2d(1, 1))); + + // Click on the fakebox, and wait for the omnibox to receive invisible focus. + // Note that simply waiting for the first OMNIBOX_FOCUS_CHANGED notification + // is not sufficient: If the omnibox had focus before, it will first lose the + // focus before gaining invisible focus. + ASSERT_NE(OMNIBOX_FOCUS_INVISIBLE, omnibox()->model()->focus_state()); + content::WindowedNotificationObserver focus_observer( + chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED, + base::Bind( + [](const OmniboxEditModel* omnibox_model) { + return omnibox_model->focus_state() == OMNIBOX_FOCUS_INVISIBLE; + }, + omnibox()->model())); + + ASSERT_TRUE( + ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN)); + ASSERT_TRUE( + ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::UP)); + + focus_observer.Wait(); + EXPECT_EQ(OMNIBOX_FOCUS_INVISIBLE, omnibox()->model()->focus_state()); + + // The fakebox should now also have focus. + ASSERT_TRUE(GetBoolFromJS(active_tab, "!!fakeboxIsFocused()", &result)); + EXPECT_TRUE(result); + + // Type "a". + ASSERT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), ui::KeyboardCode::VKEY_A, + /*control=*/false, /*shift=*/false, /*alt=*/false, /*command=*/false)); + + // The omnibox should have received visible focus. + EXPECT_EQ(OMNIBOX_FOCUS_VISIBLE, omnibox()->model()->focus_state()); + // ...and the typed text should have arrived there. + EXPECT_EQ("a", GetOmniboxText()); + + // On the JS side, the fakebox should have been hidden. + ASSERT_TRUE(GetBoolFromJS(active_tab, "!!fakeboxIsVisible()", &result)); + EXPECT_FALSE(result); +} + IN_PROC_BROWSER_TEST_F(LocalNTPTest, NTPRespectsBrowserLanguageSetting) { // Make sure the default language is not French.
diff --git a/chrome/browser/ui/views/passwords/credentials_selection_view.cc b/chrome/browser/ui/views/passwords/credentials_selection_view.cc index 54a8f37..15832cb9 100644 --- a/chrome/browser/ui/views/passwords/credentials_selection_view.cc +++ b/chrome/browser/ui/views/passwords/credentials_selection_view.cc
@@ -55,9 +55,9 @@ // The username combobox and password label. layout->StartRowWithPadding(0, column_set_id, 0, views::kRelatedControlVerticalSpacing); - combobox_ = GenerateUsernameCombobox( + GenerateUsernameCombobox( manage_passwords_bubble_model->pending_password().username_value); - layout->AddView(combobox_); + layout->AddView(combobox_.get()); views::Label* label = GeneratePasswordLabel(manage_passwords_bubble_model->pending_password()); layout->AddView(label); @@ -67,6 +67,10 @@ CredentialsSelectionView::~CredentialsSelectionView() { ReportUserActionOnce(true, -1); + // |combobox_| has a pointer to |combobox_model_|, so |combobox_| should be + // deleted before deleting of |combobox_model_|. To ensure this, let's delete + // it now. + combobox_.reset(); } const autofill::PasswordForm* @@ -77,7 +81,7 @@ return &password_forms_->at(combobox_->selected_index()); } -views::Combobox* CredentialsSelectionView::GenerateUsernameCombobox( +void CredentialsSelectionView::GenerateUsernameCombobox( const base::string16& best_matched_username) { std::vector<base::string16> usernames; size_t best_matched_username_index = password_forms_->size(); @@ -92,19 +96,18 @@ } } - views::Combobox* combobox = - new views::Combobox(new ui::SimpleComboboxModel(usernames)); + combobox_model_.reset(new ui::SimpleComboboxModel(usernames)); + combobox_.reset(new views::Combobox(combobox_model_.get())); if (best_matched_username_index < password_forms_->size()) { is_default_best_match_ = true; default_index_ = best_matched_username_index; - combobox->SetSelectedIndex(best_matched_username_index); + combobox_->SetSelectedIndex(best_matched_username_index); } else if (preferred_form_index < password_forms_->size()) { is_default_preferred_ = true; default_index_ = preferred_form_index; - combobox->SetSelectedIndex(preferred_form_index); + combobox_->SetSelectedIndex(preferred_form_index); } - return combobox; } void CredentialsSelectionView::ReportUserActionOnce(bool was_update_rejected,
diff --git a/chrome/browser/ui/views/passwords/credentials_selection_view.h b/chrome/browser/ui/views/passwords/credentials_selection_view.h index 07b6f26..b71e2985 100644 --- a/chrome/browser/ui/views/passwords/credentials_selection_view.h +++ b/chrome/browser/ui/views/passwords/credentials_selection_view.h
@@ -4,6 +4,7 @@ #ifndef CHROME_BROWSER_UI_VIEWS_PASSWORDS_CREDENTIALS_SELECTION_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_PASSWORDS_CREDENTIALS_SELECTION_VIEW_H_ +#include <memory> #include <vector> #include "base/macros.h" @@ -14,6 +15,10 @@ class Combobox; } +namespace ui { +class SimpleComboboxModel; +} + class ManagePasswordsBubbleModel; // A view where the user can select a credential. @@ -27,12 +32,12 @@ const autofill::PasswordForm* GetSelectedCredentials(); private: - views::Combobox* GenerateUsernameCombobox( - const base::string16& best_matched_username); + void GenerateUsernameCombobox(const base::string16& best_matched_username); void ReportUserActionOnce(bool was_update_rejected, int selected_index); const std::vector<autofill::PasswordForm>* password_forms_; - views::Combobox* combobox_; + std::unique_ptr<views::Combobox> combobox_; + std::unique_ptr<ui::SimpleComboboxModel> combobox_model_; int default_index_; bool is_default_best_match_; bool is_default_preferred_;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java index c4b7dc8..ef09c323 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java
@@ -27,7 +27,7 @@ for (int index = 0; index < count; index++) { suggestions.add(new SnippetArticle(category, "https://site.com/url" + prefix + index, prefix + "title" + index, "pub" + index, "txt" + index, - "https://site.com/url" + index, 0, 0)); + "https://site.com/url" + index, 0, 0, 0)); } return suggestions; }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/OWNERS b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/OWNERS new file mode 100644 index 0000000..78c3488 --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/ntp/OWNERS
diff --git a/chrome/test/data/extensions/api_test/page_capture/test.js b/chrome/test/data/extensions/api_test/page_capture/test.js index e51a99a..bc911eb 100644 --- a/chrome/test/data/extensions/api_test/page_capture/test.js +++ b/chrome/test/data/extensions/api_test/page_capture/test.js
@@ -3,7 +3,7 @@ // found in the LICENSE file. // API test for chrome.extension.pageCapture. -// browser_tests.exe --gtest_filter=ExtensionApiTest.PageCapture +// browser_tests.exe --gtest_filter=ExtensionPageCaptureApiTest.* const assertEq = chrome.test.assertEq; const assertTrue = chrome.test.assertTrue; @@ -31,6 +31,11 @@ waitForCurrentTabLoaded(function() { chrome.pageCapture.saveAsMHTML({ "tabId": tab.id }, function(data) { + if (config.customArg == "REQUEST_DENIED") { + chrome.test.assertLastError("User denied request."); + chrome.test.notifyPass(); + return; + } assertEq(undefined, chrome.runtime.lastError); assertTrue(data != null); // It should contain few KBs of data.
diff --git a/chrome/test/data/local_ntp_browsertest.js b/chrome/test/data/local_ntp_browsertest.js index 50028119..13bd93c 100644 --- a/chrome/test/data/local_ntp_browsertest.js +++ b/chrome/test/data/local_ntp_browsertest.js
@@ -22,17 +22,14 @@ * Sets up for the next test case. Recreates the default local NTP DOM. */ function setUp() { + // First, clear up the DOM and state left over from any previous test case. document.body.innerHTML = ''; + // The NTP stores some state such as fakebox focus in the body's classList. + document.body.classList = ''; + document.body.appendChild($('local-ntp-body').content.cloneNode(true)); } - -/** - * Cleans up after test execution. - */ -function tearDown() { -} - /** * Aborts a test if a condition is not met. * @param {boolean} condition The condition that must be true. @@ -44,17 +41,17 @@ } /** - * Runs all tests. + * Runs all simple tests, i.e. those that don't require interaction from the + * native side. * @return {boolean} True if all tests pass and false otherwise. */ -function runTests() { +function runSimpleTests() { var pass = true; for (var testName in window) { if (/^test.+/.test(testName) && typeof window[testName] == 'function') { try { setUp(); window[testName].call(window); - tearDown(); } catch (err) { window.console.log(testName + ' ' + err); pass = false; @@ -66,22 +63,36 @@ /** + * Creates and initializes a LocalNTP object. + * @param {boolean} isGooglePage Whether to make it a Google-branded NTP. + */ +function initLocalNTP(isGooglePage) { + configData.isGooglePage = isGooglePage; + var localNTP = LocalNTP(); + localNTP.init(); +} + + +/** * Checks whether a given HTMLElement exists and is visible. * @param {HTMLElement|undefined} elem An HTMLElement. * @return {boolean} True if the element exists and is visible. */ function elementIsVisible(elem) { - return elem && elem.offsetWidth > 0 && elem.offsetHeight > 0; + return elem && elem.offsetWidth > 0 && elem.offsetHeight > 0 && + window.getComputedStyle(elem).visibility != 'hidden'; } +// ******************************* SIMPLE TESTS ******************************* +// These are run by runSimpleTests above. + + /** * Tests that Google NTPs show a fakebox and logo. */ function testShowsFakeboxAndLogoIfGoogle() { - var localNTP = LocalNTP(); - configData.isGooglePage = true; - localNTP.init(); + initLocalNTP(/*isGooglePage=*/true); assert(elementIsVisible($('fakebox'))); assert(elementIsVisible($('logo'))); } @@ -91,9 +102,43 @@ * Tests that non-Google NTPs do not show a fakebox. */ function testDoesNotShowFakeboxIfNotGoogle() { - var localNTP = LocalNTP(); - configData.isGooglePage = false; - localNTP.init(); + initLocalNTP(/*isGooglePage=*/false); assert(!elementIsVisible($('fakebox'))); assert(!elementIsVisible($('logo'))); } + + + +// ****************************** ADVANCED TESTS ****************************** +// Advanced tests are controlled from the native side. The helpers here are +// called from native code to set up the page and to check results. + +function setupAdvancedTest() { + setUp(); + initLocalNTP(/*isGooglePage=*/true); + + assert(elementIsVisible($('fakebox'))); + + return true; +} + +function getFakeboxPositionX() { + assert(elementIsVisible($('fakebox'))); + var rect = $('fakebox').getBoundingClientRect(); + return rect.left; +} + +function getFakeboxPositionY() { + assert(elementIsVisible($('fakebox'))); + var rect = $('fakebox').getBoundingClientRect(); + return rect.top; +} + +function fakeboxIsVisible() { + return elementIsVisible($('fakebox')); +} + +function fakeboxIsFocused() { + return fakeboxIsVisible() && + document.body.classList.contains('fakebox-focused'); +}
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index d8880c32..64b5244 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -952,7 +952,8 @@ ]), }; -TEST_F('CrSettingsLanguagesTest', 'Languages', function() { +// Flaky on Win and Linux, see http://crbug/692356. +TEST_F('CrSettingsLanguagesTest', 'DISABLED_Languages', function() { mocha.run(); });
diff --git a/components/browsing_data/content/conditional_cache_counting_helper.h b/components/browsing_data/content/conditional_cache_counting_helper.h index dff721d2..5a071b7 100644 --- a/components/browsing_data/content/conditional_cache_counting_helper.h +++ b/components/browsing_data/content/conditional_cache_counting_helper.h
@@ -8,6 +8,7 @@ #include <memory> #include "base/callback_forward.h" +#include "base/sequenced_task_runner_helpers.h" #include "net/base/net_errors.h" #include "net/disk_cache/disk_cache.h"
diff --git a/components/data_reduction_proxy/core/browser/db_data_owner.h b/components/data_reduction_proxy/core/browser/db_data_owner.h index a0b045f0..126df74 100644 --- a/components/data_reduction_proxy/core/browser/db_data_owner.h +++ b/components/data_reduction_proxy/core/browser/db_data_owner.h
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h"
diff --git a/components/grpc_support/bidirectional_stream.h b/components/grpc_support/bidirectional_stream.h index d57cd87..f2a444d4 100644 --- a/components/grpc_support/bidirectional_stream.h +++ b/components/grpc_support/bidirectional_stream.h
@@ -15,6 +15,10 @@ #include "net/http/bidirectional_stream.h" #include "net/url_request/url_request_context_getter.h" +namespace tracked_objects { +class Location; +} // namespace tracked_objects + namespace net { class HttpRequestHeaders; class WrappedIOBuffer;
diff --git a/components/history/core/browser/expire_history_backend.h b/components/history/core/browser/expire_history_backend.h index b25508c6b..ebffc5b 100644 --- a/components/history/core/browser/expire_history_backend.h +++ b/components/history/core/browser/expire_history_backend.h
@@ -19,6 +19,10 @@ class GURL; class TestingProfile; +namespace base { +class SequencedTaskRunner; +} + namespace history { class HistoryBackendClient;
diff --git a/components/history/core/browser/web_history_service.h b/components/history/core/browser/web_history_service.h index 992fafa..b25ad05 100644 --- a/components/history/core/browser/web_history_service.h +++ b/components/history/core/browser/web_history_service.h
@@ -12,6 +12,7 @@ #include <string> #include <vector> +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h"
diff --git a/components/json_schema/json_schema_validator.cc b/components/json_schema/json_schema_validator.cc index 1ef11574..038b66d 100644 --- a/components/json_schema/json_schema_validator.cc +++ b/components/json_schema/json_schema_validator.cc
@@ -615,7 +615,7 @@ if (schema->GetDictionary(schema::kPatternProperties, &pattern_properties)) { for (base::DictionaryValue::Iterator it(*pattern_properties); !it.IsAtEnd(); it.Advance()) { - re2::RE2* prop_pattern = new re2::RE2(it.key()); + auto prop_pattern = base::MakeUnique<re2::RE2>(it.key()); if (!prop_pattern->ok()) { LOG(WARNING) << "Regular expression /" << it.key() << "/ is invalid: " << prop_pattern->error() << "."; @@ -627,7 +627,7 @@ } const base::DictionaryValue* prop_schema = NULL; CHECK(it.value().GetAsDictionary(&prop_schema)); - pattern_properties_pattern.push_back(base::WrapUnique(prop_pattern)); + pattern_properties_pattern.push_back(std::move(prop_pattern)); pattern_properties_schema.push_back(prop_schema); } }
diff --git a/components/memory_pressure/memory_pressure_monitor_unittest.cc b/components/memory_pressure/memory_pressure_monitor_unittest.cc index 429bdd08..139c19e 100644 --- a/components/memory_pressure/memory_pressure_monitor_unittest.cc +++ b/components/memory_pressure/memory_pressure_monitor_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include "base/bind.h" +#include "base/task_runner.h" #include "base/test/simple_test_tick_clock.h" #include "base/tracked_objects.h" #include "components/memory_pressure/memory_pressure_stats_collector.h"
diff --git a/components/ntp_snippets/content_suggestion.h b/components/ntp_snippets/content_suggestion.h index b4e64f8..ea17274c 100644 --- a/components/ntp_snippets/content_suggestion.h +++ b/components/ntp_snippets/content_suggestion.h
@@ -151,6 +151,11 @@ void set_notification_extra( std::unique_ptr<NotificationExtra> notification_extra); + const base::Time& fetch_date() const { return fetch_date_; } + void set_fetch_date(const base::Time& fetch_date) { + fetch_date_ = fetch_date; + } + private: ID id_; GURL url_; @@ -163,6 +168,11 @@ std::unique_ptr<RecentTabSuggestionExtra> recent_tab_suggestion_extra_; std::unique_ptr<NotificationExtra> notification_extra_; + // The time when the remote suggestion was fetched from the server. This field + // is only populated when the ContentSuggestion is created from a + // RemoteSuggestion. + base::Time fetch_date_; + DISALLOW_COPY_AND_ASSIGN(ContentSuggestion); };
diff --git a/components/ntp_snippets/content_suggestions_metrics.cc b/components/ntp_snippets/content_suggestions_metrics.cc index 3886086..95a1381 100644 --- a/components/ntp_snippets/content_suggestions_metrics.cc +++ b/components/ntp_snippets/content_suggestions_metrics.cc
@@ -223,8 +223,8 @@ Category category, int position_in_category, base::Time publish_date, - base::Time last_background_fetch_time, - float score) { + float score, + base::Time fetch_date) { UMA_HISTOGRAM_EXACT_LINEAR(kHistogramShown, global_position, kMaxSuggestionsTotal); LogCategoryHistogramPosition(kHistogramShown, category, position_in_category, @@ -247,8 +247,8 @@ // suggestions. UMA_HISTOGRAM_CUSTOM_TIMES( kHistogramContentSuggestionsTimeSinceLastBackgroundFetch, - base::Time::Now() - last_background_fetch_time, - base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(7), + base::Time::Now() - fetch_date, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), /*bucket_count=*/100); } }
diff --git a/components/ntp_snippets/content_suggestions_metrics.h b/components/ntp_snippets/content_suggestions_metrics.h index 6a79e5e..d004c456 100644 --- a/components/ntp_snippets/content_suggestions_metrics.h +++ b/components/ntp_snippets/content_suggestions_metrics.h
@@ -23,8 +23,8 @@ Category category, int position_in_category, base::Time publish_date, - base::Time last_background_fetch_time, - float score); + float score, + base::Time fetch_date); // TODO(crbug.com/682160): Take struct, so that one could not mix up the // order of arguments.
diff --git a/components/ntp_snippets/content_suggestions_metrics_unittest.cc b/components/ntp_snippets/content_suggestions_metrics_unittest.cc index 2424d50..fe8193e3 100644 --- a/components/ntp_snippets/content_suggestions_metrics_unittest.cc +++ b/components/ntp_snippets/content_suggestions_metrics_unittest.cc
@@ -20,23 +20,21 @@ base::HistogramTester histogram_tester; OnSuggestionShown(/*global_position=*/1, Category::FromKnownCategory(KnownCategories::ARTICLES), - /*category_position=*/3, - base::Time::Now(), - base::Time::Now() - base::TimeDelta::FromHours(2), - 0.01f); + /*category_position=*/3, base::Time::Now(), 0.01f, + base::Time::Now() - base::TimeDelta::FromHours(2)); // Test corner cases for score. OnSuggestionShown(/*global_position=*/1, Category::FromKnownCategory(KnownCategories::ARTICLES), - /*category_position=*/3, base::Time::Now(), - base::Time::Now() - base::TimeDelta::FromHours(2), 0.0f); + /*category_position=*/3, base::Time::Now(), 0.0f, + base::Time::Now() - base::TimeDelta::FromHours(2)); OnSuggestionShown(/*global_position=*/1, Category::FromKnownCategory(KnownCategories::ARTICLES), - /*category_position=*/3, base::Time::Now(), - base::Time::Now() - base::TimeDelta::FromHours(2), 1.0f); + /*category_position=*/3, base::Time::Now(), 1.0f, + base::Time::Now() - base::TimeDelta::FromHours(2)); OnSuggestionShown(/*global_position=*/1, Category::FromKnownCategory(KnownCategories::ARTICLES), - /*category_position=*/3, base::Time::Now(), - base::Time::Now() - base::TimeDelta::FromHours(2), 8.0f); + /*category_position=*/3, base::Time::Now(), 8.0f, + base::Time::Now() - base::TimeDelta::FromHours(2)); EXPECT_THAT( histogram_tester.GetAllSamples(
diff --git a/components/ntp_snippets/remote/json_request.cc b/components/ntp_snippets/remote/json_request.cc index ad14bbd..c22e35c 100644 --- a/components/ntp_snippets/remote/json_request.cc +++ b/components/ntp_snippets/remote/json_request.cc
@@ -14,8 +14,7 @@ #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/strings/stringprintf.h" -#include "base/time/tick_clock.h" -#include "base/time/time.h" +#include "base/time/clock.h" #include "base/values.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "components/ntp_snippets/category_info.h" @@ -160,13 +159,13 @@ JsonRequest::JsonRequest( base::Optional<Category> exclusive_category, - base::TickClock* tick_clock, // Needed until destruction of the request. + base::Clock* clock, // Needed until destruction of the request. const ParseJSONCallback& callback) : exclusive_category_(exclusive_category), - tick_clock_(tick_clock), + clock_(clock), parse_json_callback_(callback), weak_ptr_factory_(this) { - creation_time_ = tick_clock_->NowTicks(); + creation_time_ = clock_->Now(); } JsonRequest::~JsonRequest() { @@ -180,7 +179,7 @@ } base::TimeDelta JsonRequest::GetFetchDuration() const { - return tick_clock_->NowTicks() - creation_time_; + return clock_->Now() - creation_time_; } std::string JsonRequest::GetResponseString() const { @@ -253,9 +252,9 @@ std::unique_ptr<JsonRequest> JsonRequest::Builder::Build() const { DCHECK(!url_.is_empty()); DCHECK(url_request_context_getter_); - DCHECK(tick_clock_); - auto request = base::MakeUnique<JsonRequest>( - params_.exclusive_category, tick_clock_, parse_json_callback_); + DCHECK(clock_); + auto request = base::MakeUnique<JsonRequest>(params_.exclusive_category, + clock_, parse_json_callback_); std::string body = BuildBody(); std::string headers = BuildHeaders(); request->url_fetcher_ = BuildURLFetcher(request.get(), headers, body); @@ -299,9 +298,8 @@ return *this; } -JsonRequest::Builder& JsonRequest::Builder::SetTickClock( - base::TickClock* tick_clock) { - tick_clock_ = tick_clock; +JsonRequest::Builder& JsonRequest::Builder::SetClock(base::Clock* clock) { + clock_ = clock; return *this; }
diff --git a/components/ntp_snippets/remote/json_request.h b/components/ntp_snippets/remote/json_request.h index 423d44c..f498747 100644 --- a/components/ntp_snippets/remote/json_request.h +++ b/components/ntp_snippets/remote/json_request.h
@@ -12,6 +12,7 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "base/time/time.h" #include "components/ntp_snippets/remote/request_params.h" #include "components/ntp_snippets/status.h" #include "components/translate/core/browser/language_model.h" @@ -20,7 +21,7 @@ namespace base { class Value; -class TickClock; +class Clock; } // namespace base class FetchAPI; @@ -81,10 +82,10 @@ Builder& SetLanguageModel(const translate::LanguageModel* language_model); Builder& SetParams(const RequestParams& params); Builder& SetParseJsonCallback(ParseJSONCallback callback); - // The tick_clock borrowed from the fetcher will be injected into the + // The clock borrowed from the fetcher will be injected into the // request. It will be used at build time and after the fetch returned. // It has to be alive until the request is destroyed. - Builder& SetTickClock(base::TickClock* tick_clock); + Builder& SetClock(base::Clock* clock); Builder& SetUrl(const GURL& url); Builder& SetUrlRequestContextGetter( const scoped_refptr<net::URLRequestContextGetter>& context_getter); @@ -115,7 +116,7 @@ // Only required, if the request needs to be sent. std::string auth_header_; - base::TickClock* tick_clock_; + base::Clock* clock_; FetchAPI fetch_api_; RequestParams params_; ParseJSONCallback parse_json_callback_; @@ -131,7 +132,7 @@ }; JsonRequest(base::Optional<Category> exclusive_category, - base::TickClock* tick_clock, + base::Clock* clock, const ParseJSONCallback& callback); JsonRequest(JsonRequest&&); ~JsonRequest() override; @@ -160,11 +161,11 @@ // If set, only return results for this category. base::Optional<Category> exclusive_category_; - // Use the TickClock from the Fetcher to measure the fetch time. It will be + // Use the Clock from the Fetcher to measure the fetch time. It will be // used on creation and after the fetch returned. It has to be alive until the // request is destroyed. - base::TickClock* tick_clock_; - base::TimeTicks creation_time_; + base::Clock* clock_; + base::Time creation_time_; // This callback is called to parse a json string. It contains callbacks for // error and success cases.
diff --git a/components/ntp_snippets/remote/json_request_unittest.cc b/components/ntp_snippets/remote/json_request_unittest.cc index f49a938a..912042f 100644 --- a/components/ntp_snippets/remote/json_request_unittest.cc +++ b/components/ntp_snippets/remote/json_request_unittest.cc
@@ -66,7 +66,7 @@ {ntp_snippets::kArticleSuggestionsFeature.name}), pref_service_(base::MakeUnique<TestingPrefServiceSimple>()), mock_task_runner_(new base::TestMockTimeTaskRunner()), - tick_clock_(mock_task_runner_->GetMockTickClock()), + clock_(mock_task_runner_->GetMockClock()), request_context_getter_( new net::TestURLRequestContextGetter(mock_task_runner_.get())) { translate::LanguageModel::RegisterProfilePrefs(pref_service_->registry()); @@ -88,7 +88,7 @@ JsonRequest::Builder CreateMinimalBuilder() { JsonRequest::Builder builder; builder.SetUrl(GURL("http://valid-url.test")) - .SetTickClock(tick_clock_.get()) + .SetClock(clock_.get()) .SetUrlRequestContextGetter(request_context_getter_.get()); return builder; } @@ -97,7 +97,7 @@ variations::testing::VariationParamsManager params_manager_; std::unique_ptr<TestingPrefServiceSimple> pref_service_; scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_; - std::unique_ptr<base::TickClock> tick_clock_; + std::unique_ptr<base::Clock> clock_; scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; net::TestURLFetcherFactory fetcher_factory_;
diff --git a/components/ntp_snippets/remote/proto/ntp_snippets.proto b/components/ntp_snippets/remote/proto/ntp_snippets.proto index 59c809d..823c4c1 100644 --- a/components/ntp_snippets/remote/proto/ntp_snippets.proto +++ b/components/ntp_snippets/remote/proto/ntp_snippets.proto
@@ -25,6 +25,8 @@ repeated SnippetSourceProto sources = 8; optional bool dismissed = 9; optional int32 remote_category_id = 10; + // The time when the snippet was fetched from the server. + optional int64 fetch_date = 11; } message SnippetImageProto {
diff --git a/components/ntp_snippets/remote/remote_suggestion.cc b/components/ntp_snippets/remote/remote_suggestion.cc index f68b180..714e0168 100644 --- a/components/ntp_snippets/remote/remote_suggestion.cc +++ b/components/ntp_snippets/remote/remote_suggestion.cc
@@ -96,7 +96,8 @@ // static std::unique_ptr<RemoteSuggestion> RemoteSuggestion::CreateFromChromeReaderDictionary( - const base::DictionaryValue& dict) { + const base::DictionaryValue& dict, + const base::Time& fetch_date) { const base::DictionaryValue* content = nullptr; if (!dict.GetDictionary("contentInfo", &content)) { return nullptr; @@ -167,6 +168,7 @@ std::unique_ptr<RemoteSuggestion> snippet( new RemoteSuggestion(ids, kArticlesRemoteId)); + snippet->fetch_date_ = fetch_date; std::string title; if (content->GetString("title", &title)) { @@ -217,7 +219,8 @@ std::unique_ptr<RemoteSuggestion> RemoteSuggestion::CreateFromContentSuggestionsDictionary( const base::DictionaryValue& dict, - int remote_category_id) { + int remote_category_id, + const base::Time& fetch_date) { const base::ListValue* ids; if (!dict.GetList("ids", &ids)) { return nullptr; @@ -235,6 +238,7 @@ return nullptr; } auto snippet = MakeUnique(parsed_ids, remote_category_id); + snippet->fetch_date_ = fetch_date; if (!(dict.GetString("title", &snippet->title_) && dict.GetString("snippet", &snippet->snippet_) && @@ -281,6 +285,7 @@ : kArticlesRemoteId; std::vector<std::string> ids(proto.ids().begin(), proto.ids().end()); + auto snippet = MakeUnique(ids, remote_category_id); snippet->title_ = proto.title(); @@ -319,6 +324,10 @@ snippet->publisher_name_ = source.publisher_name; snippet->amp_url_ = source.amp_url; + if (proto.has_fetch_date()) { + snippet->fetch_date_ = base::Time::FromInternalValue(proto.fetch_date()); + } + return snippet; } @@ -371,6 +380,9 @@ source_proto->set_amp_url(amp_url_.spec()); } + if (!fetch_date_.is_null()) { + result.set_fetch_date(fetch_date_.ToInternalValue()); + } return result; } @@ -393,6 +405,7 @@ suggestion.set_notification_extra( base::MakeUnique<NotificationExtra>(extra)); } + suggestion.set_fetch_date(fetch_date_); return suggestion; }
diff --git a/components/ntp_snippets/remote/remote_suggestion.h b/components/ntp_snippets/remote/remote_suggestion.h index c08e7ecf..615c4fb 100644 --- a/components/ntp_snippets/remote/remote_suggestion.h +++ b/components/ntp_snippets/remote/remote_suggestion.h
@@ -38,14 +38,16 @@ // suggestion. The keys in the dictionary are expected to be the same as the // property name, with exceptions documented in the property comment. static std::unique_ptr<RemoteSuggestion> CreateFromChromeReaderDictionary( - const base::DictionaryValue& dict); + const base::DictionaryValue& dict, + const base::Time& fetch_date); // Creates a RemoteSuggestion from a dictionary, as returned by Chrome Content // Suggestions. Returns a null pointer if the dictionary doesn't correspond to // a valid suggestion. Maps field names to Chrome Reader field names. static std::unique_ptr<RemoteSuggestion> CreateFromContentSuggestionsDictionary(const base::DictionaryValue& dict, - int remote_category_id); + int remote_category_id, + const base::Time& fetch_date); // Creates an RemoteSuggestion from a protocol buffer. Returns a null pointer // if the protocol buffer doesn't correspond to a valid suggestion. @@ -120,6 +122,8 @@ // CategoryFactory::FromRemoteCategory. int remote_category_id() const { return remote_category_id_; } + base::Time fetch_date() const { return fetch_date_; } + // Public for testing. static base::Time TimeFromJsonString(const std::string& timestamp_str); static std::string TimeToJsonString(const base::Time& time); @@ -152,6 +156,9 @@ bool should_notify_; base::Time notification_deadline_; + // The time when the remote suggestion was fetched from the server. + base::Time fetch_date_; + DISALLOW_COPY_AND_ASSIGN(RemoteSuggestion); };
diff --git a/components/ntp_snippets/remote/remote_suggestion_unittest.cc b/components/ntp_snippets/remote/remote_suggestion_unittest.cc index 7034ebf5..e28cf28 100644 --- a/components/ntp_snippets/remote/remote_suggestion_unittest.cc +++ b/components/ntp_snippets/remote/remote_suggestion_unittest.cc
@@ -9,6 +9,7 @@ #include "base/json/json_reader.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" #include "base/values.h" #include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h" #include "testing/gmock/include/gmock/gmock.h" @@ -23,14 +24,15 @@ using ::testing::NotNull; std::unique_ptr<RemoteSuggestion> SnippetFromContentSuggestionJSON( - const std::string& json) { + const std::string& json, + const base::Time& fetch_date) { auto json_value = base::JSONReader::Read(json); base::DictionaryValue* json_dict; if (!json_value->GetAsDictionary(&json_dict)) { return nullptr; } return RemoteSuggestion::CreateFromContentSuggestionsDictionary( - *json_dict, kArticlesRemoteId); + *json_dict, kArticlesRemoteId, fetch_date); } TEST(RemoteSuggestionTest, FromChromeContentSuggestionsDictionary) { @@ -52,7 +54,8 @@ " \"deadline\": \"2016-06-30T13:01:37.000Z\"\n" " }\n" "}"; - auto snippet = SnippetFromContentSuggestionJSON(kJsonStr); + const base::Time fetch_date = base::Time::FromInternalValue(1466634774L); + auto snippet = SnippetFromContentSuggestionJSON(kJsonStr, fetch_date); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://localhost/foobar"); @@ -73,14 +76,16 @@ auto notification_duration = snippet->notification_deadline() - snippet->publish_date(); EXPECT_EQ(7200.0f, notification_duration.InSecondsF()); + EXPECT_EQ(fetch_date, snippet->fetch_date()); } std::unique_ptr<RemoteSuggestion> SnippetFromChromeReaderDict( - std::unique_ptr<base::DictionaryValue> dict) { + std::unique_ptr<base::DictionaryValue> dict, + const base::Time& fetch_date) { if (!dict) { return nullptr; } - return RemoteSuggestion::CreateFromChromeReaderDictionary(*dict); + return RemoteSuggestion::CreateFromChromeReaderDictionary(*dict, fetch_date); } const char kChromeReaderCreationTimestamp[] = "1234567890"; @@ -124,8 +129,8 @@ } TEST(RemoteSuggestionTest, TestMultipleSources) { - auto snippet = - SnippetFromChromeReaderDict(ChromeReaderSnippetWithTwoSources()); + auto snippet = SnippetFromChromeReaderDict( + ChromeReaderSnippetWithTwoSources(), base::Time()); ASSERT_THAT(snippet, NotNull()); // Expect the first source to be chosen. @@ -147,7 +152,7 @@ ASSERT_TRUE(sources->GetDictionary(1, &source)); source->Remove("ampUrl", nullptr); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://url.com"); @@ -168,7 +173,7 @@ ASSERT_TRUE(sources->GetDictionary(1, &source)); source->Remove("publisherData.sourceName", nullptr); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://url.com"); @@ -191,7 +196,7 @@ ASSERT_TRUE(sources->GetDictionary(1, &source)); source->Remove("publisherData.sourceName", nullptr); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); ASSERT_FALSE(snippet->is_complete()); } @@ -199,7 +204,7 @@ TEST(RemoteSuggestionTest, ShouldFillInCreation) { auto dict = ChromeReaderSnippetWithTwoSources(); ASSERT_TRUE(dict->Remove("contentInfo.creationTimestampSec", nullptr)); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); // Publish date should have been filled with "now" - just make sure it's not @@ -218,7 +223,7 @@ TEST(RemoteSuggestionTest, ShouldFillInExpiry) { auto dict = ChromeReaderSnippetWithTwoSources(); ASSERT_TRUE(dict->Remove("contentInfo.expiryTimestampSec", nullptr)); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); base::Time publish_date = snippet->publish_date(); @@ -235,7 +240,7 @@ auto dict = ChromeReaderSnippetWithTwoSources(); ASSERT_TRUE(dict->Remove("contentInfo.creationTimestampSec", nullptr)); ASSERT_TRUE(dict->Remove("contentInfo.expiryTimestampSec", nullptr)); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); // Publish date should have been filled with "now" - just make sure it's not @@ -255,7 +260,7 @@ TEST(RemoteSuggestionTest, ShouldNotOverwriteExpiry) { auto dict = ChromeReaderSnippetWithTwoSources(); ASSERT_TRUE(dict->Remove("contentInfo.creationTimestampSec", nullptr)); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); // Expiry date should have kept the test default value. @@ -317,7 +322,7 @@ ASSERT_TRUE(sources->GetDictionary(1, &source)); source->Remove("publisherData.sourceName", nullptr); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://url.com"); @@ -338,7 +343,7 @@ ASSERT_TRUE(sources->GetDictionary(0, &source)); source->Remove("publisherData.sourceName", nullptr); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://url.com"); @@ -350,7 +355,7 @@ TEST(RemoteSuggestionTest, TestMultipleCompleteSources3) { // Test 3 complete sources, we should choose the first complete source auto dict = ChromeReaderSnippetWithThreeSources(); - auto snippet = SnippetFromChromeReaderDict(std::move(dict)); + auto snippet = SnippetFromChromeReaderDict(std::move(dict), base::Time()); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://url.com"); @@ -374,7 +379,7 @@ " \"ampUrl\" : \"http://localhost/amp\"," " \"faviconUrl\" : \"http://localhost/favicon.ico\" " "}"; - auto snippet = SnippetFromContentSuggestionJSON(kJsonStr); + auto snippet = SnippetFromContentSuggestionJSON(kJsonStr, base::Time()); ASSERT_THAT(snippet, NotNull()); EXPECT_EQ(snippet->id(), "http://localhost/foobar"); @@ -394,6 +399,40 @@ proto.set_score(0.1f); proto.set_dismissed(false); proto.set_remote_category_id(1); + proto.set_fetch_date(1476364691); + auto source = proto.add_sources(); + source->set_url("http://cool-suggestions.com/"); + source->set_publisher_name("Great Suggestions Inc."); + source->set_amp_url("http://cdn.ampproject.org/c/foo/"); + + std::unique_ptr<RemoteSuggestion> snippet = + RemoteSuggestion::CreateFromProto(proto); + ASSERT_THAT(snippet, NotNull()); + // The snippet database relies on the fact that the first id in the protocol + // buffer is considered the unique id. + EXPECT_EQ(snippet->id(), "foo"); + // Unfortunately, we only have MessageLite protocol buffers in Chrome, so + // comparing via DebugString() or MessageDifferencer is not working. + // So we either need to compare field-by-field (maintenance heavy) or + // compare the binary version (unusable diagnostic). Deciding for the latter. + std::string proto_serialized, round_tripped_serialized; + proto.SerializeToString(&proto_serialized); + snippet->ToProto().SerializeToString(&round_tripped_serialized); + EXPECT_EQ(proto_serialized, round_tripped_serialized); +} + +TEST(RemoteSuggestionTest, CreateFromProtoIgnoreMissingFetchDate) { + SnippetProto proto; + proto.add_ids("foo"); + proto.add_ids("bar"); + proto.set_title("a suggestion title"); + proto.set_snippet("the snippet describing the suggestion."); + proto.set_salient_image_url("http://google.com/logo/"); + proto.set_publish_date(1476095492); + proto.set_expiry_date(1476354691); + proto.set_score(0.1f); + proto.set_dismissed(false); + proto.set_remote_category_id(1); auto source = proto.add_sources(); source->set_url("http://cool-suggestions.com/"); source->set_publisher_name("Great Suggestions Inc."); @@ -405,14 +444,7 @@ // The snippet database relies on the fact that the first id in the protocol // buffer is considered the unique id. EXPECT_EQ(snippet->id(), "foo"); - // Unfortunately, we only have MessageLite protocol buffers in Chrome, so - // comparing via DebugString() or MessageDifferencer is not working. - // So we either need to compare field-by-field (maintenance heavy) or - // compare the binary version (unusable diagnostic). Deciding for the latter. - std::string proto_serialized, round_tripped_serialized; - proto.SerializeToString(&proto_serialized); - snippet->ToProto().SerializeToString(&round_tripped_serialized); - EXPECT_EQ(proto_serialized, round_tripped_serialized); + EXPECT_EQ(snippet->fetch_date(), base::Time()); } // New form, from chromecontentsuggestions-pa.googleapis.com. @@ -441,8 +473,8 @@ auto json = ContentSuggestionSnippet(); json->SetBoolean("notificationInfo.shouldNotify", true); json->SetString("notificationInfo.deadline", "2016-06-30T13:01:37.000Z"); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, base::Time()); EXPECT_TRUE(snippet->should_notify()); EXPECT_EQ(7200.0f, (snippet->notification_deadline() - snippet->publish_date()) @@ -453,8 +485,8 @@ auto json = ContentSuggestionSnippet(); json->SetBoolean("notificationInfo.shouldNotify", true); json->SetInteger("notificationInfo.notificationDeadline", 0); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, base::Time()); EXPECT_TRUE(snippet->should_notify()); EXPECT_EQ(base::Time::Max(), snippet->notification_deadline()); } @@ -462,8 +494,8 @@ TEST(RemoteSuggestionTest, NotificationInfoDeadlineAbsent) { auto json = ContentSuggestionSnippet(); json->SetBoolean("notificationInfo.shouldNotify", true); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, base::Time()); EXPECT_TRUE(snippet->should_notify()); EXPECT_EQ(base::Time::Max(), snippet->notification_deadline()); } @@ -471,22 +503,23 @@ TEST(RemoteSuggestionTest, NotificationInfoShouldNotifyInvalid) { auto json = ContentSuggestionSnippet(); json->SetString("notificationInfo.shouldNotify", "non-bool"); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, base::Time()); EXPECT_FALSE(snippet->should_notify()); } TEST(RemoteSuggestionTest, NotificationInfoAbsent) { auto json = ContentSuggestionSnippet(); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, base::Time()); EXPECT_FALSE(snippet->should_notify()); } TEST(RemoteSuggestionTest, ToContentSuggestion) { auto json = ContentSuggestionSnippet(); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + const base::Time fetch_date = base::Time::FromInternalValue(1466634774L); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, fetch_date); ASSERT_THAT(snippet, NotNull()); ContentSuggestion sugg = snippet->ToContentSuggestion( Category::FromKnownCategory(KnownCategories::ARTICLES)); @@ -503,14 +536,15 @@ EXPECT_THAT(sugg.download_suggestion_extra(), IsNull()); EXPECT_THAT(sugg.recent_tab_suggestion_extra(), IsNull()); EXPECT_THAT(sugg.notification_extra(), IsNull()); + EXPECT_THAT(sugg.fetch_date(), Eq(fetch_date)); } TEST(RemoteSuggestionTest, ToContentSuggestionWithNotificationInfo) { auto json = ContentSuggestionSnippet(); json->SetBoolean("notificationInfo.shouldNotify", true); json->SetString("notificationInfo.deadline", "2016-06-30T13:01:37.000Z"); - auto snippet = - RemoteSuggestion::CreateFromContentSuggestionsDictionary(*json, 0); + auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary( + *json, 0, base::Time()); ASSERT_THAT(snippet, NotNull()); ContentSuggestion sugg = snippet->ToContentSuggestion( Category::FromKnownCategory(KnownCategories::ARTICLES));
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc index 3722a2f..eb423601 100644 --- a/components/ntp_snippets/remote/remote_suggestions_fetcher.cc +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
@@ -15,7 +15,7 @@ #include "base/path_service.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/time/default_tick_clock.h" +#include "base/time/default_clock.h" #include "base/time/time.h" #include "base/values.h" #include "components/data_use_measurement/core/data_use_user_data.h" @@ -136,7 +136,8 @@ bool AddSuggestionsFromListValue(bool content_suggestions_api, int remote_category_id, const base::ListValue& list, - RemoteSuggestion::PtrVector* suggestions) { + RemoteSuggestion::PtrVector* suggestions, + const base::Time& fetch_time) { for (const auto& value : list) { const base::DictionaryValue* dict = nullptr; if (!value->GetAsDictionary(&dict)) { @@ -146,9 +147,10 @@ std::unique_ptr<RemoteSuggestion> suggestion; if (content_suggestions_api) { suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary( - *dict, remote_category_id); + *dict, remote_category_id, fetch_time); } else { - suggestion = RemoteSuggestion::CreateFromChromeReaderDictionary(*dict); + suggestion = + RemoteSuggestion::CreateFromChromeReaderDictionary(*dict, fetch_time); } if (!suggestion) { return false; @@ -159,8 +161,10 @@ return true; } -int GetMinuteOfTheDay(bool local_time, bool reduced_resolution) { - base::Time now(base::Time::Now()); +int GetMinuteOfTheDay(bool local_time, + bool reduced_resolution, + base::Clock* clock) { + base::Time now(clock->Now()); base::Time::Exploded now_exploded{}; local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded); int now_minute = reduced_resolution @@ -255,7 +259,7 @@ ? FetchAPI::CHROME_CONTENT_SUGGESTIONS_API : FetchAPI::CHROME_READER_API), api_key_(api_key), - tick_clock_(new base::DefaultTickClock()), + clock_(new base::DefaultClock()), user_classifier_(user_classifier), request_throttler_rare_ntp_user_( pref_service, @@ -290,12 +294,14 @@ } if (!params.interactive_request) { - UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal", - GetMinuteOfTheDay(/*local_time=*/true, - /*reduced_resolution=*/true)); - UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC", - GetMinuteOfTheDay(/*local_time=*/false, - /*reduced_resolution=*/true)); + UMA_HISTOGRAM_SPARSE_SLOWLY( + "NewTabPage.Snippets.FetchTimeLocal", + GetMinuteOfTheDay(/*local_time=*/true, + /*reduced_resolution=*/true, clock_.get())); + UMA_HISTOGRAM_SPARSE_SLOWLY( + "NewTabPage.Snippets.FetchTimeUTC", + GetMinuteOfTheDay(/*local_time=*/false, + /*reduced_resolution=*/true, clock_.get())); } JsonRequest::Builder builder; @@ -304,7 +310,7 @@ .SetLanguageModel(language_model_) .SetParams(params) .SetParseJsonCallback(parse_json_callback_) - .SetTickClock(tick_clock_.get()) + .SetClock(clock_.get()) .SetUrlRequestContextGetter(url_request_context_getter_) .SetUserClassifier(*user_classifier_); @@ -442,6 +448,9 @@ FetchResult status_code, const std::string& error_details) { DCHECK(request); + // Record the time when request for fetching remote content snippets finished. + const base::Time fetch_time = clock_->Now(); + last_fetch_json_ = request->GetResponseString(); UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", @@ -453,7 +462,8 @@ return; } FetchedCategoriesVector categories; - if (!JsonToSnippets(*result, &categories)) { + + if (!JsonToSnippets(*result, &categories, fetch_time)) { LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_; FetchFinished(OptionalFetchedCategories(), std::move(callback), FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string()); @@ -489,7 +499,8 @@ bool RemoteSuggestionsFetcher::JsonToSnippets( const base::Value& parsed, - FetchedCategoriesVector* categories) { + FetchedCategoriesVector* categories, + const base::Time& fetch_time) { const base::DictionaryValue* top_dict = nullptr; if (!parsed.GetAsDictionary(&top_dict)) { return false; @@ -504,9 +515,9 @@ const base::ListValue* recos = nullptr; return top_dict->GetList("recos", &recos) && - AddSuggestionsFromListValue(/*content_suggestions_api=*/false, - kUnusedRemoteCategoryId, *recos, - &categories->back().suggestions); + AddSuggestionsFromListValue( + /*content_suggestions_api=*/false, kUnusedRemoteCategoryId, + *recos, &categories->back().suggestions, fetch_time); } case FetchAPI::CHROME_CONTENT_SUGGESTIONS_API: { @@ -533,7 +544,7 @@ if (category_value->GetList("suggestions", &suggestions_list)) { if (!AddSuggestionsFromListValue( /*content_suggestions_api=*/true, remote_category_id, - *suggestions_list, &suggestions)) { + *suggestions_list, &suggestions, fetch_time)) { return false; } }
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher.h b/components/ntp_snippets/remote/remote_suggestions_fetcher.h index 90ebbac..a5f27c1 100644 --- a/components/ntp_snippets/remote/remote_suggestions_fetcher.h +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher.h
@@ -14,6 +14,7 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "base/time/clock.h" #include "base/time/tick_clock.h" #include "components/ntp_snippets/category.h" #include "components/ntp_snippets/category_info.h" @@ -105,8 +106,8 @@ const GURL& fetch_url() const { return fetch_url_; } // Overrides internal clock for testing purposes. - void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock) { - tick_clock_ = std::move(tick_clock); + void SetClockForTesting(std::unique_ptr<base::Clock> clock) { + clock_ = std::move(clock); } private: @@ -158,7 +159,8 @@ const std::string& error_details); bool JsonToSnippets(const base::Value& parsed, - FetchedCategoriesVector* categories); + FetchedCategoriesVector* categories, + const base::Time& fetch_time); bool DemandQuotaForRequest(bool interactive_request); @@ -192,8 +194,8 @@ // API key to use for non-authenticated requests. const std::string api_key_; - // Allow for an injectable tick clock for testing. - std::unique_ptr<base::TickClock> tick_clock_; + // Allow for an injectable clock for testing. + std::unique_ptr<base::Clock> clock_; // Classifier that tells us how active the user is. Not owned. const UserClassifier* user_classifier_;
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc index 28e4d79f..a75bcfe0 100644 --- a/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc
@@ -304,7 +304,7 @@ std::move(request_context_getter), utils_.pref_service(), nullptr, base::Bind(&ParseJsonDelayed), kAPIKey, user_classifier_.get()); - fetcher_->SetTickClockForTesting(mock_task_runner_->GetMockTickClock()); + fetcher_->SetClockForTesting(mock_task_runner_->GetMockClock()); } void SignIn() { utils_.fake_signin_manager()->SignIn(kTestEmail); }
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc index a4c0406..0142bad 100644 --- a/components/ntp_tiles/most_visited_sites_unittest.cc +++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -55,6 +55,7 @@ using testing::ElementsAre; using testing::InSequence; using testing::Invoke; +using testing::IsEmpty; using testing::Mock; using testing::Return; using testing::ReturnRef; @@ -730,6 +731,76 @@ base::RunLoop().RunUntilIdle(); } +TEST_P(MostVisitedSitesWithEmptyCacheTest, + ShouldSendEmptyListIfBothTopSitesAndSuggestionsServiceEmpty) { + if (IsPopularSitesEnabledViaVariations()) { + EXPECT_CALL(mock_observer_, + OnMostVisitedURLsAvailable(ElementsAre( + MatchesTile("PopularSite1", "http://popularsite1/", + NTPTileSource::POPULAR), + MatchesTile("PopularSite2", "http://popularsite2/", + NTPTileSource::POPULAR)))); + } else { + EXPECT_CALL(mock_observer_, OnMostVisitedURLsAvailable(IsEmpty())); + } + suggestions_service_callbacks_.Notify(SuggestionsProfile()); + top_sites_callbacks_.ClearAndNotify(MostVisitedURLList{}); + + base::RunLoop().RunUntilIdle(); +} + +TEST_P(MostVisitedSitesWithEmptyCacheTest, + ShouldRepeatedlyNotifyObserverIfTopSitesNotifies) { + EXPECT_CALL( + mock_observer_, + OnMostVisitedURLsAvailable(ElementsAre( + MatchesTile("Site 1", "http://site1/", NTPTileSource::TOP_SITES), + MatchesTile("Site 2", "http://site2/", NTPTileSource::TOP_SITES), + MatchesTile("Site 3", "http://site3/", NTPTileSource::TOP_SITES)))) + .Times(5); + + suggestions_service_callbacks_.Notify(SuggestionsProfile()); + + top_sites_callbacks_.ClearAndNotify( + {MakeMostVisitedURL("Site 1", "http://site1/"), + MakeMostVisitedURL("Site 2", "http://site2/"), + MakeMostVisitedURL("Site 3", "http://site3/")}); + base::RunLoop().RunUntilIdle(); + + for (int i = 0; i < 4; ++i) { + EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false)) + .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); + mock_top_sites_->NotifyTopSitesChanged( + history::TopSitesObserver::ChangeReason::MOST_VISITED); + EXPECT_FALSE(top_sites_callbacks_.empty()); + top_sites_callbacks_.ClearAndNotify( + {MakeMostVisitedURL("Site 1", "http://site1/"), + MakeMostVisitedURL("Site 2", "http://site2/"), + MakeMostVisitedURL("Site 3", "http://site3/")}); + base::RunLoop().RunUntilIdle(); + } +} + +TEST_P(MostVisitedSitesWithEmptyCacheTest, + ShouldRepeatedlyNotifyObserverIfSuggestionsServiceNotifies) { + EXPECT_CALL(mock_observer_, + OnMostVisitedURLsAvailable( + ElementsAre(MatchesTile("Site 1", "http://site1/", + NTPTileSource::SUGGESTIONS_SERVICE), + MatchesTile("Site 2", "http://site2/", + NTPTileSource::SUGGESTIONS_SERVICE), + MatchesTile("Site 3", "http://site3/", + NTPTileSource::SUGGESTIONS_SERVICE)))) + .Times(5); + + for (int i = 0; i < 5; ++i) { + suggestions_service_callbacks_.Notify( + MakeProfile({MakeSuggestion("Site 1", "http://site1/"), + MakeSuggestion("Site 2", "http://site2/"), + MakeSuggestion("Site 3", "http://site3/")})); + } +} + INSTANTIATE_TEST_CASE_P(MostVisitedSitesWithEmptyCacheTest, MostVisitedSitesWithEmptyCacheTest, ::testing::Bool());
diff --git a/components/ntp_tiles/popular_sites_impl.cc b/components/ntp_tiles/popular_sites_impl.cc index 44c2982..566202b 100644 --- a/components/ntp_tiles/popular_sites_impl.cc +++ b/components/ntp_tiles/popular_sites_impl.cc
@@ -14,6 +14,7 @@ #include "base/path_service.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/time/time.h" #include "base/values.h" #include "components/data_use_measurement/core/data_use_user_data.h"
diff --git a/components/ntp_tiles/popular_sites_impl.h b/components/ntp_tiles/popular_sites_impl.h index 17280da0..1f0fbbe6 100644 --- a/components/ntp_tiles/popular_sites_impl.h +++ b/components/ntp_tiles/popular_sites_impl.h
@@ -19,6 +19,8 @@ #include "url/gurl.h" namespace base { +class TaskRunner; +class SequencedWorkerPool; class Value; }
diff --git a/components/password_manager/core/browser/affiliation_backend.h b/components/password_manager/core/browser/affiliation_backend.h index f99fcac9..832c305 100644 --- a/components/password_manager/core/browser/affiliation_backend.h +++ b/components/password_manager/core/browser/affiliation_backend.h
@@ -25,6 +25,7 @@ class Clock; class FilePath; class SingleThreadTaskRunner; +class TaskRunner; class ThreadChecker; class TickClock; class Time;
diff --git a/components/policy/core/common/cloud/device_management_service.h b/components/policy/core/common/cloud/device_management_service.h index c19ccc4..bd257a9 100644 --- a/components/policy/core/common/cloud/device_management_service.h +++ b/components/policy/core/common/cloud/device_management_service.h
@@ -25,6 +25,9 @@ #include "components/policy/proto/device_management_backend.pb.h" #include "net/url_request/url_fetcher_delegate.h" +namespace base { +class SequencedTaskRunner; +} namespace net { class URLRequestContextGetter;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 0b1c89b..a97cc5f 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -296,7 +296,7 @@ 'name': 'NewTabPageLocation', 'type': 'string', 'schema': { 'type': 'string' }, - 'supported_on': ['chrome.*:57-', 'chrome_os:57-'], + 'supported_on': ['chrome.*:58-', 'chrome_os:58-'], 'features': { 'can_be_recommended': True, 'dynamic_refresh': True,
diff --git a/components/quirks/quirks_client.cc b/components/quirks/quirks_client.cc index 25963a2..b949a329 100644 --- a/components/quirks/quirks_client.cc +++ b/components/quirks/quirks_client.cc
@@ -9,6 +9,7 @@ #include "base/json/json_reader.h" #include "base/strings/stringprintf.h" #include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/quirks/quirks_manager.h" #include "components/version_info/version_info.h"
diff --git a/components/quirks/quirks_manager.cc b/components/quirks/quirks_manager.cc index f3d689b..382e9fd 100644 --- a/components/quirks/quirks_manager.cc +++ b/components/quirks/quirks_manager.cc
@@ -12,6 +12,7 @@ #include "base/path_service.h" #include "base/strings/stringprintf.h" #include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/quirks/pref_names.h"
diff --git a/components/safe_browsing_db/v4_local_database_manager.cc b/components/safe_browsing_db/v4_local_database_manager.cc index 0940f2ef..65c40b2 100644 --- a/components/safe_browsing_db/v4_local_database_manager.cc +++ b/components/safe_browsing_db/v4_local_database_manager.cc
@@ -14,6 +14,7 @@ #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/metrics/histogram_macros.h" +#include "base/threading/sequenced_worker_pool.h" #include "components/safe_browsing_db/v4_feature_list.h" #include "components/safe_browsing_db/v4_protocol_manager_util.h" #include "content/public/browser/browser_thread.h"
diff --git a/components/search_provider_logos/logo_tracker_unittest.cc b/components/search_provider_logos/logo_tracker_unittest.cc index fe8a436..b124342 100644 --- a/components/search_provider_logos/logo_tracker_unittest.cc +++ b/components/search_provider_logos/logo_tracker_unittest.cc
@@ -7,6 +7,7 @@ #include <stddef.h> #include <stdint.h> +#include <memory> #include <vector> #include "base/base64.h" @@ -16,7 +17,6 @@ #include "base/json/json_writer.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" #include "base/run_loop.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" @@ -699,13 +699,14 @@ // Tests that deal with multiple listeners. -void EnqueueObservers(LogoTracker* logo_tracker, - const ScopedVector<MockLogoObserver>& observers, - size_t start_index) { +void EnqueueObservers( + LogoTracker* logo_tracker, + const std::vector<std::unique_ptr<MockLogoObserver>>& observers, + size_t start_index) { if (start_index >= observers.size()) return; - logo_tracker->GetLogo(observers[start_index]); + logo_tracker->GetLogo(observers[start_index].get()); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&EnqueueObservers, logo_tracker, base::ConstRef(observers), start_index + 1)); @@ -722,11 +723,11 @@ SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint, response); const int kNumListeners = 10; - ScopedVector<MockLogoObserver> listeners; + std::vector<std::unique_ptr<MockLogoObserver>> listeners; for (int i = 0; i < kNumListeners; ++i) { MockLogoObserver* listener = new MockLogoObserver(); listener->ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo); - listeners.push_back(listener); + listeners.push_back(base::WrapUnique(listener)); } EnqueueObservers(logo_tracker_, listeners, 0);
diff --git a/components/sessions/core/base_session_service.h b/components/sessions/core/base_session_service.h index 473b75d..385948b 100644 --- a/components/sessions/core/base_session_service.h +++ b/components/sessions/core/base_session_service.h
@@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/task/cancelable_task_tracker.h" +#include "base/threading/sequenced_worker_pool.h" #include "components/sessions/core/sessions_export.h" #include "url/gurl.h"
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java index 77490b6..347cb0c 100644 --- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java +++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
@@ -48,7 +48,7 @@ @Override public Account[] getAccountsByType(String type) { - if (!AccountManagerHelper.get(mApplicationContext).hasGetAccountsPermission()) { + if (!hasGetAccountsPermission()) { return new Account[] {}; } long now = SystemClock.elapsedRealtime(); @@ -110,7 +110,7 @@ @Override public void hasFeatures(Account account, String[] features, final Callback<Boolean> callback) { - if (!AccountManagerHelper.get(mApplicationContext).hasGetAccountsPermission()) { + if (!hasGetAccountsPermission()) { ThreadUtils.postOnUiThread(new Runnable() { @Override public void run() { @@ -153,7 +153,7 @@ public void updateCredentials( Account account, Activity activity, final Callback<Boolean> callback) { ThreadUtils.assertOnUiThread(); - if (!AccountManagerHelper.get(mApplicationContext).hasGetAccountsPermission()) { + if (!hasGetAccountsPermission()) { ThreadUtils.postOnUiThread(new Runnable() { @Override public void run() { @@ -186,4 +186,8 @@ } }, null /* handler */); } + + protected boolean hasGetAccountsPermission() { + return AccountManagerHelper.get(mApplicationContext).hasGetAccountsPermission(); + } }
diff --git a/components/storage_monitor/test_volume_mount_watcher_win.cc b/components/storage_monitor/test_volume_mount_watcher_win.cc index 115054a..36815463 100644 --- a/components/storage_monitor/test_volume_mount_watcher_win.cc +++ b/components/storage_monitor/test_volume_mount_watcher_win.cc
@@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_worker_pool.h" #include "components/storage_monitor/storage_info.h" #include "content/public/browser/browser_thread.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/storage_monitor/volume_mount_watcher_win.cc b/components/storage_monitor/volume_mount_watcher_win.cc index 6307f5b..8c11a0a 100644 --- a/components/storage_monitor/volume_mount_watcher_win.cc +++ b/components/storage_monitor/volume_mount_watcher_win.cc
@@ -24,6 +24,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/sys_info.h" #include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/time/time.h" #include "base/win/scoped_handle.h" #include "components/storage_monitor/media_storage_util.h"
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn index 31d7f480..b55d108 100644 --- a/components/subresource_filter/content/browser/BUILD.gn +++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -22,6 +22,7 @@ "//content/public/browser", "//content/public/common", "//ipc", + "//net", "//url", ] public_deps = [
diff --git a/components/subresource_filter/content/browser/DEPS b/components/subresource_filter/content/browser/DEPS index fdf9670..ef2a1e8 100644 --- a/components/subresource_filter/content/browser/DEPS +++ b/components/subresource_filter/content/browser/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+components/keyed_service/core", - "+content/public/browser", "+components/safe_browsing_db", + "+content/public/browser", + "+net/base", "+ui/base/page_transition_types.h", ]
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver.cc b/components/subresource_filter/content/browser/content_subresource_filter_driver.cc index e54de762..59714f1 100644 --- a/components/subresource_filter/content/browser/content_subresource_filter_driver.cc +++ b/components/subresource_filter/content/browser/content_subresource_filter_driver.cc
@@ -15,15 +15,15 @@ ContentSubresourceFilterDriver::~ContentSubresourceFilterDriver() {} -void ContentSubresourceFilterDriver::ActivateForProvisionalLoad( +void ContentSubresourceFilterDriver::ActivateForNextCommittedLoad( ActivationLevel activation_level, - const GURL& url, bool measure_performance) { // Must use legacy IPC to ensure the activation message arrives in-order, i.e. // before the load is committed on the renderer side. - render_frame_host_->Send(new SubresourceFilterMsg_ActivateForProvisionalLoad( - render_frame_host_->GetRoutingID(), activation_level, url, - measure_performance)); + render_frame_host_->Send( + new SubresourceFilterMsg_ActivateForNextCommittedLoad( + render_frame_host_->GetRoutingID(), activation_level, + measure_performance)); } } // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver.h b/components/subresource_filter/content/browser/content_subresource_filter_driver.h index b340999c..f194dc79 100644 --- a/components/subresource_filter/content/browser/content_subresource_filter_driver.h +++ b/components/subresource_filter/content/browser/content_subresource_filter_driver.h
@@ -8,8 +8,6 @@ #include "base/macros.h" #include "components/subresource_filter/core/common/activation_level.h" -class GURL; - namespace content { class RenderFrameHost; } // namespace content @@ -24,11 +22,10 @@ content::RenderFrameHost* render_frame_host); virtual ~ContentSubresourceFilterDriver(); - // Instructs the agent on the renderer to set up the subresource filter for - // the currently ongoing provisional document load in the frame. - virtual void ActivateForProvisionalLoad(ActivationLevel activation_level, - const GURL& url, - bool measure_performance); + // Instructs the agent on the renderer side to set up subresource filtering at + // the specified |activation_level| for the next load committed in this frame. + virtual void ActivateForNextCommittedLoad(ActivationLevel activation_level, + bool measure_performance); private: // The RenderFrameHost that this driver belongs to.
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc index 0ba6bf3..b7b6509 100644 --- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc +++ b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
@@ -17,6 +17,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "ipc/ipc_message_macros.h" +#include "net/base/net_errors.h" #include "url/gurl.h" namespace subresource_filter { @@ -170,12 +171,18 @@ void ContentSubresourceFilterDriverFactory::ActivateForFrameHostIfNeeded( content::RenderFrameHost* render_frame_host, - const GURL& url) { - if (activation_level_ != ActivationLevel::DISABLED) { + const GURL& url, + bool failed_navigation) { + // PlzNavigate: For failed navigations, ReadyToCommitNavigation is still + // called, so we end up here; but there is no longer a failed provisional load + // on the renderer side, so an activation message sent from here would turn on + // filtering for the subsequent error page load. This is probably harmless, + // but not sending an activation message is even cleaner. + if (activation_level_ != ActivationLevel::DISABLED && !failed_navigation) { auto* driver = DriverFromFrameHost(render_frame_host); DCHECK(driver); - driver->ActivateForProvisionalLoad(GetMaximumActivationLevel(), url, - measure_performance_); + driver->ActivateForNextCommittedLoad(GetMaximumActivationLevel(), + measure_performance_); } } @@ -243,7 +250,9 @@ GURL url = navigation_handle->GetURL(); const content::Referrer& referrer = navigation_handle->GetReferrer(); ui::PageTransition transition = navigation_handle->GetPageTransition(); - ReadyToCommitNavigationInternal(render_frame_host, url, referrer, transition); + ReadyToCommitNavigationInternal( + render_frame_host, url, referrer, transition, + navigation_handle->GetNetErrorCode() != net::OK); } void ContentSubresourceFilterDriverFactory::DidFinishLoad( @@ -305,9 +314,10 @@ content::RenderFrameHost* render_frame_host, const GURL& url, const content::Referrer& referrer, - ui::PageTransition transition) { + ui::PageTransition transition, + bool failed_navigation) { if (render_frame_host->GetParent()) { - ActivateForFrameHostIfNeeded(render_frame_host, url); + ActivateForFrameHostIfNeeded(render_frame_host, url, failed_navigation); return; } @@ -327,7 +337,7 @@ activation_level_ = GetMaximumActivationLevel(); measure_performance_ = activation_level_ != ActivationLevel::DISABLED && ShouldMeasurePerformanceForPageLoad(); - ActivateForFrameHostIfNeeded(render_frame_host, url); + ActivateForFrameHostIfNeeded(render_frame_host, url, failed_navigation); } bool ContentSubresourceFilterDriverFactory::DidURLMatchCurrentActivationList(
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h index 0ec55f4..0750af6c 100644 --- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h +++ b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
@@ -118,7 +118,8 @@ // activation signal should be sent. bool ShouldActivateForMainFrameURL(const GURL& url) const; void ActivateForFrameHostIfNeeded(content::RenderFrameHost* render_frame_host, - const GURL& url); + const GURL& url, + bool failed_navigation); // Internal implementation of ReadyToCommitNavigation which doesn't use // NavigationHandle to ease unit tests. @@ -126,7 +127,8 @@ content::RenderFrameHost* render_frame_host, const GURL& url, const content::Referrer& referrer, - ui::PageTransition page_transition); + ui::PageTransition page_transition, + bool failed_navigation); bool DidURLMatchCurrentActivationList(const GURL& url) const;
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc index 0109d4f..e0312a7 100644 --- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc +++ b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
@@ -2,19 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h" + #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/metrics/field_trial.h" #include "base/test/histogram_tester.h" #include "components/safe_browsing_db/util.h" -#include "components/subresource_filter/content/browser/content_subresource_filter_driver.h" -#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h" #include "components/subresource_filter/content/common/subresource_filter_messages.h" #include "components/subresource_filter/core/browser/subresource_filter_client.h" #include "components/subresource_filter/core/browser/subresource_filter_features.h" #include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h" #include "content/public/browser/web_contents.h" #include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_renderer_host.h" #include "content/public/test/web_contents_tester.h" #include "testing/gmock/include/gmock/gmock.h" @@ -135,21 +136,6 @@ {false /* expected_activation */, kActivationLevelDisabled}, }; -class MockSubresourceFilterDriver : public ContentSubresourceFilterDriver { - public: - explicit MockSubresourceFilterDriver( - content::RenderFrameHost* render_frame_host) - : ContentSubresourceFilterDriver(render_frame_host) {} - - ~MockSubresourceFilterDriver() override = default; - - MOCK_METHOD3(ActivateForProvisionalLoad, - void(ActivationLevel, const GURL&, bool)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockSubresourceFilterDriver); -}; - class MockSubresourceFilterClient : public SubresourceFilterClient { public: MockSubresourceFilterClient() {} @@ -177,22 +163,12 @@ client_ = new MockSubresourceFilterClient(); ContentSubresourceFilterDriverFactory::CreateForWebContents( web_contents(), base::WrapUnique(client())); - driver_ = new MockSubresourceFilterDriver(main_rfh()); - SetDriverForFrameHostForTesting(main_rfh(), driver()); + // Add a subframe. content::RenderFrameHostTester* rfh_tester = content::RenderFrameHostTester::For(main_rfh()); rfh_tester->InitializeRenderFrameIfNeeded(); subframe_rfh_ = rfh_tester->AppendChild("Child"); - subframe_driver_ = new MockSubresourceFilterDriver(subframe_rfh()); - SetDriverForFrameHostForTesting(subframe_rfh(), subframe_driver()); - } - - void SetDriverForFrameHostForTesting( - content::RenderFrameHost* render_frame_host, - ContentSubresourceFilterDriver* driver) { - factory()->SetDriverForFrameHostForTesting(render_frame_host, - base::WrapUnique(driver)); } ContentSubresourceFilterDriverFactory* factory() { @@ -201,11 +177,37 @@ } MockSubresourceFilterClient* client() { return client_; } - MockSubresourceFilterDriver* driver() { return driver_; } - - MockSubresourceFilterDriver* subframe_driver() { return subframe_driver_; } content::RenderFrameHost* subframe_rfh() { return subframe_rfh_; } + void ExpectActivationSignalForFrame(content::RenderFrameHost* rfh, + bool expect_activation) { + content::MockRenderProcessHost* render_process_host = + static_cast<content::MockRenderProcessHost*>(rfh->GetProcess()); + const IPC::Message* message = + render_process_host->sink().GetFirstMessageMatching( + SubresourceFilterMsg_ActivateForNextCommittedLoad::ID); + ASSERT_EQ(expect_activation, !!message); + if (expect_activation) { + std::tuple<ActivationLevel, bool> args; + SubresourceFilterMsg_ActivateForNextCommittedLoad::Read(message, &args); + EXPECT_NE(ActivationLevel::DISABLED, std::get<0>(args)); + } + render_process_host->sink().ClearMessages(); + } + + void SimulateNavigationCommit(content::RenderFrameHost* rfh, + const GURL& url, + const content::Referrer& referrer, + const ui::PageTransition transition) { + // TODO(crbug.com/688393): Once WCO::ReadyToCommitNavigation is invoked + // consistently for tests in PlzNavigate and non-PlzNavigate, remove this. + if (!content::IsBrowserSideNavigationEnabled()) { + factory()->ReadyToCommitNavigationInternal(rfh, url, referrer, transition, + false /* failed_navigation */); + } + content::RenderFrameHostTester::For(rfh)->SimulateNavigationCommit(url); + } + void BlacklistURLWithRedirectsNavigateAndCommit( const std::vector<bool>& blacklisted_urls, const std::vector<GURL>& navigation_chain, @@ -236,20 +238,10 @@ } rfh_tester->SimulateRedirect(url); } - EXPECT_CALL(*driver(), - ActivateForProvisionalLoad(::testing::_, ::testing::_, - expected_measure_performance())) - .Times(expected_activation); - // TODO(crbug.com/688393): remove the call to - // ReadyToCommitNavigationInternal once WCO::ReadyToCommitNavigation is - // invoked consistently for tests in PlzNavigate and non-PlzNavigate. - if (!content::IsBrowserSideNavigationEnabled()) { - factory()->ReadyToCommitNavigationInternal( - main_rfh(), navigation_chain.back(), referrer, transition); - } - rfh_tester->SimulateNavigationCommit(navigation_chain.back()); - ::testing::Mock::VerifyAndClearExpectations(driver()); + SimulateNavigationCommit(main_rfh(), navigation_chain.back(), referrer, + transition); + ExpectActivationSignalForFrame(main_rfh(), expected_activation); if (expected_pattern != EMPTY) { EXPECT_THAT(tester.GetAllSamples(kMatchesPatternHistogramName), @@ -267,15 +259,13 @@ } void NavigateAndCommitSubframe(const GURL& url, bool expected_activation) { - EXPECT_CALL(*subframe_driver(), - ActivateForProvisionalLoad(::testing::_, ::testing::_, - expected_measure_performance())) - .Times(expected_activation); EXPECT_CALL(*client(), ToggleNotificationVisibility(::testing::_)).Times(0); - factory()->ReadyToCommitNavigationInternal( - subframe_rfh(), url, content::Referrer(), ui::PAGE_TRANSITION_LINK); - ::testing::Mock::VerifyAndClearExpectations(subframe_driver()); + content::RenderFrameHostTester::For(subframe_rfh()) + ->SimulateNavigationStart(url); + SimulateNavigationCommit(subframe_rfh(), url, content::Referrer(), + ui::PAGE_TRANSITION_LINK); + ExpectActivationSignalForFrame(subframe_rfh(), expected_activation); ::testing::Mock::VerifyAndClearExpectations(client()); } @@ -314,6 +304,21 @@ main_rfh()); } + void EmulateFailedNavigationAndExpectNoActivation(const GURL& url) { + EXPECT_CALL(*client(), ToggleNotificationVisibility(false)).Times(1); + + // ReadyToCommitNavigation with browser-side navigation disabled is not + // called in production code for failed navigations (e.g. network errors). + // It is called with browser-side navigation enabled, in which case + // RenderFrameHostTester already calls it, no need to call it manually. + content::RenderFrameHostTester* rfh_tester = + content::RenderFrameHostTester::For(main_rfh()); + rfh_tester->SimulateNavigationStart(url); + rfh_tester->SimulateNavigationError(url, 403); + ExpectActivationSignalForFrame(main_rfh(), false); + ::testing::Mock::VerifyAndClearExpectations(client()); + } + void EmulateInPageNavigation(const std::vector<bool>& blacklisted_urls, RedirectChainMatchPattern expected_pattern, bool expected_activation) { @@ -327,13 +332,10 @@ NavigateAndExpectActivation(blacklisted_urls, {GURL(kExampleUrl)}, expected_pattern, expected_activation); - EXPECT_CALL(*driver(), ActivateForProvisionalLoad( - ::testing::_, ::testing::_, ::testing::_)) - .Times(0); EXPECT_CALL(*client(), ToggleNotificationVisibility(::testing::_)).Times(0); content::RenderFrameHostTester::For(main_rfh()) ->SimulateNavigationCommit(GURL(kExampleUrl)); - ::testing::Mock::VerifyAndClearExpectations(driver()); + ExpectActivationSignalForFrame(main_rfh(), false); ::testing::Mock::VerifyAndClearExpectations(client()); } @@ -347,10 +349,8 @@ // Owned by the factory. MockSubresourceFilterClient* client_; - MockSubresourceFilterDriver* driver_; content::RenderFrameHost* subframe_rfh_; - MockSubresourceFilterDriver* subframe_driver_; DISALLOW_COPY_AND_ASSIGN(ContentSubresourceFilterDriverFactoryTest); }; @@ -451,6 +451,17 @@ true /* expected_activation */); } +TEST_F(ContentSubresourceFilterDriverFactoryTest, FailedNavigation) { + base::FieldTrialList field_trial_list(nullptr); + testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle( + base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled, + kActivationScopeAllSites); + const GURL url(kExampleUrl); + NavigateAndExpectActivation({false}, {url}, EMPTY, + true /* expected_activation */); + EmulateFailedNavigationAndExpectNoActivation(url); +} + TEST_F(ContentSubresourceFilterDriverFactoryTest, RedirectPatternTest) { base::FieldTrialList field_trial_list(nullptr); testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
diff --git a/components/subresource_filter/content/common/document_subresource_filter.h b/components/subresource_filter/content/common/document_subresource_filter.h index 8de7df37..68f336f4 100644 --- a/components/subresource_filter/content/common/document_subresource_filter.h +++ b/components/subresource_filter/content/common/document_subresource_filter.h
@@ -72,6 +72,9 @@ ~DocumentSubresourceFilter() override; const DocumentLoadStatistics& statistics() const { return statistics_; } + bool is_performance_measuring_enabled() const { + return activation_state_.measure_performance; + } // blink::WebDocumentSubresourceFilter: LoadPolicy getLoadPolicy(const blink::WebURL& resourceUrl,
diff --git a/components/subresource_filter/content/common/subresource_filter_messages.h b/components/subresource_filter/content/common/subresource_filter_messages.h index 209c684..84ed28b 100644 --- a/components/subresource_filter/content/common/subresource_filter_messages.h +++ b/components/subresource_filter/content/common/subresource_filter_messages.h
@@ -37,13 +37,20 @@ IPC_MESSAGE_CONTROL1(SubresourceFilterMsg_SetRulesetForProcess, IPC::PlatformFileForTransit /* ruleset_file */); -// Instructs the renderer to activate subresource filtering for the currently -// ongoing provisional document load in a frame. The message must arrive after -// the provisional load starts, but before it is committed on the renderer side. +// Instructs the renderer to activate subresource filtering at the specified +// |activation_level| for the document load committed next in a frame. +// +// Without browser-side navigation, the message must arrive just before the +// provisional load is committed on the renderer side. In practice, it is often +// sent after the provisional load has already started, but this is not a +// requirement. The message will have no effect if the provisional load fails. +// +// With browser-side navigation enabled, the message must arrive just before +// FrameMsg_CommitNavigation. +// // If no message arrives, the default behavior is ActivationLevel::DISABLED. -IPC_MESSAGE_ROUTED3(SubresourceFilterMsg_ActivateForProvisionalLoad, +IPC_MESSAGE_ROUTED2(SubresourceFilterMsg_ActivateForNextCommittedLoad, subresource_filter::ActivationLevel /* activation_level */, - GURL /* url */, bool /* measure_performance */); // ----------------------------------------------------------------------------
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent.cc b/components/subresource_filter/content/renderer/subresource_filter_agent.cc index 6b08edc..fae3ab8 100644 --- a/components/subresource_filter/content/renderer/subresource_filter_agent.cc +++ b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -28,8 +28,7 @@ content::RenderFrame* render_frame, UnverifiedRulesetDealer* ruleset_dealer) : content::RenderFrameObserver(render_frame), - ruleset_dealer_(ruleset_dealer), - activation_level_for_provisional_load_(ActivationLevel::DISABLED) { + ruleset_dealer_(ruleset_dealer) { DCHECK(ruleset_dealer); } @@ -67,24 +66,21 @@ render_frame()->GetRoutingID(), statistics)); } -void SubresourceFilterAgent::OnActivateForProvisionalLoad( +void SubresourceFilterAgent::OnActivateForNextCommittedLoad( ActivationLevel activation_level, - const GURL& url, bool measure_performance) { - activation_level_for_provisional_load_ = activation_level; - url_for_provisional_load_ = url; - measure_performance_ = measure_performance; + activation_level_for_next_commit_ = activation_level; + measure_performance_for_next_commit_ = measure_performance; } void SubresourceFilterAgent::RecordHistogramsOnLoadCommitted() { // Note: ActivationLevel used to be called ActivationState, the legacy name is // kept for the histogram. - UMA_HISTOGRAM_ENUMERATION( - "SubresourceFilter.DocumentLoad.ActivationState", - static_cast<int>(activation_level_for_provisional_load_), - static_cast<int>(ActivationLevel::LAST) + 1); + UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.DocumentLoad.ActivationState", + static_cast<int>(activation_level_for_next_commit_), + static_cast<int>(ActivationLevel::LAST) + 1); - if (activation_level_for_provisional_load_ != ActivationLevel::DISABLED) { + if (activation_level_for_next_commit_ != ActivationLevel::DISABLED) { UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.DocumentLoad.RulesetIsAvailable", ruleset_dealer_->IsRulesetFileAvailable()); } @@ -109,7 +105,8 @@ // If ThreadTicks is not supported or performance measuring is switched off, // then no time measurements have been collected. - if (measure_performance_ && ScopedThreadTimers::IsSupported()) { + if (ScopedThreadTimers::IsSupported() && + filter_for_last_committed_load_->is_performance_measuring_enabled()) { UMA_HISTOGRAM_CUSTOM_MICRO_TIMES( "SubresourceFilter.DocumentLoad.SubresourceEvaluation." "TotalWallDuration", @@ -129,27 +126,13 @@ SendDocumentLoadStatistics(statistics); } -void SubresourceFilterAgent::OnDestruct() { - delete this; +void SubresourceFilterAgent::ResetActivatonStateForNextCommit() { + activation_level_for_next_commit_ = ActivationLevel::DISABLED; + measure_performance_for_next_commit_ = false; } -void SubresourceFilterAgent::DidStartProvisionalLoad( - blink::WebDataSource* data_source) { - // With PlzNavigate, DidStartProvisionalLoad and DidCommitProvisionalLoad will - // both be called in response to the one commit IPC from the browser. That - // means that they will come after OnActivateForProvisionalLoad. So we have to - // have extra logic to check that the response to OnActivateForProvisionalLoad - // isn't removed in that case. - if (!content::IsBrowserSideNavigationEnabled() || - (!data_source || - static_cast<GURL>(data_source->getRequest().url()) != - url_for_provisional_load_)) { - activation_level_for_provisional_load_ = ActivationLevel::DISABLED; - measure_performance_ = false; - } else { - url_for_provisional_load_ = GURL(); - } - filter_for_last_committed_load_.reset(); +void SubresourceFilterAgent::OnDestruct() { + delete this; } void SubresourceFilterAgent::DidCommitProvisionalLoad( @@ -158,11 +141,13 @@ if (is_same_page_navigation) return; + filter_for_last_committed_load_.reset(); + std::vector<GURL> ancestor_document_urls = GetAncestorDocumentURLs(); if (ancestor_document_urls.front().SchemeIsHTTPOrHTTPS() || ancestor_document_urls.front().SchemeIsFile()) { RecordHistogramsOnLoadCommitted(); - if (activation_level_for_provisional_load_ != ActivationLevel::DISABLED && + if (activation_level_for_next_commit_ != ActivationLevel::DISABLED && ruleset_dealer_->IsRulesetFileAvailable()) { base::OnceClosure first_disallowed_load_callback( base::BindOnce(&SubresourceFilterAgent:: @@ -171,9 +156,10 @@ auto ruleset = ruleset_dealer_->GetRuleset(); DCHECK(ruleset); - ActivationState activation_state = ComputeActivationState( - activation_level_for_provisional_load_, measure_performance_, - ancestor_document_urls, ruleset.get()); + ActivationState activation_state = + ComputeActivationState(activation_level_for_next_commit_, + measure_performance_for_next_commit_, + ancestor_document_urls, ruleset.get()); DCHECK(!ancestor_document_urls.empty()); std::unique_ptr<DocumentSubresourceFilter> filter( new DocumentSubresourceFilter( @@ -184,7 +170,14 @@ SetSubresourceFilterForCommittedLoad(std::move(filter)); } } - activation_level_for_provisional_load_ = ActivationLevel::DISABLED; + + ResetActivatonStateForNextCommit(); +} + +void SubresourceFilterAgent::DidFailProvisionalLoad( + const blink::WebURLError& error) { + // TODO(engedy): Add a test with `frame-ancestor` violation to exercise this. + ResetActivatonStateForNextCommit(); } void SubresourceFilterAgent::DidFinishLoad() { @@ -197,8 +190,8 @@ bool SubresourceFilterAgent::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(SubresourceFilterAgent, message) - IPC_MESSAGE_HANDLER(SubresourceFilterMsg_ActivateForProvisionalLoad, - OnActivateForProvisionalLoad) + IPC_MESSAGE_HANDLER(SubresourceFilterMsg_ActivateForNextCommittedLoad, + OnActivateForNextCommittedLoad) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled;
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent.h b/components/subresource_filter/content/renderer/subresource_filter_agent.h index 75533a0d2..0a72abe2 100644 --- a/components/subresource_filter/content/renderer/subresource_filter_agent.h +++ b/components/subresource_filter/content/renderer/subresource_filter_agent.h
@@ -62,26 +62,26 @@ const DocumentLoadStatistics& statistics); private: - void OnActivateForProvisionalLoad(ActivationLevel activation_level, - const GURL& url, - bool measure_performance); + void OnActivateForNextCommittedLoad(ActivationLevel activation_level, + bool measure_performance); void RecordHistogramsOnLoadCommitted(); void RecordHistogramsOnLoadFinished(); + void ResetActivatonStateForNextCommit(); // content::RenderFrameObserver: void OnDestruct() override; - void DidStartProvisionalLoad(blink::WebDataSource* data_source) override; void DidCommitProvisionalLoad(bool is_new_navigation, bool is_same_page_navigation) override; + void DidFailProvisionalLoad(const blink::WebURLError& error) override; void DidFinishLoad() override; bool OnMessageReceived(const IPC::Message& message) override; // Owned by the ChromeContentRendererClient and outlives us. UnverifiedRulesetDealer* ruleset_dealer_; - ActivationLevel activation_level_for_provisional_load_; - GURL url_for_provisional_load_; - bool measure_performance_ = false; + ActivationLevel activation_level_for_next_commit_ = ActivationLevel::DISABLED; + bool measure_performance_for_next_commit_ = false; + base::WeakPtr<DocumentSubresourceFilter> filter_for_last_committed_load_; DISALLOW_COPY_AND_ASSIGN(SubresourceFilterAgent);
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc b/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc index 0329363..0b1bf896 100644 --- a/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc +++ b/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
@@ -21,6 +21,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h" #include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "url/gurl.h" @@ -137,8 +138,8 @@ bool measure_performance = false) { agent_as_rfo()->DidStartProvisionalLoad(nullptr); EXPECT_TRUE(agent_as_rfo()->OnMessageReceived( - SubresourceFilterMsg_ActivateForProvisionalLoad( - 0, activation_level, GURL(), measure_performance))); + SubresourceFilterMsg_ActivateForNextCommittedLoad( + 0, activation_level, measure_performance))); agent_as_rfo()->DidCommitProvisionalLoad( true /* is_new_navigation */, false /* is_same_page_navigation */); } @@ -302,7 +303,9 @@ FinishLoad(); // Resource loads after the in-page navigation should not be counted toward - // the figures below, as they came after the original page load event. + // the figures below, as they came after the original page load event. There + // should be no samples recorded into subresource count histograms during the + // final load where there is no activation. histogram_tester.ExpectUniqueSample(kSubresourcesTotal, 2, 1); histogram_tester.ExpectUniqueSample(kSubresourcesEvaluated, 2, 1); histogram_tester.ExpectUniqueSample(kSubresourcesMatchedRules, 1, 1); @@ -404,10 +407,8 @@ FinishLoad(); } -// If a provisional load is aborted, the RenderFrameObservers might not receive -// any further notifications about that load. It is thus possible that there -// will be two RenderFrameObserver::DidStartProvisionalLoad in a row. Make sure -// that the activation decision does not outlive the first provisional load. +// Make sure that the activation decision does not outlive a failed provisional +// load (and affect the second next load). TEST_F(SubresourceFilterAgentTest, Enabled_FilteringNoLongerEffectAfterProvisionalLoadIsCancelled) { ASSERT_NO_FATAL_FAILURE( @@ -415,8 +416,9 @@ ExpectNoSubresourceFilterGetsInjected(); agent_as_rfo()->DidStartProvisionalLoad(nullptr); EXPECT_TRUE(agent_as_rfo()->OnMessageReceived( - SubresourceFilterMsg_ActivateForProvisionalLoad( - 0, ActivationLevel::ENABLED, GURL(), true))); + SubresourceFilterMsg_ActivateForNextCommittedLoad( + 0, ActivationLevel::ENABLED, true))); + agent_as_rfo()->DidFailProvisionalLoad(blink::WebURLError()); agent_as_rfo()->DidStartProvisionalLoad(nullptr); agent_as_rfo()->DidCommitProvisionalLoad(true /* is_new_navigation */, false /* is_same_page_navigation */);
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc index cb5bb9d..34e79b1 100644 --- a/components/variations/service/variations_service.cc +++ b/components/variations/service/variations_service.cc
@@ -17,6 +17,7 @@ #include "base/strings/string_util.h" #include "base/sys_info.h" #include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/timer/elapsed_timer.h" #include "base/values.h" #include "base/version.h"
diff --git a/components/web_restrictions/browser/web_restrictions_client.cc b/components/web_restrictions/browser/web_restrictions_client.cc index 5513920..6a81598 100644 --- a/components/web_restrictions/browser/web_restrictions_client.cc +++ b/components/web_restrictions/browser/web_restrictions_client.cc
@@ -9,6 +9,7 @@ #include "base/location.h" #include "base/logging.h" #include "base/task_scheduler/post_task.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread_restrictions.h" #include "content/public/browser/browser_thread.h" #include "jni/WebRestrictionsClient_jni.h"
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py index 640c34e..9874072 100755 --- a/content/test/gpu/generate_buildbot_json.py +++ b/content/test/gpu/generate_buildbot_json.py
@@ -1316,6 +1316,7 @@ ], 'args': [ '--use-test-data-path', + '--test_video_data=test-25fps.h264:320:240:250:258:::1', ], }, }
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h index 16fe810..c4c2c51 100644 --- a/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h +++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_win.h
@@ -13,6 +13,10 @@ #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h" #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h" +namespace base { +class SequencedTaskRunner; +} + namespace device { class BluetoothRemoteGattCharacteristicWin;
diff --git a/device/usb/usb_device_android.h b/device/usb/usb_device_android.h index af6a59e..699354e 100644 --- a/device/usb/usb_device_android.h +++ b/device/usb/usb_device_android.h
@@ -9,6 +9,10 @@ #include "base/memory/weak_ptr.h" #include "device/usb/usb_device.h" +namespace base { +class SequencedTaskRunner; +} + namespace device { class UsbServiceAndroid;
diff --git a/docs/documentation_best_practices.md b/docs/documentation_best_practices.md index 328a98f..4e4df45 100644 --- a/docs/documentation_best_practices.md +++ b/docs/documentation_best_practices.md
@@ -97,7 +97,7 @@ design. However, once the code is implemented, design docs should serve as archives of these decisions, not as half-correct docs (they are often misused). See - [Implementation state](#implementation_state_determines_document_location) + [Implementation state](#Implementation-state-determines-document-repository) below. ## Implementation state determines document repository
diff --git a/docs/linux_sandboxing.md b/docs/linux_sandboxing.md index dfbdf3a..2bd09a2 100644 --- a/docs/linux_sandboxing.md +++ b/docs/linux_sandboxing.md
@@ -41,11 +41,11 @@ | **Name** | **Layer and process** | **Linux flavors where available** | **State** | |:---------|:----------------------|:----------------------------------|:----------| -| [Setuid sandbox](#The_setuid_sandbox.md) | Layer-1 in Zygote processes (renderers, PPAPI, [NaCl](https://www.chromium.org/nativeclient), some utility processes) | Linux distributions and Chrome OS | Enabled by default (old kernels) and maintained | -| [User namespaces sandbox](#User_namespaces_sandbox.md) | Modern alternative to the setuid sandbox. Layer-1 in Zygote processes (renderers, PPAPI, [NaCl](https://www.chromium.org/nativeclient), some utility processes) | Linux distributions and Chrome OS (kernel >= 3.8) | Enabled by default (modern kernels) and actively developed | -| [Seccomp-BPF](#The_seccomp-bpf_sandbox.md) | Layer-2 in some Zygote processes (renderers, PPAPI, [NaCl](https://www.chromium.org/nativeclient)), Layer-1 + Layer-2 in GPU process | Linux kernel >= 3.5, Chrome OS and Ubuntu | Enabled by default and actively developed | -| [Seccomp-legacy](#The_seccomp_sandbox.md) | Layer-2 in renderers | All | [Deprecated](https://src.chromium.org/viewvc/chrome?revision=197301&view=revision) | -| [SELinux](#SELinux.md) | Layer-1 in Zygote processes (renderers, PPAPI) | SELinux distributions | [Deprecated](https://src.chromium.org/viewvc/chrome?revision=200838&view=revision) | +| [Setuid sandbox](#The-setuid-sandbox) | Layer-1 in Zygote processes (renderers, PPAPI, [NaCl](https://www.chromium.org/nativeclient), some utility processes) | Linux distributions and Chrome OS | Enabled by default (old kernels) and maintained | +| [User namespaces sandbox](#User-namespaces-sandbox) | Modern alternative to the setuid sandbox. Layer-1 in Zygote processes (renderers, PPAPI, [NaCl](https://www.chromium.org/nativeclient), some utility processes) | Linux distributions and Chrome OS (kernel >= 3.8) | Enabled by default (modern kernels) and actively developed | +| [Seccomp-BPF](#The-sandbox-1) | Layer-2 in some Zygote processes (renderers, PPAPI, [NaCl](https://www.chromium.org/nativeclient)), Layer-1 + Layer-2 in GPU process | Linux kernel >= 3.5, Chrome OS and Ubuntu | Enabled by default and actively developed | +| [Seccomp-legacy](#The-sandbox-2) | Layer-2 in renderers | All | [Deprecated](https://src.chromium.org/viewvc/chrome?revision=197301&view=revision) | +| [SELinux](#SELinux) | Layer-1 in Zygote processes (renderers, PPAPI) | SELinux distributions | [Deprecated](https://src.chromium.org/viewvc/chrome?revision=200838&view=revision) | | AppArmor | Outer layer-1 in Zygote processes (renderers, PPAPI) | Not used | Deprecated | ## The setuid sandbox
diff --git a/docs/testing/test_browser_dialog.md b/docs/testing/test_browser_dialog.md index c960455..44cf92a 100644 --- a/docs/testing/test_browser_dialog.md +++ b/docs/testing/test_browser_dialog.md
@@ -49,7 +49,7 @@ * The body of the test is always just "`RunDialog();`". * "`default`" is the `std::string` passed to `ShowDialog()` and can be customized. See - [Testing additional dialog "styles"](#Testing-additional-dialog-styles_). + [Testing additional dialog "styles"](#Testing-additional-dialog-styles). * The text before `default` (in this case) must always be "`InvokeDialog_`". ### Concrete examples
diff --git a/docs/testing/web_platform_tests.md b/docs/testing/web_platform_tests.md index 878ef50..e20e83af 100644 --- a/docs/testing/web_platform_tests.md +++ b/docs/testing/web_platform_tests.md
@@ -11,9 +11,14 @@ that will [soon be merged into web-platform-tests](https://github.com/w3c/csswg-test/issues/1102). +Chromium has 2-way import/export process with the upstream web-platform-tests +repository, where tests are imported into +[LayoutTests/external/wpt](../../third_party/WebKit/LayoutTests/external/wpt) +and any changes to the imported tests are also exported to web-platform-tests. + [TOC] -## Import +## Importing tests Chromium has mirrors ([web-platform-tests](https://chromium.googlesource.com/external/w3c/web-platform-tests/), @@ -21,7 +26,7 @@ GitHub repos, and periodically imports a subset of the tests so that they are run as part of the regular Blink layout test testing process. -The goal of this process are to be able to run the Web Platform Tests unmodified +The goal of this process are to be able to run Web Platform Tests unmodified locally just as easily as we can run the Blink tests, and ensure that we are tracking tip-of-tree in the Web Platform Tests repository as closely as possible, and running as many of the tests as possible. @@ -39,6 +44,8 @@ - Recent CLs created by [blink-w3c-test-autoroller@chromium.org](https://codereview.chromium.org/search?owner=blink-w3c-test-autoroller%40chromium.org). +Automatic imports are intended to run at least once every 24 hours. + ### Skipped tests We control which tests are imported via a file called @@ -70,17 +77,17 @@ If you want to import immediately (in order to try the tests out locally, etc) you can also run `wpt-import --allow-local-commits`, but this is not required. -## Contributing tests back to the Web Platform Tests project. +## Writing tests -If you need to make changes to Web Platform Tests, just commit your changes -directly to -[LayoutTests/external/wpt](../../third_party/WebKit/LayoutTests/external/wpt) +To contribute changes to Web Platform Tests, just commit your changes directly +to [LayoutTests/external/wpt](../../third_party/WebKit/LayoutTests/external/wpt) and the changes will be automatically upstreamed within 24 hours. -Note that tests in Web Platform Tests are expected to match behavior defined by -the relevant WHATWG or W3C specification, not simply Blink's behavior. If in -doubt, please request code review from someone with expertise in the relevant -specification text. +Changes involving adding, removing or modifying tests can all be upstreamed. +Any changes outside of +[external/wpt](../../third_party/WebKit/LayoutTests/external/wpt) will not be +upstreamed, and any changes `*-expected.txt`, `OWNERS`, and `MANIFEST.json`, +will also not be upstreamed. Note: if you're adding a new test in `external/wpt`, you'll need to re-generate MANIFEST.json manually until [CL 2644783003](https://crrev.com/2644783003) is @@ -91,18 +98,47 @@ --tests-root=LayoutTests/external/wpt ``` -### What kinds of changes can be upstreamed? +Most tests are written using testharness.js, see +[Writing Layout Tests](./writing_layout_tests.md) and +[Layout Tests Tips](./layout_tests_tips.md) for general guidelines. -In general, changes involving adding, removing or modifying tests can all be -upstreamed. From a Chromium commit, any changes outside of -[external/wpt](../../third_party/WebKit/LayoutTests/external/wpt) will not be -upstreamed, and any changes `*-expected.txt`, `OWNERS`, and `MANIFEST.json`, -will also not be upstreamed. +### Write tests against specifications + +Tests in Web Platform Tests are expected to match behavior defined by the +relevant specification. In other words, all assertions that a test makes +should be derived from a specification's normative requirements, and not go +beyond them. It is often necessary to change the specification to clarify what +is and isn't required. + +When the standards discussion is still ongoing or blocked on some implementation +successfully shipping the hoped-for behavior, write the tests outside of +web-platform-tests and upstream them when the specification is finally updated. +Optionally, it may be possible to write deliberately failing tests against the +current specification and later update them. + +### Tests that require testing APIs + +Tests that depend on `internals.*`, `eventSender.*` or other internal testing +APIs cannot yet be written as part of web-platform-tests. + +An alternative is to write manual tests that are automated with scripts from +[wpt_automation](../../third_party/WebKit/LayoutTests/external/wpt_automation). +Such tests still require case-by-case automation to run for other browser +engines, but are more valuable than purely manual tests. + +*** note +TODO(foolip): Figure out and document a more scalable test automation solution. +*** + +### Adding new top-level directories Entirely new top-level directories should generally be added upstream, since that's the only way to add an OWNERS file upstream. After adding a new top-level directory upstream, you should add a line for it in `W3CImportExpectations`. +Adding the new directory (and `W3CImportExpectations` entry) in Chromium and +later adding an OWNERS file upstream also works. + ### Will the exported commits be linked to my GitHub profile? The email you commit with in Chromium will be the author of the commit on @@ -123,3 +159,18 @@ processes for getting new tests committed the W3C repos are at http://testthewebforward.org/docs/. Some specifics are at http://testthewebforward.org/docs/github-101.html. + +## Reviewing tests + +Anyone who can review code and tests in Chromium can also review changes in +[external/wpt](../../third_party/WebKit/LayoutTests/external/wpt) +that will be automatically upstreamed. There will be no additional review in +web-platform-tests as part of the export process. + +If upstream reviewers have feedback on the changes, discuss on the pull request +created during export, and if necessary work on a new pull request to iterate +until everyone is satisfied. + +When reviewing tests, check that they match the relevant specification, which +may not fully match the implementation. See also +[Write tests against specifications](#Write-tests-against-specifications).
diff --git a/docs/testing/writing_layout_tests.md b/docs/testing/writing_layout_tests.md index ce83bec..13ad74e 100644 --- a/docs/testing/writing_layout_tests.md +++ b/docs/testing/writing_layout_tests.md
@@ -212,10 +212,6 @@ [synthetic events](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events). *** -*** note -TODO: document wpt_automation. Manual tests might end up moving here. -*** - ### Relying on Blink-Specific Testing APIs Tests that cannot be expressed using the Web Platform APIs or WPT's testing APIs
diff --git a/docs/using_a_linux_chroot.md b/docs/using_a_linux_chroot.md index 96fadfd3..da37cfe 100644 --- a/docs/using_a_linux_chroot.md +++ b/docs/using_a_linux_chroot.md
@@ -80,7 +80,7 @@ following conditions. 1. Make sure `DISPLAY` is set. See the - [Running X apps](#Running_X_apps) section above. + [Running X apps](#Running-X-apps) section above. 1. Install `xdg-utils`, which includes `xdg-open`, a utility for finding the right application to open a file or URL with. 1. Install [Chrome](https://www.google.com/intl/en/chrome/browser/).
diff --git a/extensions/browser/api/api_resource_manager.h b/extensions/browser/api/api_resource_manager.h index 4d052ba..4bbb932 100644 --- a/extensions/browser/api/api_resource_manager.h +++ b/extensions/browser/api/api_resource_manager.h
@@ -13,7 +13,9 @@ #include "base/memory/ref_counted.h" #include "base/scoped_observer.h" #include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" #include "base/threading/non_thread_safe.h" +#include "base/threading/sequenced_worker_pool.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/browser_context_keyed_api_factory.h"
diff --git a/extensions/browser/content_hash_fetcher.cc b/extensions/browser/content_hash_fetcher.cc index fda8d67f0..7962630b 100644 --- a/extensions/browser/content_hash_fetcher.cc +++ b/extensions/browser/content_hash_fetcher.cc
@@ -19,6 +19,7 @@ #include "base/metrics/histogram_macros.h" #include "base/synchronization/lock.h" #include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/timer/elapsed_timer.h" #include "base/version.h" #include "content/public/browser/browser_thread.h"
diff --git a/extensions/browser/content_hash_fetcher_unittest.cc b/extensions/browser/content_hash_fetcher_unittest.cc index ac1adc8..7e16b736 100644 --- a/extensions/browser/content_hash_fetcher_unittest.cc +++ b/extensions/browser/content_hash_fetcher_unittest.cc
@@ -14,6 +14,7 @@ #include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/version.h" #include "content/public/browser/browser_thread.h" #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/extensions/browser/content_verify_job.cc b/extensions/browser/content_verify_job.cc index 4eee6a07..56552f8 100644 --- a/extensions/browser/content_verify_job.cc +++ b/extensions/browser/content_verify_job.cc
@@ -9,6 +9,7 @@ #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/timer/elapsed_timer.h" #include "content/public/browser/browser_thread.h" #include "crypto/secure_hash.h"
diff --git a/extensions/browser/updater/update_client_config.cc b/extensions/browser/updater/update_client_config.cc index 36ab34f..e5bb7ea 100644 --- a/extensions/browser/updater/update_client_config.cc +++ b/extensions/browser/updater/update_client_config.cc
@@ -4,6 +4,7 @@ #include "extensions/browser/updater/update_client_config.h" +#include "base/threading/sequenced_worker_pool.h" #include "content/public/browser/browser_thread.h" namespace extensions {
diff --git a/extensions/browser/user_script_loader.h b/extensions/browser/user_script_loader.h index 117b9ac..a6a5d7c9 100644 --- a/extensions/browser/user_script_loader.h +++ b/extensions/browser/user_script_loader.h
@@ -9,6 +9,7 @@ #include <memory> #include <set> +#include "base/callback_forward.h" #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/shared_memory.h"
diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc index d5b14a0..a6a11f56 100644 --- a/extensions/common/permissions/api_permission_set.cc +++ b/extensions/common/permissions/api_permission_set.cc
@@ -197,6 +197,13 @@ PermissionIDSet::PermissionIDSet() { } +PermissionIDSet::PermissionIDSet( + std::initializer_list<APIPermission::ID> permissions) { + for (auto permission : permissions) { + permissions_.insert(PermissionID(permission)); + } +} + PermissionIDSet::PermissionIDSet(const PermissionIDSet& other) = default; PermissionIDSet::~PermissionIDSet() { @@ -236,9 +243,13 @@ return params; } +bool PermissionIDSet::ContainsID(PermissionID permission_id) const { + auto it = permissions_.lower_bound(permission_id); + return it != permissions_.end() && it->id() == permission_id.id(); +} + bool PermissionIDSet::ContainsID(APIPermission::ID permission_id) const { - auto it = permissions_.lower_bound(PermissionID(permission_id)); - return it != permissions_.end() && it->id() == permission_id; + return ContainsID(PermissionID(permission_id)); } bool PermissionIDSet::ContainsAllIDs( @@ -260,6 +271,14 @@ return false; } +bool PermissionIDSet::ContainsAnyID(const PermissionIDSet& other) const { + for (const auto& id : other) { + if (ContainsID(id)) + return true; + } + return false; +} + PermissionIDSet PermissionIDSet::GetAllPermissionsWithID( APIPermission::ID permission_id) const { PermissionIDSet subset;
diff --git a/extensions/common/permissions/api_permission_set.h b/extensions/common/permissions/api_permission_set.h index 36d6f0e..51e2954c 100644 --- a/extensions/common/permissions/api_permission_set.h +++ b/extensions/common/permissions/api_permission_set.h
@@ -116,6 +116,7 @@ using const_iterator = std::set<PermissionID>::const_iterator; PermissionIDSet(); + PermissionIDSet(std::initializer_list<APIPermission::ID> permissions); PermissionIDSet(const PermissionIDSet& other); virtual ~PermissionIDSet(); @@ -132,6 +133,7 @@ std::vector<base::string16> GetAllPermissionParameters() const; // Check if the set contains a permission with the given ID. + bool ContainsID(PermissionID permission_id) const; bool ContainsID(APIPermission::ID permission_id) const; // Check if the set contains permissions with all the given IDs. @@ -139,6 +141,7 @@ // Check if the set contains any permission with one of the given IDs. bool ContainsAnyID(const std::set<APIPermission::ID>& permission_ids) const; + bool ContainsAnyID(const PermissionIDSet& other) const; // Returns all the permissions in this set with the given ID. PermissionIDSet GetAllPermissionsWithID(
diff --git a/extensions/renderer/extension_frame_helper.h b/extensions/renderer/extension_frame_helper.h index ee91a76d..518e9ca 100644 --- a/extensions/renderer/extension_frame_helper.h +++ b/extensions/renderer/extension_frame_helper.h
@@ -7,6 +7,7 @@ #include <vector> +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/public/common/console_message_level.h"
diff --git a/headless/BUILD.gn b/headless/BUILD.gn index 151af1bd..fc234f7 100644 --- a/headless/BUILD.gn +++ b/headless/BUILD.gn
@@ -206,12 +206,15 @@ "lib/browser/headless_devtools_client_impl.h", "lib/browser/headless_devtools_manager_delegate.cc", "lib/browser/headless_devtools_manager_delegate.h", + "lib/browser/headless_macros.h", "lib/browser/headless_platform_event_source.cc", "lib/browser/headless_platform_event_source.h", "lib/browser/headless_url_request_context_getter.cc", "lib/browser/headless_url_request_context_getter.h", "lib/browser/headless_web_contents_impl.cc", "lib/browser/headless_web_contents_impl.h", + "lib/headless_crash_reporter_client.cc", + "lib/headless_crash_reporter_client.h", "lib/headless_content_client.cc", "lib/headless_content_client.h", "lib/headless_content_main_delegate.cc", @@ -276,6 +279,7 @@ ":gen_devtools_client_api", ":version_header", "//base", + "//components/crash/content/browser", "//components/security_state/content", "//components/security_state/core", "//content/public/app:both",
diff --git a/headless/DEPS b/headless/DEPS index edf61b6..2e51ac4e 100644 --- a/headless/DEPS +++ b/headless/DEPS
@@ -1,4 +1,6 @@ include_rules = [ + "+components/crash/content/app", + "+components/crash/content/browser", "+content/public/app", "+content/public/browser", "+content/public/common",
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc index 04300eec..7c187b0 100644 --- a/headless/app/headless_shell.cc +++ b/headless/app/headless_shell.cc
@@ -7,6 +7,7 @@ #include <string> #include "base/base64.h" +#include "base/base_switches.h" #include "base/bind.h" #include "base/callback.h" #include "base/command_line.h" @@ -441,6 +442,13 @@ if (!ValidateCommandLine(command_line)) return EXIT_FAILURE; + if (command_line.HasSwitch(::switches::kEnableCrashReporter)) + builder.SetCrashReporterEnabled(true); + if (command_line.HasSwitch(switches::kCrashDumpsDir)) { + builder.SetCrashDumpsDir( + command_line.GetSwitchValuePath(switches::kCrashDumpsDir)); + } + if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { std::string address = kDevToolsHttpServerAddress; if (command_line.HasSwitch(switches::kRemoteDebuggingAddress)) {
diff --git a/headless/app/headless_shell_switches.cc b/headless/app/headless_shell_switches.cc index 0675059..f20a6065 100644 --- a/headless/app/headless_shell_switches.cc +++ b/headless/app/headless_shell_switches.cc
@@ -7,6 +7,9 @@ namespace headless { namespace switches { +// The directory breakpad should store minidumps in. +const char kCrashDumpsDir[] = "crash-dumps-dir"; + // Instructs headless_shell to cause network fetches to complete in order of // creation. This removes a significant source of network related // non-determinism at the cost of slower page loads.
diff --git a/headless/app/headless_shell_switches.h b/headless/app/headless_shell_switches.h index 654675c..929fda6 100644 --- a/headless/app/headless_shell_switches.h +++ b/headless/app/headless_shell_switches.h
@@ -9,6 +9,7 @@ namespace headless { namespace switches { +extern const char kCrashDumpsDir[]; extern const char kDeterministicFetch[]; extern const char kDumpDom[]; extern const char kHideScrollbars[];
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc index 101d15c..a6db422 100644 --- a/headless/lib/browser/headless_content_browser_client.cc +++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -7,28 +7,99 @@ #include <memory> #include <unordered_set> +#include "base/base_switches.h" #include "base/callback.h" +#include "base/command_line.h" #include "base/json/json_reader.h" #include "base/memory/ptr_util.h" +#include "base/path_service.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/storage_partition.h" +#include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" #include "headless/grit/headless_lib_resources.h" #include "headless/lib/browser/headless_browser_context_impl.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_browser_main_parts.h" #include "headless/lib/browser/headless_devtools_manager_delegate.h" +#include "headless/lib/headless_macros.h" #include "storage/browser/quota/quota_settings.h" #include "ui/base/resource/resource_bundle.h" +#if defined(HEADLESS_USE_BREAKPAD) +#include "base/debug/leak_annotations.h" +#include "components/crash/content/app/breakpad_linux.h" +#include "components/crash/content/browser/crash_handler_host_linux.h" +#include "content/public/common/content_descriptors.h" +#endif // defined(HEADLESS_USE_BREAKPAD) + namespace headless { namespace { const char kCapabilityPath[] = "interface_provider_specs.navigation:frame.provides.renderer"; + +#if defined(HEADLESS_USE_BREAKPAD) +breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost( + const std::string& process_type, + const HeadlessBrowser::Options& options) { + base::FilePath dumps_path = options.crash_dumps_dir; + if (dumps_path.empty()) { + bool ok = PathService::Get(base::DIR_MODULE, &dumps_path); + DCHECK(ok); + } + + { + ANNOTATE_SCOPED_MEMORY_LEAK; +#if defined(OFFICIAL_BUILD) + // Upload crash dumps in official builds, unless we're running in unattended + // mode (not to be confused with headless mode in general -- see + // chrome/common/env_vars.cc). + static const char kHeadless[] = "CHROME_HEADLESS"; + bool upload = (getenv(kHeadless) == nullptr); +#else + bool upload = false; +#endif + breakpad::CrashHandlerHostLinux* crash_handler = + new breakpad::CrashHandlerHostLinux(process_type, dumps_path, upload); + crash_handler->StartUploaderThread(); + return crash_handler; + } +} + +int GetCrashSignalFD(const base::CommandLine& command_line, + const HeadlessBrowser::Options& options) { + if (!breakpad::IsCrashReporterEnabled()) + return -1; + + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + + if (process_type == switches::kRendererProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = + CreateCrashHandlerHost(process_type, options); + return crash_handler->GetDeathSignalSocket(); + } + + if (process_type == switches::kPpapiPluginProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = + CreateCrashHandlerHost(process_type, options); + return crash_handler->GetDeathSignalSocket(); + } + + if (process_type == switches::kGpuProcess) { + static breakpad::CrashHandlerHostLinux* crash_handler = + CreateCrashHandlerHost(process_type, options); + return crash_handler->GetDeathSignalSocket(); + } + + return -1; +} +#endif // defined(HEADLESS_USE_BREAKPAD) + } // namespace HeadlessContentBrowserClient::HeadlessContentBrowserClient( @@ -99,4 +170,25 @@ callback); } +void HeadlessContentBrowserClient::GetAdditionalMappedFilesForChildProcess( + const base::CommandLine& command_line, + int child_process_id, + content::FileDescriptorInfo* mappings) { +#if defined(HEADLESS_USE_BREAKPAD) + int crash_signal_fd = GetCrashSignalFD(command_line, *browser_->options()); + if (crash_signal_fd >= 0) + mappings->Share(kCrashDumpSignal, crash_signal_fd); +#endif // defined(HEADLESS_USE_BREAKPAD) +} + +void HeadlessContentBrowserClient::AppendExtraCommandLineSwitches( + base::CommandLine* command_line, + int child_process_id) { +#if defined(HEADLESS_USE_BREAKPAD) + // This flag tells child processes to also turn on crash reporting. + if (breakpad::IsCrashReporterEnabled()) + command_line->AppendSwitch(switches::kEnableCrashReporter); +#endif // defined(HEADLESS_USE_BREAKPAD) +} + } // namespace headless
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h index 202219f38..aa188832 100644 --- a/headless/lib/browser/headless_content_browser_client.h +++ b/headless/lib/browser/headless_content_browser_client.h
@@ -28,6 +28,12 @@ content::BrowserContext* context, content::StoragePartition* partition, const storage::OptionalQuotaSettingsCallback& callback) override; + void GetAdditionalMappedFilesForChildProcess( + const base::CommandLine& command_line, + int child_process_id, + content::FileDescriptorInfo* mappings) override; + void AppendExtraCommandLineSwitches(base::CommandLine* command_line, + int child_process_id) override; private: HeadlessBrowserImpl* browser_; // Not owned.
diff --git a/headless/lib/embedder_mojo_browsertest.cc b/headless/lib/embedder_mojo_browsertest.cc index 92800f1..51cc2cb 100644 --- a/headless/lib/embedder_mojo_browsertest.cc +++ b/headless/lib/embedder_mojo_browsertest.cc
@@ -66,7 +66,7 @@ pak_path, ui::SCALE_FACTOR_NONE); } - // HeadlessWebContentsObserver implementation: + // HeadlessWebContents::Observer implementation: void DevToolsTargetReady() override { EXPECT_TRUE(web_contents_->GetDevToolsTarget()); web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
diff --git a/headless/lib/headless_browser_browsertest.cc b/headless/lib/headless_browser_browsertest.cc index 0c4557c..3c67da1 100644 --- a/headless/lib/headless_browser_browsertest.cc +++ b/headless/lib/headless_browser_browsertest.cc
@@ -5,11 +5,15 @@ #include <memory> #include "base/command_line.h" +#include "base/files/file_enumerator.h" #include "base/files/file_util.h" #include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" +#include "content/public/common/url_constants.h" #include "content/public/test/browser_test.h" +#include "headless/lib/headless_macros.h" +#include "headless/public/devtools/domains/inspector.h" #include "headless/public/devtools/domains/network.h" #include "headless/public/devtools/domains/page.h" #include "headless/public/headless_browser.h" @@ -642,4 +646,88 @@ base::DeleteFile(launcher_stamp, false); } +class CrashReporterTest : public HeadlessBrowserTest, + public HeadlessWebContents::Observer, + inspector::ExperimentalObserver { + public: + CrashReporterTest() : devtools_client_(HeadlessDevToolsClient::Create()) {} + ~CrashReporterTest() override {} + + void SetUp() override { + base::ThreadRestrictions::SetIOAllowed(true); + base::CreateNewTempDirectory("CrashReporterTest", &crash_dumps_dir_); + EXPECT_FALSE(options()->enable_crash_reporter); + options()->enable_crash_reporter = true; + options()->crash_dumps_dir = crash_dumps_dir_; + HeadlessBrowserTest::SetUp(); + } + + void TearDown() override { + base::ThreadRestrictions::SetIOAllowed(true); + base::DeleteFile(crash_dumps_dir_, /* recursive */ false); + } + + // HeadlessWebContents::Observer implementation: + void DevToolsTargetReady() override { + EXPECT_TRUE(web_contents_->GetDevToolsTarget()); + web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); + devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); + } + + // inspector::ExperimentalObserver implementation: + void OnTargetCrashed(const inspector::TargetCrashedParams&) override { + FinishAsynchronousTest(); + } + + protected: + HeadlessBrowserContext* browser_context_ = nullptr; + HeadlessWebContents* web_contents_ = nullptr; + std::unique_ptr<HeadlessDevToolsClient> devtools_client_; + base::FilePath crash_dumps_dir_; +}; + +// TODO(skyostil): Minidump generation currently is only supported on Linux. +#if defined(HEADLESS_USE_BREAKPAD) +#define MAYBE_GenerateMinidump GenerateMinidump +#else +#define MAYBE_GenerateMinidump DISABLED_GenerateMinidump +#endif // defined(HEADLESS_USE_BREAKPAD) +IN_PROC_BROWSER_TEST_F(CrashReporterTest, MAYBE_GenerateMinidump) { + // Navigates a tab to chrome://crash and checks that a minidump is generated. + // Note that we only test renderer crashes here -- browser crashes need to be + // tested with a separate harness. + // + // The case where crash reporting is disabled is covered by + // HeadlessCrashObserverTest. + browser_context_ = browser()->CreateBrowserContextBuilder().Build(); + + web_contents_ = browser_context_->CreateWebContentsBuilder() + .SetInitialURL(GURL(content::kChromeUICrashURL)) + .Build(); + + web_contents_->AddObserver(this); + RunAsynchronousTest(); + + // The target has crashed and should no longer be there. + EXPECT_FALSE(web_contents_->GetDevToolsTarget()); + + // Check that one minidump got created. + { + base::ThreadRestrictions::SetIOAllowed(true); + base::FileEnumerator it(crash_dumps_dir_, /* recursive */ false, + base::FileEnumerator::FILES); + base::FilePath minidump = it.Next(); + EXPECT_FALSE(minidump.empty()); + EXPECT_EQ(".dmp", minidump.Extension()); + EXPECT_TRUE(it.Next().empty()); + } + + web_contents_->RemoveObserver(this); + web_contents_->Close(); + web_contents_ = nullptr; + + browser_context_->Close(); + browser_context_ = nullptr; +} + } // namespace headless
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc index 60888175..fc8f645 100644 --- a/headless/lib/headless_content_main_delegate.cc +++ b/headless/lib/headless_content_main_delegate.cc
@@ -4,16 +4,21 @@ #include "headless/lib/headless_content_main_delegate.h" +#include "base/base_switches.h" #include "base/command_line.h" #include "base/files/file_util.h" +#include "base/lazy_instance.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/trace_event/trace_event.h" +#include "components/crash/content/app/breakpad_linux.h" #include "content/public/browser/browser_main_runner.h" #include "content/public/common/content_switches.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_content_browser_client.h" +#include "headless/lib/headless_crash_reporter_client.h" +#include "headless/lib/headless_macros.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" #include "ui/gfx/switches.h" @@ -31,6 +36,9 @@ const int kTraceEventBrowserProcessSortIndex = -6; HeadlessContentMainDelegate* g_current_headless_content_main_delegate = nullptr; + +base::LazyInstance<HeadlessCrashReporterClient>::Leaky g_headless_crash_client = + LAZY_INSTANCE_INITIALIZER; } // namespace HeadlessContentMainDelegate::HeadlessContentMainDelegate( @@ -131,8 +139,31 @@ DCHECK(success); } +void HeadlessContentMainDelegate::InitCrashReporter( + const base::CommandLine& command_line) { + const std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + crash_reporter::SetCrashReporterClient(g_headless_crash_client.Pointer()); + g_headless_crash_client.Pointer()->set_crash_dumps_dir( + browser_->options()->crash_dumps_dir); + + if (!browser_->options()->enable_crash_reporter) { + DCHECK(!breakpad::IsCrashReporterEnabled()); + return; + } +#if defined(HEADLESS_USE_BREAKPAD) + if (process_type != switches::kZygoteProcess) + breakpad::InitCrashReporter(process_type); +#endif // defined(HEADLESS_USE_BREAKPAD) +} + void HeadlessContentMainDelegate::PreSandboxStartup() { - InitLogging(*base::CommandLine::ForCurrentProcess()); + const base::CommandLine& command_line( + *base::CommandLine::ForCurrentProcess()); + const std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + InitLogging(command_line); + InitCrashReporter(command_line); InitializeResourceBundle(); } @@ -163,7 +194,14 @@ } void HeadlessContentMainDelegate::ZygoteForked() { - // TODO(skyostil): Disable the zygote host. + const base::CommandLine& command_line( + *base::CommandLine::ForCurrentProcess()); + const std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + // Unconditionally try to turn on crash reporting since we do not have access + // to the latest browser options at this point when testing. Breakpad will + // bail out gracefully if the browser process hasn't enabled crash reporting. + breakpad::InitCrashReporter(process_type); } // static
diff --git a/headless/lib/headless_content_main_delegate.h b/headless/lib/headless_content_main_delegate.h index 7be7f51e..35eff70c 100644 --- a/headless/lib/headless_content_main_delegate.h +++ b/headless/lib/headless_content_main_delegate.h
@@ -44,6 +44,7 @@ friend class HeadlessBrowserTest; void InitLogging(const base::CommandLine& command_line); + void InitCrashReporter(const base::CommandLine& command_line); static void InitializeResourceBundle(); static HeadlessContentMainDelegate* GetInstance();
diff --git a/headless/lib/headless_crash_reporter_client.cc b/headless/lib/headless_crash_reporter_client.cc new file mode 100644 index 0000000..a319161 --- /dev/null +++ b/headless/lib/headless_crash_reporter_client.cc
@@ -0,0 +1,63 @@ +// 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. + +#include "headless/lib/headless_crash_reporter_client.h" + +#include <utility> + +#include "base/command_line.h" +#include "base/path_service.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "content/public/common/content_switches.h" +#include "headless/public/version.h" + +namespace headless { + +HeadlessCrashReporterClient::HeadlessCrashReporterClient() = default; +HeadlessCrashReporterClient::~HeadlessCrashReporterClient() = default; + +#if defined(OS_POSIX) && !defined(OS_MACOSX) +void HeadlessCrashReporterClient::GetProductNameAndVersion( + const char** product_name, + const char** version) { + *product_name = "HeadlessChrome"; + *version = PRODUCT_VERSION; +} + +base::FilePath HeadlessCrashReporterClient::GetReporterLogFilename() { + return base::FilePath(FILE_PATH_LITERAL("uploads.log")); +} +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + +bool HeadlessCrashReporterClient::GetCrashDumpLocation( +#if defined(OS_WIN) + base::string16* crash_dir +#else + base::FilePath* crash_dir +#endif + ) { + base::FilePath crash_directory = crash_dumps_dir_; + if (crash_directory.empty() && + !base::PathService::Get(base::DIR_MODULE, &crash_directory)) { + return false; + } +#if defined(OS_WIN) + *crash_dir = crash_directory.value(); +#else + *crash_dir = std::move(crash_directory); +#endif + return true; +} + +bool HeadlessCrashReporterClient::EnableBreakpadForProcess( + const std::string& process_type) { + return process_type == switches::kRendererProcess || + process_type == switches::kPpapiPluginProcess || + process_type == switches::kZygoteProcess || + process_type == switches::kGpuProcess; +} + +} // namespace content
diff --git a/headless/lib/headless_crash_reporter_client.h b/headless/lib/headless_crash_reporter_client.h new file mode 100644 index 0000000..42dc35b --- /dev/null +++ b/headless/lib/headless_crash_reporter_client.h
@@ -0,0 +1,51 @@ +// 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. + +#ifndef HEADLESS_LIB_HEADLESS_CRASH_REPORTER_CLIENT_H_ +#define HEADLESS_LIB_HEADLESS_CRASH_REPORTER_CLIENT_H_ + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "build/build_config.h" +#include "components/crash/content/app/crash_reporter_client.h" + +namespace headless { + +class HeadlessCrashReporterClient : public crash_reporter::CrashReporterClient { + public: + HeadlessCrashReporterClient(); + ~HeadlessCrashReporterClient() override; + + void set_crash_dumps_dir(const base::FilePath& dir) { + crash_dumps_dir_ = dir; + } + const base::FilePath& crash_dumps_dir() const { return crash_dumps_dir_; } + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // Returns a textual description of the product type and version to include + // in the crash report. + void GetProductNameAndVersion(const char** product_name, + const char** version) override; + + base::FilePath GetReporterLogFilename() override; +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + +#if defined(OS_WIN) + bool GetCrashDumpLocation(base::string16* crash_dir) override; +#else + bool GetCrashDumpLocation(base::FilePath* crash_dir) override; +#endif + + bool EnableBreakpadForProcess(const std::string& process_type) override; + + private: + base::FilePath crash_dumps_dir_; + + DISALLOW_COPY_AND_ASSIGN(HeadlessCrashReporterClient); +}; + +} // namespace headless + +#endif // HEADLESS_LIB_HEADLESS_CRASH_REPORTER_CLIENT_H_
diff --git a/headless/lib/headless_devtools_client_browsertest.cc b/headless/lib/headless_devtools_client_browsertest.cc index 6dbd79fa..5aa7e76 100644 --- a/headless/lib/headless_devtools_client_browsertest.cc +++ b/headless/lib/headless_devtools_client_browsertest.cc
@@ -21,6 +21,7 @@ #include "headless/public/headless_devtools_client.h" #include "headless/public/headless_devtools_target.h" #include "headless/test/headless_browser_test.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -30,6 +31,8 @@ EXPECT_EQ((expected).height(), (actual).height()); \ } while (false) +using testing::ElementsAre; + namespace headless { namespace { @@ -789,4 +792,63 @@ HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsMethodCallErrorTest); +class HeadlessDevToolsNetworkBlockedUrlTest + : public HeadlessAsyncDevTooledBrowserTest, + public page::Observer, + public network::Observer { + public: + void RunDevTooledTest() override { + EXPECT_TRUE(embedded_test_server()->Start()); + devtools_client_->GetPage()->AddObserver(this); + devtools_client_->GetPage()->Enable(); + devtools_client_->GetNetwork()->AddObserver(this); + devtools_client_->GetNetwork()->Enable(); + devtools_client_->GetNetwork()->GetExperimental()->AddBlockedURL( + network::AddBlockedURLParams::Builder() + .SetUrl("dom_tree_test.css") + .Build()); + devtools_client_->GetPage()->Navigate( + embedded_test_server()->GetURL("/dom_tree_test.html").spec()); + } + + std::string GetUrlPath(const std::string& url) const { + GURL gurl(url); + return gurl.path(); + } + + void OnRequestWillBeSent( + const network::RequestWillBeSentParams& params) override { + std::string path = GetUrlPath(params.GetRequest()->GetUrl()); + requests_to_be_sent_.push_back(path); + request_id_to_path_[params.GetRequestId()] = path; + } + + void OnResponseReceived( + const network::ResponseReceivedParams& params) override { + responses_received_.push_back(GetUrlPath(params.GetResponse()->GetUrl())); + } + + void OnLoadingFailed(const network::LoadingFailedParams& failed) override { + failures_.push_back(request_id_to_path_[failed.GetRequestId()]); + EXPECT_EQ(network::BlockedReason::INSPECTOR, failed.GetBlockedReason()); + } + + void OnLoadEventFired(const page::LoadEventFiredParams&) override { + EXPECT_THAT(requests_to_be_sent_, + ElementsAre("/dom_tree_test.html", "/dom_tree_test.css", + "/iframe.html")); + EXPECT_THAT(responses_received_, + ElementsAre("/dom_tree_test.html", "/iframe.html")); + EXPECT_THAT(failures_, ElementsAre("/dom_tree_test.css")); + FinishAsynchronousTest(); + } + + std::map<std::string, std::string> request_id_to_path_; + std::vector<std::string> requests_to_be_sent_; + std::vector<std::string> responses_received_; + std::vector<std::string> failures_; +}; + +HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNetworkBlockedUrlTest); + } // namespace headless
diff --git a/headless/lib/headless_macros.h b/headless/lib/headless_macros.h new file mode 100644 index 0000000..4b7bedb --- /dev/null +++ b/headless/lib/headless_macros.h
@@ -0,0 +1,12 @@ +// 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. + +#ifndef HEADLESS_LIB_HEADLESS_MACROS_H_ +#define HEADLESS_LIB_HEADLESS_MACROS_H_ + +#if defined(OS_POSIX) && !defined(OS_MACOSX) +#define HEADLESS_USE_BREAKPAD +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + +#endif // HEADLESS_LIB_HEADLESS_MACROS_H_
diff --git a/headless/public/headless_browser.cc b/headless/public/headless_browser.cc index a879e1d6..474809c 100644 --- a/headless/public/headless_browser.cc +++ b/headless/public/headless_browser.cc
@@ -28,7 +28,8 @@ gl_implementation("osmesa"), user_agent(content::BuildUserAgentFromProduct(kProductName)), window_size(kDefaultWindowSize), - incognito_mode(true) {} + incognito_mode(true), + enable_crash_reporter(false) {} Options::Options(Options&& options) = default; @@ -108,6 +109,16 @@ return *this; } +Builder& Builder::SetCrashReporterEnabled(bool enabled) { + options_.enable_crash_reporter = enabled; + return *this; +} + +Builder& Builder::SetCrashDumpsDir(const base::FilePath& dir) { + options_.crash_dumps_dir = dir; + return *this; +} + Options Builder::Build() { return std::move(options_); }
diff --git a/headless/public/headless_browser.h b/headless/public/headless_browser.h index a1ebc5a..9997494 100644 --- a/headless/public/headless_browser.h +++ b/headless/public/headless_browser.h
@@ -157,6 +157,12 @@ // exposed WebPreferences API, so use with care. base::Callback<void(WebPreferences*)> override_web_preferences_callback; + // Minidump crash reporter settings. Crash reporting is disabled by default. + // By default crash dumps are written to the directory containing the + // executable. + bool enable_crash_reporter; + base::FilePath crash_dumps_dir; + // Reminder: when adding a new field here, do not forget to add it to // HeadlessBrowserContextOptions (where appropriate). private: @@ -190,6 +196,8 @@ Builder& SetIncognitoMode(bool incognito_mode); Builder& SetOverrideWebPreferencesCallback( base::Callback<void(WebPreferences*)> callback); + Builder& SetCrashReporterEnabled(bool enabled); + Builder& SetCrashDumpsDir(const base::FilePath& dir); Options Build();
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 9166d46b..2b111e9 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -939,9 +939,6 @@ <message name="IDS_IOS_PAYMENT_REQUEST_CANCEL_BUTTON" desc="Label of the button to cancel the request for payment presented to the user [iOS only]."> Cancel </message> - <message name="IDS_IOS_PAYMENT_REQUEST_METHOD_SELECTION_BUTTON" desc="Label of the button to select a payment method (credit card) [iOS only]."> - Select - </message> <message name="IDS_IOS_PAYMENT_REQUEST_METHOD_SELECTION_TITLE" desc="Title of the view that allows the user to select the payment method (available credit cards or payment apps) for satisfying a payment request [iOS only]."> Payment </message> @@ -984,6 +981,9 @@ <message name="IDS_IOS_PAYMENT_REQUEST_TOTAL_HEADER" desc="Label of the header of the section which displays the total charge for a payment request [iOS only]."> Total </message> + <message name="IDS_IOS_PAYMENT_REQUEST_TOTAL_UPDATED_LABEL" desc="Label placed on the section which displays the total charge for a payment request, indicating that the total charge was updated. [iOS only]."> + Updated + </message> <message name="IDS_IOS_PHOTO_LIBRARY_USAGE_DESCRIPTION" desc="Specifies the reason for accessing the user's photo library while the app is in use [Length: unlimited] [iOS only]."> This lets you save and upload photos. </message>
diff --git a/ios/chrome/browser/payments/payment_method_selection_coordinator.mm b/ios/chrome/browser/payments/payment_method_selection_coordinator.mm index 72168a91..6cf1de3 100644 --- a/ios/chrome/browser/payments/payment_method_selection_coordinator.mm +++ b/ios/chrome/browser/payments/payment_method_selection_coordinator.mm
@@ -14,6 +14,12 @@ base::scoped_nsobject<PaymentMethodSelectionViewController> _viewController; } +// Called when the user selects a payment method. The cell is checked, the +// UI is locked so that the user can't interact with it, then the delegate is +// notified. The delay is here to let the user get a visual feedback of the +// selection before this view disappears. +- (void)delayedNotifyDelegateOfSelection:(autofill::CreditCard*)paymentMethod; + @end @implementation PaymentMethodSelectionCoordinator @@ -52,8 +58,7 @@ (PaymentMethodSelectionViewController*)controller didSelectPaymentMethod: (autofill::CreditCard*)paymentMethod { - [_delegate paymentMethodSelectionCoordinator:self - didSelectPaymentMethod:paymentMethod]; + [self delayedNotifyDelegateOfSelection:paymentMethod]; } - (void)paymentMethodSelectionViewControllerDidReturn: @@ -61,4 +66,23 @@ [_delegate paymentMethodSelectionCoordinatorDidReturn:self]; } +- (void)delayedNotifyDelegateOfSelection:(autofill::CreditCard*)paymentMethod { + _viewController.get().view.userInteractionEnabled = NO; + base::WeakNSObject<PaymentMethodSelectionCoordinator> weakSelf(self); + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, + static_cast<int64_t>(0.2 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + base::scoped_nsobject<PaymentMethodSelectionCoordinator> strongSelf( + [weakSelf retain]); + // Early return if the coordinator has been deallocated. + if (!strongSelf) + return; + + _viewController.get().view.userInteractionEnabled = YES; + [_delegate paymentMethodSelectionCoordinator:self + didSelectPaymentMethod:paymentMethod]; + }); +} + @end
diff --git a/ios/chrome/browser/payments/payment_method_selection_view_controller.mm b/ios/chrome/browser/payments/payment_method_selection_view_controller.mm index b16e65a..2ab51c9 100644 --- a/ios/chrome/browser/payments/payment_method_selection_view_controller.mm +++ b/ios/chrome/browser/payments/payment_method_selection_view_controller.mm
@@ -53,6 +53,9 @@ // provided by the page invoking the Payment Request API. This is a weak // pointer and should outlive this class. PaymentRequest* _paymentRequest; + + // The currently selected item. May be nil. + PaymentMethodItem* _selectedItem; } // Called when the user presses the return button. @@ -97,6 +100,7 @@ - (void)loadModel { [super loadModel]; CollectionViewModel* model = self.collectionViewModel; + _selectedItem = nil; [model addSectionWithIdentifier:SectionIdentifierPayment]; @@ -108,14 +112,15 @@ base::SysUTF16ToNSString(paymentMethod->TypeAndLastFourDigits()); paymentMethodItem.methodDetail = base::SysUTF16ToNSString( paymentMethod->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL)); - int methodTypeIconID = autofill::data_util::GetPaymentRequestData(paymentMethod->type()) .icon_resource_id; paymentMethodItem.methodTypeIcon = NativeImage(methodTypeIconID); - if (_paymentRequest->selected_credit_card() == paymentMethod) + if (_paymentRequest->selected_credit_card() == paymentMethod) { paymentMethodItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark; + _selectedItem = paymentMethodItem; + } [model addItem:paymentMethodItem toSectionWithIdentifier:SectionIdentifierPayment]; } @@ -147,17 +152,37 @@ didSelectItemAtIndexPath:(NSIndexPath*)indexPath { [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; - NSInteger itemType = - [self.collectionViewModel itemTypeForIndexPath:indexPath]; - if (itemType == ItemTypePaymentMethod) { - DCHECK(indexPath.item < (NSInteger)_paymentRequest->credit_cards().size()); + CollectionViewModel* model = self.collectionViewModel; + + CollectionViewItem* item = [model itemAtIndexPath:indexPath]; + if (item.type == ItemTypePaymentMethod) { + // Update the currently selected cell, if any. + if (_selectedItem) { + _selectedItem.accessoryType = MDCCollectionViewCellAccessoryNone; + [self reconfigureCellsForItems:@[ _selectedItem ] + inSectionWithIdentifier:SectionIdentifierPayment]; + } + + // Update the newly selected cell. + PaymentMethodItem* newlySelectedItem = + base::mac::ObjCCastStrict<PaymentMethodItem>(item); + newlySelectedItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark; + [self reconfigureCellsForItems:@[ newlySelectedItem ] + inSectionWithIdentifier:SectionIdentifierPayment]; + + // Update the reference to the selected item. + _selectedItem = newlySelectedItem; + + // Notify the delegate of the selection. + NSInteger index = [model indexInItemTypeForIndexPath:indexPath]; + DCHECK(index < (NSInteger)_paymentRequest->credit_cards().size()); [_delegate paymentMethodSelectionViewController:self - didSelectPaymentMethod:_paymentRequest->credit_cards() - [indexPath.item]]; + didSelectPaymentMethod:_paymentRequest + ->credit_cards()[index]]; } // TODO(crbug.com/602666): Present a credit card addition UI when - // itemType == ItemAddMethod. + // itemType == ItemTypeAddMethod. } #pragma mark MDCCollectionViewStylingDelegate
diff --git a/ios/chrome/browser/payments/payment_request_coordinator.mm b/ios/chrome/browser/payments/payment_request_coordinator.mm index 62f04ff..0336ff5 100644 --- a/ios/chrome/browser/payments/payment_request_coordinator.mm +++ b/ios/chrome/browser/payments/payment_request_coordinator.mm
@@ -10,6 +10,7 @@ #import "base/ios/weak_nsobject.h" #include "base/mac/objc_property_releaser.h" #include "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_profile.h" @@ -38,7 +39,10 @@ @end -@implementation PaymentRequestCoordinator +@implementation PaymentRequestCoordinator { + // The selected shipping address, pending approval from the page. + autofill::AutofillProfile* _pendingShippingAddress; +} @synthesize paymentRequest = _paymentRequest; @synthesize pageFavicon = _pageFavicon; @@ -128,8 +132,46 @@ } - (void)updatePaymentDetails:(web::PaymentDetails)paymentDetails { + BOOL totalValueChanged = + (_paymentRequest->payment_details().total != paymentDetails.total); _paymentRequest->set_payment_details(paymentDetails); - [_viewController updatePaymentSummarySection]; + + if (!paymentDetails.error.empty()) { + // Display error in the shipping address/option selection view. + if (_shippingAddressSelectionCoordinator) { + _paymentRequest->set_selected_shipping_profile(nil); + [_shippingAddressSelectionCoordinator stopSpinnerAndDisplayError]; + } else if (_shippingOptionSelectionCoordinator) { + [_shippingOptionSelectionCoordinator stopSpinnerAndDisplayError]; + } + // Update the payment request summary view. + [_viewController loadModel]; + [[_viewController collectionView] reloadData]; + } else { + // Update the payment summary section. + [_viewController + updatePaymentSummaryWithTotalValueChanged:totalValueChanged]; + + if (_shippingAddressSelectionCoordinator) { + // Set the selected shipping address and update the selected shipping + // address in the payment request summary view. + _paymentRequest->set_selected_shipping_profile(_pendingShippingAddress); + _pendingShippingAddress = nil; + [_viewController updateSelectedShippingAddressUI]; + + // Dismiss the shipping address selection view. + [_shippingAddressSelectionCoordinator stop]; + _shippingAddressSelectionCoordinator.reset(); + } else if (_shippingOptionSelectionCoordinator) { + // Update the selected shipping option in the payment request summary + // view. The updated selection is already reflected in |_paymentRequest|. + [_viewController updateSelectedShippingOptionUI]; + + // Dismiss the shipping option selection view. + [_shippingOptionSelectionCoordinator stop]; + _shippingOptionSelectionCoordinator.reset(); + } + } } #pragma mark - PaymentRequestViewControllerDelegate @@ -190,6 +232,9 @@ - (void)paymentItemsDisplayCoordinatorDidReturn: (PaymentItemsDisplayCoordinator*)coordinator { + // Clear the 'Updated' label on the payment summary item, if there is one. + [_viewController updatePaymentSummaryWithTotalValueChanged:NO]; + [_itemsDisplayCoordinator stop]; _itemsDisplayCoordinator.reset(); } @@ -205,19 +250,18 @@ (ShippingAddressSelectionCoordinator*)coordinator didSelectShippingAddress: (autofill::AutofillProfile*)shippingAddress { - _paymentRequest->set_selected_shipping_profile(shippingAddress); - [_viewController updateSelectedShippingAddress:shippingAddress]; + _pendingShippingAddress = shippingAddress; web::PaymentAddress address = payment_request_utils::PaymentAddressFromAutofillProfile(shippingAddress); [_delegate paymentRequestCoordinator:self didSelectShippingAddress:address]; - - [_shippingAddressSelectionCoordinator stop]; - _shippingAddressSelectionCoordinator.reset(); } - (void)shippingAddressSelectionCoordinatorDidReturn: (ShippingAddressSelectionCoordinator*)coordinator { + // Clear the 'Updated' label on the payment summary item, if there is one. + [_viewController updatePaymentSummaryWithTotalValueChanged:NO]; + [_shippingAddressSelectionCoordinator stop]; _shippingAddressSelectionCoordinator.reset(); } @@ -228,17 +272,15 @@ (ShippingOptionSelectionCoordinator*)coordinator didSelectShippingOption: (web::PaymentShippingOption*)shippingOption { - [_viewController updateSelectedShippingOption:shippingOption]; - [_delegate paymentRequestCoordinator:self didSelectShippingOption:*shippingOption]; - - [_shippingOptionSelectionCoordinator stop]; - _shippingOptionSelectionCoordinator.reset(); } - (void)shippingOptionSelectionCoordinatorDidReturn: (ShippingAddressSelectionCoordinator*)coordinator { + // Clear the 'Updated' label on the payment summary item, if there is one. + [_viewController updatePaymentSummaryWithTotalValueChanged:NO]; + [_shippingOptionSelectionCoordinator stop]; _shippingOptionSelectionCoordinator.reset(); } @@ -250,8 +292,10 @@ didSelectPaymentMethod:(autofill::CreditCard*)creditCard { _paymentRequest->set_selected_credit_card(creditCard); - [_viewController loadModel]; - [[_viewController collectionView] reloadData]; + [_viewController updateSelectedPaymentMethodUI]; + + // Clear the 'Updated' label on the payment summary item, if there is one. + [_viewController updatePaymentSummaryWithTotalValueChanged:NO]; [_methodSelectionCoordinator stop]; _methodSelectionCoordinator.reset(); @@ -259,6 +303,9 @@ - (void)paymentMethodSelectionCoordinatorDidReturn: (PaymentMethodSelectionCoordinator*)coordinator { + // Clear the 'Updated' label on the payment summary item, if there is one. + [_viewController updatePaymentSummaryWithTotalValueChanged:NO]; + [_methodSelectionCoordinator stop]; _methodSelectionCoordinator.reset(); }
diff --git a/ios/chrome/browser/payments/payment_request_manager.mm b/ios/chrome/browser/payments/payment_request_manager.mm index 946cfa2..3a4b2a0 100644 --- a/ios/chrome/browser/payments/payment_request_manager.mm +++ b/ios/chrome/browser/payments/payment_request_manager.mm
@@ -184,10 +184,13 @@ } - (void)cancelRequest { + [self cancelRequestWithErrorMessage:@"Request canceled."]; +} + +- (void)cancelRequestWithErrorMessage:(NSString*)errorMessage { [self dismissUI]; - [_paymentRequestJsManager - rejectRequestPromiseWithErrorMessage:@"Request cancelled by user." - completionHandler:nil]; + [_paymentRequestJsManager rejectRequestPromiseWithErrorMessage:errorMessage + completionHandler:nil]; } - (void)close { @@ -263,6 +266,9 @@ if (command == "paymentRequest.requestShow") { return [self handleRequestShow:JSONCommand]; } + if (command == "paymentRequest.requestCancel") { + return [self handleRequestCancel]; + } if (command == "paymentRequest.responseComplete") { return [self handleResponseComplete]; } @@ -314,6 +320,17 @@ return YES; } +- (BOOL)handleRequestCancel { + // TODO(crbug.com/602666): Check that there is already a pending request. + + [_unblockEventQueueTimer invalidate]; + [_paymentResponseTimeoutTimer invalidate]; + + [self cancelRequestWithErrorMessage:@"Request canceled by the page."]; + + return YES; +} + - (BOOL)handleResponseComplete { // TODO(crbug.com/602666): Check that there *is* a pending response here. // TODO(crbug.com/602666): Indicate success or failure in the UI. @@ -393,7 +410,7 @@ - (void)paymentRequestCoordinatorDidCancel: (PaymentRequestCoordinator*)coordinator { - [self cancelRequest]; + [self cancelRequestWithErrorMessage:@"Request canceled by user."]; } - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator
diff --git a/ios/chrome/browser/payments/payment_request_view_controller.h b/ios/chrome/browser/payments/payment_request_view_controller.h index 90a6f2a2..d0f9e5631 100644 --- a/ios/chrome/browser/payments/payment_request_view_controller.h +++ b/ios/chrome/browser/payments/payment_request_view_controller.h
@@ -11,10 +11,6 @@ #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h" #include "ios/web/public/payments/payment_request.h" -namespace autofill { -class AutofillProfile; -} - extern NSString* const kPaymentRequestCollectionViewId; @class PaymentRequestViewController; @@ -64,16 +60,19 @@ // The delegate to be notified when the user confirms or cancels the request. @property(nonatomic, weak) id<PaymentRequestViewControllerDelegate> delegate; -// Updates the payment summary section UI. -- (void)updatePaymentSummarySection; +// Updates the payment summary section UI. If |totalValueChanged| is YES, +// adds a label to the total amount item indicating that the total amount was +// updated. +- (void)updatePaymentSummaryWithTotalValueChanged:(BOOL)totalValueChanged; -// Sets the selected shipping address and updates the UI. -- (void)updateSelectedShippingAddress: - (autofill::AutofillProfile*)shippingAddress; +// Updates the selected shipping address. +- (void)updateSelectedShippingAddressUI; -// Sets the selected shipping option and updates the UI. -- (void)updateSelectedShippingOption: - (web::PaymentShippingOption*)shippingOption; +// Updates the selected shipping option. +- (void)updateSelectedShippingOptionUI; + +// Updates the selected payment method. +- (void)updateSelectedPaymentMethodUI; // Initializes this object with an instance of PaymentRequest which owns an // instance of web::PaymentRequest as provided by the page invoking the Payment
diff --git a/ios/chrome/browser/payments/payment_request_view_controller.mm b/ios/chrome/browser/payments/payment_request_view_controller.mm index 3e02a17..a24b998 100644 --- a/ios/chrome/browser/payments/payment_request_view_controller.mm +++ b/ios/chrome/browser/payments/payment_request_view_controller.mm
@@ -67,6 +67,7 @@ ItemTypeSelectShippingOption, ItemTypePaymentTitle, ItemTypePaymentMethod, + ItemTypeAddPaymentMethod, }; } // namespace @@ -84,6 +85,7 @@ PriceItem* _paymentSummaryItem; ShippingAddressItem* _selectedShippingAddressItem; CollectionViewTextItem* _selectedShippingOptionItem; + PaymentMethodItem* _selectedPaymentMethodItem; base::mac::ObjCPropertyReleaser _propertyReleaser_PaymentRequestViewController; @@ -211,7 +213,8 @@ _paymentSummaryItem = [[[PriceItem alloc] initWithType:ItemTypeSummaryTotal] autorelease]; [self fillPaymentSummaryItem:_paymentSummaryItem - withPaymentItem:_paymentRequest->payment_details().total]; + withPaymentItem:_paymentRequest->payment_details().total + withTotalValueChanged:NO]; if (!_paymentRequest->payment_details().display_items.empty()) { _paymentSummaryItem.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator; @@ -222,6 +225,7 @@ // Shipping section. [model addSectionWithIdentifier:SectionIdentifierShipping]; + CollectionViewTextItem* shippingTitle = [[[CollectionViewTextItem alloc] initWithType:ItemTypeShippingTitle] autorelease]; shippingTitle.text = @@ -254,21 +258,25 @@ [model addItem:shippingAddressItem toSectionWithIdentifier:SectionIdentifierShipping]; - CollectionViewTextItem* shippingOptionItem = nil; + CollectionViewItem* shippingOptionItem = nil; if (_paymentRequest->selected_shipping_option()) { _selectedShippingOptionItem = [[[CollectionViewTextItem alloc] initWithType:ItemTypeShippingOption] autorelease]; shippingOptionItem = _selectedShippingOptionItem; [self fillShippingOptionItem:_selectedShippingOptionItem withOption:_paymentRequest->selected_shipping_option()]; + _selectedShippingOptionItem.accessoryType = + MDCCollectionViewCellAccessoryDisclosureIndicator; } else { - shippingOptionItem = [[[CollectionViewTextItem alloc] - initWithType:ItemTypeSelectShippingOption] autorelease]; - shippingOptionItem.text = l10n_util::GetNSString( + CollectionViewDetailItem* selectShippingOptionItem = + [[[CollectionViewDetailItem alloc] + initWithType:ItemTypeSelectShippingOption] autorelease]; + shippingOptionItem = selectShippingOptionItem; + selectShippingOptionItem.text = l10n_util::GetNSString( IDS_IOS_PAYMENT_REQUEST_SHIPPING_OPTION_SELECTION_TITLE); + selectShippingOptionItem.accessoryType = + MDCCollectionViewCellAccessoryDisclosureIndicator; } - shippingOptionItem.accessoryType = - MDCCollectionViewCellAccessoryDisclosureIndicator; shippingOptionItem.accessibilityTraits |= UIAccessibilityTraitButton; [model addItem:shippingOptionItem toSectionWithIdentifier:SectionIdentifierShipping]; @@ -276,45 +284,36 @@ // Payment method section. [model addSectionWithIdentifier:SectionIdentifierPayment]; - CollectionViewTextItem* paymentTitle = [[[CollectionViewTextItem alloc] - initWithType:ItemTypePaymentTitle] autorelease]; - paymentTitle.text = - l10n_util::GetNSString(IDS_IOS_PAYMENT_REQUEST_PAYMENT_METHOD_HEADER); - [model setHeader:paymentTitle - forSectionWithIdentifier:SectionIdentifierPayment]; - + CollectionViewItem* paymentMethodItem = nil; if (_paymentRequest->selected_credit_card()) { - PaymentMethodItem* paymentMethod = [[[PaymentMethodItem alloc] + CollectionViewTextItem* paymentTitle = [[[CollectionViewTextItem alloc] + initWithType:ItemTypePaymentTitle] autorelease]; + paymentTitle.text = + l10n_util::GetNSString(IDS_IOS_PAYMENT_REQUEST_PAYMENT_METHOD_HEADER); + [model setHeader:paymentTitle + forSectionWithIdentifier:SectionIdentifierPayment]; + + _selectedPaymentMethodItem = [[[PaymentMethodItem alloc] initWithType:ItemTypePaymentMethod] autorelease]; - - paymentMethod.methodID = base::SysUTF16ToNSString( - _paymentRequest->selected_credit_card()->TypeAndLastFourDigits()); - paymentMethod.methodDetail = base::SysUTF16ToNSString( - _paymentRequest->selected_credit_card()->GetRawInfo( - autofill::CREDIT_CARD_NAME_FULL)); - - int selectedMethodCardTypeIconID = - autofill::data_util::GetPaymentRequestData( - _paymentRequest->selected_credit_card()->type()) - .icon_resource_id; - paymentMethod.methodTypeIcon = NativeImage(selectedMethodCardTypeIconID); - - paymentMethod.accessoryType = + paymentMethodItem = _selectedPaymentMethodItem; + [self fillPaymentMethodItem:_selectedPaymentMethodItem + withPaymentMethod:_paymentRequest->selected_credit_card()]; + _selectedPaymentMethodItem.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator; - paymentMethod.accessibilityTraits |= UIAccessibilityTraitButton; - [model addItem:paymentMethod - toSectionWithIdentifier:SectionIdentifierPayment]; } else { - CollectionViewTextItem* paymentMethod = [[[CollectionViewTextItem alloc] - initWithType:ItemTypePaymentMethod] autorelease]; - NSString* selectText = - l10n_util::GetNSString(IDS_IOS_PAYMENT_REQUEST_METHOD_SELECTION_BUTTON); - paymentMethod.text = - [selectText uppercaseStringWithLocale:[NSLocale currentLocale]]; - paymentMethod.accessibilityTraits |= UIAccessibilityTraitButton; - [model addItem:paymentMethod - toSectionWithIdentifier:SectionIdentifierPayment]; + CollectionViewDetailItem* addPaymentMethodItem = + [[[CollectionViewDetailItem alloc] + initWithType:ItemTypeAddPaymentMethod] autorelease]; + paymentMethodItem = addPaymentMethodItem; + addPaymentMethodItem.text = + l10n_util::GetNSString(IDS_IOS_PAYMENT_REQUEST_PAYMENT_METHOD_HEADER); + addPaymentMethodItem.detailText = [l10n_util::GetNSString( + IDS_IOS_PAYMENT_REQUEST_ADD_SHIPPING_ADDRESS_BUTTON) + uppercaseStringWithLocale:[NSLocale currentLocale]]; } + paymentMethodItem.accessibilityTraits |= UIAccessibilityTraitButton; + [model addItem:paymentMethodItem + toSectionWithIdentifier:SectionIdentifierPayment]; } - (void)viewDidLoad { @@ -327,40 +326,48 @@ UIEdgeInsetsMake(0, kSeparatorEdgeInset, 0, kSeparatorEdgeInset); } -- (void)updatePaymentSummarySection { +- (void)updatePaymentSummaryWithTotalValueChanged:(BOOL)totalValueChanged { [self fillPaymentSummaryItem:_paymentSummaryItem - withPaymentItem:_paymentRequest->payment_details().total]; + withPaymentItem:_paymentRequest->payment_details().total + withTotalValueChanged:totalValueChanged]; NSIndexPath* indexPath = [self.collectionViewModel indexPathForItem:_paymentSummaryItem inSectionWithIdentifier:SectionIdentifierSummary]; [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; } -- (void)updateSelectedShippingAddress: - (autofill::AutofillProfile*)shippingAddress { - _paymentRequest->set_selected_shipping_profile(shippingAddress); +- (void)updateSelectedShippingAddressUI { [self fillShippingAddressItem:_selectedShippingAddressItem - withAddress:shippingAddress]; + withAddress:_paymentRequest->selected_shipping_profile()]; NSIndexPath* indexPath = [self.collectionViewModel indexPathForItem:_selectedShippingAddressItem inSectionWithIdentifier:SectionIdentifierShipping]; [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; } -- (void)updateSelectedShippingOption: - (web::PaymentShippingOption*)shippingOption { +- (void)updateSelectedShippingOptionUI { [self fillShippingOptionItem:_selectedShippingOptionItem - withOption:shippingOption]; + withOption:_paymentRequest->selected_shipping_option()]; NSIndexPath* indexPath = [self.collectionViewModel indexPathForItem:_selectedShippingOptionItem inSectionWithIdentifier:SectionIdentifierShipping]; [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; } +- (void)updateSelectedPaymentMethodUI { + [self fillPaymentMethodItem:_selectedPaymentMethodItem + withPaymentMethod:_paymentRequest->selected_credit_card()]; + NSIndexPath* indexPath = + [self.collectionViewModel indexPathForItem:_selectedPaymentMethodItem + inSectionWithIdentifier:SectionIdentifierPayment]; + [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; +} + #pragma mark - Helper methods - (void)fillPaymentSummaryItem:(PriceItem*)item - withPaymentItem:(web::PaymentItem)paymentItem { + withPaymentItem:(web::PaymentItem)paymentItem + withTotalValueChanged:(BOOL)totalValueChanged { item.item = l10n_util::GetNSString(IDS_IOS_PAYMENT_REQUEST_TOTAL_HEADER); payments::CurrencyFormatter* currencyFormatter = _paymentRequest->GetOrCreateCurrencyFormatter(); @@ -368,6 +375,10 @@ IDS_IOS_PAYMENT_REQUEST_PAYMENT_ITEMS_TOTAL_FORMAT, base::UTF8ToUTF16(currencyFormatter->formatted_currency_code()), currencyFormatter->Format(base::UTF16ToASCII(paymentItem.amount.value)))); + item.notification = + totalValueChanged + ? l10n_util::GetNSString(IDS_IOS_PAYMENT_REQUEST_TOTAL_UPDATED_LABEL) + : nil; } - (void)fillShippingAddressItem:(ShippingAddressItem*)item @@ -386,6 +397,17 @@ currencyFormatter->Format(base::UTF16ToASCII(option->amount.value))); } +- (void)fillPaymentMethodItem:(PaymentMethodItem*)item + withPaymentMethod:(autofill::CreditCard*)creditCard { + item.methodID = base::SysUTF16ToNSString(creditCard->TypeAndLastFourDigits()); + item.methodDetail = base::SysUTF16ToNSString( + creditCard->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL)); + int selectedMethodCardTypeIconID = + autofill::data_util::GetPaymentRequestData(creditCard->type()) + .icon_resource_id; + item.methodTypeIcon = NativeImage(selectedMethodCardTypeIconID); +} + #pragma mark UICollectionViewDataSource - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView @@ -442,6 +464,7 @@ [_delegate paymentRequestViewControllerDidSelectShippingOptionItem:self]; break; case ItemTypePaymentMethod: + case ItemTypeAddPaymentMethod: [_delegate paymentRequestViewControllerDidSelectPaymentMethodItem:self]; break; default: @@ -454,19 +477,27 @@ - (CGFloat)collectionView:(UICollectionView*)collectionView cellHeightAtIndexPath:(NSIndexPath*)indexPath { - NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; - if ((type == ItemTypePaymentMethod && - _paymentRequest->selected_credit_card()) || - (type == ItemTypeShippingAddress)) { - CollectionViewItem* item = - [self.collectionViewModel itemAtIndexPath:indexPath]; - return [MDCCollectionViewCell - cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) - forItem:item]; - } else if (type == ItemTypeShippingOption) { - return MDCCellDefaultTwoLineHeight; - } else { - return MDCCellDefaultOneLineHeight; + CollectionViewItem* item = + [self.collectionViewModel itemAtIndexPath:indexPath]; + switch (item.type) { + case ItemTypeShippingAddress: + case ItemTypePaymentMethod: + return [MDCCollectionViewCell + cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) + forItem:item]; + case ItemTypeShippingOption: + return MDCCellDefaultTwoLineHeight; + case ItemTypeSummaryPageInfo: + case ItemTypeSummaryTotal: + case ItemTypeShippingTitle: + case ItemTypeAddShippingAddress: + case ItemTypeSelectShippingOption: + case ItemTypePaymentTitle: + case ItemTypeAddPaymentMethod: + return MDCCellDefaultOneLineHeight; + default: + NOTREACHED(); + return MDCCellDefaultOneLineHeight; } }
diff --git a/ios/chrome/browser/payments/resources/payment_request_manager.js b/ios/chrome/browser/payments/resources/payment_request_manager.js index ab68593..f5a136bb 100644 --- a/ios/chrome/browser/payments/resources/payment_request_manager.js +++ b/ios/chrome/browser/payments/resources/payment_request_manager.js
@@ -229,7 +229,11 @@ updateEvent = null; }) .catch(function() { - // TODO(crbug.com/602666): Handle the reject scenario. + var message = { + 'command': 'paymentRequest.requestCancel', + }; + __gCrWeb.message.invokeOnHost(message); + updateEvent = null; }); };
diff --git a/ios/chrome/browser/payments/shipping_address_selection_coordinator.h b/ios/chrome/browser/payments/shipping_address_selection_coordinator.h index 09c0a63..a2885b8 100644 --- a/ios/chrome/browser/payments/shipping_address_selection_coordinator.h +++ b/ios/chrome/browser/payments/shipping_address_selection_coordinator.h
@@ -48,6 +48,9 @@ @property(nonatomic, weak) id<ShippingAddressSelectionCoordinatorDelegate> delegate; +// Stops the spinner and displays the error provided in the payment details. +- (void)stopSpinnerAndDisplayError; + @end #endif // IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_ADDRESS_SELECTION_COORDINATOR_H_
diff --git a/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm b/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm index fa47450..ea34945 100644 --- a/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm +++ b/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm
@@ -6,6 +6,7 @@ #import "base/ios/weak_nsobject.h" #include "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" #include "components/autofill/core/browser/autofill_profile.h" #include "ios/chrome/browser/payments/payment_request.h" #import "ios/chrome/browser/payments/shipping_address_selection_view_controller.h" @@ -55,12 +56,25 @@ _viewController.reset(); } +- (void)stopSpinnerAndDisplayError { + // Re-enable user interactions that were disabled earlier in + // delayedNotifyDelegateOfSelection. + _viewController.get().view.userInteractionEnabled = YES; + + [_viewController setIsLoading:NO]; + [_viewController + setErrorMessage:base::SysUTF16ToNSString( + _paymentRequest->payment_details().error)]; + [_viewController loadModel]; + [[_viewController collectionView] reloadData]; +} + #pragma mark - ShippingAddressSelectionViewControllerDelegate - (void)shippingAddressSelectionViewController: (ShippingAddressSelectionViewController*)controller - selectedShippingAddress: - (autofill::AutofillProfile*)shippingAddress { + didSelectShippingAddress: + (autofill::AutofillProfile*)shippingAddress { [self delayedNotifyDelegateOfSelection:shippingAddress]; } @@ -74,7 +88,8 @@ _viewController.get().view.userInteractionEnabled = NO; base::WeakNSObject<ShippingAddressSelectionCoordinator> weakSelf(self); dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), + dispatch_time(DISPATCH_TIME_NOW, + static_cast<int64_t>(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ base::scoped_nsobject<ShippingAddressSelectionCoordinator> strongSelf( [weakSelf retain]); @@ -82,7 +97,10 @@ if (!strongSelf) return; - _viewController.get().view.userInteractionEnabled = YES; + [_viewController setIsLoading:YES]; + [_viewController loadModel]; + [[_viewController collectionView] reloadData]; + [_delegate shippingAddressSelectionCoordinator:self didSelectShippingAddress:shippingAddress]; });
diff --git a/ios/chrome/browser/payments/shipping_address_selection_view_controller.h b/ios/chrome/browser/payments/shipping_address_selection_view_controller.h index 910066c..5bc73bb 100644 --- a/ios/chrome/browser/payments/shipping_address_selection_view_controller.h +++ b/ios/chrome/browser/payments/shipping_address_selection_view_controller.h
@@ -23,8 +23,8 @@ // Notifies the delegate that the user has selected a shipping address. - (void)shippingAddressSelectionViewController: (ShippingAddressSelectionViewController*)controller - selectedShippingAddress: - (autofill::AutofillProfile*)shippingAddress; + didSelectShippingAddress: + (autofill::AutofillProfile*)shippingAddress; // Notifies the delegate that the user has chosen to return to the previous // screen without making a selection.
diff --git a/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm b/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm index 62460bc7..38fc8cd 100644 --- a/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm +++ b/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm
@@ -59,6 +59,7 @@ // pointer and should outlive this class. PaymentRequest* _paymentRequest; + // The currently selected item. May be nil. ShippingAddressItem* _selectedItem; } @@ -224,15 +225,15 @@ [self reconfigureCellsForItems:@[ newlySelectedItem ] inSectionWithIdentifier:SectionIdentifierShippingAddress]; - // Update the reference to the the selected item. + // Update the reference to the selected item. _selectedItem = newlySelectedItem; // Notify the delegate of the selection. NSInteger index = [model indexInItemTypeForIndexPath:indexPath]; DCHECK(index < (NSInteger)_paymentRequest->shipping_profiles().size()); [_delegate shippingAddressSelectionViewController:self - selectedShippingAddress: - _paymentRequest->shipping_profiles()[index]]; + didSelectShippingAddress: + _paymentRequest->shipping_profiles()[index]]; } // TODO(crbug.com/602666): Present a shipping address addition UI when // itemType == ItemTypeAddShippingAddress.
diff --git a/ios/chrome/browser/payments/shipping_option_selection_coordinator.h b/ios/chrome/browser/payments/shipping_option_selection_coordinator.h index 81030f22..23ea0a3 100644 --- a/ios/chrome/browser/payments/shipping_option_selection_coordinator.h +++ b/ios/chrome/browser/payments/shipping_option_selection_coordinator.h
@@ -45,6 +45,9 @@ @property(nonatomic, weak) id<ShippingOptionSelectionCoordinatorDelegate> delegate; +// Stops the spinner and displays the error provided in the payment details. +- (void)stopSpinnerAndDisplayError; + @end #endif // IOS_CHROME_BROWSER_PAYMENTS_SHIPPING_OPTION_SELECTION_COORDINATOR_H_
diff --git a/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm b/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm index f652c90..af8b8869 100644 --- a/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm +++ b/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm
@@ -6,6 +6,7 @@ #import "base/ios/weak_nsobject.h" #include "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" #import "ios/chrome/browser/payments/shipping_option_selection_view_controller.h" @interface ShippingOptionSelectionCoordinator ()< @@ -53,12 +54,25 @@ _viewController.reset(); } +- (void)stopSpinnerAndDisplayError { + // Re-enable user interactions that were disabled earlier in + // delayedNotifyDelegateOfSelection. + _viewController.get().view.userInteractionEnabled = YES; + + [_viewController setIsLoading:NO]; + [_viewController + setErrorMessage:base::SysUTF16ToNSString( + _paymentRequest->payment_details().error)]; + [_viewController loadModel]; + [[_viewController collectionView] reloadData]; +} + #pragma mark - ShippingOptionSelectionViewControllerDelegate - (void)shippingOptionSelectionViewController: (ShippingOptionSelectionViewController*)controller - selectedShippingOption: - (web::PaymentShippingOption*)shippingOption { + didSelectShippingOption: + (web::PaymentShippingOption*)shippingOption { [self delayedNotifyDelegateOfSelection:shippingOption]; } @@ -72,15 +86,18 @@ _viewController.get().view.userInteractionEnabled = NO; base::WeakNSObject<ShippingOptionSelectionCoordinator> weakSelf(self); dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), + dispatch_time(DISPATCH_TIME_NOW, + static_cast<int64_t>(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ base::scoped_nsobject<ShippingOptionSelectionCoordinator> strongSelf( [weakSelf retain]); // Early return if the coordinator has been deallocated. if (!strongSelf) return; + [_viewController setIsLoading:YES]; + [_viewController loadModel]; + [[_viewController collectionView] reloadData]; - _viewController.get().view.userInteractionEnabled = YES; [_delegate shippingOptionSelectionCoordinator:self didSelectShippingOption:shippingOption]; });
diff --git a/ios/chrome/browser/payments/shipping_option_selection_view_controller.h b/ios/chrome/browser/payments/shipping_option_selection_view_controller.h index 76ed2c7..e1a4648d 100644 --- a/ios/chrome/browser/payments/shipping_option_selection_view_controller.h +++ b/ios/chrome/browser/payments/shipping_option_selection_view_controller.h
@@ -19,8 +19,8 @@ // Notifies the delegate that the user has selected a shipping option. - (void)shippingOptionSelectionViewController: (ShippingOptionSelectionViewController*)controller - selectedShippingOption: - (web::PaymentShippingOption*)shippingOption; + didSelectShippingOption: + (web::PaymentShippingOption*)shippingOption; // Notifies the delegate that the user has chosen to return to the previous // screen without making a selection.
diff --git a/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm b/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm index a1f756a33..e5d40e6 100644 --- a/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm +++ b/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm
@@ -55,6 +55,7 @@ // pointer and should outlive this class. PaymentRequest* _paymentRequest; + // The currently selected item. May be nil. CollectionViewTextItem* _selectedItem; } @@ -209,7 +210,7 @@ [self reconfigureCellsForItems:@[ newlySelectedItem ] inSectionWithIdentifier:SectionIdentifierShippingOption]; - // Update the reference to the the selected item. + // Update the reference to the selected item. _selectedItem = newlySelectedItem; // Notify the delegate of the selection. @@ -217,7 +218,7 @@ DCHECK(index < (NSInteger)_paymentRequest->shipping_options().size()); [_delegate shippingOptionSelectionViewController:self - selectedShippingOption:_paymentRequest + didSelectShippingOption:_paymentRequest ->shipping_options()[index]]; } }
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn index a8942eb0..6a71d9c 100644 --- a/ios/chrome/browser/tabs/BUILD.gn +++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -33,7 +33,6 @@ sources = [ "tab.mm", "tab_model.mm", - "tab_model_order_controller.mm", "tab_model_synced_window_delegate.mm", "tab_model_synced_window_delegate_getter.mm", ] @@ -121,6 +120,7 @@ "tab_model_list.mm", "tab_model_observers.h", "tab_model_observers.mm", + "tab_model_order_controller.mm", ] deps = [ ":tabs",
diff --git a/ios/chrome/browser/tabs/tab_model.h b/ios/chrome/browser/tabs/tab_model.h index c577489..766eeeb 100644 --- a/ios/chrome/browser/tabs/tab_model.h +++ b/ios/chrome/browser/tabs/tab_model.h
@@ -164,10 +164,6 @@ // as forward or back navigations (incrementing/decrementing the navigation // index) may result in incorrect tab pairings. -// Returns the first tab in the model opened by the specified tab at its current -// navigation index. The search starts at the beginning of the list and stops at -// |tab|. Returns nil if no tab meets these constraints. -- (Tab*)firstTabWithOpener:(Tab*)tab; // Returns the last tab in the model opened by the specified tab at its current // navigation index. The search starts at |tab|. Returns nil if no tab meets // these constraints.
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm index 08fc2f5..81d4528f2 100644 --- a/ios/chrome/browser/tabs/tab_model.mm +++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -364,33 +364,6 @@ return nil; } -- (Tab*)firstTabWithOpener:(Tab*)tab { - if (!tab) - return nil; - NSUInteger stopIndex = [self indexOfTab:tab]; - if (stopIndex == NSNotFound) - return nil; - NSString* parentID = tab.tabId; - // Match the navigation index as well as the session id, to better match the - // state of the tab. I.e. two tabs are opened via a link from tab A, and then - // a new url is loaded into tab A, and more tabs opened from that url, the - // latter two tabs should not be grouped with the former two. The navigation - // index is the simplest way to detect navigation changes. - DCHECK([tab navigationManager]); - NSInteger parentNavIndex = [tab navigationManager]->GetCurrentItemIndex(); - for (NSUInteger i = 0; i < stopIndex; ++i) { - Tab* tabToCheck = [_tabs objectAtIndex:i]; - DCHECK([tabToCheck navigationManager]); - CRWSessionController* sessionController = - [tabToCheck navigationManager]->GetSessionController(); - if ([sessionController.openerId isEqualToString:parentID] && - sessionController.openerNavigationIndex == parentNavIndex) { - return tabToCheck; - } - } - return nil; -} - - (Tab*)lastTabWithOpener:(Tab*)tab { NSUInteger startIndex = [self indexOfTab:tab]; if (startIndex == NSNotFound)
diff --git a/ios/chrome/browser/tabs/tab_model_order_controller.h b/ios/chrome/browser/tabs/tab_model_order_controller.h index 2fec1f2..d3e3805 100644 --- a/ios/chrome/browser/tabs/tab_model_order_controller.h +++ b/ios/chrome/browser/tabs/tab_model_order_controller.h
@@ -12,14 +12,6 @@ namespace TabModelOrderConstants { -// InsertionPolicy is a general policy for opening all new tabs. -enum InsertionPolicy { - // Newly created tabs are created after the selection. This is the default. - kInsertAfter, - // Newly created tabs are inserted before the selection. - kInsertBefore, -}; - // InsertionAdjacency allows different links to choose to open tabs directly // before or after a given tab, depending on context. enum InsertionAdjacency { @@ -35,18 +27,10 @@ // heuristics plugged into a TabStripModel. Closely parallels // chrome/browser/tabs/tab_strip_model_order_controller.h // but without the dependence on TabContentsWrapper and TabStripModel. -@interface TabModelOrderController : NSObject { - @private - TabModel* model_; // weak, owns us - TabModelOrderConstants::InsertionPolicy insertionPolicy_; -} - -// Sets the insertion policy for new tabs. Default is |kInsertAfter|. -@property(nonatomic, assign) - TabModelOrderConstants::InsertionPolicy insertionPolicy; +@interface TabModelOrderController : NSObject // Initializer, |model| must be non-nil and is not retained. -- (id)initWithTabModel:(TabModel*)model; +- (instancetype)initWithTabModel:(TabModel*)model; // Determines where to place a newly opened tab by using the transition and // adjacency flags. @@ -56,7 +40,7 @@ adjacency:(TabModelOrderConstants::InsertionAdjacency) adjacency; -// Returns the index at which to append tabs, based on |insertionPolicy|. +// Returns the index at which to append tabs. - (NSUInteger)insertionIndexForAppending; // Returns the tab in which to shift selection after a tab is closed. May
diff --git a/ios/chrome/browser/tabs/tab_model_order_controller.mm b/ios/chrome/browser/tabs/tab_model_order_controller.mm index 215540b6..0eb1f32d 100644 --- a/ios/chrome/browser/tabs/tab_model_order_controller.mm +++ b/ios/chrome/browser/tabs/tab_model_order_controller.mm
@@ -6,17 +6,18 @@ #include "base/logging.h" -@implementation TabModelOrderController +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif -@synthesize insertionPolicy = insertionPolicy_; +@implementation TabModelOrderController { + __weak TabModel* model_; +} -- (id)initWithTabModel:(TabModel*)model { +- (instancetype)initWithTabModel:(TabModel*)model { DCHECK(model); - self = [super init]; - if (self) { + if ((self = [super init])) model_ = model; - insertionPolicy_ = TabModelOrderConstants::kInsertAfter; - } return self; } @@ -28,35 +29,23 @@ if (model_.isEmpty) return 0; - if (PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK) && - parentTab) { - NSUInteger referenceIndex = [model_ indexOfTab:parentTab]; - Tab* openLocation = nil; - if (insertionPolicy_ == TabModelOrderConstants::kInsertAfter) { - openLocation = [model_ lastTabWithOpener:parentTab]; - } else { - openLocation = [model_ firstTabWithOpener:parentTab]; - } - if (openLocation) - referenceIndex = [model_ indexOfTab:openLocation]; + if (!parentTab) + return [self insertionIndexForAppending]; - // If the reference tab is no longer in the model (e.g., it was closed - // before the open request was handled), fall through to default behavior. - if (referenceIndex != NSNotFound) { - BOOL insertAfter = - insertionPolicy_ == TabModelOrderConstants::kInsertAfter || - adjacency == TabModelOrderConstants::kAdjacentAfter; - NSUInteger delta = insertAfter ? 1 : 0; - return referenceIndex + delta; - } - } - // In other cases, such as a normal new tab, open at the end. - return [self insertionIndexForAppending]; + if (!PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK)) + return [self insertionIndexForAppending]; + + NSUInteger referenceIndex = [model_ indexOfTab:parentTab]; + Tab* openLocation = [model_ lastTabWithOpener:parentTab]; + if (openLocation) + referenceIndex = [model_ indexOfTab:openLocation]; + + DCHECK_NE(referenceIndex, static_cast<NSUInteger>(NSNotFound)); + return referenceIndex + 1; } - (NSUInteger)insertionIndexForAppending { - return insertionPolicy_ == TabModelOrderConstants::kInsertAfter ? model_.count - : 0; + return model_.count; } - (Tab*)determineNewSelectedTabFromRemovedTab:(Tab*)removedTab { @@ -69,6 +58,7 @@ const NSUInteger numberOfTabs = model_.count; if (numberOfTabs < 2) return nil; + DCHECK(numberOfTabs >= 2); DCHECK(removedTab == model_.currentTab);
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm index bb29e05d..c28266e 100644 --- a/ios/chrome/browser/tabs/tab_model_unittest.mm +++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -456,7 +456,6 @@ EXPECT_TRUE([tab_model_ isEmpty]); EXPECT_FALSE([tab_model_ nextTabWithOpener:nil afterTab:nil]); EXPECT_FALSE([tab_model_ lastTabWithOpener:nil]); - EXPECT_FALSE([tab_model_ firstTabWithOpener:nil]); } TEST_F(TabModelTest, OpenersNothingOpenedGeneral) { @@ -473,14 +472,12 @@ // All should fail since this hasn't opened anything else. EXPECT_FALSE([tab_model_ nextTabWithOpener:tab afterTab:nil]); EXPECT_FALSE([tab_model_ lastTabWithOpener:tab]); - EXPECT_FALSE([tab_model_ firstTabWithOpener:tab]); // Add more items to the tab, expect the same results. [tab_model_ addTabWithURL:kURL referrer:kEmptyReferrer windowName:nil]; [tab_model_ addTabWithURL:kURL referrer:kEmptyReferrer windowName:nil]; EXPECT_FALSE([tab_model_ nextTabWithOpener:tab afterTab:nil]); EXPECT_FALSE([tab_model_ lastTabWithOpener:tab]); - EXPECT_FALSE([tab_model_ firstTabWithOpener:tab]); } TEST_F(TabModelTest, OpenersNothingOpenedFirst) { @@ -494,7 +491,6 @@ // All should fail since this hasn't opened anything else. EXPECT_FALSE([tab_model_ nextTabWithOpener:tab afterTab:nil]); EXPECT_FALSE([tab_model_ lastTabWithOpener:tab]); - EXPECT_FALSE([tab_model_ firstTabWithOpener:tab]); } TEST_F(TabModelTest, OpenersNothingOpenedLast) { @@ -507,16 +503,13 @@ // All should fail since this hasn't opened anything else. EXPECT_FALSE([tab_model_ nextTabWithOpener:tab afterTab:nil]); EXPECT_FALSE([tab_model_ lastTabWithOpener:tab]); - EXPECT_FALSE([tab_model_ firstTabWithOpener:tab]); } TEST_F(TabModelTest, OpenersChildTabBeforeOpener) { Tab* parent_tab = [tab_model_ insertTabWithWebState:CreateWebState(@"window") atIndex:[tab_model_ count]]; // Insert child at start - Tab* child_tab = - [tab_model_ insertTabWithWebState:CreateChildWebState(parent_tab) - atIndex:0]; + [tab_model_ insertTabWithWebState:CreateChildWebState(parent_tab) atIndex:0]; // Insert a few more between them. [tab_model_ insertTabWithWebState:CreateWebState(@"window") atIndex:1]; @@ -524,7 +517,6 @@ EXPECT_FALSE([tab_model_ nextTabWithOpener:parent_tab afterTab:nil]); EXPECT_FALSE([tab_model_ lastTabWithOpener:parent_tab]); - EXPECT_EQ([tab_model_ firstTabWithOpener:parent_tab], child_tab); } TEST_F(TabModelTest, OpenersChildTabAfterOpener) { @@ -545,7 +537,6 @@ EXPECT_EQ([tab_model_ nextTabWithOpener:parent_tab afterTab:child_tab1], child_tab2); EXPECT_EQ([tab_model_ lastTabWithOpener:parent_tab], child_tab2); - EXPECT_FALSE([tab_model_ firstTabWithOpener:parent_tab]); } TEST_F(TabModelTest, AddWithOrderController) {
diff --git a/ios/chrome/common/BUILD.gn b/ios/chrome/common/BUILD.gn index 49a2cf1e..d0b2a47 100644 --- a/ios/chrome/common/BUILD.gn +++ b/ios/chrome/common/BUILD.gn
@@ -30,22 +30,10 @@ libs = [ "QuartzCore.framework" ] } -source_set("noarc_unit_tests") { +source_set("unit_tests") { testonly = true sources = [ "block_unittest.mm", - ] - deps = [ - ":common", - "//base", - "//testing/gtest", - ] -} - -source_set("unit_tests") { - configs += [ "//build/config/compiler:enable_arc" ] - testonly = true - sources = [ "ns_regular_expression_unittest.mm", "string_util_unittest.mm", "x_callback_url_unittest.cc", @@ -56,6 +44,25 @@ "//base", "//testing/gtest", ] + configs += [ "//build/config/compiler:enable_arc" ] +} + +# The block_unittest.mm has been duplicated during the migration of iOS code +# to ARC (see crbug.com/624363). As this test checks the interaction of C++ +# objects and Objective-C blocks, it is necessary to keep two version of the +# test compiled with and without ARC. Remove this file when the conversion to +# ARC is complete. +source_set("noarc_unit_tests") { + visibility = [ ":unit_tests" ] + testonly = true + sources = [ + "noarc_block_unittest.mm", + ] + deps = [ + ":common", + "//base", + "//testing/gtest", + ] } buildflag_header("ios_app_bundle_id_prefix_header") {
diff --git a/ios/chrome/common/block_unittest.mm b/ios/chrome/common/block_unittest.mm index 3ff46f8..352583c 100644 --- a/ios/chrome/common/block_unittest.mm +++ b/ios/chrome/common/block_unittest.mm
@@ -8,11 +8,17 @@ #include "base/memory/ref_counted.h" #include "testing/platform_test.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + // This test verifies assumptions about the murky world of interaction between // C++ objects and blocks. Just to make sure. namespace { +using BlockTest = PlatformTest; + class RefCountedObject : public base::RefCounted<RefCountedObject> { public: RefCountedObject() {} @@ -37,7 +43,7 @@ virtual ~RefCountedObject() {} }; -TEST_F(PlatformTest, BlockAndCPlusPlus) { +TEST_F(BlockTest, BlockAndCPlusPlus) { RefCountedObject* object = new RefCountedObject(); object->AddRef(); EXPECT_TRUE(object->HasOneRef()); @@ -49,22 +55,23 @@ } EXPECT_TRUE(object->HasOneRef()); - void (^heap_block)(int) = 0; - { - scoped_refptr<RefCountedObject> object_ptr(object); - EXPECT_EQ(2, object->refcount()); - void* object_void_ptr = (void*)object; + @autoreleasepool { + void (^heap_block)(int) = nil; + { + scoped_refptr<RefCountedObject> object_ptr(object); + EXPECT_EQ(2, object->refcount()); + void* object_void_ptr = (void*)object; - void (^stack_block)(int) = ^(int expected) { - EXPECT_EQ(object_void_ptr, object_ptr.get()); - EXPECT_EQ(expected, object_ptr.get()->refcount()); - }; - stack_block(3); - heap_block = [stack_block copy]; - stack_block(4); + void (^stack_block)(int) = ^(int expected) { + EXPECT_EQ(object_void_ptr, object_ptr.get()); + EXPECT_EQ(expected, object_ptr.get()->refcount()); + }; + stack_block(4); + heap_block = [stack_block copy]; + stack_block(4); + } + heap_block(2); } - heap_block(2); - [heap_block release]; EXPECT_TRUE(object->HasOneRef()); { scoped_refptr<RefCountedObject> object_test2_ptr(object); @@ -74,8 +81,8 @@ object->Release(); } -TEST_F(PlatformTest, BlockAndVectors) { - void (^heap_block)(void) = 0; +TEST_F(BlockTest, BlockAndVectors) { + void (^heap_block)(void) = nil; { std::vector<int> vector; vector.push_back(0); @@ -94,7 +101,6 @@ stack_block(); } heap_block(); - [heap_block release]; } } // namespace
diff --git a/ios/chrome/common/noarc_block_unittest.mm b/ios/chrome/common/noarc_block_unittest.mm new file mode 100644 index 0000000..defab36e --- /dev/null +++ b/ios/chrome/common/noarc_block_unittest.mm
@@ -0,0 +1,106 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Foundation/Foundation.h> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "testing/platform_test.h" + +#if defined(__has_feature) && __has_feature(objc_arc) +#error "This file must not be compiled with ARC." +#endif + +// This test verifies assumptions about the murky world of interaction between +// C++ objects and blocks. Just to make sure. + +namespace { + +using NoArcBlockTest = PlatformTest; + +class RefCountedObject : public base::RefCounted<RefCountedObject> { + public: + RefCountedObject() {} + + // Refcount is private in the superclass, fake it by counting how many times + // release can be called until there is one count left, then retain the count + // back. + int refcount() { + int count = 1; + while (!HasOneRef()) { + bool check = base::subtle::RefCountedBase::Release(); + EXPECT_FALSE(check); + ++count; + } + for (int ii = 1; ii < count; ii++) + base::subtle::RefCountedBase::AddRef(); + return count; + } + + protected: + friend base::RefCounted<RefCountedObject>; + virtual ~RefCountedObject() {} +}; + +TEST_F(NoArcBlockTest, BlockAndCPlusPlus) { + RefCountedObject* object = new RefCountedObject(); + object->AddRef(); + EXPECT_TRUE(object->HasOneRef()); + EXPECT_EQ(1, object->refcount()); + + { + scoped_refptr<RefCountedObject> object_test_ptr(object); + EXPECT_EQ(2, object->refcount()); + } + EXPECT_TRUE(object->HasOneRef()); + + void (^heap_block)(int) = nil; + { + scoped_refptr<RefCountedObject> object_ptr(object); + EXPECT_EQ(2, object->refcount()); + void* object_void_ptr = (void*)object; + + void (^stack_block)(int) = ^(int expected) { + EXPECT_EQ(object_void_ptr, object_ptr.get()); + EXPECT_EQ(expected, object_ptr.get()->refcount()); + }; + stack_block(3); + heap_block = [stack_block copy]; + stack_block(4); + } + heap_block(2); + [heap_block release]; + EXPECT_TRUE(object->HasOneRef()); + { + scoped_refptr<RefCountedObject> object_test2_ptr(object); + EXPECT_EQ(2, object->refcount()); + } + EXPECT_TRUE(object->HasOneRef()); + object->Release(); +} + +TEST_F(NoArcBlockTest, BlockAndVectors) { + void (^heap_block)(void) = nil; + { + std::vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + + void (^stack_block)(void) = ^{ + EXPECT_EQ(3ul, vector.size()); + EXPECT_EQ(2, vector[2]); + }; + stack_block(); + vector[2] = 42; + vector.push_back(22); + stack_block(); + heap_block = [stack_block copy]; + stack_block(); + } + heap_block(); + [heap_block release]; +} + +} // namespace
diff --git a/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java b/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java index 84278b3..19a3e33 100644 --- a/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java +++ b/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java
@@ -27,7 +27,6 @@ import android.net.wifi.WifiManager; import android.os.Build; import android.telephony.TelephonyManager; -import android.util.Log; import org.chromium.base.ApplicationState; import org.chromium.base.ApplicationStatus; @@ -1016,7 +1015,6 @@ mConnectionType = newConnectionType; mWifiSSID = newWifiSSID; - Log.d(TAG, "Network connectivity changed, type is: " + mConnectionType); mObserver.onConnectionTypeChanged(newConnectionType); }
diff --git a/net/http/http_stream_factory_impl_job_controller.cc b/net/http/http_stream_factory_impl_job_controller.cc index 5a264f5..0bb08374 100644 --- a/net/http/http_stream_factory_impl_job_controller.cc +++ b/net/http/http_stream_factory_impl_job_controller.cc
@@ -54,6 +54,7 @@ alternative_job_net_error_(OK), job_bound_(false), main_job_is_blocked_(false), + main_job_is_resumed_(false), bound_job_(nullptr), can_start_alternative_proxy_job_(false), privacy_mode_(PRIVACY_MODE_DISABLED), @@ -549,6 +550,10 @@ } void HttpStreamFactoryImpl::JobController::ResumeMainJob() { + if (main_job_is_resumed_) + return; + + main_job_is_resumed_ = true; main_job_->net_log().AddEvent( NetLogEventType::HTTP_STREAM_JOB_RESUMED, NetLog::Int64Callback("delay", main_job_wait_time_.InMilliseconds())); @@ -560,15 +565,24 @@ void HttpStreamFactoryImpl::JobController::MaybeResumeMainJob( Job* job, const base::TimeDelta& delay) { + DCHECK(delay == base::TimeDelta() || delay == main_job_wait_time_); DCHECK(job == main_job_.get() || job == alternative_job_.get()); - if (!main_job_is_blocked_ || job != alternative_job_.get() || !main_job_) + if (job != alternative_job_.get() || !main_job_) return; main_job_is_blocked_ = false; - if (!main_job_->is_waiting()) + if (!main_job_->is_waiting()) { + // There are two cases where the main job is not in WAIT state: + // 1) The main job hasn't got to waiting state, do not yet post a task to + // resume since that will happen in ShouldWait(). + // 2) The main job has passed waiting state, so the main job does not need + // to be resumed. return; + } + + main_job_wait_time_ = delay; ResumeMainJobLater(main_job_wait_time_); }
diff --git a/net/http/http_stream_factory_impl_job_controller.h b/net/http/http_stream_factory_impl_job_controller.h index 41d1341e..df246a58 100644 --- a/net/http/http_stream_factory_impl_job_controller.h +++ b/net/http/http_stream_factory_impl_job_controller.h
@@ -308,6 +308,10 @@ // True if the main job has to wait for the alternative job: i.e., the main // job must not create a connection until it is resumed. bool main_job_is_blocked_; + + // True if the main job was blocked and has been resumed in ResumeMainJob(). + bool main_job_is_resumed_; + // Waiting time for the main job before it is resumed. base::TimeDelta main_job_wait_time_;
diff --git a/net/http/http_stream_factory_impl_job_controller_unittest.cc b/net/http/http_stream_factory_impl_job_controller_unittest.cc index 577363bb..8327b2f9 100644 --- a/net/http/http_stream_factory_impl_job_controller_unittest.cc +++ b/net/http/http_stream_factory_impl_job_controller_unittest.cc
@@ -915,18 +915,87 @@ DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); EXPECT_TRUE(job_controller_->main_job()); EXPECT_TRUE(job_controller_->alternative_job()); + EXPECT_TRUE(job_controller_->main_job()->is_waiting()); // The alternative job stalls as host resolution hangs when creating the QUIC // request and controller should resume the main job after delay. - // Verify the waiting time for delayed main job. + base::RunLoop run_loop; EXPECT_CALL(*job_factory_.main_job(), Resume()) + .Times(1) + .WillOnce(testing::DoAll( + testing::Invoke(testing::CreateFunctor( + &JobControllerPeer::VerifyWaitingTimeForMainJob, job_controller_, + base::TimeDelta::FromMicroseconds(15))), + testing::Invoke([&run_loop]() { run_loop.Quit(); }))); + + // Wait for the main job to be resumed. + run_loop.Run(); + + EXPECT_TRUE(job_controller_->main_job()); + EXPECT_TRUE(job_controller_->alternative_job()); + + // |alternative_job| fails but should not report status to Request. + EXPECT_CALL(request_delegate_, OnStreamFailed(_, _)).Times(0); + + EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_)); + // OnStreamFailed will not resume the main job again since it's been resumed + // already. + EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0); + + job_controller_->OnStreamFailed(job_factory_.alternative_job(), + ERR_NETWORK_CHANGED, SSLConfig()); +} + +TEST_F(HttpStreamFactoryImplJobControllerTest, + ResumeMainJobImmediatelyOnStreamFailed) { + HangingResolver* resolver = new HangingResolver(); + session_deps_.host_resolver.reset(resolver); + + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("https://www.google.com"); + + Initialize(request_info, false, false); + + // Enable delayed TCP and set time delay for waiting job. + QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory(); + test::QuicStreamFactoryPeer::SetDelayTcpRace(quic_stream_factory, true); + quic_stream_factory->set_require_confirmation(false); + ServerNetworkStats stats1; + stats1.srtt = base::TimeDelta::FromMicroseconds(10); + session_->http_server_properties()->SetServerNetworkStats( + url::SchemeHostPort(GURL("https://www.google.com")), stats1); + + // Set a SPDY alternative service for the server. + url::SchemeHostPort server(request_info.url); + AlternativeService alternative_service(kProtoQUIC, server.host(), 443); + SetAlternativeService(request_info, alternative_service); + + request_.reset( + job_controller_->Start(request_info, &request_delegate_, nullptr, + NetLogWithSource(), HttpStreamRequest::HTTP_STREAM, + DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); + EXPECT_TRUE(job_controller_->main_job()); + EXPECT_TRUE(job_controller_->alternative_job()); + EXPECT_TRUE(job_controller_->main_job()->is_waiting()); + + // |alternative_job| fails but should not report status to Request. + EXPECT_CALL(request_delegate_, OnStreamFailed(_, _)).Times(0); + + // The alternative job stalls as host resolution hangs when creating the QUIC + // request and controller should resume the main job with delay. + // OnStreamFailed should resume the main job immediately. + EXPECT_CALL(*job_factory_.main_job(), Resume()) + .Times(1) .WillOnce(Invoke(testing::CreateFunctor( &JobControllerPeer::VerifyWaitingTimeForMainJob, job_controller_, - base::TimeDelta::FromMicroseconds(15)))); + base::TimeDelta::FromMicroseconds(0)))); + + job_controller_->OnStreamFailed(job_factory_.alternative_job(), + ERR_NETWORK_CHANGED, SSLConfig()); base::RunLoop().RunUntilIdle(); } - // Verifies that the alternative proxy server job is not created if the URL // scheme is HTTPS. TEST_F(HttpStreamFactoryImplJobControllerTest, HttpsURL) {
diff --git a/net/http/http_stream_factory_test_util.cc b/net/http/http_stream_factory_test_util.cc index 62d89c2..50d08307 100644 --- a/net/http/http_stream_factory_test_util.cc +++ b/net/http/http_stream_factory_test_util.cc
@@ -33,7 +33,9 @@ proxy_ssl_config, destination, origin_url, - net_log) {} + net_log) { + DCHECK(!is_waiting()); +} MockHttpStreamFactoryImplJob::MockHttpStreamFactoryImplJob( HttpStreamFactoryImpl::Job::Delegate* delegate,
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index bfd2442c..5151c89 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc
@@ -12,11 +12,9 @@ #include <unistd.h> #include "base/run_loop.h" -#include "net/base/sockaddr_storage.h" #include "net/quic/core/crypto/quic_random.h" #include "net/quic/core/quic_connection.h" #include "net/quic/core/quic_data_reader.h" -#include "net/quic/core/quic_flags.h" #include "net/quic/core/quic_packets.h" #include "net/quic/core/quic_server_id.h" #include "net/quic/core/spdy_utils.h"
diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h index 1329ae0..4c0e673 100644 --- a/net/tools/quic/quic_client.h +++ b/net/tools/quic/quic_client.h
@@ -14,7 +14,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/strings/string_piece.h" #include "net/quic/core/quic_client_push_promise_index.h" #include "net/quic/core/quic_config.h" #include "net/quic/core/quic_spdy_stream.h" @@ -96,7 +95,7 @@ } private: - friend class net::test::QuicClientPeer; + friend class test::QuicClientPeer; // Actually clean up |fd|. void CleanUpUDPSocketImpl(int fd);
diff --git a/net/tools/quic/quic_client_base.cc b/net/tools/quic/quic_client_base.cc index bbdadfd..a8d4397 100644 --- a/net/tools/quic/quic_client_base.cc +++ b/net/tools/quic/quic_client_base.cc
@@ -5,6 +5,7 @@ #include "net/tools/quic/quic_client_base.h" #include "net/quic/core/crypto/quic_random.h" +#include "net/quic/core/quic_flags.h" #include "net/quic/core/quic_server_id.h" #include "net/quic/core/spdy_utils.h" #include "net/quic/platform/api/quic_logging.h" @@ -72,6 +73,8 @@ QUIC_LOG(ERROR) << "Invalid response headers"; } latest_response_headers_ = response_headers.DebugString(); + preliminary_response_headers_ = + client_stream->preliminary_headers().DebugString(); latest_response_header_block_ = response_headers.Clone(); latest_response_body_ = client_stream->data(); latest_response_trailers_ = @@ -469,6 +472,11 @@ return latest_response_headers_; } +const string& QuicClientBase::preliminary_response_headers() const { + QUIC_BUG_IF(!store_response_) << "Response not stored!"; + return preliminary_response_headers_; +} + const SpdyHeaderBlock& QuicClientBase::latest_response_header_block() const { QUIC_BUG_IF(!store_response_) << "Response not stored!"; return latest_response_header_block_;
diff --git a/net/tools/quic/quic_client_base.h b/net/tools/quic/quic_client_base.h index 6647283..e65b5cd 100644 --- a/net/tools/quic/quic_client_base.h +++ b/net/tools/quic/quic_client_base.h
@@ -8,19 +8,12 @@ #ifndef NET_TOOLS_QUIC_QUIC_CLIENT_BASE_H_ #define NET_TOOLS_QUIC_QUIC_CLIENT_BASE_H_ -#include <memory> #include <string> #include "base/macros.h" #include "net/quic/core/crypto/crypto_handshake.h" -#include "net/quic/core/crypto/quic_crypto_client_config.h" -#include "net/quic/core/quic_alarm_factory.h" -#include "net/quic/core/quic_bandwidth.h" #include "net/quic/core/quic_client_push_promise_index.h" #include "net/quic/core/quic_config.h" -#include "net/quic/core/quic_connection.h" -#include "net/quic/core/quic_packet_writer.h" -#include "net/quic/core/quic_packets.h" #include "net/quic/platform/api/quic_socket_address.h" #include "net/tools/quic/quic_client_session.h" #include "net/tools/quic/quic_spdy_client_stream.h" @@ -45,17 +38,14 @@ // The client uses these objects to keep track of any data to resend upon // receipt of a stateless reject. Recall that the client API allows callers - // to optimistically send data to the server prior to - // handshake-confirmation. + // to optimistically send data to the server prior to handshake-confirmation. // If the client subsequently receives a stateless reject, it must tear down - // its existing session, create a new session, and resend all previously - // sent + // its existing session, create a new session, and resend all previously sent // data. It uses these objects to keep track of all the sent data, and to // resend the data upon a subsequent connection. class QuicDataToResend { public: - // |headers| may be null, since it's possible to send data without - // headers. + // |headers| may be null, since it's possible to send data without headers. QuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers, base::StringPiece body, bool fin); @@ -97,8 +87,7 @@ bool Connect(); // Start the crypto handshake. This can be done in place of the synchronous - // Connect(), but callers are responsible for making sure the crypto - // handshake + // Connect(), but callers are responsible for making sure the crypto handshake // completes. void StartConnect(); @@ -106,8 +95,7 @@ void Disconnect(); // Returns true if the crypto handshake has yet to establish encryption. - // Returns false if encryption is active (even if the server hasn't - // confirmed + // Returns false if encryption is active (even if the server hasn't confirmed // the handshake) or if the connection has been closed. bool EncryptionBeingEstablished(); @@ -166,12 +154,10 @@ } // UseTokenBinding enables token binding negotiation in the client. This - // should only be called before the initial Connect(). The client will - // still + // should only be called before the initial Connect(). The client will still // need to check that token binding is negotiated with the server, and add // token binding headers to requests if so. server, and add token binding - // headers to requests if so. The negotiated token binding parameters can - // be + // headers to requests if so. The negotiated token binding parameters can be // found on the QuicCryptoNegotiatedParameters object in // token_binding_key_param. void UseTokenBinding() { @@ -190,8 +176,7 @@ QuicCryptoClientConfig* crypto_config() { return &crypto_config_; } - // Change the initial maximum packet size of the connection. Has to be - // called + // Change the initial maximum packet size of the connection. Has to be called // before Connect()/StartConnect() in order to have any effect. void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) { initial_max_packet_length_ = initial_max_packet_length; @@ -209,8 +194,7 @@ // instead. int GetNumSentClientHellos(); - // Gather the stats for the last session and update the stats for the - // overall + // Gather the stats for the last session and update the stats for the overall // connection. void UpdateStats(); @@ -271,6 +255,7 @@ size_t latest_response_code() const; const std::string& latest_response_headers() const; + const std::string& preliminary_response_headers() const; const SpdyHeaderBlock& latest_response_header_block() const; const std::string& latest_response_body() const; const std::string& latest_response_trailers() const; @@ -306,8 +291,7 @@ // Runs one iteration of the event loop. virtual void RunEventLoop() = 0; - // Used during initialization: creates the UDP socket FD, sets socket - // options, + // Used during initialization: creates the UDP socket FD, sets socket options, // and binds the socket to our address. virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address, QuicIpAddress bind_to_address, @@ -325,8 +309,7 @@ // returned. Otherwise, the next random ID will be returned. QuicConnectionId GetNextConnectionId(); - // Returns the next server-designated ConnectionId from the cached config - // for + // Returns the next server-designated ConnectionId from the cached config for // |server_id_|, if it exists. Otherwise, returns 0. QuicConnectionId GetNextServerDesignatedConnectionId(); @@ -410,8 +393,7 @@ // Alarm factory to be used by created connections. Must outlive |session_|. std::unique_ptr<QuicAlarmFactory> alarm_factory_; - // Writer used to actually send packets to the wire. Must outlive - // |session_|. + // Writer used to actually send packets to the wire. Must outlive |session_|. std::unique_ptr<QuicPacketWriter> writer_; // Index of pending promised streams. Must outlive |session_|. @@ -421,8 +403,7 @@ std::unique_ptr<QuicClientSession> session_; // This vector contains QUIC versions which we currently support. - // This should be ordered such that the highest supported version is the - // first + // This should be ordered such that the highest supported version is the first // element, with subsequent elements in descending order (versions can be // skipped as necessary). We will always pick supported_versions_[0] as the // initial version to use. @@ -434,10 +415,8 @@ // The number of stateless rejects received during the current/latest // connection. - // TODO(jokulik): Consider some consistent naming scheme (or other) for - // member - // variables that are kept per-request, per-connection, and over the - // client's + // TODO(jokulik): Consider some consistent naming scheme (or other) for member + // variables that are kept per-request, per-connection, and over the client's // lifetime. int num_stateless_rejects_received_; @@ -448,8 +427,7 @@ // opposed to that associated with the last session object). QuicErrorCode connection_error_; - // True when the client is attempting to connect or re-connect the session - // (in + // True when the client is attempting to connect or re-connect the session (in // the case of a stateless reject). Set to false between a call to // Disconnect() and the subsequent call to StartConnect(). When // connected_or_attempting_connect_ is false, the session object corresponds @@ -462,6 +440,8 @@ int latest_response_code_; // HTTP/2 headers from most recent response. std::string latest_response_headers_; + // preliminary 100 Continue HTTP/2 headers from most recent response, if any. + std::string preliminary_response_headers_; // HTTP/2 headers from most recent response. SpdyHeaderBlock latest_response_header_block_; // Body of most recent response.
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc index 7b899af..07d13e1 100644 --- a/net/tools/quic/quic_client_bin.cc +++ b/net/tools/quic/quic_client_bin.cc
@@ -107,28 +107,28 @@ class FakeProofVerifier : public ProofVerifier { public: net::QuicAsyncStatus VerifyProof( - const string& hostname, - const uint16_t port, - const string& server_config, - net::QuicVersion quic_version, - StringPiece chlo_hash, - const std::vector<string>& certs, - const string& cert_sct, - const string& signature, - const net::ProofVerifyContext* context, - string* error_details, - std::unique_ptr<net::ProofVerifyDetails>* details, - std::unique_ptr<net::ProofVerifierCallback> callback) override { + const string& /*hostname*/, + const uint16_t /*port*/, + const string& /*server_config*/, + net::QuicVersion /*quic_version*/, + StringPiece /*chlo_hash*/, + const std::vector<string>& /*certs*/, + const string& /*cert_sct*/, + const string& /*signature*/, + const net::ProofVerifyContext* /*context*/, + string* /*error_details*/, + std::unique_ptr<net::ProofVerifyDetails>* /*details*/, + std::unique_ptr<net::ProofVerifierCallback> /*callback*/) override { return net::QUIC_SUCCESS; } net::QuicAsyncStatus VerifyCertChain( - const std::string& hostname, - const std::vector<std::string>& certs, - const net::ProofVerifyContext* verify_context, - std::string* error_details, - std::unique_ptr<net::ProofVerifyDetails>* verify_details, - std::unique_ptr<net::ProofVerifierCallback> callback) override { + const std::string& /*hostname*/, + const std::vector<std::string>& /*certs*/, + const net::ProofVerifyContext* /*verify_context*/, + std::string* /*error_details*/, + std::unique_ptr<net::ProofVerifyDetails>* /*verify_details*/, + std::unique_ptr<net::ProofVerifierCallback> /*callback*/) override { return net::QUIC_SUCCESS; } }; @@ -339,6 +339,13 @@ cout << "body: " << body << endl; } cout << endl; + + if (!client.preliminary_response_headers().empty()) { + cout << "Preliminary response headers: " + << client.preliminary_response_headers() << endl; + cout << endl; + } + cout << "Response:" << endl; cout << "headers: " << client.latest_response_headers() << endl; string response_body = client.latest_response_body();
diff --git a/net/tools/quic/quic_client_test.cc b/net/tools/quic/quic_client_test.cc index 313c23f..8f3ff967 100644 --- a/net/tools/quic/quic_client_test.cc +++ b/net/tools/quic/quic_client_test.cc
@@ -60,7 +60,7 @@ return client; } -TEST(QuicClientTest, DoNotLeakFDs) { +TEST(QuicClientTest, DoNotLeakSocketFDs) { // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause // port exhaustion in long running processes which repeatedly create clients. @@ -68,9 +68,6 @@ // around some ASAN weirdness. crypto_test_utils::ProofVerifierForTesting().reset(); - // Make sure that the QuicClient doesn't leak FDs. Doing so could cause port - // exhaustion in long running processes which repeatedly create clients. - // Record initial number of FDs, after creation of EpollServer. EpollServer eps; size_t number_of_open_fds = NumOpenSocketFDs();
diff --git a/net/tools/quic/quic_http_response_cache.cc b/net/tools/quic/quic_http_response_cache.cc index b65c8e5..68c57de 100644 --- a/net/tools/quic/quic_http_response_cache.cc +++ b/net/tools/quic/quic_http_response_cache.cc
@@ -23,11 +23,10 @@ namespace net { -QuicHttpResponseCache::ServerPushInfo::ServerPushInfo( - QuicUrl request_url, - SpdyHeaderBlock headers, - net::SpdyPriority priority, - string body) +QuicHttpResponseCache::ServerPushInfo::ServerPushInfo(QuicUrl request_url, + SpdyHeaderBlock headers, + SpdyPriority priority, + string body) : request_url(request_url), headers(std::move(headers)), priority(priority), @@ -45,6 +44,12 @@ QuicHttpResponseCache::Response::~Response() {} +QuicHttpResponseCache::ResourceFile::ResourceFile( + const base::FilePath& file_name) + : file_name_(file_name), file_name_string_(file_name.AsUTF8Unsafe()) {} + +QuicHttpResponseCache::ResourceFile::~ResourceFile() {} + void QuicHttpResponseCache::ResourceFile::Read() { base::ReadFileToString(FilePath(file_name_), &file_contents_); @@ -123,12 +128,6 @@ StringPiece(file_contents_.data() + start, file_contents_.size() - start); } -QuicHttpResponseCache::ResourceFile::ResourceFile( - const base::FilePath& file_name) - : file_name_(file_name), file_name_string_(file_name.AsUTF8Unsafe()) {} - -QuicHttpResponseCache::ResourceFile::~ResourceFile() {} - void QuicHttpResponseCache::ResourceFile::SetHostPathFromBase( StringPiece base) { size_t path_start = base.find_first_of('/'); @@ -277,7 +276,7 @@ return; } push_resources.push_back(ServerPushInfo(url, response->headers().Clone(), - net::kV3LowestPriority, + kV3LowestPriority, response->body().as_string())); } MaybeAddServerPushResources(resource_file->host(), resource_file->path(), @@ -320,7 +319,7 @@ QUIC_BUG << "Response for '" << key << "' already exists!"; return; } - std::unique_ptr<Response> new_response = QuicMakeUnique<Response>(); + auto new_response = QuicMakeUnique<Response>(); new_response->set_response_type(response_type); new_response->set_headers(std::move(response_headers)); new_response->set_body(response_body);
diff --git a/net/tools/quic/quic_http_response_cache.h b/net/tools/quic/quic_http_response_cache.h index 13efd31d..695987c 100644 --- a/net/tools/quic/quic_http_response_cache.h +++ b/net/tools/quic/quic_http_response_cache.h
@@ -15,7 +15,6 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/strings/string_piece.h" -#include "net/http/http_response_headers.h" #include "net/quic/core/spdy_utils.h" #include "net/quic/platform/api/quic_mutex.h" #include "net/quic/platform/api/quic_url.h" @@ -88,8 +87,11 @@ void Read(); + // |base| is |file_name_| with |cache_directory| prefix stripped. void SetHostPathFromBase(base::StringPiece base); + const std::string& file_name() { return file_name_string_; } + base::StringPiece host() { return host_; } void set_host(base::StringPiece host) { host_ = host; } @@ -102,8 +104,6 @@ const std::vector<base::StringPiece>& push_urls() { return push_urls_; } - const std::string& file_name() { return file_name_string_; } - protected: void HandleXOriginalUrl(); void HandlePushUrls(const std::vector<base::StringPiece>& push_urls);
diff --git a/net/tools/quic/quic_http_response_cache_test.cc b/net/tools/quic/quic_http_response_cache_test.cc index 037c384..fbd8175f 100644 --- a/net/tools/quic/quic_http_response_cache_test.cc +++ b/net/tools/quic/quic_http_response_cache_test.cc
@@ -2,20 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <string> +#include "net/tools/quic/quic_http_response_cache.h" #include "base/files/file_path.h" -#include "base/memory/singleton.h" #include "base/path_service.h" #include "net/quic/platform/api/quic_map_util.h" #include "net/quic/platform/api/quic_str_cat.h" #include "net/quic/platform/api/quic_text_utils.h" -#include "net/spdy/spdy_framer.h" -#include "net/tools/quic/quic_http_response_cache.h" #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; -using net::SpdyHeaderBlock; using std::string; namespace net { @@ -190,6 +186,7 @@ cache_.AddSimpleResponseWithServerPushResources( request_host, "/", 200, response_body, push_resources); + string request_url = request_host + "/"; std::list<ServerPushInfo> resources = cache_.GetServerPushResources(request_url);
diff --git a/net/tools/quic/quic_reject_reason_decoder_bin.cc b/net/tools/quic/quic_reject_reason_decoder_bin.cc index aba0199..d695f9d 100644 --- a/net/tools/quic/quic_reject_reason_decoder_bin.cc +++ b/net/tools/quic/quic_reject_reason_decoder_bin.cc
@@ -13,7 +13,6 @@ #include "net/quic/core/crypto/crypto_utils.h" using base::CommandLine; -using base::StringToUint; using net::HandshakeFailureReason; using net::CryptoUtils; using net::MAX_FAILURE_REASON; @@ -31,7 +30,7 @@ } uint32_t packed_error = 0; - if (!StringToUint(args[0], &packed_error)) { + if (!base::StringToUint(args[0], &packed_error)) { cerr << "Unable to parse: " << args[0] << "\n"; return 2; }
diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc index 66543552..91b11a318 100644 --- a/net/tools/quic/quic_server.cc +++ b/net/tools/quic/quic_server.cc
@@ -13,8 +13,6 @@ #include <memory> -#include "net/base/ip_endpoint.h" -#include "net/base/sockaddr_storage.h" #include "net/quic/core/crypto/crypto_handshake.h" #include "net/quic/core/crypto/quic_random.h" #include "net/quic/core/quic_crypto_stream.h" @@ -34,7 +32,9 @@ #ifndef SO_RXQ_OVFL #define SO_RXQ_OVFL 40 #endif + namespace net { + namespace { // Specifies the directory used during QuicHttpResponseCache @@ -112,7 +112,7 @@ bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) { fd_ = QuicSocketUtils::CreateUDPSocket(address, &overflow_supported_); if (fd_ < 0) { - LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); + QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); return false; } @@ -177,7 +177,7 @@ event->out_ready_mask = 0; if (event->in_events & EPOLLIN) { - DVLOG(1) << "EPOLLIN"; + QUIC_DVLOG(1) << "EPOLLIN"; if (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop) { dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h index 47859b9..2a3aca040 100644 --- a/net/tools/quic/quic_server.h +++ b/net/tools/quic/quic_server.h
@@ -11,8 +11,6 @@ #ifndef NET_TOOLS_QUIC_QUIC_SERVER_H_ #define NET_TOOLS_QUIC_QUIC_SERVER_H_ -#include <stddef.h> - #include <memory> #include "base/macros.h"
diff --git a/net/tools/quic/quic_server_test.cc b/net/tools/quic/quic_server_test.cc index dc2a94c9..d880dcc 100644 --- a/net/tools/quic/quic_server_test.cc +++ b/net/tools/quic/quic_server_test.cc
@@ -130,9 +130,8 @@ char buf[1024]; memset(buf, 0, arraysize(buf)); sockaddr_storage storage = server_address_.generic_address(); - socklen_t storage_size = sizeof(storage); int rc = sendto(fd, buf, arraysize(buf), 0, - reinterpret_cast<sockaddr*>(&storage), storage_size); + reinterpret_cast<sockaddr*>(&storage), sizeof(storage)); if (rc < 0) { QUIC_DLOG(INFO) << errno << " " << strerror(errno); }
diff --git a/net/tools/quic/quic_simple_crypto_server_stream_helper.cc b/net/tools/quic/quic_simple_crypto_server_stream_helper.cc index a25b872..6f310eb 100644 --- a/net/tools/quic/quic_simple_crypto_server_stream_helper.cc +++ b/net/tools/quic/quic_simple_crypto_server_stream_helper.cc
@@ -13,8 +13,8 @@ QuicSimpleCryptoServerStreamHelper::~QuicSimpleCryptoServerStreamHelper() {} QuicConnectionId - QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject( - QuicConnectionId /*connection_id*/) const { +QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject( + QuicConnectionId /*connection_id*/) const { return random_->RandUint64(); }
diff --git a/net/tools/quic/quic_simple_server_session.cc b/net/tools/quic/quic_simple_server_session.cc index f138d93e..5f60d7427 100644 --- a/net/tools/quic/quic_simple_server_session.cc +++ b/net/tools/quic/quic_simple_server_session.cc
@@ -9,7 +9,6 @@ #include "net/quic/core/proto/cached_network_parameters.pb.h" #include "net/quic/core/quic_connection.h" #include "net/quic/core/quic_flags.h" -#include "net/quic/core/quic_spdy_session.h" #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_ptr_util.h" #include "net/tools/quic/quic_simple_server_stream.h" @@ -154,12 +153,12 @@ QuicHttpResponseCache::ServerPushInfo resource, const SpdyHeaderBlock& original_request_headers) { QuicUrl push_request_url = resource.request_url; - string path = push_request_url.path(); SpdyHeaderBlock spdy_headers = original_request_headers.Clone(); // :authority could be different from original request. spdy_headers[":authority"] = push_request_url.host(); - spdy_headers[":path"] = path; + spdy_headers[":path"] = push_request_url.path(); + ; // Push request always use GET. spdy_headers[":method"] = "GET"; spdy_headers["referer"] = request_url; @@ -197,7 +196,7 @@ QuicSimpleServerStream* promised_stream = static_cast<QuicSimpleServerStream*>( CreateOutgoingDynamicStream(promised_info.priority)); - DCHECK(promised_stream != nullptr); + DCHECK_NE(promised_stream, nullptr); DCHECK_EQ(promised_info.stream_id, promised_stream->id()); QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id();
diff --git a/net/tools/quic/quic_simple_server_session.h b/net/tools/quic/quic_simple_server_session.h index b8efb35..f9ff7da 100644 --- a/net/tools/quic/quic_simple_server_session.h +++ b/net/tools/quic/quic_simple_server_session.h
@@ -27,10 +27,6 @@ namespace net { -class QuicConfig; -class QuicConnection; -class QuicCryptoServerConfig; - namespace test { class QuicSimpleServerSessionPeer; } // namespace test
diff --git a/net/tools/quic/quic_simple_server_session_test.cc b/net/tools/quic/quic_simple_server_session_test.cc index 1af7ffe0..5d1cac2 100644 --- a/net/tools/quic/quic_simple_server_session_test.cc +++ b/net/tools/quic/quic_simple_server_session_test.cc
@@ -8,7 +8,6 @@ #include <memory> #include "base/macros.h" -#include "base/strings/string_number_conversions.h" #include "net/quic/core/crypto/quic_crypto_server_config.h" #include "net/quic/core/crypto/quic_random.h" #include "net/quic/core/proto/cached_network_parameters.pb.h" @@ -34,7 +33,6 @@ #include "testing/gtest/include/gtest/gtest.h" using std::string; -using testing::StrictMock; using testing::_; using testing::AtLeast; using testing::InSequence;
diff --git a/net/tools/quic/quic_simple_server_stream.cc b/net/tools/quic/quic_simple_server_stream.cc index e362950..ed960b2d 100644 --- a/net/tools/quic/quic_simple_server_stream.cc +++ b/net/tools/quic/quic_simple_server_stream.cc
@@ -152,8 +152,10 @@ return; } - // Examing response status, if it was not pure integer as typical h2 response - // status, send error response. + // Examing response status, if it was not pure integer as typical h2 + // response status, send error response. Notice that + // QuicHttpResponseCache push urls are strictly authority + path only, + // scheme is not included (see |QuicHttpResponseCache::GetKey()|). string request_url = request_headers_[":authority"].as_string() + request_headers_[":path"].as_string(); int response_code;
diff --git a/net/tools/quic/quic_simple_server_stream.h b/net/tools/quic/quic_simple_server_stream.h index 2aa7b759..1471aada 100644 --- a/net/tools/quic/quic_simple_server_stream.h +++ b/net/tools/quic/quic_simple_server_stream.h
@@ -5,8 +5,6 @@ #ifndef NET_TOOLS_QUIC_QUIC_SIMPLE_SERVER_STREAM_H_ #define NET_TOOLS_QUIC_QUIC_SIMPLE_SERVER_STREAM_H_ -#include <stddef.h> - #include <string> #include "base/macros.h" @@ -18,7 +16,6 @@ namespace net { - namespace test { class QuicSimpleServerStreamPeer; } // namespace test
diff --git a/net/tools/quic/quic_simple_server_stream_test.cc b/net/tools/quic/quic_simple_server_stream_test.cc index 67a01790..3e083ff 100644 --- a/net/tools/quic/quic_simple_server_stream_test.cc +++ b/net/tools/quic/quic_simple_server_stream_test.cc
@@ -8,11 +8,7 @@ #include <memory> #include <utility> -#include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" -#include "net/quic/core/quic_connection.h" -#include "net/quic/core/quic_flags.h" -#include "net/quic/core/quic_packets.h" #include "net/quic/core/quic_utils.h" #include "net/quic/core/spdy_utils.h" #include "net/quic/platform/api/quic_ptr_util.h" @@ -21,7 +17,6 @@ #include "net/quic/test_tools/quic_stream_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/test/gtest_util.h" -#include "net/tools/epoll_server/epoll_server.h" #include "net/tools/quic/quic_http_response_cache.h" #include "net/tools/quic/quic_simple_server_session.h" #include "testing/gmock/include/gmock/gmock.h" @@ -32,11 +27,9 @@ using testing::_; using testing::AnyNumber; using testing::Invoke; -using testing::InvokeArgument; using testing::InSequence; using testing::Return; using testing::StrictMock; -using testing::WithArgs; namespace net { namespace test { @@ -209,8 +202,8 @@ kInitialStreamFlowControlWindowForTest); session_.config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); - stream_ = new QuicSimpleServerStreamPeer(::net::test::kClientDataStreamId1, - &session_, &response_cache_); + stream_ = new QuicSimpleServerStreamPeer(kClientDataStreamId1, &session_, + &response_cache_); // Register stream_ in dynamic_stream_map_ and pass ownership to session_. session_.ActivateStream(QuicWrapUnique(stream_)); } @@ -447,9 +440,8 @@ stream_->set_fin_received(true); InSequence s; - EXPECT_CALL(session_, - PromisePushResourcesMock(host + request_path, _, - ::net::test::kClientDataStreamId1, _)); + EXPECT_CALL(session_, PromisePushResourcesMock(host + request_path, _, + kClientDataStreamId1, _)); EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, _)); EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)) .Times(1) @@ -613,27 +605,27 @@ TEST_P(QuicSimpleServerStreamTest, InvalidHeadersWithFin) { char arr[] = { - 0x3a, 0x68, 0x6f, 0x73, // :hos - 0x74, 0x00, 0x00, 0x00, // t... - 0x00, 0x00, 0x00, 0x00, // .... - 0x07, 0x3a, 0x6d, 0x65, // .:me - 0x74, 0x68, 0x6f, 0x64, // thod - 0x00, 0x00, 0x00, 0x03, // .... - 0x47, 0x45, 0x54, 0x00, // GET. - 0x00, 0x00, 0x05, 0x3a, // ...: - 0x70, 0x61, 0x74, 0x68, // path - 0x00, 0x00, 0x00, 0x04, // .... - 0x2f, 0x66, 0x6f, 0x6f, // /foo - 0x00, 0x00, 0x00, 0x07, // .... - 0x3a, 0x73, 0x63, 0x68, // :sch - 0x65, 0x6d, 0x65, 0x00, // eme. - 0x00, 0x00, 0x00, 0x00, // .... - 0x00, 0x00, 0x08, 0x3a, // ...: - 0x76, 0x65, 0x72, 0x73, // vers - '\x96', 0x6f, 0x6e, 0x00, // <i(69)>on. - 0x00, 0x00, 0x08, 0x48, // ...H - 0x54, 0x54, 0x50, 0x2f, // TTP/ - 0x31, 0x2e, 0x31, // 1.1 + 0x3a, 0x68, 0x6f, 0x73, // :hos + 0x74, 0x00, 0x00, 0x00, // t... + 0x00, 0x00, 0x00, 0x00, // .... + 0x07, 0x3a, 0x6d, 0x65, // .:me + 0x74, 0x68, 0x6f, 0x64, // thod + 0x00, 0x00, 0x00, 0x03, // .... + 0x47, 0x45, 0x54, 0x00, // GET. + 0x00, 0x00, 0x05, 0x3a, // ...: + 0x70, 0x61, 0x74, 0x68, // path + 0x00, 0x00, 0x00, 0x04, // .... + 0x2f, 0x66, 0x6f, 0x6f, // /foo + 0x00, 0x00, 0x00, 0x07, // .... + 0x3a, 0x73, 0x63, 0x68, // :sch + 0x65, 0x6d, 0x65, 0x00, // eme. + 0x00, 0x00, 0x00, 0x00, // .... + 0x00, 0x00, 0x08, 0x3a, // ...: + 0x76, 0x65, 0x72, 0x73, // vers + 0x96, 0x6f, 0x6e, 0x00, // <i(69)>on. + 0x00, 0x00, 0x08, 0x48, // ...H + 0x54, 0x54, 0x50, 0x2f, // TTP/ + 0x31, 0x2e, 0x31, // 1.1 }; StringPiece data(arr, arraysize(arr)); QuicStreamFrame frame(stream_->id(), true, 0, data);
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json index 4796d08..b8fdecf 100644 --- a/testing/buildbot/chromium.gpu.fyi.json +++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -10868,7 +10868,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": false, @@ -11334,7 +11335,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -11813,7 +11815,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": false, @@ -12318,7 +12321,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": false, @@ -12826,7 +12830,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -13318,7 +13323,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -13761,7 +13767,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -14223,7 +14230,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": false, @@ -14746,7 +14754,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -15228,7 +15237,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": false, @@ -15710,7 +15720,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": false, @@ -16310,7 +16321,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -16818,7 +16830,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -17335,7 +17348,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true,
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json index 6c491b58..1f74d1d7 100644 --- a/testing/buildbot/chromium.gpu.json +++ b/testing/buildbot/chromium.gpu.json
@@ -1825,7 +1825,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true, @@ -2139,7 +2140,8 @@ }, { "args": [ - "--use-test-data-path" + "--use-test-data-path", + "--test_video_data=test-25fps.h264:320:240:250:258:::1" ], "swarming": { "can_use_on_swarming_builders": true,
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index 45a433b..b15cd66 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -333,15 +333,6 @@ libfuzzer_options = [ "max_len=500" ] } -fuzzer_test("third_party_re2_fuzzer") { - sources = [ - "re2_fuzzer.cc", - ] - deps = [ - "//third_party/re2:re2", - ] -} - fuzzer_test("libxml_xml_regexp_compile_fuzzer") { sources = [ "libxml_xml_regexp_compile_fuzzer.cc",
diff --git a/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc b/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc index 464a6e95..8c08d101 100644 --- a/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc +++ b/testing/libfuzzer/fuzzers/libxml_xml_read_memory_fuzzer.cc
@@ -2,10 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stddef.h> -#include <stdint.h> +#include <cassert> +#include <cstddef> +#include <cstdint> + +#include <functional> +#include <limits> +#include <string> #include "libxml/parser.h" +#include "libxml/xmlsave.h" void ignore (void* ctx, const char* msg, ...) { // Error handler to avoid spam of error messages from libxml parser. @@ -14,9 +20,25 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { xmlSetGenericErrorFunc(NULL, &ignore); - if (auto doc = xmlReadMemory(reinterpret_cast<const char*>(data), - static_cast<int>(size), "noname.xml", NULL, 0)) { - xmlFreeDoc(doc); + // Test default empty options value and one random combination of flags. + const std::string data_string(reinterpret_cast<const char*>(data), size); + const std::size_t data_hash = std::hash<std::string>()(data_string); + const int max_option_value = std::numeric_limits<int>::max(); + const int random_option = data_hash & max_option_value; + const int options[] = {0, random_option}; + + for (const auto option_value : options) { + if (auto doc = xmlReadMemory(data_string.c_str(), data_string.length(), + "noname.xml", NULL, option_value)) { + auto buffer = xmlBufferCreate(); + assert(buffer); + + auto context = xmlSaveToBuffer(buffer, NULL, 0); + xmlSaveDoc(context, doc); + xmlSaveClose(context); + xmlFreeDoc(doc); + xmlBufferFree(buffer); + } } return 0;
diff --git a/testing/libfuzzer/fuzzers/re2_fuzzer.cc b/testing/libfuzzer/fuzzers/re2_fuzzer.cc deleted file mode 100644 index 023a9ab8..0000000 --- a/testing/libfuzzer/fuzzers/re2_fuzzer.cc +++ /dev/null
@@ -1,105 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Copyright 2016 The RE2 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 <stddef.h> -#include <stdint.h> - -#include <map> -#include <string> - -#include "re2/re2.h" - -using re2::StringPiece; -using std::string; - -// NOT static, NOT signed. -uint8_t dummy = 0; - -void Test(StringPiece pattern, const RE2::Options& options, StringPiece text) { - RE2 re(pattern, options); - if (!re.ok()) - return; - - // Don't waste time fuzzing high-fanout programs. - // (They can also cause bug reports due to fuzzer timeouts.) - std::map<int, int> histogram; - int fanout = re.ProgramFanout(&histogram); - if (fanout > 9) - return; - - StringPiece sp1, sp2, sp3, sp4; - string s1, s2, s3, s4; - int i1, i2, i3, i4; - double d1, d2, d3, d4; - - RE2::FullMatch(text, re, &sp1, &sp2, &sp3, &sp4); - RE2::PartialMatch(text, re, &s1, &s2, &s3, &s4); - - sp1 = sp2 = text; - RE2::Consume(&sp1, re, &i1, &i2, &i3, &i4); - RE2::FindAndConsume(&sp2, re, &d1, &d2, &d3, &d4); - - s3 = s4 = text.ToString(); - RE2::Replace(&s3, re, ""); - RE2::GlobalReplace(&s4, re, ""); - - // Exercise some other API functionality. - dummy += re.NumberOfCapturingGroups(); - dummy += RE2::QuoteMeta(pattern).size(); -} - -// Entry point for libFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size == 0 || size > 1024) - return 0; - - // The one-at-a-time hash by Bob Jenkins. - uint32_t hash = 0; - for (size_t i = 0; i < size; i++) { - hash += data[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - - RE2::Options options; - options.set_log_errors(false); - options.set_encoding(hash & 1 ? RE2::Options::EncodingLatin1 - : RE2::Options::EncodingUTF8); - options.set_posix_syntax(hash & 2); - options.set_longest_match(hash & 4); - options.set_literal(hash & 8); - options.set_never_nl(hash & 16); - options.set_dot_nl(hash & 32); - options.set_never_capture(hash & 64); - options.set_case_sensitive(hash & 128); - options.set_perl_classes(hash & 256); - options.set_word_boundary(hash & 512); - options.set_one_line(hash & 1024); - - const char* ptr = reinterpret_cast<const char*>(data); - int len = static_cast<int>(size); - - StringPiece pattern(ptr, len); - StringPiece text(ptr, len); - Test(pattern, options, text); - - for (int i = 2; i <= 4; i++) { - if (len < i) - break; - - int frac = len / i; - pattern = StringPiece(ptr, frac); - text = StringPiece(ptr + frac, len - frac); - Test(pattern, options, text); - } - - return 0; -}
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations index c76eadc..1f8037fb 100644 --- a/third_party/WebKit/LayoutTests/W3CImportExpectations +++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -222,7 +222,8 @@ external/wpt/config.default.json [ Skip ] external/wpt/conformance-checkers [ Skip ] external/wpt/console [ Skip ] -external/wpt/content-security-policy [ Skip ] +## Owners: mkwst@chromium.org +# external/wpt/content-security-policy [ Pass ] external/wpt/cookies [ Skip ] external/wpt/cors [ Skip ] external/wpt/csp [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list-expected.txt b/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list-expected.txt deleted file mode 100644 index e848cb8c..0000000 --- a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list-expected.txt +++ /dev/null
@@ -1,164 +0,0 @@ - -Indenting: -| " -" -| <pre> -| <ul> -| <li> -| "<#selection-anchor>hello<#selection-focus>" -| " -" - -yields: -| " -" -| <pre> -| <ul> -| <ul> -| <li> -| "<#selection-anchor>hello<#selection-focus>" -| " -" - -Indenting: -| " -" -| <pre> -| <ul> -| <li> -| "<#selection-anchor>hello -world" -| "<#selection-focus> -" -| " -" - -yields: -| " -" -| <pre> -| <ul> -| <ul> -| <li> -| "<#selection-anchor>hello" -| " -" -| "world<#selection-focus>" -| " -" -| " -" - -Indenting: -| " -" -| <ul> -| <li> -| <pre> -| "<#selection-anchor>hello -world -webkit<#selection-focus> -" -| " -" - -yields: -| " -" -| <ul> -| <li> -| <pre> -| <blockquote> -| style="margin: 0 0 0 40px; border: none; padding: 0px;" -| "hello" -| <br> -| "world" -| <br> -| "webkit" -| " -" - -Indenting: -| " -" -| <ul> -| <li> -| <pre> -| "<#selection-anchor>hello<#selection-focus> -world -webkit -" -| " -" - -yields: -| " -" -| <ul> -| <li> -| <pre> -| <blockquote> -| style="margin: 0 0 0 40px; border: none; padding: 0px;" -| "<#selection-anchor>hello<#selection-focus>" -| "world -webkit -" -| " -" - -Indenting: -| " -" -| <ul> -| <li> -| <pre> -| "hello -<#selection-anchor>world<#selection-focus> -webkit -" -| " -" - -yields: -| " -" -| <ul> -| <li> -| <pre> -| "hello -" -| <blockquote> -| style="margin: 0 0 0 40px; border: none; padding: 0px;" -| "<#selection-anchor>world<#selection-focus>" -| "webkit -" -| " -" - -Indenting: -| " -" -| <ul> -| <li> -| <pre> -| "hello -world -<#selection-anchor>webki<#selection-focus>t -" -| " -" - -yields: -| " -" -| <ul> -| <li> -| <pre> -| "hello -world -" -| <blockquote> -| style="margin: 0 0 0 40px; border: none; padding: 0px;" -| "<#selection-anchor>webki<#selection-focus>t" -| " -"
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list.html b/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list.html index 1026c82..51be63c 100644 --- a/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list.html +++ b/third_party/WebKit/LayoutTests/editing/execCommand/indent-pre-list.html
@@ -1,82 +1,98 @@ -<!DOCTYPE html> -<html> -<body> -<script src="../../resources/dump-as-markup.js"></script> -<div id="test0" contenteditable> -<pre> -<ul><li>hello</li></ul></pre> -</div> - -<div id="test1" contenteditable> -<pre><ul><li>hello -world</li> -</ul></pre> -</div> - -<div id="test2" contenteditable> -<ul><li><pre> -hello -world -webkit -</pre></li></ul> -</div> - -<div id="test3" contenteditable> -<ul><li><pre> -hello -world -webkit -</pre></li></ul> -</div> - -<div id="test4" contenteditable> -<ul><li><pre> -hello -world -webkit -</pre></li></ul> -</div> - -<div id="test5" contenteditable> -<ul><li><pre> -hello -world -webkit -</pre></li></ul> -</div> - +<!doctype html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../assert_selection.js"></script> <script> +test(() => assert_selection( + [ + '<div contenteditable>', + '^<pre>\n<ul><li>hello</li></ul></pre>|', + '</div>', + ].join(''), + 'indent', + [ + '<div contenteditable>', + '<pre><ul><ul><li>^hello|</li></ul></ul></pre>', + '</div>', + ].join('')), '0 select all children'); -function testIndentation(containerId, selector) { - var container = document.getElementById(containerId); - selector(container); - Markup.dump(container, 'Indenting'); - document.execCommand('indent', false, null); - Markup.dump(container, 'yields'); -} +test(() => assert_selection( + [ + '<div contenteditable>', + '^<pre><ul><li>hello\nworld</li></ul></pre>|', + '</div>', + ].join(''), + 'indent', + [ + '<div contenteditable>', + '<pre><ul><ul><li>^hello\nworld|</li></ul></ul></pre>', + '</div>', + ].join('')), '1 select all children with newline'); -function selectAll(container) { - window.getSelection().selectAllChildren(container); -} +test(() => assert_selection( + [ + '<div contenteditable>', + '^<ul><li><pre>\nhello\nworld\nblink\n</pre></li></ul>|', + '</div>', + ].join(''), + 'indent', + [ + '<div contenteditable>', + '<ul><li><pre>', + '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">', + '^hello<br>world<br>blink|', + '</blockquote>', + '</pre></li></ul>', + '</div>', + ].join('')), '2 select all children with newlines'); -function selectorForLineN(line) { - return function (container) { - window.getSelection().collapse(container, 0); - for (var i = 0; i < line - 1; i++) - window.getSelection().modify('move', 'forward', 'line'); - window.getSelection().modify('extend', 'forward', 'line'); - // Avoid including \n at the end of line. - window.getSelection().modify('extend', 'backward', 'character'); - } -} +test(() => assert_selection( + [ + '<div contenteditable>', + '<ul><li><pre>\n^hello|\nworld\nblink\n</pre></li></ul>', + '</div>', + ].join(''), + 'indent', + [ + '<div contenteditable>', + '<ul><li><pre>', + '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">', + '^hello|', + '</blockquote>world\nblink\n', + '</pre></li></ul>', + '</div>', + ].join('')), '3 select line 1'); -testIndentation('test0', selectAll); -testIndentation('test1', selectAll); -testIndentation('test2', selectAll); -testIndentation('test3', selectorForLineN(1)); -testIndentation('test4', selectorForLineN(2)); -testIndentation('test5', selectorForLineN(3)); +test(() => assert_selection( + [ + '<div contenteditable>', + '<ul><li><pre>\nhello\n^world|\nblink\n</pre></li></ul>', + '</div>', + ].join(''), + 'indent', + [ + '<div contenteditable>', + '<ul><li><pre>hello\n', + '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">', + '^world|</blockquote>blink\n', + '</pre></li></ul>', + '</div>', + ].join('')), '4 select line 2'); +test(() => assert_selection( + [ + '<div contenteditable>', + '<ul><li><pre>\nhello\nworld\n^blin|k\n</pre></li></ul>', + '</div>', + ].join(''), + 'indent', + [ + '<div contenteditable>', + '<ul><li><pre>hello\nworld\n', + '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">', + '^blin|k', + '</blockquote>', + '</pre></li></ul>', + '</div>', + ].join('')), '5 select line 3'); </script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/cleanup-on-move-expected.txt b/third_party/WebKit/LayoutTests/editing/pasteboard/cleanup-on-move-expected.txt deleted file mode 100644 index db5f8e7..0000000 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/cleanup-on-move-expected.txt +++ /dev/null
@@ -1,37 +0,0 @@ -This test drags a selection of a couple of <li> and drops them after another <li> in the same list. It verifies that no empty <li> (actually with a <br> inside) are left after the moving operation. - -The original list looks like this. 'two' and 'three' are selected and are going to be dropped after 'four': -| " -" -| <li> -| id="one" -| "one" -| <li> -| id="two" -| "<#selection-anchor>two" -| <li> -| id="three" -| "three<#selection-focus>" -| <li> -| id="four" -| "four" -| " -" - -'two' and 'three' should appear as <li> after 'four' and no empty <li> (nor a <br> placeholder) should be there: -| " -" -| <li> -| id="one" -| "one" -| <li> -| id="four" -| "four" -| <li> -| id="two" -| "two" -| <li> -| id="three" -| "three" -| " -"
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/cleanup-on-move.html b/third_party/WebKit/LayoutTests/editing/pasteboard/cleanup-on-move.html deleted file mode 100644 index 09cde65..0000000 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/cleanup-on-move.html +++ /dev/null
@@ -1,23 +0,0 @@ -<!DOCTYPE html> - -<body> -<script src="../../resources/dump-as-markup.js"></script> -<script src="resources/select-and-drag.js"></script> -<div contenteditable="true"> -<ol id="test"> -<li id="one">one</li><li id="two">two</li><li id="three">three</li><li id="four">four</li> -</ol> -</div> - -<script> -Markup.description('This test drags a selection of a couple of <li> and drops them after another <li> in the same list. It verifies that no empty <li> (actually with a <br> inside) are left after the moving operation.'); - -if (window.testRunner) { - selectListItems("two", "three", 1); - Markup.dump("test", "The original list looks like this. 'two' and 'three' are selected and are going to be dropped after 'four'"); - dragSelectionToTarget("two", "four"); - Markup.dump("test", "'two' and 'three' should appear as <li> after 'four' and no empty <li> (nor a <br> placeholder) should be there"); -} -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style-expected.txt b/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style-expected.txt deleted file mode 100644 index 8de3641a..0000000 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style-expected.txt +++ /dev/null
@@ -1,18 +0,0 @@ -This tests dragging and dropping a URL. The content before and after the drag and drop should match. - -before: -| <a> -| href="http://webkit.org/" -| style="color:orange" -| target="_blank" -| title="some title" -| "<#selection-anchor>drag me<#selection-focus>" - -after: -| <a> -| href="http://webkit.org/" -| style="color: orange;" -| target="_blank" -| title="some title" -| "<#selection-anchor>drag me<#selection-focus>" -| <br>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style.html b/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style.html index 9bdcca8..91e5f11d 100644 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style.html +++ b/third_party/WebKit/LayoutTests/editing/pasteboard/drag-drop-url-with-style.html
@@ -1,40 +1,50 @@ -<!DOCTYPE html> -<html> -<body> -<p id="description">This tests dragging and dropping a URL. The content before and after the drag and drop should match.</p> -<div contenteditable><a href="http://webkit.org/" title="some title" target="_blank" style="color:orange">drag me</a></div> -<p>to the box blow:</p> -<div id="destination" contenteditable ondrop="setTimeout(dump, 0);" style="border: solid 2px blue; padding: 5px;"><br></div> -<script src="../../resources/dump-as-markup.js"></script> +<!doctype html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../assert_selection.js"></script> <script> - -Markup.waitUntilDone(); - -Markup.description(document.getElementById('description').textContent); - -var target = document.getElementsByTagName("a")[0]; -getSelection().selectAllChildren(target); -Markup.dump(target.parentNode, 'before'); - -function dump() { - Markup.dump('destination', 'after'); - Markup.notifyDone(); +function computePoint(selection, target) { + const x = selection.document.offsetLeft + target.offsetLeft + + target.offsetWidth / 2; + const y = selection.document.offsetTop + target.offsetTop + + target.offsetHeight / 2; + return {x, y}; } -if (window.eventSender) { - var x = target.offsetLeft + target.offsetWidth / 2; - var y = target.offsetTop + target.offsetHeight / 2; - eventSender.mouseMoveTo(x, y); - eventSender.mouseDown(); +test(() => { + if (!window.eventSender) { + assert_unreached('This test requires eventSender.'); + return; + } - var destination = document.getElementById("destination"); - eventSender.leapForward(300); - eventSender.mouseMoveTo(destination.offsetLeft + 10, destination.offsetTop + destination.offsetHeight / 2); - eventSender.mouseUp(); + assert_selection( + [ + '<div contenteditable>', + '<a href="http://dev.chromium.org/"', + ' title="some title" style="color:orange">^drag me|</a>', + '</div>', + '<p>to the box blow:</p>', + '<div contenteditable id="drop"><br></div>', + ].join(''), + selection => { + const source = selection.document.querySelector('a'); + const sourcePoint = computePoint(selection, source); + eventSender.mouseMoveTo(sourcePoint.x, sourcePoint.y); + eventSender.mouseDown(); - setTimeout(function () { destination.innerHTML = 'FAIL'; dump(); }, 100); -} - + const drop = selection.document.getElementById('drop'); + const dropPoint = computePoint(selection, drop); + eventSender.leapForward(300); + eventSender.mouseMoveTo(dropPoint.x, dropPoint.y); + eventSender.mouseUp(); + }, + [ + '<div contenteditable></div>', + '<p>to the box blow:</p>', + '<div contenteditable id="drop">', + '<a href="http://dev.chromium.org/"', + ' style="color: orange;" title="some title">^drag me|</a>', + '</div>', + ].join('')); +}, 'Drag-and-Drop should keep attributes of A'); </script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item-expected.txt b/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item-expected.txt deleted file mode 100644 index 5e238e01..0000000 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item-expected.txt +++ /dev/null
@@ -1,65 +0,0 @@ -This test drags a selection of one <li> and drops them after another <li> in the first list. The same test is repeated in the second list but this time with two selected items instead of one. It verifies that the behavior is independent of how many fully selected items we move, i.e. fully selected <li> are moved as <li> and not just as plain text - -The original list looks like this. 'two' is selected and is going to be dropped after 'four': -| " -" -| <li> -| id="one" -| "one" -| <li> -| id="two" -| "<#selection-anchor>two<#selection-focus>" -| <li> -| id="three" -| "three" -| <li> -| id="four" -| "four" -| " -" - -'two' should appear as a new <li> after 'four': -| " -" -| <li> -| id="one" -| "one" -| <li> -| id="three" -| "three" -| <li> -| id="four" -| "four" -| <li> -| id="two" -| "two" -| " -" - -The original list looks like this. 'two' and 'three' are selected and are going to be dropped after 'four': -| <li> -| id="one" -| "one" -| <li> -| id="two" -| "<#selection-anchor>two" -| <li> -| id="three" -| "three<#selection-focus>" -| <li> -| id="four" -| "four" - -'two' and 'three' should appear as <li> after 'four': -| <li> -| id="one" -| "one" -| <li> -| id="four" -| "four" -| <li> -| id="two" -| "two" -| <li> -| id="three" -| "three"
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item.html b/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item.html index ca1a0db..0f62645 100644 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item.html +++ b/third_party/WebKit/LayoutTests/editing/pasteboard/drag-list-item.html
@@ -1,31 +1,81 @@ -<!DOCTYPE html> - -<body> -<script src="../../resources/dump-as-markup.js"></script> -<script src="resources/select-and-drag.js"></script> -<div contenteditable="true"> -<ol id="test"> -<li id="one">one</li><li id="two">two</li><li id="three">three</li><li id="four">four</li> -</ol> -</div> - +<!doctype html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../assert_selection.js"></script> <script> -Markup.description('This test drags a selection of one <li> and drops them after another <li> in the first list. The same test is repeated in the second list but this time with two selected items instead of one. It verifies that the behavior is independent of how many fully selected items we move, i.e. fully selected <li> are moved as <li> and not just as plain text'); - -if (window.testRunner) { - - selectListItems("two", "two", 1); - Markup.dump("test", "The original list looks like this. 'two' is selected and is going to be dropped after 'four'"); - dragSelectionToTarget("two", "four"); - Markup.dump("test", "'two' should appear as a new <li> after 'four'"); - - document.getElementById("test").innerHTML = "<li id=\"one\">one</li><li id=\"two\">two</li>" - + "<li id=\"three\">three</li><li id=\"four\">four</li>"; - selectListItems("two", "three", 1); - Markup.dump("test", "The original list looks like this. 'two' and 'three' are selected and are going to be dropped after 'four'"); - dragSelectionToTarget("two", "four"); - Markup.dump("test", "'two' and 'three' should appear as <li> after 'four'"); +function computePoint(selection, targetId) { + const target = selection.document.getElementById(targetId); + const x = selection.document.offsetLeft + target.offsetLeft + + target.offsetWidth / 2 + target.parentNode.offsetLeft; + const y = selection.document.offsetTop + target.offsetTop + + target.offsetHeight / 2 + target.parentNode.offsetTop; + return {x, y}; } + +function dragSelectionToTarget(selection, sourceId, dropId) { + const sourcePoint = computePoint(selection, sourceId); + eventSender.mouseMoveTo(sourcePoint.x, sourcePoint.y); + eventSender.mouseDown(); + eventSender.leapForward(200); + + const dropPoint = computePoint(selection, dropId); + eventSender.mouseMoveTo(dropPoint.x, dropPoint.y); + eventSender.mouseUp(); +} + +test(() => { + if (!window.eventSender) { + assert_unreached('This test requires eventSender.'); + return; + } + + assert_selection( + [ + '<div contenteditable>', + '<ol id="test">', + '<li id="one">one</li>', + '<li id="two">^two|</li>', + '<li id="three">three</li>', + '<li id="four">four</li>', + '</ol>', + '</div>', + ].join(''), + selection => dragSelectionToTarget(selection, 'two', 'four'), + [ + '<div contenteditable>', + '<ol id="test">', + '<li id="one">one</li>', + '<li id="three">three</li>', + '<li id="four">four</li>', + '<li id="two">^two|</li>', + '</ol>', + '</div>', + ].join(''), + 'Move list item two to four'); + + + assert_selection( + [ + '<div contenteditable>', + '<ol id="test">', + '<li id="one">one</li>', + '<li id="two">^two</li>', + '<li id="three">three|</li>', + '<li id="four">four</li>', + '</ol>', + '</div>', + ].join(''), + selection => dragSelectionToTarget(selection, 'two', 'four'), + [ + '<div contenteditable>', + '<ol id="test">', + '<li id="one">one</li>', + '<li id="four">four</li>', + '<li id="two">^two</li>', + '<li id="three">three|</li>', + '</ol>', + '</div>', + ].join(''), + 'Move list item two and three to four'); +}, 'Drag-and-Drop list item'); </script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/paste_div_with_style.html b/third_party/WebKit/LayoutTests/editing/pasteboard/paste_div_with_style.html new file mode 100644 index 0000000..169cf0de --- /dev/null +++ b/third_party/WebKit/LayoutTests/editing/pasteboard/paste_div_with_style.html
@@ -0,0 +1,35 @@ +<!doctype html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../assert_selection.js"></script> +<script> +test(() => { + if (!window.testRunner) { + assert_unreached('This test should be run with testRunner.'); + return; + } + + assert_selection( + [ + '<div contenteditable>', + '<div>', + '^foo', + '<div style="color: rgb(255, 0, 0);">', + '<div>bar|</div>', + '</div>', + '</div>', + ].join(''), + selection => { + selection.document.execCommand('copy'); + selection.document.execCommand('paste'); + }, + [ + '<div contenteditable>', + 'foo', + '<div style="color: rgb(255, 0, 0);">', + 'bar|', + '</div>', + '</div>', + ].join('')); +}, 'paste should not create empty style or font tags'); +</script>
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/testcase-9507-expected.txt b/third_party/WebKit/LayoutTests/editing/pasteboard/testcase-9507-expected.txt deleted file mode 100644 index b56eb73..0000000 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/testcase-9507-expected.txt +++ /dev/null
@@ -1,42 +0,0 @@ -When copying some text, under certain circumstances, empty style (or font) tags are created. This test checks that there is no empty <font> tag after 'foo' and before 'bar'. - -After copy: -| " -" -| <div> -| class="editing" -| id="test" -| " -<#selection-anchor>foo -" -| <div> -| style="color: rgb(255, 0, 0);" -| " -" -| <div> -| "bar<#selection-focus>" -| " -" -| " -" -| " -" - -After paste: -| " -" -| <div> -| class="editing" -| id="test" -| "foo" -| <div> -| style="color: rgb(255, 0, 0);" -| "bar<#selection-caret>" -| <div> -| style="color: rgb(255, 0, 0);" -| " -" -| " -" -| " -"
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/testcase-9507.html b/third_party/WebKit/LayoutTests/editing/pasteboard/testcase-9507.html deleted file mode 100644 index e54d600..0000000 --- a/third_party/WebKit/LayoutTests/editing/pasteboard/testcase-9507.html +++ /dev/null
@@ -1,45 +0,0 @@ -<html> -<head> -<style> -.editing { - border: 2px solid red; - font-size: 24px; -} -.explanation { - border: 2px solid blue; - padding: 12px; - font-size: 24px; - margin-bottom: 24px; -} -.scenario { margin-bottom: 16px;} -.scenario:first-line { font-weight: bold; margin-bottom: 16px;} -.expected-results:first-line { font-weight: bold } -</style> -<script src="../editing.js"></script> -<script src="../../resources/dump-as-markup.js"></script> -<title>Editing Test</title> -</head> -<body> -<p id="description">When copying some text, under certain circumstances, empty style (or font) tags are created. This test checks that there is no empty <font> tag after 'foo' and before 'bar'.</p> -<div contenteditable id="root"> -<div id="test" class="editing"> -foo -<div style="color: rgb(255, 0, 0);" > -<div>bar</div> -</div> -</div> -</div> -<script> - -Markup.description(document.getElementById('description').textContent); - -setSelectionCommand(document.getElementById("test"),0,document.getElementById("test"),3); -copyCommand(); -Markup.dump('root', 'After copy'); - -pasteCommand(); -Markup.dump('root', 'After paste'); - -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-metadata-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-metadata-expected.txt index fac9d93f..63339810 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-metadata-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/reflection-metadata-expected.txt
@@ -67,118 +67,31 @@ PASS link.integrity: 32 tests PASS link.hreflang: 32 tests PASS link.type: 32 tests -FAIL link.referrerPolicy: typeof IDL attribute assert_equals: expected "string" but got "undefined" -FAIL link.referrerPolicy: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f foo " assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to undefined assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to 7 assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to 1.5 assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to true assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to false assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to NaN assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to Infinity assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to -Infinity assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to null assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "no-referrer" assert_equals: IDL get expected (string) "no-referrer" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xno-referrer" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "no-referrer\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "o-referrer" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "NO-REFERRER" assert_equals: IDL get expected (string) "no-referrer" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "no-referrer-when-downgrade" assert_equals: IDL get expected (string) "no-referrer-when-downgrade" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xno-referrer-when-downgrade" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "no-referrer-when-downgrade\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "o-referrer-when-downgrade" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "NO-REFERRER-WHEN-DOWNGRADE" assert_equals: IDL get expected (string) "no-referrer-when-downgrade" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "same-origin" assert_equals: IDL get expected (string) "same-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xsame-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "same-origin\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "ame-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "SAME-ORIGIN" assert_equals: IDL get expected (string) "same-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "origin" assert_equals: IDL get expected (string) "origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xorigin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "origin\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "rigin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "ORIGIN" assert_equals: IDL get expected (string) "origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "strict-origin" assert_equals: IDL get expected (string) "strict-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xstrict-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "strict-origin\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "trict-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "STRICT-ORIGIN" assert_equals: IDL get expected (string) "strict-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "origin-when-cross-origin" assert_equals: IDL get expected (string) "origin-when-cross-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xorigin-when-cross-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "origin-when-cross-origin\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "rigin-when-cross-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "ORIGIN-WHEN-CROSS-ORIGIN" assert_equals: IDL get expected (string) "origin-when-cross-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "strict-origin-when-cross-origin" assert_equals: IDL get expected (string) "strict-origin-when-cross-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xstrict-origin-when-cross-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "strict-origin-when-cross-origin\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "trict-origin-when-cross-origin" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "STRICT-ORIGIN-WHEN-CROSS-ORIGIN" assert_equals: IDL get expected (string) "strict-origin-when-cross-origin" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "unsafe-url" assert_equals: IDL get expected (string) "unsafe-url" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "xunsafe-url" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "unsafe-url\0" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "nsafe-url" assert_equals: IDL get expected (string) "" but got (undefined) undefined -FAIL link.referrerPolicy: setAttribute() to "UNSAFE-URL" assert_equals: IDL get expected (string) "unsafe-url" but got (undefined) undefined -FAIL link.referrerPolicy: IDL set to "" assert_equals: getAttribute() expected "" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f foo " but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to 7 assert_equals: getAttribute() expected "7" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to true assert_equals: getAttribute() expected "true" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to false assert_equals: getAttribute() expected "false" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to null assert_equals: IDL get expected (string) "" but got (object) null -FAIL link.referrerPolicy: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to object "test-valueOf" assert_equals: getAttribute() expected "test-valueOf" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "no-referrer" assert_equals: getAttribute() expected "no-referrer" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xno-referrer" assert_equals: getAttribute() expected "xno-referrer" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "no-referrer\0" assert_equals: getAttribute() expected "no-referrer\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "o-referrer" assert_equals: getAttribute() expected "o-referrer" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "NO-REFERRER" assert_equals: getAttribute() expected "NO-REFERRER" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "no-referrer-when-downgrade" assert_equals: getAttribute() expected "no-referrer-when-downgrade" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xno-referrer-when-downgrade" assert_equals: getAttribute() expected "xno-referrer-when-downgrade" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "no-referrer-when-downgrade\0" assert_equals: getAttribute() expected "no-referrer-when-downgrade\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "o-referrer-when-downgrade" assert_equals: getAttribute() expected "o-referrer-when-downgrade" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "NO-REFERRER-WHEN-DOWNGRADE" assert_equals: getAttribute() expected "NO-REFERRER-WHEN-DOWNGRADE" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "same-origin" assert_equals: getAttribute() expected "same-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xsame-origin" assert_equals: getAttribute() expected "xsame-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "same-origin\0" assert_equals: getAttribute() expected "same-origin\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "ame-origin" assert_equals: getAttribute() expected "ame-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "SAME-ORIGIN" assert_equals: getAttribute() expected "SAME-ORIGIN" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "origin" assert_equals: getAttribute() expected "origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xorigin" assert_equals: getAttribute() expected "xorigin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "origin\0" assert_equals: getAttribute() expected "origin\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "rigin" assert_equals: getAttribute() expected "rigin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "ORIGIN" assert_equals: getAttribute() expected "ORIGIN" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "strict-origin" assert_equals: getAttribute() expected "strict-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xstrict-origin" assert_equals: getAttribute() expected "xstrict-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "strict-origin\0" assert_equals: getAttribute() expected "strict-origin\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "trict-origin" assert_equals: getAttribute() expected "trict-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "STRICT-ORIGIN" assert_equals: getAttribute() expected "STRICT-ORIGIN" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "origin-when-cross-origin" assert_equals: getAttribute() expected "origin-when-cross-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xorigin-when-cross-origin" assert_equals: getAttribute() expected "xorigin-when-cross-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "origin-when-cross-origin\0" assert_equals: getAttribute() expected "origin-when-cross-origin\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "rigin-when-cross-origin" assert_equals: getAttribute() expected "rigin-when-cross-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "ORIGIN-WHEN-CROSS-ORIGIN" assert_equals: getAttribute() expected "ORIGIN-WHEN-CROSS-ORIGIN" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "strict-origin-when-cross-origin" assert_equals: getAttribute() expected "strict-origin-when-cross-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xstrict-origin-when-cross-origin" assert_equals: getAttribute() expected "xstrict-origin-when-cross-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "strict-origin-when-cross-origin\0" assert_equals: getAttribute() expected "strict-origin-when-cross-origin\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "trict-origin-when-cross-origin" assert_equals: getAttribute() expected "trict-origin-when-cross-origin" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "STRICT-ORIGIN-WHEN-CROSS-ORIGIN" assert_equals: getAttribute() expected "STRICT-ORIGIN-WHEN-CROSS-ORIGIN" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "unsafe-url" assert_equals: getAttribute() expected "unsafe-url" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "xunsafe-url" assert_equals: getAttribute() expected "xunsafe-url" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "unsafe-url\0" assert_equals: getAttribute() expected "unsafe-url\0" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "nsafe-url" assert_equals: getAttribute() expected "nsafe-url" but got "UNSAFE-URL" -FAIL link.referrerPolicy: IDL set to "UNSAFE-URL" assert_equals: IDL get expected "unsafe-url" but got "UNSAFE-URL" +PASS link.referrerPolicy: 27 tests +FAIL link.referrerPolicy: setAttribute() to "same-origin" assert_equals: IDL get expected "same-origin" but got "" +PASS link.referrerPolicy: 3 tests +FAIL link.referrerPolicy: setAttribute() to "SAME-ORIGIN" assert_equals: IDL get expected "same-origin" but got "" +PASS link.referrerPolicy: 5 tests +FAIL link.referrerPolicy: setAttribute() to "strict-origin" assert_equals: IDL get expected "strict-origin" but got "" +PASS link.referrerPolicy: 3 tests +FAIL link.referrerPolicy: setAttribute() to "STRICT-ORIGIN" assert_equals: IDL get expected "strict-origin" but got "" +PASS link.referrerPolicy: 5 tests +FAIL link.referrerPolicy: setAttribute() to "strict-origin-when-cross-origin" assert_equals: IDL get expected "strict-origin-when-cross-origin" but got "" +PASS link.referrerPolicy: 3 tests +FAIL link.referrerPolicy: setAttribute() to "STRICT-ORIGIN-WHEN-CROSS-ORIGIN" assert_equals: IDL get expected "strict-origin-when-cross-origin" but got "" +PASS link.referrerPolicy: 30 tests +FAIL link.referrerPolicy: IDL set to "same-origin" assert_equals: IDL get expected "same-origin" but got "" +PASS link.referrerPolicy: 3 tests +FAIL link.referrerPolicy: IDL set to "SAME-ORIGIN" assert_equals: IDL get expected "same-origin" but got "" +PASS link.referrerPolicy: 5 tests +FAIL link.referrerPolicy: IDL set to "strict-origin" assert_equals: IDL get expected "strict-origin" but got "" +PASS link.referrerPolicy: 3 tests +FAIL link.referrerPolicy: IDL set to "STRICT-ORIGIN" assert_equals: IDL get expected "strict-origin" but got "" +PASS link.referrerPolicy: 5 tests +FAIL link.referrerPolicy: IDL set to "strict-origin-when-cross-origin" assert_equals: IDL get expected "strict-origin-when-cross-origin" but got "" +PASS link.referrerPolicy: 3 tests +FAIL link.referrerPolicy: IDL set to "STRICT-ORIGIN-WHEN-CROSS-ORIGIN" assert_equals: IDL get expected "strict-origin-when-cross-origin" but got "" +PASS link.referrerPolicy: 5 tests PASS link.charset: 32 tests PASS link.rev: 32 tests PASS link.target: 32 tests
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/data-url-shared.html b/third_party/WebKit/LayoutTests/external/wpt/workers/data-url-shared.html index 05d3b51..3950584 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/workers/data-url-shared.html +++ b/third_party/WebKit/LayoutTests/external/wpt/workers/data-url-shared.html
@@ -26,11 +26,13 @@ // Communications goes both ways assert_worker_sends_pass('communication goes both ways', 'application/javascript', 'port.onmessage = function(e) { port.postMessage("PASS"); }'); -// 'data:' workers are cross-origin +// test access to storage APIs +// once https://github.com/w3c/IndexedDB/pull/150 lands, this is spec conforming assert_worker_throws('indexedDB inaccessible', 'self.indexedDB.open("someDBName")'); -assert_worker_throws('localStorage inaccessible', 'self.localStorage.testItem'); -assert_worker_sends_pass('cross-origin worker', '', 'fetch("/").then(() => port.postMessage("FAIL"), () => port.postMessage("PASS"))'); +assert_worker_throws('Web SQL Database inaccessible', 'self.openDatabase("someDBName", "1.0", "someDBName", 1);'); +// 'data:' workers are cross-origin +assert_worker_sends_pass('cross-origin worker', '', 'fetch("/").then(() => port.postMessage("FAIL"), () => port.postMessage("PASS"))'); // 'data:' workers have opaque origin assert_worker_sends_pass('worker has opaque origin', 'application/javascript', 'if (self.location.origin == "null") port.postMessage("PASS"); else port.postMessage("FAIL");'); </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html b/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html index da468e79..3a4eb6c 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html +++ b/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html
@@ -41,9 +41,12 @@ // Communications goes both ways assert_worker_sends_pass('communication goes both ways', 'application/javascript', 'onmessage = function(e) { self.postMessage("PASS"); }'); -// 'data:' workers are cross-origin +// test access to storage APIs +// once https://github.com/w3c/IndexedDB/pull/150 lands, this is spec conforming assert_worker_throws('indexedDB inaccessible', 'self.indexedDB.open("someDBName")'); -assert_worker_throws('localStorage inaccessible', 'self.localStorage.testItem'); +assert_worker_throws('Web SQL Database inaccessible', 'self.openDatabase("someDBName", "1.0", "someDBName", 1);'); + +// 'data:' workers are cross-origin assert_worker_sends_pass('cross-origin worker', '', 'fetch("/").then(() => self.postMessage("FAIL"), () => self.postMessage("PASS"))'); // 'data:' workers have opaque origin
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction-expected.txt deleted file mode 100644 index 1c53dfc..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction-expected.txt +++ /dev/null
@@ -1,16 +0,0 @@ -Tests that canvas 2d context supports 'direction' attribute: PASS -Tests that context.direction is 'ltr' with parent element having unspecified direction: PASS -Tests that context.direction is 'rtl' with parent element having direction as rtl: PASS -Tests that context.direction is overridden by 'rtl' with parent element having unspecified direction: PASS -Tests that context.direction is overridden by 'ltr' with parent element having direction as rtl: PASS -Tests that context.direction is overridden by 'inherit' with parent element having unspecified direction: PASS -Tests that context.direction is overridden by 'inherit' with parent element having direction as rtl: PASS -Tests that change in element's dir attribute is reflected in context.direction as rtl: PASS -Tests that change in element's dir attribute is reflected in context.direction as ltr: PASS -Tests that context.direction reflects the valid direction after save/restore context operations: PASS -Tests that context.reset() sets the context.direction to ltr: PASS -Tests that context.reset() sets the context.direction to rtl: PASS -Tests that invalid direction value RTL has no effect on the context.direction: PASS -Tests that invalid direction value LTR has no effect on the context.direction: PASS -Tests that invalid direction value INHERIT has no effect on the context.direction: PASS -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction.html index bf576b2..acf2acb 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-direction.html
@@ -1,16 +1,7 @@ <html> <head> -<style> -.pass { - color: green; - font-weight: bold; -} - -.fail { - color: red; - font-weight: bold; -} -</style> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> </head> <body> <span>Tests that canvas 2d context supports 'direction' attribute: <span id="supported"></span></span> @@ -61,36 +52,16 @@ <div id="results"> </div> <script> -if (window.testRunner) - testRunner.dumpAsText(); - -var newCanvasElement = document.createElement('canvas'); -document.getElementById('supported').textContent = newCanvasElement.getContext('2d').direction ? 'PASS' : 'FAIL'; -document.getElementById('supported').className = newCanvasElement.getContext('2d').direction ? 'pass' : 'fail'; - var fontSettings = "12px 'Arial'"; -function appendResult(description, result) +function verifyDrawText(canvasId, text, expectedDirection) { - var descriptionElement = document.createElement('span'); - var resultElement = document.createElement('span'); - descriptionElement.innerHTML = description; - resultElement.textContent = result; - resultElement.className = (result === 'PASS') ? 'pass' : 'fail'; - descriptionElement.appendChild(resultElement); - document.getElementById("results").appendChild(descriptionElement); - document.getElementById("results").appendChild(document.createElement('br')); -} - -function verifyDrawText(canvasId, resultId, text, expectedDirection) -{ + console.log(canvasId, text, expectedDirection); var canvasElement = document.getElementById(canvasId); - var resultElement = document.getElementById(resultId); var ctx = canvasElement.getContext('2d'); var width = canvasElement.width/2; var height = canvasElement.height; - resultElement.textContent = (ctx.direction == expectedDirection) ? 'PASS' : 'FAIL'; - resultElement.className = (ctx.direction == expectedDirection) ? 'pass' : 'fail'; + assert_equals(ctx.direction, expectedDirection); ctx.moveTo(width, 0); ctx.lineTo(width, height); ctx.stroke(); @@ -98,68 +69,63 @@ ctx.fillText(text, width, height/2); } -function verifyDrawTextWithSpecifiedDirection(canvasId, resultId, text, direction, expectedDirection) +function verifyDrawTextWithSpecifiedDirection(testItem) { - var canvasElement = document.getElementById(canvasId); - var resultElement = document.getElementById(resultId); + console.log(testItem); + var canvasElement = document.getElementById(testItem['canvasId']); var ctx = canvasElement.getContext('2d'); var width = canvasElement.width/2; var height = canvasElement.height; var currentDirection = ctx.direction; - ctx.direction = direction; - resultElement.textContent = (currentDirection && (ctx.direction == expectedDirection)) ? 'PASS' : 'FAIL'; - resultElement.className = (currentDirection && (ctx.direction == expectedDirection)) ? 'pass' : 'fail'; + ctx.direction = testItem['direction']; + assert_not_equals(currentDirection, null); + assert_equals(ctx.direction, testItem['expectedDirection']); ctx.moveTo(width, 0); ctx.lineTo(width, height); ctx.stroke(); ctx.font = fontSettings; - ctx.fillText(text, width, height/2); + ctx.fillText(testItem['text'], width, height/2); } -function verifyDirectionAfterReset(canvasId, text, direction, expectedDirection) +function verifyDirectionAfterReset(testItem) { - var canvasElement = document.getElementById(canvasId); + var canvasElement = document.getElementById(testItem['canvasId']); var width = canvasElement.width/2; var height = canvasElement.height; var ctx = canvasElement.getContext('2d'); - ctx.direction = direction; + ctx.direction = testItem['direction']; ctx.moveTo(width, 0); ctx.lineTo(width, height); ctx.stroke(); ctx.font = fontSettings; - ctx.fillText(text, width, height/2); + ctx.fillText(testItem['text'], width, height/2); canvasElement.width = canvasElement.width + 1; - var description = 'Tests that context.reset() sets the context.direction to ' + expectedDirection + ': '; - var result = (ctx.direction == expectedDirection) ? 'PASS' : 'FAIL'; - appendResult(description, result); + assert_equals(ctx.direction, testItem['expectedDirection']); document.body.removeChild(canvasElement.parentElement); } -function verifyDirectionAfterAttributeChange(canvasId, resultId, text, newDirection, forParentElement) +function verifyDirectionAfterAttributeChange(testItem) { - var canvasElement = document.getElementById(canvasId); - var resultElement = document.getElementById(resultId); + var canvasElement = document.getElementById(testItem['canvasId']); var ctx = canvasElement.getContext('2d'); var width = canvasElement.width/2; var height = canvasElement.height; - if (forParentElement) - canvasElement.parentElement.dir = newDirection; + if (testItem['forParentElement']) + canvasElement.parentElement.dir = testItem['newDirection']; else - canvasElement.dir = newDirection; - resultElement.textContent = ctx.direction == newDirection ? 'PASS' : 'FAIL'; - resultElement.className = ctx.direction == newDirection ? 'pass' : 'fail'; + canvasElement.dir = testItem['newDirection']; + assert_equals(ctx.direction, testItem['newDirection']); ctx.moveTo(width, 0); ctx.lineTo(width, height); ctx.stroke(); ctx.font = fontSettings; - ctx.fillText(text, width, height/2); + ctx.fillText(testItem['text'], width, height/2); } -function verifyDirectionAcrossSaveRestores(canvasId, resultId, testVector) +function verifyDirectionAcrossSaveRestores(canvasId, testVector) { var canvasElement = document.getElementById(canvasId); - var resultElement = document.getElementById(resultId); var ctx = canvasElement.getContext('2d'); var width = canvasElement.width/2; var height = 0; @@ -182,8 +148,7 @@ if (ctx.direction == testVector[i - 1].direction) validDirectionCount++; } - resultElement.textContent = validDirectionCount == testVectorLength - 1 ? 'PASS' : 'FAIL'; - resultElement.className = validDirectionCount == testVectorLength - 1 ? 'pass' : 'fail'; + assert_equals(validDirectionCount, testVectorLength - 1); } function verifyInvalidDirection(direction) @@ -191,35 +156,61 @@ var ctx = document.createElement('canvas').getContext('2d'); var currentDirection = ctx.direction; ctx.direction = direction; - var description = 'Tests that invalid direction value ' + direction + ' has no effect on the context.direction: '; - var result = (ctx.direction == currentDirection) ? 'PASS' : 'FAIL'; - appendResult(description, result); + assert_equals(ctx.direction, currentDirection); } -verifyDrawText('canvas1', 'result1', 'Left-to-Right text', 'ltr'); -verifyDrawText('canvas2', 'result2', 'Right-to-Left text', 'rtl'); +test(function(t) { -verifyDrawTextWithSpecifiedDirection('canvas3', 'result3', 'Right-to-Left text', 'rtl', 'rtl'); -verifyDrawTextWithSpecifiedDirection('canvas4', 'result4', 'Left-to-Right text', 'ltr', 'ltr'); -verifyDrawTextWithSpecifiedDirection('canvas5', 'result5', 'Left-to-Right text', 'inherit', 'ltr'); -verifyDrawTextWithSpecifiedDirection('canvas6', 'result6', 'Right-to-Left text', 'inherit', 'rtl'); + var newCanvasElement = document.createElement('canvas'); + assert_not_equals(newCanvasElement.getContext('2d').direction, null); + + var drawTextTests = [ + ['DrawTextTest1', 'canvas1', 'Left-to-Right text', 'ltr'], + ['DrawTextTest2', 'canvas2', 'Right-to-Left text', 'rtl'], + ]; + generate_tests(verifyDrawText, drawTextTests); + + var drawTextWithSpecifiedDirectionTests = [ + ['DrawTextWithSpecifiedDirectionTest1', + {canvasId: 'canvas3', text: 'Right-to-Left text', direction: 'rtl', expectedDirection: 'rtl'}], + ['DrawTextWithSpecifiedDirectionTest2', + {canvasId: 'canvas4', text: 'Left-to-Right text', direction: 'ltr', expectedDirection: 'ltr'}], + ['DrawTextWithSpecifiedDirectionTest3', + {canvasId: 'canvas5', text: 'Left-to-Right text', direction: 'inherit', expectedDirection: 'ltr'}], + ['DrawTextWithSpecifiedDirectionTest4', + {canvasId: 'canvas6', text: 'Right-to-Left text', direction: 'inherit', expectedDirection: 'rtl'}], + ]; + generate_tests(verifyDrawTextWithSpecifiedDirection, drawTextWithSpecifiedDirectionTests); + + var directionAfterResetTests = [ + ['DirectionAfterResetTest1', + {canvasId: 'canvas7', text: 'Right-to-Left', direction: 'rtl', expectedDirection: 'ltr'}], + ['DirectionAfterResetTest2', + {canvasId: 'canvas8', text: 'Right-to-Left', direction: 'ltr', expectedDirection: 'rtl'}], + ]; + generate_tests(verifyDirectionAfterReset, directionAfterResetTests); + + var directionAfterAttributeChangeTests = [ + ['DirectionAfterResetTest1', + {canvasId: 'canvas9', text: 'Right-to-Left text', newDirection: 'rtl', forParentElement: true}], + ['DirectionAfterResetTest2', + {canvasId: 'canvas10', text: 'Left-to-Right text', newDirection: 'ltr', forParentElement: false}], + ]; + generate_tests(verifyDirectionAfterAttributeChange, directionAfterAttributeChangeTests); + + verifyDirectionAcrossSaveRestores('canvas11', + [{ text: 'Left-to-Right text', direction: 'ltr' }, + { text: 'Right-to-Left text', direction: 'rtl' }, + { text: 'Right-to-Left text', direction: 'rtl' }, + { text: 'Left-to-Right text', direction: 'ltr' }, + { text: 'Right-to-Left text', direction: 'rtl' }, + { text: 'Right-to-Left text', direction: 'rtl' }]); + var invalidDirectionTests = [ + ['InvalidDirectionTestRTL', 'RTL'], + ['InvalidDirectionTestLTR', 'LTR'], + ['InvalidDirectionTestINHERIT', 'INHERIT'], + ]; + generate_tests(verifyInvalidDirection, invalidDirectionTests); -verifyDirectionAfterReset('canvas7', 'Right-to-Left', 'rtl', 'ltr'); -verifyDirectionAfterReset('canvas8', 'Right-to-Left', 'ltr', 'rtl'); - -verifyDirectionAfterAttributeChange('canvas9', 'result9', 'Right-to-Left text', 'rtl', true); -verifyDirectionAfterAttributeChange('canvas10', 'result10', 'Left-to-Right text', 'ltr', false); - -verifyDirectionAcrossSaveRestores('canvas11', - 'result11', - [{ text: 'Left-to-Right text', direction: 'ltr' }, - { text: 'Right-to-Left text', direction: 'rtl' }, - { text: 'Right-to-Left text', direction: 'rtl' }, - { text: 'Left-to-Right text', direction: 'ltr' }, - { text: 'Right-to-Left text', direction: 'rtl' }, - { text: 'Right-to-Left text', direction: 'rtl' }]); - -verifyInvalidDirection('RTL'); -verifyInvalidDirection('LTR'); -verifyInvalidDirection('INHERIT'); +}, "Verify that canvas 2d context supports 'direction' attribute."); </script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow-expected.txt deleted file mode 100644 index 5b3196b..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow-expected.txt +++ /dev/null
@@ -1,57 +0,0 @@ -Ensure correct behavior when drawing a canvas on a canvas with shadows. A blue and red checkered pattern should be displayed. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 255 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 255 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 255 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 127 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 127 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 127 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 100 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 100 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 100 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 50 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 50 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 50 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow.html index c6e02b2f..95a0cc9 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-draw-canvas-on-canvas-shadow.html
@@ -1,9 +1,114 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <body> -<script src="script-tests/canvas-draw-canvas-on-canvas-shadow.js"></script> +<script> +test(function(t) { + var canvas = document.createElement('canvas'); + document.body.appendChild(canvas); + canvas.setAttribute('width', '600'); + canvas.setAttribute('height', '600'); + var ctx = canvas.getContext('2d'); + ctx.shadowOffsetX = 100; + ctx.shadowOffsetY = 100; + + var aCanvas = document.createElement('canvas'); + aCanvas.width = 300; + aCanvas.height = 300; + + var aCtx = aCanvas.getContext('2d'); + aCtx.fillStyle = 'rgba(0, 0, 255, 1.0)'; + aCtx.fillRect(100, 100, 100, 100); + + ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; + ctx.drawImage(aCanvas, 0, 0); + + ctx.shadowColor = 'rgba(255, 0, 0, 0.5)'; + ctx.drawImage(aCanvas, 0, 200); + + ctx.shadowBlur = 5; + ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; + ctx.drawImage(aCanvas, 200, 0); + + ctx.shadowColor = 'rgba(255, 0, 0, 0.5)'; + ctx.drawImage(aCanvas, 200, 200); + + var d; // imageData.data + + // Verify solid shadow. + d = ctx.getImageData(200, 205, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 255); + + d = ctx.getImageData(299, 295, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 255); + + d = ctx.getImageData(200, 299, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 255); + + // Verify solid alpha shadow. + d = ctx.getImageData(200, 405, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 127, 15); + + d = ctx.getImageData(299, 405, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 127, 15); + + d = ctx.getImageData(205, 499, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 127, 15); + + // Verify blurry shadow. + d = ctx.getImageData(500, 211, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 100, 15); + + d = ctx.getImageData(399, 205, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 100, 15); + + d = ctx.getImageData(450, 300, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 100, 15); + + // Verify blurry alpha shadow. + d = ctx.getImageData(500, 411, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 50, 15); + + d = ctx.getImageData(399, 405, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 50, 15); + + d = ctx.getImageData(450, 500, 1, 1).data; + assert_equals(d[0], 255); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_approx_equals(d[3], 50, 15); +}, "Ensure correct behavior when drawing a canvas on a canvas with shadows. A blue and red checkered pattern should be displayed."); +</script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-expected.txt deleted file mode 100644 index 7e0ed18..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated-expected.txt +++ /dev/null
@@ -1,13 +0,0 @@ -When drawing an animated image to a canvas, the poster frame (or the first frame) should be printed. -This test passes if the canvas is filled with the color rgb(64, 4, 30). - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS successfullyParsed is true - -TEST COMPLETE -PASS imageData.data[0] is 64 -PASS imageData.data[1] is 4 -PASS imageData.data[2] is 30 -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated.html index 454f940..ae15679 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-animated.html
@@ -1,44 +1,27 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> - <head> - <script src="../../resources/js-test.js"></script> - </head> - <body onload="javascript:ready()"> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> - <!-- This image is an animated GIF (1px by 1px). Animation time is 0ms. First frame has the color rgb(64, 4, 30). The second one has the color rgb(10, 153, 30) --> - <img id="image" src="data:image/gif;base64,R0lGODlhAQABAIACAEAEHgqZHiH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAkAAAEALAAAAAABAAEAAAICRAEAIfkECAAA/wAsAAAAAAEAAQAAAgJMAQA7" alt="Animated Image" /> - <canvas id="canvas" width="1" height="1"></canvas> +<!-- This image is an animated GIF (1px by 1px). Animation time is 0ms. First frame has the color rgb(64, 4, 30). The second one has the color rgb(10, 153, 30) --> +<img id="image" src="data:image/gif;base64,R0lGODlhAQABAIACAEAEHgqZHiH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAkAAAEALAAAAAABAAEAAAICRAEAIfkECAAA/wAsAAAAAAEAAQAAAgJMAQA7" alt="Animated Image" /> - <script> - description("When drawing an animated image to a canvas, the poster frame (or the first frame) should be printed.<br/>This test passes if the canvas is filled with the color rgb(64, 4, 30)."); +<canvas id="canvas" width="1" height="1"></canvas> - if (window.testRunner) { - testRunner.waitUntilDone(); - } +<script> +async_test(t => { + window.onload = function() { + var canvas = document.getElementById("canvas"); + var image = document.getElementById("image"); + var canvasContext = canvas.getContext("2d"); + t.step(function(){ + canvasContext.drawImage(image, 0, 0); + imageData = canvasContext.getImageData(0, 0, 1, 1); + assert_equals(imageData.data[0], 64); + assert_equals(imageData.data[1], 4); + assert_equals(imageData.data[2], 30); + }); + t.done(); + } +}, 'When drawing an animated image to a canvas, the poster frame (or the first frame) should be printed. This test passes if the canvas is filled with the color rgb(64, 4, 30).'); +</script> +</body> - - function ready() { - var canvas = document.getElementById("canvas"); - var image = document.getElementById("image"); - - var canvasContext = canvas.getContext("2d"); - - window.setTimeout(function() { - - canvasContext.drawImage(image, 0, 0); - - imageData = canvasContext.getImageData(0, 0, 1, 1); - - shouldBe("imageData.data[0]", "64"); - shouldBe("imageData.data[1]", "4"); - shouldBe("imageData.data[2]", "30"); - - if (window.testRunner) - testRunner.notifyDone(); - - }, 200); - } - </script> - - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete-expected.txt deleted file mode 100644 index e74bab1..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete-expected.txt +++ /dev/null
@@ -1,19 +0,0 @@ -Test that drawImage() does nothing with an incomplete image or video - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS ctx.drawImage(img, 0, 0) is undefined -PASS imgdata[0] is 255 -PASS imgdata[1] is 0 -PASS imgdata[2] is 0 -PASS imgdata[3] is 255 -PASS ctx.drawImage(video, 0, 0) is undefined -PASS imgdata[0] is 255 -PASS imgdata[1] is 0 -PASS imgdata[2] is 0 -PASS imgdata[3] is 255 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete.html index d5d62217..c377ff8e 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-incomplete.html
@@ -1,9 +1,33 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <body> -<script src="script-tests/canvas-drawImage-incomplete.js"></script> +<script> +test(function(t) { + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext('2d'); + ctx.fillStyle = 'red'; + ctx.fillRect(0,0,150,150); + + var img = new Image(); + img.src = '../../http/tests/misc/resources/image-slow.pl'; + + var video = document.createElement("video"); + + assert_equals(ctx.drawImage(img, 0, 0), undefined); + + var imgdata = ctx.getImageData(0, 0, 1, 1).data; + assert_equals(imgdata[0], 255); + assert_equals(imgdata[1], 0); + assert_equals(imgdata[2], 0); + assert_equals(imgdata[3], 255); + + assert_equals(ctx.drawImage(video, 0, 0), undefined); + + imgdata = ctx.getImageData(0, 0, 1, 1).data; + assert_equals(imgdata[0], 255); + assert_equals(imgdata[1], 0); + assert_equals(imgdata[2], 0); + assert_equals(imgdata[3], 255); +}, "Test that drawImage() does nothing with an incomplete image or video"); +</script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video-expected.txt deleted file mode 100644 index 7006e940..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video-expected.txt +++ /dev/null
@@ -1,10 +0,0 @@ -Verify that consecutive drawImage from a live video correctly propagates frame updates. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS successfullyParsed is true - -TEST COMPLETE -PASS imagesAreTheSame is false -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video.html index 2e70eb4..3dd5f50 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-live-video.html
@@ -7,60 +7,57 @@ </style> </head> <body> - <canvas id="canvas"></canvas> - <video id="video"> - <source src="../../media/resources/test-live.webm" type='video/webm' /> - </video> - <script src="../../resources/js-test.js"></script> - <script> - description('Verify that consecutive drawImage from a live video correctly propagates frame updates.'); - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } - +<canvas id="canvas"></canvas> +<video id="video"> + <source src="../../media/resources/test-live.webm" type='video/webm' /> +</video> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script> +async_test(t => { var canvas = document.getElementById("canvas"); canvas.width = 100; canvas.height = 100; var ctx = canvas.getContext("2d"); - var video = document.getElementById("video"); - video.addEventListener("playing", drawFirstFrame, true); - video.play(); + var video = document.getElementById("video"); + video.addEventListener("playing", drawFirstFrame, true); + video.play(); - function drawFirstFrame() { - video.removeEventListener("playing", drawFirstFrame, true); - ctx.drawImage(video, 0, 0, canvas.width, canvas.height); - requestAnimationFrame(function() { - video.addEventListener("timeupdate", updateVideo, true); - }); - } - - var referenceImageData; - var processedFirstFrame = false; - var imagesAreTheSame; - - function updateVideo() { - if (!processedFirstFrame) { + function drawFirstFrame() { + video.removeEventListener("playing", drawFirstFrame, true); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); - referenceImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - processedFirstFrame = true; - } else { - video.removeEventListener("timeupdate", updateVideo, true); - ctx.drawImage(video, 0, 0, canvas.width, canvas.height); - var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - imagesAreTheSame = true; - for(var i = 0; i < imageData.data.length; ++i) { - if (imageData.data[i] != referenceImageData.data[i]) { - imagesAreTheSame = false; - break; - } - } - shouldBeFalse("imagesAreTheSame"); - if (window.testRunner) - testRunner.notifyDone(); + requestAnimationFrame(function() { + video.addEventListener("timeupdate", updateVideo, true); + }); } - } - </script> + + var referenceImageData; + var processedFirstFrame = false; + var imagesAreTheSame; + + function updateVideo() { + t.step(function(){ + if (!processedFirstFrame) { + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + referenceImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + processedFirstFrame = true; + } else { + video.removeEventListener("timeupdate", updateVideo, true); + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + imagesAreTheSame = true; + for(var i = 0; i < imageData.data.length; ++i) { + if (imageData.data[i] != referenceImageData.data[i]) { + imagesAreTheSame = false; + break; + } + } + assert_false(imagesAreTheSame); + t.done(); + } + }); + } +}, 'Verify that consecutive drawImage from a live video correctly propagates frame updates.'); +</script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self-expected.txt deleted file mode 100644 index 6f6ab20..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self-expected.txt +++ /dev/null
@@ -1,25 +0,0 @@ -Tests drawImage with self as source image, copy composite operation, and downsized destination rect. Regression test for issue crbug.com/330711 - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS imgdata[0] is 0 -PASS imgdata[1] is 255 -PASS imgdata[2] is 0 -PASS imgdata[3] is 255 -PASS imgdata[0] is 0 -PASS imgdata[1] is 255 -PASS imgdata[2] is 0 -PASS imgdata[3] is 255 -PASS imgdata[0] is 0 -PASS imgdata[1] is 0 -PASS imgdata[2] is 0 -PASS imgdata[3] is 0 -PASS imgdata[0] is 0 -PASS imgdata[1] is 0 -PASS imgdata[2] is 0 -PASS imgdata[3] is 0 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self.html index 8702e20f..29b3117 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-scaled-copy-to-self.html
@@ -1,52 +1,44 @@ -<!DOCTYPE html> -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> -<body> -<p id="description"></p> -<div id="console"></div> - +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script> -description("Tests drawImage with self as source image, copy composite operation, and downsized destination rect. Regression test for issue crbug.com/330711"); - -var canvas = document.createElement("canvas"); -canvas.width = 10; -canvas.height = 10; -var ctx = canvas.getContext('2d'); - -ctx.globalCompositeOperation = 'copy'; -ctx.fillStyle = '#0f0'; -ctx.fillRect(0, 0, 10, 10); -ctx.drawImage(canvas, 0, 0, 10, 10, 0, 0, 5, 5); - -var imageData = ctx.getImageData(0, 0, 1, 1); -var imgdata = imageData.data; -shouldBe("imgdata[0]", "0"); -shouldBe("imgdata[1]", "255"); -shouldBe("imgdata[2]", "0"); -shouldBe("imgdata[3]", "255"); - -var imageData = ctx.getImageData(4, 4, 1, 1); -var imgdata = imageData.data; -shouldBe("imgdata[0]", "0"); -shouldBe("imgdata[1]", "255"); -shouldBe("imgdata[2]", "0"); -shouldBe("imgdata[3]", "255"); - -var imageData = ctx.getImageData(4, 5, 1, 1); -var imgdata = imageData.data; -shouldBe("imgdata[0]", "0"); -shouldBe("imgdata[1]", "0"); -shouldBe("imgdata[2]", "0"); -shouldBe("imgdata[3]", "0"); - -var imageData = ctx.getImageData(5, 4, 1, 1); -var imgdata = imageData.data; -shouldBe("imgdata[0]", "0"); -shouldBe("imgdata[1]", "0"); -shouldBe("imgdata[2]", "0"); -shouldBe("imgdata[3]", "0"); +test(function(t) { + var canvas = document.createElement("canvas"); + canvas.width = 10; + canvas.height = 10; + var ctx = canvas.getContext('2d'); + + ctx.globalCompositeOperation = 'copy'; + ctx.fillStyle = '#0f0'; + ctx.fillRect(0, 0, 10, 10); + ctx.drawImage(canvas, 0, 0, 10, 10, 0, 0, 5, 5); + + var imageData = ctx.getImageData(0, 0, 1, 1); + var imgdata = imageData.data; + assert_equals(imgdata[0], 0); + assert_equals(imgdata[1], 255); + assert_equals(imgdata[2], 0); + assert_equals(imgdata[3], 255); + + imageData = ctx.getImageData(4, 4, 1, 1); + imgdata = imageData.data; + assert_equals(imgdata[0], 0); + assert_equals(imgdata[1], 255); + assert_equals(imgdata[2], 0); + assert_equals(imgdata[3], 255); + + imageData = ctx.getImageData(4, 5, 1, 1); + imgdata = imageData.data; + assert_equals(imgdata[0], 0); + assert_equals(imgdata[1], 0); + assert_equals(imgdata[2], 0); + assert_equals(imgdata[3], 0); + + imageData = ctx.getImageData(5, 4, 1, 1); + imgdata = imageData.data; + assert_equals(imgdata[0], 0); + assert_equals(imgdata[1], 0); + assert_equals(imgdata[2], 0); + assert_equals(imgdata[3], 0); +}, 'Tests drawImage with self as source image, copy composite operation, and downsized destination rect. Regression test for issue crbug.com/330711'); </script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow-expected.txt deleted file mode 100644 index 702162b..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow-expected.txt +++ /dev/null
@@ -1,60 +0,0 @@ -Ensure correct behavior of canvas with image shadow. A square with a cut-out top-right corner should be displayed with solid shadow (top) and blur shadow (bottom). - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 0 -PASS d[0] is 240 -PASS d[1] is 50 -PASS d[2] is 50 -PASS d[3] is 255 -PASS d[0] is 240 -PASS d[1] is 50 -PASS d[2] is 50 -PASS d[3] is 255 -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 0 -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 0 -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 0 -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 255 -PASS d[3] should not be 255 and it's not. -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 255 -PASS d[3] should not be 255 and it's not. -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 255 -PASS d[3] should not be 255 and it's not. -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 255 -PASS d[3] should not be 255 and it's not. -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 255 -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 0 -PASS d[0] is 0 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 0 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow.html index 10f2cad4..b4f72581 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-drawImage-shadow.html
@@ -1,9 +1,144 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <body> -<script src="script-tests/canvas-drawImage-shadow.js"></script> +<script> +// Create auxiliary canvas to draw to and create an image from. +// This is done instead of simply loading an image from the file system +// because that would throw a SECURITY_ERR DOM Exception. +var aCanvas = document.createElement('canvas'); +aCanvas.setAttribute('width', '200'); +aCanvas.setAttribute('height', '200'); +var aCtx = aCanvas.getContext('2d'); + +// Draw a circle on the same canvas. +aCtx.beginPath(); +aCtx.fillStyle = 'green'; +aCtx.arc(100, 100, 150, 0, -Math.PI/2, false); +aCtx.fill(); + +// Create the image object to be drawn on the master canvas. +var img = new Image(); +img.onload = drawImageToCanvasAndCheckPixels; +img.src = aCanvas.toDataURL(); // set a data URI of the base64 enconded image as the source + +// Create master canvas. +var canvas = document.createElement('canvas'); +document.body.appendChild(canvas); +canvas.setAttribute('width', '600'); +canvas.setAttribute('height', '600'); +var ctx = canvas.getContext('2d'); + +function drawImageToCanvasAndCheckPixels() { + ctx.shadowOffsetX = 250; + ctx.shadowColor = 'rgba(240, 50, 50, 1.0)'; + ctx.drawImage(img, 50, 50); + + ctx.shadowOffsetX = 250; + ctx.shadowBlur = 6; + ctx.shadowColor = 'rgba(50, 50, 200, 0.9)'; + ctx.shadowColor = 'rgba(0, 0, 255, 1.0)'; + ctx.drawImage(img, 50, 300); + + checkPixels(); +} + +function checkPixels() { + test(function(t) { + var imageData, data; + + // Verify solid shadow. + imageData = ctx.getImageData(260, 300, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 0); + + imageData = ctx.getImageData(350, 100, 1, 1); + d = imageData.data; + assert_equals(d[0], 240); + assert_equals(d[1], 50); + assert_equals(d[2], 50); + assert_equals(d[3], 255); + + imageData = ctx.getImageData(400, 200, 1, 1); + d = imageData.data; + assert_equals(d[0], 240); + assert_equals(d[1], 50); + assert_equals(d[2], 50); + assert_equals(d[3], 255); + + imageData = ctx.getImageData(490, 65, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 0); + + imageData = ctx.getImageData(485, 65, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 0); + + // Verify blurry shadow. + imageData = ctx.getImageData(260, 400, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 0); + + imageData = ctx.getImageData(350, 300, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 255); + assert_not_equals(d[3], 255); + + imageData = ctx.getImageData(300, 400, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 255); + assert_not_equals(d[3], 255); + + imageData = ctx.getImageData(300, 500, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 255); + assert_not_equals(d[3], 255); + + imageData = ctx.getImageData(400, 500, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 255); + assert_not_equals(d[3], 255); + + imageData = ctx.getImageData(400, 400, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 255); + + imageData = ctx.getImageData(490, 315, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 0); + + imageData = ctx.getImageData(485, 320, 1, 1); + d = imageData.data; + assert_equals(d[0], 0); + assert_equals(d[1], 0); + assert_equals(d[2], 0); + assert_equals(d[3], 0); + + }, "Ensure correct behavior of canvas with image shadow. A square with a cut-out top-right corner should be displayed with solid shadow (top) and blur shadow (bottom)."); +} +</script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding-expected.txt deleted file mode 100644 index 79cf508..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding-expected.txt +++ /dev/null
@@ -1,15 +0,0 @@ -This tests canvas full arc fill with nonzero winding rule. Eight green concentric thick circumferences should be displayed. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS data[1] is 0 -PASS data[1] is 255 -PASS data[1] is 255 -PASS data[1] is 0 -PASS data[1] is 0 -PASS data[1] is 255 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding.html index bbb3b70..1615b68 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-360-winding.html
@@ -1,9 +1,56 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <body> -<script src="script-tests/canvas-ellipse-360-winding.js"></script> +<script> +test(function(t) { + var canvas = document.createElement('canvas'); + document.body.appendChild(canvas) + canvas.setAttribute('width', '300'); + canvas.setAttribute('height', '150'); + var ctx = canvas.getContext('2d'); + + var r; + var anticlockwise = true; + ctx.beginPath(); + for (r = 200; r >= 10; r -= 10) { + ctx.moveTo(150 + r, 75); + // Test that anticlockwise is both optional and defaults to false. + if (anticlockwise) { + ctx.ellipse(150, 75, r, r * 1.2, 0, 0, Math.PI * 2, anticlockwise); + } else { + ctx.ellipse(150, 75, r, r * 1.2, 0, 0, Math.PI * 2); + } + ctx.closePath(); + anticlockwise = !anticlockwise; + } + ctx.fillStyle = 'rgba(0, 255, 0, 1)'; + ctx.strokeStyle = 'rgba(0, 255, 0, 1)'; + ctx.fill(); + ctx.stroke(); + + var imageData = ctx.getImageData(297, 75, 1, 1); + var data = imageData.data; + assert_equals(data[1], 0); + + imageData = ctx.getImageData(295, 144, 1, 1); + data = imageData.data; + assert_equals(data[1], 255); + + imageData = ctx.getImageData(272, 144, 1, 1); + data = imageData.data; + assert_equals(data[1], 255); + + imageData = ctx.getImageData(262, 144, 1, 1); + data = imageData.data; + assert_equals(data[1], 0); + + imageData = ctx.getImageData(239, 144, 1, 1); + data = imageData.data; + assert_equals(data[1], 0); + + imageData = ctx.getImageData(228, 144, 1, 1); + data = imageData.data; + assert_equals(data[1], 255); +}, "This tests canvas full arc fill with nonzero winding rule. Eight green concentric thick circumferences should be displayed."); +</script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-expected.txt deleted file mode 100644 index 558a20c..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse-expected.txt +++ /dev/null
@@ -1,24 +0,0 @@ -This test checks ellipse API in canvas v5 - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 255 -PASS data[0] is 255 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 255 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 0 -PASS data[0] is 255 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse.html index 8758e9f6..e73e926 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse.html +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-ellipse.html
@@ -1,9 +1,90 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <body> -<script src="script-tests/canvas-ellipse.js"></script> +<script> +test(function(t) { + var canvas = document.createElement('canvas'); + document.body.appendChild(canvas); + canvas.setAttribute('width', '400'); + canvas.setAttribute('height', '400'); + var ctx = canvas.getContext('2d'); + + ctx.fillStyle="rgba(255, 255, 255, 1.0)"; + ctx.fillRect(0, 0, 400, 400); + + ctx.strokeStyle="rgba(0, 0, 0, 1.0)"; + ctx.lineWidth = 10; + ctx.beginPath(); + ctx.moveTo(0, 100); + ctx.ellipse(200, 200, 100, 150, Math.PI / 9, -Math.PI, Math.PI * 5 / 9, false); + ctx.lineTo(0, 300); + ctx.stroke(); + + var imageData, data; + + // Verify the method must add a straight line from the last point in the subpath + // to the start point of the ellipse. + imageData = ctx.getImageData(5, 103, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(49, 130, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(103, 163, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(36, 108, 1, 1); + data = imageData.data; + assert_equals(data[0], 255); + + // Verify ellipse API draws well. + imageData = ctx.getImageData(101, 179, 1, 1); + data = imageData.data; + assert_equals(data[0], 255); + + imageData = ctx.getImageData(119, 132, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(260, 62, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(301, 122, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(273, 272, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(169, 344, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(196, 362, 1, 1); + data = imageData.data; + assert_equals(data[0], 255); + + // Verify the last point of ellipse is the start point of the next subpath. + imageData = ctx.getImageData(128, 331, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(65, 315, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(9, 302, 1, 1); + data = imageData.data; + assert_equals(data[0], 0); + + imageData = ctx.getImageData(58, 300, 1, 1); + data = imageData.data; + assert_equals(data[0], 255); +}, "This test checks ellipse API in canvas v5"); +</script> </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-draw-canvas-on-canvas-shadow.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-draw-canvas-on-canvas-shadow.js deleted file mode 100644 index 79c81374..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-draw-canvas-on-canvas-shadow.js +++ /dev/null
@@ -1,133 +0,0 @@ -description("Ensure correct behavior when drawing a canvas on a canvas with shadows. A blue and red checkered pattern should be displayed."); - -function print(message, color) -{ - var paragraph = document.createElement("div"); - paragraph.appendChild(document.createTextNode(message)); - paragraph.style.fontFamily = "monospace"; - if (color) - paragraph.style.color = color; - document.getElementById("console").appendChild(paragraph); -} - -function shouldBeAround(a, b) -{ - var evalA; - try { - evalA = eval(a); - } catch(e) { - evalA = e; - } - - if (Math.abs(evalA - b) < 15) - print("PASS " + a + " is around " + b , "green") - else - print("FAIL " + a + " is not around " + b + " (actual: " + evalA + ")", "red"); -} - -var canvas = document.createElement('canvas'); -document.body.appendChild(canvas); -canvas.setAttribute('width', '600'); -canvas.setAttribute('height', '600'); -var ctx = canvas.getContext('2d'); -ctx.shadowOffsetX = 100; -ctx.shadowOffsetY = 100; - -var aCanvas = document.createElement('canvas'); -aCanvas.width = 300; -aCanvas.height = 300; - -var aCtx = aCanvas.getContext('2d'); -aCtx.fillStyle = 'rgba(0, 0, 255, 1.0)'; -aCtx.fillRect(100, 100, 100, 100); - -ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; -ctx.drawImage(aCanvas, 0, 0); - -ctx.shadowColor = 'rgba(255, 0, 0, 0.5)'; -ctx.drawImage(aCanvas, 0, 200); - -ctx.shadowBlur = 5; -ctx.shadowColor = 'rgba(255, 0, 0, 1.0)'; -ctx.drawImage(aCanvas, 200, 0); - -ctx.shadowColor = 'rgba(255, 0, 0, 0.5)'; -ctx.drawImage(aCanvas, 200, 200); - -var d; // imageData.data - -// Verify solid shadow. -d = ctx.getImageData(200, 205, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBe('d[3]', '255'); - -d = ctx.getImageData(299, 295, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBe('d[3]', '255'); - -d = ctx.getImageData(200, 299, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBe('d[3]', '255'); - -// Verify solid alpha shadow. -d = ctx.getImageData(200, 405, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '127'); - -d = ctx.getImageData(299, 405, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '127'); - -d = ctx.getImageData(205, 499, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '127'); - -// Verify blurry shadow. -d = ctx.getImageData(500, 211, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '100'); - -d = ctx.getImageData(399, 205, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '100'); - -d = ctx.getImageData(450, 300, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '100'); - -// Verify blurry alpha shadow. -d = ctx.getImageData(500, 411, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '50'); - -d = ctx.getImageData(399, 405, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '50'); - -d = ctx.getImageData(450, 500, 1, 1).data; -shouldBe('d[0]', '255'); -shouldBe('d[1]', '0'); -shouldBe('d[2]', '0'); -shouldBeAround('d[3]', '50');
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-drawImage-incomplete.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-drawImage-incomplete.js deleted file mode 100644 index c5094a2b..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-drawImage-incomplete.js +++ /dev/null
@@ -1,30 +0,0 @@ -description("Test that drawImage() does nothing with an incomplete image or video"); - -if (window.testRunner) - testRunner.dumpAsText(); - -var canvas = document.createElement("canvas"); -var ctx = canvas.getContext('2d'); -ctx.fillStyle = 'red'; -ctx.fillRect(0,0,150,150); - -var img = new Image(); -img.src = '../../http/tests/misc/resources/image-slow.pl'; - -var video = document.createElement("video"); - -shouldBe("ctx.drawImage(img, 0, 0)", "undefined"); - -var imgdata = ctx.getImageData(0, 0, 1, 1).data; -shouldBe("imgdata[0]", "255"); -shouldBe("imgdata[1]", "0"); -shouldBe("imgdata[2]", "0"); -shouldBe("imgdata[3]", "255"); - -shouldBe("ctx.drawImage(video, 0, 0)", "undefined"); - -imgdata = ctx.getImageData(0, 0, 1, 1).data; -shouldBe("imgdata[0]", "255"); -shouldBe("imgdata[1]", "0"); -shouldBe("imgdata[2]", "0"); -shouldBe("imgdata[3]", "255");
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-drawImage-shadow.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-drawImage-shadow.js deleted file mode 100644 index ebeb11f..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-drawImage-shadow.js +++ /dev/null
@@ -1,166 +0,0 @@ -description("Ensure correct behavior of canvas with image shadow. A square with a cut-out top-right corner should be displayed with solid shadow (top) and blur shadow (bottom)."); - -function print(message, color) -{ - var paragraph = document.createElement("div"); - paragraph.appendChild(document.createTextNode(message)); - paragraph.style.fontFamily = "monospace"; - if (color) - paragraph.style.color = color; - document.getElementById("console").appendChild(paragraph); -} - -function shouldNotBe(a, b) -{ - var evalA; - try { - evalA = eval(a); - } catch(e) { - evalA = e; - } - - if (evalA != b) - print("PASS " + a + " should not be " + b + " and it's not.", "green") - else - print("FAIL " + a + " should not be " + b + " but it is.", "red"); -} - -// Create auxiliary canvas to draw to and create an image from. -// This is done instead of simply loading an image from the file system -// because that would throw a SECURITY_ERR DOM Exception. -var aCanvas = document.createElement('canvas'); -aCanvas.setAttribute('width', '200'); -aCanvas.setAttribute('height', '200'); -var aCtx = aCanvas.getContext('2d'); - -// Draw a circle on the same canvas. -aCtx.beginPath(); -aCtx.fillStyle = 'green'; -aCtx.arc(100, 100, 150, 0, -Math.PI/2, false); -aCtx.fill(); - -// Create the image object to be drawn on the master canvas. -var img = new Image(); -img.onload = drawImageToCanvasAndCheckPixels; -img.src = aCanvas.toDataURL(); // set a data URI of the base64 enconded image as the source - -// Create master canvas. -var canvas = document.createElement('canvas'); -document.body.appendChild(canvas); -canvas.setAttribute('width', '600'); -canvas.setAttribute('height', '600'); -var ctx = canvas.getContext('2d'); - -function drawImageToCanvasAndCheckPixels() { - ctx.shadowOffsetX = 250; - ctx.shadowColor = 'rgba(240, 50, 50, 1.0)'; - ctx.drawImage(img, 50, 50); - - ctx.shadowOffsetX = 250; - ctx.shadowBlur = 6; - ctx.shadowColor = 'rgba(50, 50, 200, 0.9)'; - ctx.shadowColor = 'rgba(0, 0, 255, 1.0)'; - ctx.drawImage(img, 50, 300); - - checkPixels(); -} - -function checkPixels() { - var imageData, data; - - // Verify solid shadow. - imageData = ctx.getImageData(260, 300, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '0'); - shouldBe('d[3]', '0'); - - imageData = ctx.getImageData(350, 100, 1, 1); - d = imageData.data; - shouldBe('d[0]', '240'); - shouldBe('d[1]', '50'); - shouldBe('d[2]', '50'); - shouldBe('d[3]', '255'); - - imageData = ctx.getImageData(400, 200, 1, 1); - d = imageData.data; - shouldBe('d[0]', '240'); - shouldBe('d[1]', '50'); - shouldBe('d[2]', '50'); - shouldBe('d[3]', '255'); - - imageData = ctx.getImageData(490, 65, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '0'); - shouldBe('d[3]', '0'); - - imageData = ctx.getImageData(485, 65, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '0'); - shouldBe('d[3]', '0'); - - // Verify blurry shadow. - imageData = ctx.getImageData(260, 400, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '0'); - shouldBe('d[3]', '0'); - - imageData = ctx.getImageData(350, 300, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '255'); - shouldNotBe('d[3]', '255'); - - imageData = ctx.getImageData(300, 400, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '255'); - shouldNotBe('d[3]', '255'); - - imageData = ctx.getImageData(300, 500, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '255'); - shouldNotBe('d[3]', '255'); - - imageData = ctx.getImageData(400, 500, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '255'); - shouldNotBe('d[3]', '255'); - - imageData = ctx.getImageData(400, 400, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '255'); - - imageData = ctx.getImageData(490, 315, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '0'); - shouldBe('d[3]', '0'); - - imageData = ctx.getImageData(485, 320, 1, 1); - d = imageData.data; - shouldBe('d[0]', '0'); - shouldBe('d[1]', '0'); - shouldBe('d[2]', '0'); - shouldBe('d[3]', '0'); - - finishJSTest(); -} - -window.jsTestIsAsync = true;
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-ellipse-360-winding.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-ellipse-360-winding.js deleted file mode 100644 index 64b2eb05..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-ellipse-360-winding.js +++ /dev/null
@@ -1,50 +0,0 @@ -description("This tests canvas full arc fill with nonzero winding rule. Eight green concentric thick circumferences should be displayed."); - -var canvas = document.createElement('canvas'); -document.body.appendChild(canvas) -canvas.setAttribute('width', '300'); -canvas.setAttribute('height', '150'); -var ctx = canvas.getContext('2d'); - -var r; -var anticlockwise = true; -ctx.beginPath(); -for (r = 200; r >= 10; r -= 10) { - ctx.moveTo(150 + r, 75); - // Test that anticlockwise is both optional and defaults to false. - if (anticlockwise) { - ctx.ellipse(150, 75, r, r * 1.2, 0, 0, Math.PI * 2, anticlockwise); - } else { - ctx.ellipse(150, 75, r, r * 1.2, 0, 0, Math.PI * 2); - } - ctx.closePath(); - anticlockwise = !anticlockwise; -} -ctx.fillStyle = 'rgba(0, 255, 0, 1)'; -ctx.strokeStyle = 'rgba(0, 255, 0, 1)'; -ctx.fill(); -ctx.stroke(); - -var imageData = ctx.getImageData(297, 75, 1, 1); -var data = imageData.data; -shouldBe("data[1]", "0"); - -imageData = ctx.getImageData(295, 144, 1, 1); -data = imageData.data; -shouldBe("data[1]", "255"); - -imageData = ctx.getImageData(272, 144, 1, 1); -data = imageData.data; -shouldBe("data[1]", "255"); - -imageData = ctx.getImageData(262, 144, 1, 1); -data = imageData.data; -shouldBe("data[1]", "0"); - -imageData = ctx.getImageData(239, 144, 1, 1); -data = imageData.data; -shouldBe("data[1]", "0"); - -imageData = ctx.getImageData(228, 144, 1, 1); -data = imageData.data; -shouldBe("data[1]", "255");
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-ellipse.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-ellipse.js deleted file mode 100644 index a1ce576..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-ellipse.js +++ /dev/null
@@ -1,85 +0,0 @@ -description("This test checks ellipse API in canvas v5"); - -var canvas = document.createElement('canvas'); -document.body.appendChild(canvas); -canvas.setAttribute('width', '400'); -canvas.setAttribute('height', '400'); -var ctx = canvas.getContext('2d'); - -ctx.fillStyle="rgba(255, 255, 255, 1.0)"; -ctx.fillRect(0, 0, 400, 400); - -ctx.strokeStyle="rgba(0, 0, 0, 1.0)"; -ctx.lineWidth = 10; -ctx.beginPath(); -ctx.moveTo(0, 100); -ctx.ellipse(200, 200, 100, 150, Math.PI / 9, -Math.PI, Math.PI * 5 / 9, false); -ctx.lineTo(0, 300); -ctx.stroke(); - -var imageData, data; - -// Verify the method must add a straight line from the last point in the subpath -// to the start point of the ellipse. -imageData = ctx.getImageData(5, 103, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(49, 130, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(103, 163, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(36, 108, 1, 1); -data = imageData.data; -shouldBe('data[0]', '255'); - -// Verify ellipse API draws well. -imageData = ctx.getImageData(101, 179, 1, 1); -data = imageData.data; -shouldBe('data[0]', '255'); - -imageData = ctx.getImageData(119, 132, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(260, 62, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(301, 122, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(273, 272, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(169, 344, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(196, 362, 1, 1); -data = imageData.data; -shouldBe('data[0]', '255'); - -// Verify the last point of ellipse is the start point of the next subpath. -imageData = ctx.getImageData(128, 331, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(65, 315, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(9, 302, 1, 1); -data = imageData.data; -shouldBe('data[0]', '0'); - -imageData = ctx.getImageData(58, 300, 1, 1); -data = imageData.data; -shouldBe('data[0]', '255'); -
diff --git a/third_party/WebKit/LayoutTests/fast/parser/resources/document-open-in-unload-inner.html b/third_party/WebKit/LayoutTests/fast/parser/resources/document-open-in-unload-inner.html index 93988e5..273175f 100644 --- a/third_party/WebKit/LayoutTests/fast/parser/resources/document-open-in-unload-inner.html +++ b/third_party/WebKit/LayoutTests/fast/parser/resources/document-open-in-unload-inner.html
@@ -1,5 +1,5 @@ This test passes if it doesn't crash. -<iframe src="data:text/plain,Hi"></iframe> +<iframe srcdoc="Hi"></iframe> <script> frames[0].onunload = function () { document.open();
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html index 168c1e9..46d26cf 100644 --- a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html +++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html
@@ -37,6 +37,12 @@ "collapsed": 0 }; +var EXPECTED_DISPLAY = { + "primary": "inline", + "fallback": "inline", + "collapsed": "none", +}; + var numPendingImages = Object.keys(INITIAL_URLS).length * Object.keys(FINAL_URLS).length; @@ -68,8 +74,11 @@ .then(t.step_func_done(_ => { assert_equals(img.clientWidth, EXPECTED_WIDTH[finalState], "Image has incorrect width for the expected final state."); + let style = window.getComputedStyle(img); + assert_equals(style.display, EXPECTED_DISPLAY[finalState], + "Images has incorrect computed style for the final state."); })); - }, "State transition " + initialState + " to " + finalState); + }, "State transition " + initialState + " to " + finalState + ". "); cell.append(img); } }
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html index 714b255..4a9e5bd 100644 --- a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html +++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html
@@ -16,6 +16,8 @@ i.onload = t.step_func_done(_ => { assert_equals(i.clientWidth, 100, "Images that are not disallowed should be displayed at their natural width."); assert_equals(i.clientHeight, 100, "Images that are not disallowed should be displayed at their natural height."); + let style = window.getComputedStyle(i); + assert_equals(style.display, "inline", "Images that are not disallowed should be display:inline"); }); i.onerror = t.unreached_func(); i.src = "http://localhost:8000/resources/redirect.php?url=/subresource_filter/resources/alpha.png"; @@ -30,6 +32,8 @@ assert_equals(i.clientHeight, 0, "Images that are disallowed should be collapsed."); assert_equals(i.naturalWidth, 0, "Images that are disallowed should not be loaded."); assert_equals(i.naturalHeight, 0, "Images that are disallowed should not be loaded."); + let style = window.getComputedStyle(i); + assert_equals(style.display, "none", "Images that are disallowed should be set to display:none"); }); i.src = "http://localhost:8000/resources/redirect.php?url=/subresource_filter/resources/beta.png"; document.body.append(i);
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html index 34bcfcb9..25460c8 100644 --- a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html +++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html
@@ -16,6 +16,8 @@ i.onload = t.step_func_done(_ => { assert_equals(i.clientWidth, 100, "Images that are not disallowed should be displayed at their natural width."); assert_equals(i.clientHeight, 100, "Images that are not disallowed should be displayed at their natural height."); + let style = window.getComputedStyle(i); + assert_equals(style.display, "inline", "Images that are not disallowed should be display:inline"); }); i.onerror = t.unreached_func(); i.src = "resources/alpha.png"; @@ -30,6 +32,8 @@ assert_equals(i.clientHeight, 0, "Images that are disallowed should be collapsed."); assert_equals(i.naturalWidth, 0, "Images that are disallowed should not be loaded."); assert_equals(i.naturalHeight, 0, "Images that are disallowed should not be loaded."); + let style = window.getComputedStyle(i); + assert_equals(style.display, "none", "Images that are disallowed should be set to display:none"); }); i.src = "resources/beta.png"; document.body.append(i);
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html index b7b0346..96ddb45 100644 --- a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html +++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html
@@ -35,6 +35,8 @@ i.onload = t.step_func_done(_ => { assert_equals(i.clientWidth, 100, "Images that are not disallowed should be displayed at their natural width."); assert_equals(i.clientHeight, 100, "Images that are not disallowed should be displayed at their natural height."); + let style = window.getComputedStyle(i); + assert_equals(style.display, "inline", "Images that are not disallowed should be display:inline"); }); document.body.append(p); }, "Images whose selected source URL is not disallowed should still be displayed as normal."); @@ -48,6 +50,8 @@ assert_equals(i.clientHeight, 0, "Images that are disallowed should be collapsed."); assert_equals(i.naturalWidth, 0, "Images that are disallowed should not be loaded."); assert_equals(i.naturalHeight, 0, "Images that are disallowed should not be loaded."); + let style = window.getComputedStyle(i); + assert_equals(style.display, "none", "Images that are disallowed should be set to display:none"); }); document.body.append(p); }, "Images whose selected source URL is disallowed should not be loaded and should be collapsed in the layout.");
diff --git a/third_party/WebKit/LayoutTests/media/autoplay-when-visible-multiple-times.html b/third_party/WebKit/LayoutTests/media/autoplay-when-visible-multiple-times.html new file mode 100644 index 0000000..8f2b782 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/autoplay-when-visible-multiple-times.html
@@ -0,0 +1,53 @@ +<!DOCTYPE html> +<title>Test behaviour of autoplay muted videos with regards to visibility</title> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<script src="media-file.js"></script> +<body> +<script> + window.internals.settings.setMediaPlaybackRequiresUserGesture(true); + window.internals.runtimeFlags.autoplayMutedVideosEnabled = true; + + var gCycleCount = 0; + + var video; + + function runStepsWhenInvisible(t) { + assert_true(video.paused); + video.style.top = '0px'; + + video.onplay = null; + video.onpause = t.unreached_func(); + video.addEventListener('play', t.step_func(_ => runStepsWhenVisible(t)), { once: true }); + } + + function runStepsWhenVisible(t) { + assert_false(video.paused); + + if (gCycleCount++ >= 3) { + t.done(); + return; + } + video.style.top = '-10000px'; + + video.onpause = null; + video.onplay = t.unreached_func(); + video.addEventListener('pause', t.step_func(_ => runStepsWhenInvisible(t)), { once: true }); + } + + async_test(function(t) { + // Create a video off screen. + { + video = document.createElement('video'); + video.src = findMediaFile('video', 'content/test'); + video.muted = true; + video.autoplay = true; + video.loop = true; + video.style.position = 'absolute'; + video.style.top = '-10000px'; + document.body.appendChild(video); + } + + video.addEventListener('canplay', t.step_func(_ => runStepsWhenInvisible(t)), { once : true }); + }); +</script>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/editing/pasteboard/testcase-9507-expected.png b/third_party/WebKit/LayoutTests/platform/mac/editing/pasteboard/testcase-9507-expected.png deleted file mode 100644 index 23ec171..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/editing/pasteboard/testcase-9507-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt index 25aa826..1a999a1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2269,6 +2269,7 @@ getter import getter integrity getter media + getter referrerPolicy getter rel getter relList getter rev @@ -2285,6 +2286,7 @@ setter hreflang setter integrity setter media + setter referrerPolicy setter rel setter relList setter rev
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt index f0b97d2..6490af03 100644 --- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2198,6 +2198,7 @@ getter import getter integrity getter media + getter referrerPolicy getter rel getter relList getter rev @@ -2214,6 +2215,7 @@ setter hreflang setter integrity setter media + setter referrerPolicy setter rel setter relList setter rev
diff --git a/third_party/WebKit/LayoutTests/svg/animations/animval-web-animations-flush-crash.html b/third_party/WebKit/LayoutTests/svg/animations/animval-web-animations-flush-crash.html new file mode 100644 index 0000000..4edd58f --- /dev/null +++ b/third_party/WebKit/LayoutTests/svg/animations/animval-web-animations-flush-crash.html
@@ -0,0 +1,25 @@ +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<style> +@keyframes anim { 0% { opacity: 0; } } +* { animation-name: anim; animation-duration: 4s; } +</style> +<p>PASS if no crash</p> +<script> +async_test(function(t) { + window.onload = t.step_func(function() { + let parent = document.createElement("div"); + element = parent.appendChild( + document.createElementNS("http://www.w3.org/2000/svg", "marker")); + + setTimeout(t.step_func_done(function() { + element.computedName; + let before = performance.now(); + while (performance.now() - before < 17) + ; + element.className.animVal; + })); + }); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-draw-canvas-on-canvas-shadow-expected.txt b/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-draw-canvas-on-canvas-shadow-expected.txt deleted file mode 100644 index 5b3196b..0000000 --- a/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-draw-canvas-on-canvas-shadow-expected.txt +++ /dev/null
@@ -1,57 +0,0 @@ -Ensure correct behavior when drawing a canvas on a canvas with shadows. A blue and red checkered pattern should be displayed. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 255 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 255 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is 255 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 127 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 127 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 127 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 100 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 100 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 100 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 50 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 50 -PASS d[0] is 255 -PASS d[1] is 0 -PASS d[2] is 0 -PASS d[3] is around 50 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt index 76eb251..9735bc1 100644 --- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -667,6 +667,7 @@ property import property integrity property media + property referrerPolicy property rel property relList property rev
diff --git a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt index b147ad6..37f38d9 100644 --- a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
@@ -684,6 +684,7 @@ property import property integrity property media + property referrerPolicy property rel property relList property rev
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt index 9b3ee241..f13bb6a 100644 --- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -2811,6 +2811,7 @@ getter import getter integrity getter media + getter referrerPolicy getter rel getter relList getter rev @@ -2828,6 +2829,7 @@ setter hreflang setter integrity setter media + setter referrerPolicy setter rel setter relList setter rev
diff --git a/third_party/WebKit/PerformanceTests/Editing/page-down-with-many-lines.html b/third_party/WebKit/PerformanceTests/Editing/page-down-with-many-lines.html new file mode 100644 index 0000000..e52f831 --- /dev/null +++ b/third_party/WebKit/PerformanceTests/Editing/page-down-with-many-lines.html
@@ -0,0 +1,26 @@ +<!doctype html> +<script src="../resources/runner.js"></script> +<textarea id="text" style="width:300px; height:300px" spellcheck="false"></textarea> +<script> +const kCount = 10; +const kLines = 20000 +const kCharactersPerPage = 482; + +text.textContent = (() => { + const result = []; + for (let count = 0; count < kLines; ++count) + result.push(`${('00000' + count).slice(-5)} of brown foxes\n`); + return result.join(''); +})(); +text.focus(); + +PerfTestRunner.measureRunsPerSecond({ + description: 'Measures performance of move-page-down on many lines', + run: () => { + const cursorIndex = text.value.length - 1 - kCharactersPerPage * kCount; + text.setSelectionRange(cursorIndex, cursorIndex); + for (let counter = 0; counter < kCount; ++counter) + testRunner.execCommand("MovePageDown"); + }, +}); +</script>
diff --git a/third_party/WebKit/PerformanceTests/Editing/page-up-with-many-lines.html b/third_party/WebKit/PerformanceTests/Editing/page-up-with-many-lines.html new file mode 100644 index 0000000..d21819e --- /dev/null +++ b/third_party/WebKit/PerformanceTests/Editing/page-up-with-many-lines.html
@@ -0,0 +1,25 @@ +<!doctype html> +<script src="../resources/runner.js"></script> +<textarea id="text" style="width:300px; height:300px" spellcheck="false"></textarea> +<script> +const kCount = 10; +const kLines = 20000 + +text.textContent = (() => { + const result = []; + for (let count = 0; count < kLines; ++count) + result.push(`${('00000' + count).slice(-5)} of brown foxes\n`); + return result.join(''); +})(); +text.focus(); + +PerfTestRunner.measureRunsPerSecond({ + description: 'Measures performance of move-page-up on many lines', + run: () => { + const cursorIndex = text.value.length - 1; + text.setSelectionRange(cursorIndex, cursorIndex); + for (let counter = 0; counter < kCount; ++counter) + testRunner.execCommand("MovePageUp"); + }, +}); +</script>
diff --git a/third_party/WebKit/Source/SpecMapping.md b/third_party/WebKit/Source/SpecMapping.md index ea90bcc..ac54be9 100644 --- a/third_party/WebKit/Source/SpecMapping.md +++ b/third_party/WebKit/Source/SpecMapping.md
@@ -17,6 +17,21 @@ interface where the main implementation is [LocalFrame](https://cs.chromium.org/src/third_party/WebKit/Source/core/frame/LocalFrame.h). +### [origins](https://html.spec.whatwg.org/multipage/browsers.html#concept-origin) + +An origin corresponds to the +[SecurityOrigin](https://cs.chromium.org/src/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.h) +class. You can test for +[same-origin](https://html.spec.whatwg.org/multipage/browsers.html#same-origin) +using `SecurityOrigin::canAccess` and for [same-origin +domain](https://html.spec.whatwg.org/multipage/browsers.html#same-origin-domain) +using `SecurityOrigin::isSameSchemeHostPort`. + +The [Suborigins spec](https://w3c.github.io/webappsec-suborigins/) extends +HTML's definition of origins. To check for same-origin and same-origin domain +use `SecurityOrigin::canAccessCheckSuborigins` and +`SecurityOrigin::isSameSchemeHostPortAndSuborigin`. + ### [Window object](https://html.spec.whatwg.org/#window) A Window object corresponds to the
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h index a7c1ceb..3212b4a 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h +++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -1131,24 +1131,6 @@ // http://www.w3.org/TR/WebIDL/#delete enum DeleteResult { DeleteSuccess, DeleteReject, DeleteUnknownProperty }; -class V8IsolateInterruptor final : public BlinkGCInterruptor { - public: - explicit V8IsolateInterruptor(v8::Isolate* isolate) : m_isolate(isolate) {} - - static void onInterruptCallback(v8::Isolate* isolate, void* data) { - V8IsolateInterruptor* interruptor = - reinterpret_cast<V8IsolateInterruptor*>(data); - interruptor->onInterrupted(); - } - - void requestInterrupt() override { - m_isolate->RequestInterrupt(&onInterruptCallback, this); - } - - private: - v8::Isolate* m_isolate; -}; - // Freeze a V8 object. The type of the first parameter and the return value is // intentionally v8::Value so that this function can wrap ToV8(). // If the argument isn't an object, this will crash.
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp index 29dffc50..a0c4d830 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -429,8 +429,6 @@ } ASSERT(ThreadState::mainThreadState()); - ThreadState::mainThreadState()->addInterruptor( - WTF::makeUnique<V8IsolateInterruptor>(isolate)); ThreadState::mainThreadState()->registerTraceDOMWrappers( isolate, V8GCController::traceDOMWrappers, ScriptWrappableVisitor::invalidateDeadObjectsInMarkingDeque,
diff --git a/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl index a036d58..2dbda141 100644 --- a/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl +++ b/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
@@ -493,9 +493,9 @@ return; } - // FIXME: Only pass context/exceptionState if instance really requires it. - ExecutionContext* context = currentExecutionContext(info.GetIsolate()); - instance->postMessage(context, message.release(), transferables.messagePorts, exceptionState); + // FIXME: Only pass scriptState/exceptionState if instance really requires it. + ScriptState* scriptState = ScriptState::current(info.GetIsolate()); + instance->postMessage(scriptState, message.release(), transferables.messagePorts, exceptionState); } {% endmacro %}
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp index adefe418..0620126 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -7696,9 +7696,9 @@ return; } - // FIXME: Only pass context/exceptionState if instance really requires it. - ExecutionContext* context = currentExecutionContext(info.GetIsolate()); - instance->postMessage(context, message.release(), transferables.messagePorts, exceptionState); + // FIXME: Only pass scriptState/exceptionState if instance really requires it. + ScriptState* scriptState = ScriptState::current(info.GetIsolate()); + instance->postMessage(scriptState, message.release(), transferables.messagePorts, exceptionState); } static void activityLoggingForAllWorldsPerWorldBindingsVoidMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/WebKit/Source/core/css/resolver/SharedStyleFinder.cpp b/third_party/WebKit/Source/core/css/resolver/SharedStyleFinder.cpp index 27dc1c6..6c0529d 100644 --- a/third_party/WebKit/Source/core/css/resolver/SharedStyleFinder.cpp +++ b/third_party/WebKit/Source/core/css/resolver/SharedStyleFinder.cpp
@@ -45,6 +45,7 @@ #include "core/dom/shadow/ElementShadow.h" #include "core/dom/shadow/InsertionPoint.h" #include "core/html/HTMLElement.h" +#include "core/html/HTMLImageElement.h" #include "core/html/HTMLInputElement.h" #include "core/html/HTMLOptGroupElement.h" #include "core/html/HTMLOptionElement.h" @@ -286,6 +287,12 @@ if (candidate.isHTMLElement() && toHTMLElement(candidate).hasDirectionAuto()) return false; + if (isHTMLImageElement(candidate) && isHTMLImageElement(element()) && + toHTMLImageElement(candidate).isCollapsed() != + toHTMLImageElement(element()).isCollapsed()) { + return false; + } + if (candidate.isLink() && m_context.elementLinkState() != style->insideLink()) return false;
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp index 291b8c7..127e05c9 100644 --- a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp +++ b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
@@ -39,6 +39,7 @@ #include "core/frame/Settings.h" #include "core/frame/UseCounter.h" #include "core/html/HTMLIFrameElement.h" +#include "core/html/HTMLImageElement.h" #include "core/html/HTMLInputElement.h" #include "core/html/HTMLPlugInElement.h" #include "core/html/HTMLTableCellElement.h" @@ -209,6 +210,12 @@ return; } + if (isHTMLImageElement(element)) { + if (toHTMLImageElement(element).isCollapsed()) + style.setDisplay(EDisplay::None); + return; + } + if (isHTMLTableElement(element)) { // Tables never support the -webkit-* values for text-align and will reset // back to the default.
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp index 4294b0b7..dc5974b 100644 --- a/third_party/WebKit/Source/core/dom/Document.cpp +++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -2640,7 +2640,8 @@ } if (enteredDocument) { - if (!getSecurityOrigin()->canAccess(enteredDocument->getSecurityOrigin())) { + if (!getSecurityOrigin()->isSameSchemeHostPortAndSuborigin( + enteredDocument->getSecurityOrigin())) { exceptionState.throwSecurityError( "Can only call open() on same-origin documents."); return;
diff --git a/third_party/WebKit/Source/core/dom/MessagePort.cpp b/third_party/WebKit/Source/core/dom/MessagePort.cpp index ca49716..136192f 100644 --- a/third_party/WebKit/Source/core/dom/MessagePort.cpp +++ b/third_party/WebKit/Source/core/dom/MessagePort.cpp
@@ -26,7 +26,9 @@ #include "core/dom/MessagePort.h" +#include <memory> #include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/ScriptState.h" #include "bindings/core/v8/SerializedScriptValue.h" #include "bindings/core/v8/SerializedScriptValueFactory.h" #include "core/dom/ExceptionCode.h" @@ -40,7 +42,6 @@ #include "wtf/Functional.h" #include "wtf/PtrUtil.h" #include "wtf/text/AtomicString.h" -#include <memory> namespace blink { @@ -57,7 +58,7 @@ DCHECK(!m_started || !isEntangled()); } -void MessagePort::postMessage(ExecutionContext* context, +void MessagePort::postMessage(ScriptState* scriptState, PassRefPtr<SerializedScriptValue> message, const MessagePortArray& ports, ExceptionState& exceptionState) { @@ -76,7 +77,8 @@ } } std::unique_ptr<MessagePortChannelArray> channels = - MessagePort::disentanglePorts(context, ports, exceptionState); + MessagePort::disentanglePorts(scriptState->getExecutionContext(), ports, + exceptionState); if (exceptionState.hadException()) return;
diff --git a/third_party/WebKit/Source/core/dom/MessagePort.h b/third_party/WebKit/Source/core/dom/MessagePort.h index 43dfa0d..af79d45 100644 --- a/third_party/WebKit/Source/core/dom/MessagePort.h +++ b/third_party/WebKit/Source/core/dom/MessagePort.h
@@ -45,6 +45,7 @@ class ExceptionState; class ExecutionContext; class MessagePort; +class ScriptState; class SerializedScriptValue; // Not to be confused with WebMessagePortChannelArray; this one uses Vector and @@ -62,7 +63,7 @@ static MessagePort* create(ExecutionContext&); ~MessagePort() override; - void postMessage(ExecutionContext*, + void postMessage(ScriptState*, PassRefPtr<SerializedScriptValue> message, const MessagePortArray&, ExceptionState&);
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp b/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp index 5fb8f93..36590e96 100644 --- a/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp +++ b/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp
@@ -139,26 +139,144 @@ EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com:8000/"))); } +TEST_F(CSPSourceTest, SchemeIsEmpty) { + KURL base; + + // Self scheme is http. + { + Persistent<ContentSecurityPolicy> csp(ContentSecurityPolicy::create()); + csp->setupSelf(*SecurityOrigin::createFromString("http://a.com/")); + CSPSource source(csp.get(), "", "a.com", 0, "/", CSPSource::NoWildcard, + CSPSource::NoWildcard); + EXPECT_TRUE(source.matches(KURL(base, "http://a.com"))); + EXPECT_TRUE(source.matches(KURL(base, "https://a.com"))); + EXPECT_TRUE(source.matches(KURL(base, "http-so://a.com"))); + EXPECT_TRUE(source.matches(KURL(base, "https-so://a.com"))); + EXPECT_FALSE(source.matches(KURL(base, "ftp://a.com"))); + } + + // Self scheme is https. + { + Persistent<ContentSecurityPolicy> csp(ContentSecurityPolicy::create()); + csp->setupSelf(*SecurityOrigin::createFromString("https://a.com/")); + CSPSource source(csp.get(), "", "a.com", 0, "/", CSPSource::NoWildcard, + CSPSource::NoWildcard); + EXPECT_FALSE(source.matches(KURL(base, "http://a.com"))); + EXPECT_TRUE(source.matches(KURL(base, "https://a.com"))); + EXPECT_FALSE(source.matches(KURL(base, "http-so://a.com"))); + // TODO(mkwst, arthursonzogni): Maybe it should return true. + // See http://crbug.com/692442 + EXPECT_FALSE(source.matches(KURL(base, "https-so://a.com"))); + EXPECT_FALSE(source.matches(KURL(base, "ftp://a.com"))); + } + + // Self scheme is not in the http familly. + { + Persistent<ContentSecurityPolicy> csp(ContentSecurityPolicy::create()); + csp->setupSelf(*SecurityOrigin::createFromString("ftp://a.com/")); + CSPSource source(csp.get(), "", "a.com", 0, "/", CSPSource::NoWildcard, + CSPSource::NoWildcard); + EXPECT_FALSE(source.matches(KURL(base, "http://a.com"))); + EXPECT_TRUE(source.matches(KURL(base, "ftp://a.com"))); + } + + // Self scheme is unique + { + Persistent<ContentSecurityPolicy> csp(ContentSecurityPolicy::create()); + csp->setupSelf( + *SecurityOrigin::createFromString("non-standard-scheme://a.com/")); + CSPSource source(csp.get(), "", "a.com", 0, "/", CSPSource::NoWildcard, + CSPSource::NoWildcard); + // TODO(mkwst, arthursonzogni): This result might be wrong. + // See http://crbug.com/692449 + EXPECT_FALSE(source.matches(KURL(base, "http://a.com"))); + // TODO(mkwst, arthursonzogni): This result might be wrong. + // See http://crbug.com/692449 + EXPECT_FALSE(source.matches(KURL(base, "non-standard-scheme://a.com"))); + } +} + TEST_F(CSPSourceTest, InsecureHostSchemePortMatchesSecurePort) { KURL base; - CSPSource source(csp.get(), "http", "example.com", 80, "/", - CSPSource::NoWildcard, CSPSource::NoWildcard); - EXPECT_TRUE(source.matches(KURL(base, "http://example.com/"))); - EXPECT_TRUE(source.matches(KURL(base, "http://example.com:80/"))); - EXPECT_TRUE(source.matches(KURL(base, "http://example.com:443/"))); - EXPECT_TRUE(source.matches(KURL(base, "https://example.com/"))); - EXPECT_TRUE(source.matches(KURL(base, "https://example.com:80/"))); - EXPECT_TRUE(source.matches(KURL(base, "https://example.com:443/"))); - EXPECT_FALSE(source.matches(KURL(base, "http://example.com:8443/"))); - EXPECT_FALSE(source.matches(KURL(base, "https://example.com:8443/"))); + // source scheme is "http" + { + CSPSource source(csp.get(), "http", "example.com", 80, "/", + CSPSource::NoWildcard, CSPSource::NoWildcard); + EXPECT_TRUE(source.matches(KURL(base, "http://example.com/"))); + EXPECT_TRUE(source.matches(KURL(base, "http://example.com:80/"))); + // TODO(mkwst, arthursonzogni): It is weird to upgrade the port without the + // sheme. See http://crbug.com/692499 + EXPECT_TRUE(source.matches(KURL(base, "http://example.com:443/"))); + EXPECT_TRUE(source.matches(KURL(base, "https://example.com/"))); + // TODO(mkwst, arthursonzogni): It is weird to upgrade the scheme without + // the port. See http://crbug.com/692499 + EXPECT_TRUE(source.matches(KURL(base, "https://example.com:80/"))); + EXPECT_TRUE(source.matches(KURL(base, "https://example.com:443/"))); - EXPECT_FALSE(source.matches(KURL(base, "http://not-example.com/"))); - EXPECT_FALSE(source.matches(KURL(base, "http://not-example.com:80/"))); - EXPECT_FALSE(source.matches(KURL(base, "http://not-example.com:443/"))); - EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com/"))); - EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com:80/"))); - EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com:443/"))); + EXPECT_FALSE(source.matches(KURL(base, "http://example.com:8443/"))); + EXPECT_FALSE(source.matches(KURL(base, "https://example.com:8443/"))); + + EXPECT_FALSE(source.matches(KURL(base, "http://not-example.com/"))); + EXPECT_FALSE(source.matches(KURL(base, "http://not-example.com:80/"))); + EXPECT_FALSE(source.matches(KURL(base, "http://not-example.com:443/"))); + EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com/"))); + EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com:80/"))); + EXPECT_FALSE(source.matches(KURL(base, "https://not-example.com:443/"))); + } + + // source scheme is empty + { + Persistent<ContentSecurityPolicy> csp(ContentSecurityPolicy::create()); + csp->setupSelf(*SecurityOrigin::createFromString("http://example.com")); + CSPSource source(csp.get(), "", "example.com", 80, "/", + CSPSource::NoWildcard, CSPSource::NoWildcard); + EXPECT_TRUE(source.matches(KURL(base, "http://example.com/"))); + EXPECT_TRUE(source.matches(KURL(base, "https://example.com:443"))); + // TODO(mkwst, arthursonzogni): It is weird to upgrade the port without the + // sheme. See http://crbug.com/692499 + EXPECT_TRUE(source.matches(KURL(base, "http://example.com:443"))); + } +} + +TEST_F(CSPSourceTest, HostMatches) { + KURL base; + Persistent<ContentSecurityPolicy> csp(ContentSecurityPolicy::create()); + csp->setupSelf(*SecurityOrigin::createFromString("http://a.com")); + + // Host is * (source-expression = "http://*") + { + CSPSource source(csp.get(), "http", "", 0, "", CSPSource::HasWildcard, + CSPSource::NoWildcard); + EXPECT_TRUE(source.matches(KURL(base, "http://a.com"))); + EXPECT_TRUE(source.matches(KURL(base, "http://."))); + } + + // Host is *.foo.bar + { + CSPSource source(csp.get(), "", "foo.bar", 0, "", CSPSource::HasWildcard, + CSPSource::NoWildcard); + EXPECT_FALSE(source.matches(KURL(base, "http://a.com"))); + EXPECT_FALSE(source.matches(KURL(base, "http://bar"))); + EXPECT_FALSE(source.matches(KURL(base, "http://foo.bar"))); + EXPECT_FALSE(source.matches(KURL(base, "http://o.bar"))); + EXPECT_TRUE(source.matches(KURL(base, "http://*.foo.bar"))); + EXPECT_TRUE(source.matches(KURL(base, "http://sub.foo.bar"))); + EXPECT_TRUE(source.matches(KURL(base, "http://sub.sub.foo.bar"))); + // Please see http://crbug.com/692505 + EXPECT_TRUE(source.matches(KURL(base, "http://.foo.bar"))); + } + + // Host is exact. + { + CSPSource source(csp.get(), "", "foo.bar", 0, "", CSPSource::NoWildcard, + CSPSource::NoWildcard); + EXPECT_TRUE(source.matches(KURL(base, "http://foo.bar"))); + EXPECT_FALSE(source.matches(KURL(base, "http://sub.foo.bar"))); + EXPECT_FALSE(source.matches(KURL(base, "http://bar"))); + // Please see http://crbug.com/692505 + EXPECT_FALSE(source.matches(KURL(base, "http://.foo.bar"))); + } } TEST_F(CSPSourceTest, DoesNotSubsume) {
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp index e779e7b..a574c90b 100644 --- a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
@@ -334,11 +334,6 @@ return ImageCandidate(); } -bool HTMLImageElement::layoutObjectIsNeeded(const ComputedStyle& style) { - return m_layoutDisposition != LayoutDisposition::Collapsed && - HTMLElement::layoutObjectIsNeeded(style); -} - LayoutObject* HTMLImageElement::createLayoutObject(const ComputedStyle& style) { const ContentData* contentData = style.contentData(); if (contentData && contentData->isImage()) { @@ -367,7 +362,6 @@ void HTMLImageElement::attachLayoutTree(const AttachContext& context) { HTMLElement::attachLayoutTree(context); - if (layoutObject() && layoutObject()->isImage()) { LayoutImage* layoutImage = toLayoutImage(layoutObject()); LayoutImageResource* layoutImageResource = layoutImage->imageResource(); @@ -853,6 +847,10 @@ setLayoutDisposition(LayoutDisposition::PrimaryContent); } +bool HTMLImageElement::isCollapsed() const { + return m_layoutDisposition == LayoutDisposition::Collapsed; +} + void HTMLImageElement::setLayoutDisposition(LayoutDisposition layoutDisposition, bool forceReattach) { if (m_layoutDisposition == layoutDisposition && !forceReattach)
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.h b/third_party/WebKit/Source/core/html/HTMLImageElement.h index b831d14..37bf7e40 100644 --- a/third_party/WebKit/Source/core/html/HTMLImageElement.h +++ b/third_party/WebKit/Source/core/html/HTMLImageElement.h
@@ -114,6 +114,7 @@ virtual void ensureCollapsedOrFallbackContent(); virtual void ensureFallbackForGeneratedContent(); virtual void ensurePrimaryContent(); + bool isCollapsed() const; // CanvasImageSource implementation PassRefPtr<Image> getSourceImageForCanvas(SourceImageStatus*, @@ -187,7 +188,6 @@ void setLayoutDisposition(LayoutDisposition, bool forceReattach = false); void attachLayoutTree(const AttachContext& = AttachContext()) override; - bool layoutObjectIsNeeded(const ComputedStyle&) override; LayoutObject* createLayoutObject(const ComputedStyle&) override; bool canStartSelection() const override { return false; }
diff --git a/third_party/WebKit/Source/core/html/HTMLLinkElement.idl b/third_party/WebKit/Source/core/html/HTMLLinkElement.idl index 8a98ffe..e66a9382 100644 --- a/third_party/WebKit/Source/core/html/HTMLLinkElement.idl +++ b/third_party/WebKit/Source/core/html/HTMLLinkElement.idl
@@ -33,6 +33,7 @@ [CEReactions, Reflect] attribute DOMString hreflang; [CEReactions, Reflect] attribute DOMString type; [Reflect] attribute DOMString as; + [CEReactions, Reflect, ReflectOnly=("","no-referrer","origin","no-referrer-when-downgrade","origin-when-cross-origin","unsafe-url"), ReflectMissing="", ReflectInvalid=""] attribute DOMString referrerPolicy; [CEReactions, PutForwards=value] readonly attribute DOMTokenList sizes; // obsolete members
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp index 9156bba5..ae482da6 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -412,7 +412,7 @@ m_playing(false), m_shouldDelayLoadEvent(false), m_haveFiredLoadedData(false), - m_autoplaying(true), + m_canAutoplay(true), m_muted(false), m_paused(true), m_seeking(false), @@ -863,9 +863,9 @@ // attribute. setPlaybackRate(defaultPlaybackRate()); - // 8 - Set the error attribute to null and the autoplaying flag to true. + // 8 - Set the error attribute to null and the can autoplay flag to true. m_error = nullptr; - m_autoplaying = true; + m_canAutoplay = true; // 9 - Invoke the media element's resource selection algorithm. invokeResourceSelectionAlgorithm(); @@ -1751,7 +1751,7 @@ m_paused = false; scheduleEvent(EventTypeNames::play); scheduleNotifyPlaying(); - m_autoplaying = false; + m_canAutoplay = false; } } else { m_autoplayUmaHelper->recordCrossOriginAutoplayResult( @@ -2098,7 +2098,7 @@ bool HTMLMediaElement::shouldAutoplay() { if (document().isSandboxed(SandboxAutomaticFeatures)) return false; - return m_autoplaying && m_paused && autoplay(); + return m_canAutoplay && m_paused && autoplay(); } String HTMLMediaElement::preload() const { @@ -2247,6 +2247,11 @@ if (m_error && m_error->code() == MediaError::kMediaErrSrcNotSupported) return NotSupportedError; + if (m_autoplayVisibilityObserver) { + m_autoplayVisibilityObserver->stop(); + m_autoplayVisibilityObserver = nullptr; + } + playInternal(); return nullptr; @@ -2284,7 +2289,7 @@ scheduleResolvePlayPromises(); } - m_autoplaying = false; + m_canAutoplay = false; setIgnorePreloadNone(); updatePlayState(); @@ -2299,6 +2304,11 @@ webMediaPlayer()->setBufferingStrategy( WebMediaPlayer::BufferingStrategy::Aggressive); + if (m_autoplayVisibilityObserver) { + m_autoplayVisibilityObserver->stop(); + m_autoplayVisibilityObserver = nullptr; + } + pauseInternal(); } @@ -2308,7 +2318,7 @@ if (m_networkState == kNetworkEmpty) invokeResourceSelectionAlgorithm(); - m_autoplaying = false; + m_canAutoplay = false; if (!m_paused) { m_paused = true; @@ -4001,24 +4011,21 @@ } void HTMLMediaElement::onVisibilityChangedForAutoplay(bool isVisible) { - if (!isVisible) + if (!isVisible) { + if (m_canAutoplay && autoplay()) { + pauseInternal(); + m_canAutoplay = true; + } return; + } if (shouldAutoplay()) { m_paused = false; scheduleEvent(EventTypeNames::play); scheduleNotifyPlaying(); - m_autoplaying = false; updatePlayState(); } - - // TODO(zqzhang): There's still flaky leak if onVisibilityChangedForAutoplay() - // is never called. The leak comes from either ElementVisibilityObserver or - // IntersectionObserver. Should keep an eye on it. See - // https://crbug.com/627539 - m_autoplayVisibilityObserver->stop(); - m_autoplayVisibilityObserver = nullptr; } void HTMLMediaElement::clearWeakMembers(Visitor* visitor) {
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h index d3d8cb9..1b3a7d9 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -487,8 +487,6 @@ void changeNetworkStateFromLoadingToIdle(); - bool isAutoplaying() const { return m_autoplaying; } - WebMediaPlayer::CORSMode corsMode() const; // Returns the "direction of playback" value as specified in the HTML5 spec. @@ -639,7 +637,7 @@ bool m_playing : 1; bool m_shouldDelayLoadEvent : 1; bool m_haveFiredLoadedData : 1; - bool m_autoplaying : 1; + bool m_canAutoplay : 1; bool m_muted : 1; bool m_paused : 1; bool m_seeking : 1;
diff --git a/third_party/WebKit/Source/core/svg/SVGElement.cpp b/third_party/WebKit/Source/core/svg/SVGElement.cpp index d6b1fec8..2167da9 100644 --- a/third_party/WebKit/Source/core/svg/SVGElement.cpp +++ b/third_party/WebKit/Source/core/svg/SVGElement.cpp
@@ -243,6 +243,8 @@ map, *this, propertyFromAttribute(attribute)->baseValueBase()); InvalidatableInterpolation::applyStack(entry.value, environment); } + if (!hasSVGRareData()) + return; svgRareData()->setWebAnimatedAttributesDirty(false); }
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp index b763bb5..371b4e4 100644 --- a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.cpp
@@ -30,7 +30,9 @@ #include "core/workers/DedicatedWorkerGlobalScope.h" +#include <memory> #include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/ScriptState.h" #include "bindings/core/v8/SerializedScriptValue.h" #include "core/frame/csp/ContentSecurityPolicy.h" #include "core/origin_trials/OriginTrialContext.h" @@ -38,7 +40,6 @@ #include "core/workers/InProcessWorkerObjectProxy.h" #include "core/workers/WorkerClients.h" #include "core/workers/WorkerThreadStartupData.h" -#include <memory> namespace blink { @@ -84,13 +85,14 @@ } void DedicatedWorkerGlobalScope::postMessage( - ExecutionContext* context, + ScriptState* scriptState, PassRefPtr<SerializedScriptValue> message, const MessagePortArray& ports, ExceptionState& exceptionState) { // Disentangle the port in preparation for sending it to the remote context. std::unique_ptr<MessagePortChannelArray> channels = - MessagePort::disentanglePorts(context, ports, exceptionState); + MessagePort::disentanglePorts(scriptState->getExecutionContext(), ports, + exceptionState); if (exceptionState.hadException()) return; workerObjectProxy().postMessageToWorkerObject(std::move(message),
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h index f74077e..c5f5af8 100644 --- a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h
@@ -41,6 +41,7 @@ class DedicatedWorkerThread; class InProcessWorkerObjectProxy; +class ScriptState; class WorkerThreadStartupData; class CORE_EXPORT DedicatedWorkerGlobalScope final : public WorkerGlobalScope { @@ -58,7 +59,7 @@ // EventTarget const AtomicString& interfaceName() const override; - void postMessage(ExecutionContext*, + void postMessage(ScriptState*, PassRefPtr<SerializedScriptValue>, const MessagePortArray&, ExceptionState&);
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp index 89d1a73..80a0571 100644 --- a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp +++ b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.cpp
@@ -4,14 +4,15 @@ #include "core/workers/InProcessWorkerBase.h" +#include <memory> #include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/ScriptState.h" #include "core/events/MessageEvent.h" #include "core/frame/csp/ContentSecurityPolicy.h" #include "core/inspector/InspectorInstrumentation.h" #include "core/workers/InProcessWorkerMessagingProxy.h" #include "core/workers/WorkerScriptLoader.h" #include "platform/loader/fetch/ResourceFetcher.h" -#include <memory> namespace blink { @@ -26,14 +27,15 @@ m_contextProxy->parentObjectDestroyed(); } -void InProcessWorkerBase::postMessage(ExecutionContext* context, +void InProcessWorkerBase::postMessage(ScriptState* scriptState, PassRefPtr<SerializedScriptValue> message, const MessagePortArray& ports, ExceptionState& exceptionState) { DCHECK(m_contextProxy); // Disentangle the port in preparation for sending it to the remote context. std::unique_ptr<MessagePortChannelArray> channels = - MessagePort::disentanglePorts(context, ports, exceptionState); + MessagePort::disentanglePorts(scriptState->getExecutionContext(), ports, + exceptionState); if (exceptionState.hadException()) return; m_contextProxy->postMessageToWorkerGlobalScope(std::move(message),
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h index e0a174c..5397737 100644 --- a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h +++ b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h
@@ -21,6 +21,7 @@ class ExceptionState; class ExecutionContext; class InProcessWorkerMessagingProxy; +class ScriptState; class WorkerScriptLoader; // Base class for workers that operate in the same process as the document that @@ -31,7 +32,7 @@ public: ~InProcessWorkerBase() override; - void postMessage(ExecutionContext*, + void postMessage(ScriptState*, PassRefPtr<SerializedScriptValue> message, const MessagePortArray&, ExceptionState&);
diff --git a/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp b/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp index 6fb752a..6664105 100644 --- a/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp +++ b/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp
@@ -66,9 +66,6 @@ addWorkerIsolate(m_isolate); V8Initializer::initializeWorker(m_isolate); - std::unique_ptr<V8IsolateInterruptor> interruptor = - WTF::makeUnique<V8IsolateInterruptor>(m_isolate); - ThreadState::current()->addInterruptor(std::move(interruptor)); ThreadState::current()->registerTraceDOMWrappers( m_isolate, V8GCController::traceDOMWrappers, ScriptWrappableVisitor::invalidateDeadObjectsInMarkingDeque,
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp index 2e9b20e6..6f07428 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -724,14 +724,17 @@ // aria-labelledby. So we need to explicitly call the style resolver to check // whether it's invisible or display:none, rather than relying on the style // cached in the LayoutObject. - Document* doc = getDocument(); - if (doc && doc->frame() && getNode() && getNode()->isElementNode()) { - RefPtr<ComputedStyle> style = - doc->ensureStyleResolver().styleForElement(toElement(getNode())); - return style->display() == EDisplay::None || - style->visibility() != EVisibility::kVisible; + Document* document = getDocument(); + if (!document || !document->frame()) + return false; + if (Node* node = getNode()) { + if (node->isConnected() && node->isElementNode()) { + RefPtr<ComputedStyle> style = + document->ensureStyleResolver().styleForElement(toElement(node)); + return style->display() == EDisplay::None || + style->visibility() != EVisibility::kVisible; + } } - return false; }
diff --git a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp index 8bdb908..08b41a1a 100644 --- a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp
@@ -4,6 +4,8 @@ #include "modules/compositorworker/CompositorWorkerGlobalScope.h" +#include <memory> +#include "bindings/core/v8/ScriptState.h" #include "bindings/core/v8/SerializedScriptValue.h" #include "core/dom/CompositorWorkerProxyClient.h" #include "core/workers/InProcessWorkerObjectProxy.h" @@ -11,7 +13,6 @@ #include "modules/EventTargetModules.h" #include "modules/compositorworker/CompositorWorkerThread.h" #include "wtf/AutoReset.h" -#include <memory> namespace blink { @@ -68,13 +69,14 @@ } void CompositorWorkerGlobalScope::postMessage( - ExecutionContext* executionContext, + ScriptState* scriptState, PassRefPtr<SerializedScriptValue> message, const MessagePortArray& ports, ExceptionState& exceptionState) { // Disentangle the port in preparation for sending it to the remote context. std::unique_ptr<MessagePortChannelArray> channels = - MessagePort::disentanglePorts(executionContext, ports, exceptionState); + MessagePort::disentanglePorts(scriptState->getExecutionContext(), ports, + exceptionState); if (exceptionState.hadException()) return; workerObjectProxy().postMessageToWorkerObject(std::move(message),
diff --git a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h index c2978e03..a8953fa 100644 --- a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h +++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h
@@ -33,7 +33,7 @@ // EventTarget const AtomicString& interfaceName() const override; - void postMessage(ExecutionContext*, + void postMessage(ScriptState*, PassRefPtr<SerializedScriptValue>, const MessagePortArray&, ExceptionState&);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/Client.idl b/third_party/WebKit/Source/modules/serviceworkers/Client.idl index 1e1f77e..6d0d439 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/Client.idl +++ b/third_party/WebKit/Source/modules/serviceworkers/Client.idl
@@ -10,5 +10,5 @@ readonly attribute USVString url; readonly attribute ContextFrameType frameType; readonly attribute DOMString id; - [PostMessage, RaisesException, CallWith=ExecutionContext] void postMessage(SerializedScriptValue message, optional sequence<Transferable> transfer); + [PostMessage, RaisesException, CallWith=ScriptState] void postMessage(SerializedScriptValue message, optional sequence<Transferable> transfer); };
diff --git a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp index 98c5f09..6a61ae0 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp
@@ -4,6 +4,7 @@ #include "modules/serviceworkers/InstallEvent.h" +#include "bindings/core/v8/ScriptState.h" #include "core/dom/ExceptionCode.h" #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h" #include "public/platform/WebSecurityOrigin.h" @@ -23,7 +24,7 @@ InstallEvent::~InstallEvent() {} -void InstallEvent::registerForeignFetch(ExecutionContext* executionContext, +void InstallEvent::registerForeignFetch(ScriptState* scriptState, const ForeignFetchOptions& options, ExceptionState& exceptionState) { if (!isBeingDispatched()) { @@ -55,6 +56,7 @@ } } + ExecutionContext* executionContext = scriptState->getExecutionContext(); ServiceWorkerGlobalScopeClient* client = ServiceWorkerGlobalScopeClient::from(executionContext);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.h b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.h index f7b18ef..29eda4c 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.h +++ b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.h
@@ -24,7 +24,7 @@ ~InstallEvent() override; - void registerForeignFetch(ExecutionContext*, + void registerForeignFetch(ScriptState*, const ForeignFetchOptions&, ExceptionState&);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.idl b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.idl index 97cbdbc..c19df52a 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.idl +++ b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.idl
@@ -8,5 +8,5 @@ Constructor(DOMString type, optional ExtendableEventInit eventInitDict), Exposed=ServiceWorker, ] interface InstallEvent : ExtendableEvent { - [OriginTrialEnabled=ForeignFetch, CallWith=ExecutionContext, RaisesException] void registerForeignFetch(ForeignFetchOptions options); + [OriginTrialEnabled=ForeignFetch, CallWith=ScriptState, RaisesException] void registerForeignFetch(ForeignFetchOptions options); };
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp index ea09389..b319462 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
@@ -4,6 +4,7 @@ #include "modules/serviceworkers/NavigatorServiceWorker.h" +#include "bindings/core/v8/ScriptState.h" #include "core/dom/Document.h" #include "core/frame/LocalDOMWindow.h" #include "core/frame/LocalFrame.h" @@ -51,9 +52,10 @@ } ServiceWorkerContainer* NavigatorServiceWorker::serviceWorker( - ExecutionContext* executionContext, + ScriptState* scriptState, Navigator& navigator, ExceptionState& exceptionState) { + ExecutionContext* executionContext = scriptState->getExecutionContext(); DCHECK(!navigator.frame() || executionContext->getSecurityOrigin()->canAccessCheckSuborigins( navigator.frame()->securityContext()->getSecurityOrigin())); @@ -62,9 +64,10 @@ } ServiceWorkerContainer* NavigatorServiceWorker::serviceWorker( - ExecutionContext* executionContext, + ScriptState* scriptState, Navigator& navigator, String& errorMessage) { + ExecutionContext* executionContext = scriptState->getExecutionContext(); DCHECK(!navigator.frame() || executionContext->getSecurityOrigin()->canAccessCheckSuborigins( navigator.frame()->securityContext()->getSecurityOrigin()));
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.h b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.h index 4bb8eb5..78d14fb 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.h +++ b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.h
@@ -15,6 +15,7 @@ class Document; class ExceptionState; class Navigator; +class ScriptState; class ServiceWorkerContainer; class MODULES_EXPORT NavigatorServiceWorker final @@ -26,10 +27,10 @@ static NavigatorServiceWorker* from(Document&); static NavigatorServiceWorker& from(Navigator&); static NavigatorServiceWorker* toNavigatorServiceWorker(Navigator&); - static ServiceWorkerContainer* serviceWorker(ExecutionContext*, + static ServiceWorkerContainer* serviceWorker(ScriptState*, Navigator&, ExceptionState&); - static ServiceWorkerContainer* serviceWorker(ExecutionContext*, + static ServiceWorkerContainer* serviceWorker(ScriptState*, Navigator&, String& errorMessage); void clearServiceWorker();
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.idl b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.idl index d173434..3d987003 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.idl +++ b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.idl
@@ -4,5 +4,5 @@ // https://w3c.github.io/ServiceWorker/#navigator-service-worker partial interface Navigator { - [RaisesException, CallWith=ExecutionContext] readonly attribute ServiceWorkerContainer serviceWorker; + [RaisesException, CallWith=ScriptState] readonly attribute ServiceWorkerContainer serviceWorker; };
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp index f3eeaf7..824dce3 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp
@@ -30,7 +30,9 @@ #include "modules/serviceworkers/ServiceWorker.h" +#include <memory> #include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/ScriptState.h" #include "core/dom/ExceptionCode.h" #include "core/dom/MessagePort.h" #include "core/events/Event.h" @@ -40,7 +42,6 @@ #include "public/platform/WebSecurityOrigin.h" #include "public/platform/WebString.h" #include "public/platform/modules/serviceworker/WebServiceWorkerState.h" -#include <memory> namespace blink { @@ -48,7 +49,7 @@ return EventTargetNames::ServiceWorker; } -void ServiceWorker::postMessage(ExecutionContext* context, +void ServiceWorker::postMessage(ScriptState* scriptState, PassRefPtr<SerializedScriptValue> message, const MessagePortArray& ports, ExceptionState& exceptionState) { @@ -63,7 +64,8 @@ // Disentangle the port in preparation for sending it to the remote context. std::unique_ptr<MessagePortChannelArray> channels = - MessagePort::disentanglePorts(context, ports, exceptionState); + MessagePort::disentanglePorts(scriptState->getExecutionContext(), ports, + exceptionState); if (exceptionState.hadException()) return; if (m_handle->serviceWorker()->state() == WebServiceWorkerStateRedundant) {
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h index 27c77bf..3e4958d 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h
@@ -43,6 +43,8 @@ namespace blink { +class ScriptState; + class MODULES_EXPORT ServiceWorker final : public AbstractWorker, public ActiveScriptWrappable<ServiceWorker>, @@ -60,7 +62,7 @@ // Eager finalization needed to promptly release owned WebServiceWorker. EAGERLY_FINALIZE(); - void postMessage(ExecutionContext*, + void postMessage(ScriptState*, PassRefPtr<SerializedScriptValue> message, const MessagePortArray&, ExceptionState&);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp index 7816a93..d5a99c61 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp
@@ -5,13 +5,14 @@ #include "modules/serviceworkers/ServiceWorkerClient.h" #include "modules/serviceworkers/ServiceWorkerWindowClient.h" +#include <memory> #include "bindings/core/v8/CallbackPromiseAdapter.h" #include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/ScriptState.h" #include "bindings/core/v8/SerializedScriptValue.h" #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h" #include "public/platform/WebString.h" #include "wtf/RefPtr.h" -#include <memory> namespace blink { @@ -63,10 +64,11 @@ return String(); } -void ServiceWorkerClient::postMessage(ExecutionContext* context, +void ServiceWorkerClient::postMessage(ScriptState* scriptState, PassRefPtr<SerializedScriptValue> message, const MessagePortArray& ports, ExceptionState& exceptionState) { + ExecutionContext* context = scriptState->getExecutionContext(); // Disentangle the port in preparation for sending it to the remote context. std::unique_ptr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(context, ports, exceptionState);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h index 1da5eba..fb89c1e 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h
@@ -16,8 +16,8 @@ namespace blink { -class ExecutionContext; class ScriptPromiseResolver; +class ScriptState; class MODULES_EXPORT ServiceWorkerClient : public GarbageCollectedFinalized<ServiceWorkerClient>, @@ -38,7 +38,7 @@ String url() const { return m_url; } String frameType() const; String id() const { return m_uuid; } - void postMessage(ExecutionContext*, + void postMessage(ScriptState*, PassRefPtr<SerializedScriptValue> message, const MessagePortArray&, ExceptionState&);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerLinkResource.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerLinkResource.cpp index d8414347..a8acb36 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerLinkResource.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerLinkResource.cpp
@@ -5,6 +5,7 @@ #include "modules/serviceworkers/ServiceWorkerLinkResource.h" #include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/ScriptState.h" #include "core/dom/Document.h" #include "core/frame/DOMWindow.h" #include "core/frame/LocalFrame.h" @@ -81,7 +82,8 @@ String errorMessage; ServiceWorkerContainer* container = NavigatorServiceWorker::serviceWorker( - &document, *document.frame()->domWindow()->navigator(), errorMessage); + ScriptState::forMainWorld(m_owner->document().frame()), + *document.frame()->domWindow()->navigator(), errorMessage); if (!container) { document.addConsoleMessage(ConsoleMessage::create(
diff --git a/third_party/WebKit/Source/platform/heap/BlinkGCInterruptor.cpp b/third_party/WebKit/Source/platform/heap/BlinkGCInterruptor.cpp index 1593d586e..0409eaa 100644 --- a/third_party/WebKit/Source/platform/heap/BlinkGCInterruptor.cpp +++ b/third_party/WebKit/Source/platform/heap/BlinkGCInterruptor.cpp
@@ -12,7 +12,6 @@ void BlinkGCInterruptor::onInterrupted() { ThreadState* state = ThreadState::current(); ASSERT(state); - ASSERT(!state->isAtSafePoint()); state->safePoint(BlinkGC::HeapPointersOnStack); }
diff --git a/third_party/WebKit/Source/platform/heap/Heap.cpp b/third_party/WebKit/Source/platform/heap/Heap.cpp index bf086aa..2d986a1 100644 --- a/third_party/WebKit/Source/platform/heap/Heap.cpp +++ b/third_party/WebKit/Source/platform/heap/Heap.cpp
@@ -156,24 +156,9 @@ m_ephemeronStack(CallbackStack::create()) { if (ThreadState::current()->isMainThread()) s_mainThreadHeap = this; - - MutexLocker locker(ThreadHeap::allHeapsMutex()); - allHeaps().insert(this); } ThreadHeap::~ThreadHeap() { - MutexLocker locker(ThreadHeap::allHeapsMutex()); - allHeaps().remove(this); -} - -RecursiveMutex& ThreadHeap::allHeapsMutex() { - DEFINE_THREAD_SAFE_STATIC_LOCAL(RecursiveMutex, mutex, (new RecursiveMutex)); - return mutex; -} - -HashSet<ThreadHeap*>& ThreadHeap::allHeaps() { - DEFINE_STATIC_LOCAL(HashSet<ThreadHeap*>, heaps, ()); - return heaps; } void ThreadHeap::attach(ThreadState* thread) { @@ -212,15 +197,6 @@ } return nullptr; } - -bool ThreadHeap::isAtSafePoint() { - MutexLocker locker(m_threadAttachMutex); - for (ThreadState* state : m_threads) { - if (!state->isAtSafePoint()) - return false; - } - return true; -} #endif Address ThreadHeap::checkAndMarkPointer(Visitor* visitor, Address address) {
diff --git a/third_party/WebKit/Source/platform/heap/Heap.h b/third_party/WebKit/Source/platform/heap/Heap.h index ee57a32..905b19c 100644 --- a/third_party/WebKit/Source/platform/heap/Heap.h +++ b/third_party/WebKit/Source/platform/heap/Heap.h
@@ -233,7 +233,6 @@ static ThreadHeap* mainThreadHeap() { return s_mainThreadHeap; } #if DCHECK_IS_ON() - bool isAtSafePoint(); BasePage* findPageFromAddress(Address); #endif @@ -294,9 +293,6 @@ void enterSafePoint(ThreadState*); void leaveSafePoint(); - static RecursiveMutex& allHeapsMutex(); - static HashSet<ThreadHeap*>& allHeaps(); - // Is the finalizable GC object still alive, but slated for lazy sweeping? // If a lazy sweep is in progress, returns true if the object was found // to be not reachable during the marking phase, but it has yet to be swept
diff --git a/third_party/WebKit/Source/platform/heap/SafePoint.h b/third_party/WebKit/Source/platform/heap/SafePoint.h index 4fe8a90..79930e7 100644 --- a/third_party/WebKit/Source/platform/heap/SafePoint.h +++ b/third_party/WebKit/Source/platform/heap/SafePoint.h
@@ -19,7 +19,6 @@ ThreadState* state = ThreadState::current()) : m_state(state) { if (m_state) { - RELEASE_ASSERT(!m_state->isAtSafePoint()); m_state->enterSafePoint(stackState, this); } }
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.cpp b/third_party/WebKit/Source/platform/heap/ThreadState.cpp index 2860b2c8..d1d0619 100644 --- a/third_party/WebKit/Source/platform/heap/ThreadState.cpp +++ b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
@@ -105,7 +105,6 @@ m_startOfStack(reinterpret_cast<intptr_t*>(WTF::getStackStart())), m_endOfStack(reinterpret_cast<intptr_t*>(WTF::getStackStart())), m_safePointScopeMarker(nullptr), - m_atSafePoint(false), m_interruptors(), m_sweepForbidden(false), m_noAllocationCount(0), @@ -620,30 +619,27 @@ "ThreadState::performIdleLazySweep", "idleDeltaInSeconds", deadlineSeconds - monotonicallyIncreasingTime()); - bool sweepCompleted = true; SweepForbiddenScope scope(this); - { - double startTime = WTF::currentTimeMS(); - ScriptForbiddenIfMainThreadScope scriptForbiddenScope; + ScriptForbiddenIfMainThreadScope scriptForbiddenScope; - for (int i = 0; i < BlinkGC::NumberOfArenas; i++) { - // lazySweepWithDeadline() won't check the deadline until it sweeps - // 10 pages. So we give a small slack for safety. - double slack = 0.001; - double remainingBudget = - deadlineSeconds - slack - monotonicallyIncreasingTime(); - if (remainingBudget <= 0 || - !m_arenas[i]->lazySweepWithDeadline(deadlineSeconds)) { - // We couldn't finish the sweeping within the deadline. - // We request another idle task for the remaining sweeping. - scheduleIdleLazySweep(); - sweepCompleted = false; - break; - } + double startTime = WTF::currentTimeMS(); + bool sweepCompleted = true; + for (int i = 0; i < BlinkGC::NumberOfArenas; i++) { + // lazySweepWithDeadline() won't check the deadline until it sweeps + // 10 pages. So we give a small slack for safety. + double slack = 0.001; + double remainingBudget = + deadlineSeconds - slack - monotonicallyIncreasingTime(); + if (remainingBudget <= 0 || + !m_arenas[i]->lazySweepWithDeadline(deadlineSeconds)) { + // We couldn't finish the sweeping within the deadline. + // We request another idle task for the remaining sweeping. + scheduleIdleLazySweep(); + sweepCompleted = false; + break; } - - accumulateSweepingTime(WTF::currentTimeMS() - startTime); } + accumulateSweepingTime(WTF::currentTimeMS() - startTime); if (sweepCompleted) postSweep(); @@ -982,25 +978,23 @@ return; SweepForbiddenScope scope(this); - { - ScriptForbiddenIfMainThreadScope scriptForbiddenScope; + ScriptForbiddenIfMainThreadScope scriptForbiddenScope; - TRACE_EVENT0("blink_gc,devtools.timeline", "ThreadState::completeSweep"); - double startTime = WTF::currentTimeMS(); + TRACE_EVENT0("blink_gc,devtools.timeline", "ThreadState::completeSweep"); + double startTime = WTF::currentTimeMS(); - static_assert(BlinkGC::EagerSweepArenaIndex == 0, - "Eagerly swept arenas must be processed first."); - for (int i = 0; i < BlinkGC::NumberOfArenas; i++) - m_arenas[i]->completeSweep(); + static_assert(BlinkGC::EagerSweepArenaIndex == 0, + "Eagerly swept arenas must be processed first."); + for (int i = 0; i < BlinkGC::NumberOfArenas; i++) + m_arenas[i]->completeSweep(); - double timeForCompleteSweep = WTF::currentTimeMS() - startTime; - accumulateSweepingTime(timeForCompleteSweep); + double timeForCompleteSweep = WTF::currentTimeMS() - startTime; + accumulateSweepingTime(timeForCompleteSweep); - if (isMainThread()) { - DEFINE_STATIC_LOCAL(CustomCountHistogram, completeSweepHistogram, - ("BlinkGC.CompleteSweep", 1, 10 * 1000, 50)); - completeSweepHistogram.count(timeForCompleteSweep); - } + if (isMainThread()) { + DEFINE_STATIC_LOCAL(CustomCountHistogram, completeSweepHistogram, + ("BlinkGC.CompleteSweep", 1, 10 * 1000, 50)); + completeSweepHistogram.count(timeForCompleteSweep); } postSweep(); @@ -1104,7 +1098,6 @@ ThreadHeap::reportMemoryUsageForTracing(); runScheduledGC(stackState); - ASSERT(!m_atSafePoint); m_stackState = BlinkGC::HeapPointersOnStack; } @@ -1142,8 +1135,6 @@ #endif ASSERT(stackState == BlinkGC::NoHeapPointersOnStack || scopeMarker); runScheduledGC(stackState); - ASSERT(!m_atSafePoint); - m_atSafePoint = true; m_stackState = stackState; m_safePointScopeMarker = scopeMarker; m_heap->enterSafePoint(this); @@ -1151,9 +1142,7 @@ void ThreadState::leaveSafePoint() { ASSERT(checkThread()); - ASSERT(m_atSafePoint); m_heap->leaveSafePoint(); - m_atSafePoint = false; m_stackState = BlinkGC::HeapPointersOnStack; clearSafePointScopeMarker(); } @@ -1281,11 +1270,11 @@ ASSERT(!sweepForbidden()); TRACE_EVENT0("blink_gc", "ThreadState::invokePreFinalizers"); + SweepForbiddenScope sweepForbidden(this); + ScriptForbiddenIfMainThreadScope scriptForbidden; + double startTime = WTF::currentTimeMS(); if (!m_orderedPreFinalizers.isEmpty()) { - SweepForbiddenScope sweepForbidden(this); - ScriptForbiddenIfMainThreadScope scriptForbidden; - // Call the prefinalizers in the opposite order to their registration. // // The prefinalizer callback wrapper returns |true| when its associated
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.h b/third_party/WebKit/Source/platform/heap/ThreadState.h index 82c9934c..f709153 100644 --- a/third_party/WebKit/Source/platform/heap/ThreadState.h +++ b/third_party/WebKit/Source/platform/heap/ThreadState.h
@@ -249,9 +249,7 @@ // Support for disallowing allocation. Mainly used for sanity // checks asserts. - bool isAllocationAllowed() const { - return !isAtSafePoint() && !m_noAllocationCount; - } + bool isAllocationAllowed() const { return !m_noAllocationCount; } void enterNoAllocationScope() { m_noAllocationCount++; } void leaveNoAllocationScope() { m_noAllocationCount--; } bool isWrapperTracingForbidden() { return isMixinInConstruction(); } @@ -333,7 +331,6 @@ // Mark current thread as running inside safepoint. void enterSafePoint(BlinkGC::StackState, void*); void leaveSafePoint(); - bool isAtSafePoint() const { return m_atSafePoint; } void addInterruptor(std::unique_ptr<BlinkGCInterruptor>); @@ -639,7 +636,6 @@ void* m_safePointScopeMarker; Vector<Address> m_safePointStackCopy; - bool m_atSafePoint; Vector<std::unique_ptr<BlinkGCInterruptor>> m_interruptors; bool m_sweepForbidden; size_t m_noAllocationCount;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc index 312136a..7406dc4 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.cc
@@ -39,11 +39,16 @@ return task_queue_manager_->delegate()->NowTicks(); } -void RealTimeDomain::RequestWakeup(base::TimeTicks now, base::TimeDelta delay) { +void RealTimeDomain::RequestWakeupAt(base::TimeTicks now, + base::TimeTicks run_time) { // NOTE this is only called if the scheduled runtime is sooner than any // previously scheduled runtime, or there is no (outstanding) previously // scheduled runtime. - task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, now, delay); + task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, this, now, run_time); +} + +void RealTimeDomain::CancelWakeupAt(base::TimeTicks run_time) { + task_queue_manager_->CancelDelayedWork(this, run_time); } base::Optional<base::TimeDelta> RealTimeDomain::DelayTillNextTask(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h index 6883599..1457204 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h +++ b/third_party/WebKit/Source/platform/scheduler/base/real_time_domain.h
@@ -29,7 +29,8 @@ protected: void OnRegisterWithTaskQueueManager( TaskQueueManager* task_queue_manager) override; - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override; + void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override; + void CancelWakeupAt(base::TimeTicks run_time) override; void AsValueIntoInternal( base::trace_event::TracedValue* state) const override;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc index 762f079..2d1e295 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
@@ -276,11 +276,14 @@ main_thread_only().delayed_incoming_queue.push(std::move(pending_task)); // If |pending_task| is at the head of the queue, then make sure a wakeup - // is requested. - if (main_thread_only().delayed_incoming_queue.top().delayed_run_time == - delayed_run_time) { - main_thread_only().time_domain->ScheduleDelayedWork( - this, pending_task.delayed_run_time, now); + // is requested if the queue is enabled. Note we still want to schedule a + // wakeup even if blocked by a fence, because we'd break throttling logic + // otherwise. + base::TimeTicks next_delayed_task = + main_thread_only().delayed_incoming_queue.top().delayed_run_time; + if (next_delayed_task == delayed_run_time && IsQueueEnabled()) { + main_thread_only().time_domain->ScheduleDelayedWork(this, delayed_run_time, + now); } TraceQueueSize(false); @@ -404,7 +407,8 @@ } base::Optional<base::TimeTicks> TaskQueueImpl::GetNextScheduledWakeUp() { - if (main_thread_only().delayed_incoming_queue.empty()) + // Note we don't scheduled a wakeup for disabled queues. + if (main_thread_only().delayed_incoming_queue.empty() || !IsQueueEnabled()) return base::nullopt; return main_thread_only().delayed_incoming_queue.top().delayed_run_time; @@ -799,10 +803,18 @@ return; if (enable) { + if (!main_thread_only().delayed_incoming_queue.empty()) { + main_thread_only().time_domain->ScheduleDelayedWork( + this, + main_thread_only().delayed_incoming_queue.top().delayed_run_time, + main_thread_only().time_domain->Now()); + } // Note the selector calls TaskQueueManager::OnTaskQueueEnabled which posts // a DoWork if needed. main_thread_only().task_queue_manager->selector_.EnableQueue(this); } else { + if (!main_thread_only().delayed_incoming_queue.empty()) + main_thread_only().time_domain->CancelDelayedWork(this); main_thread_only().task_queue_manager->selector_.DisableQueue(this); } }
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc index e42fee1..49fd2f8 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -230,10 +230,13 @@ void TaskQueueManager::MaybeScheduleDelayedWork( const tracked_objects::Location& from_here, + TimeDomain* requesting_time_domain, base::TimeTicks now, - base::TimeDelta delay) { + base::TimeTicks run_time) { DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK_GE(delay, base::TimeDelta()); + // Make sure we don't cancel another TimeDomain's wakeup. + DCHECK(!next_delayed_do_work_ || + next_delayed_do_work_.time_domain() == requesting_time_domain); { base::AutoLock lock(any_thread_lock_); @@ -247,22 +250,35 @@ } } - // De-duplicate DoWork posts. - base::TimeTicks run_time = now + delay; - if (next_scheduled_delayed_do_work_time_ <= run_time && - !next_scheduled_delayed_do_work_time_.is_null()) + // If there's a delayed DoWork scheduled to run sooner, we don't need to do + // anything because DoWork will post a delayed continuation as needed. + if (next_delayed_do_work_ && next_delayed_do_work_.run_time() <= run_time) return; + cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); + + base::TimeDelta delay = std::max(base::TimeDelta(), run_time - now); TRACE_EVENT1(disabled_by_default_tracing_category_, "TaskQueueManager::MaybeScheduleDelayedWork::PostDelayedTask", "delay_ms", delay.InMillisecondsF()); cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); - next_scheduled_delayed_do_work_time_ = run_time; + next_delayed_do_work_ = NextDelayedDoWork(run_time, requesting_time_domain); delegate_->PostDelayedTask( from_here, cancelable_delayed_do_work_closure_.callback(), delay); } +void TaskQueueManager::CancelDelayedWork(TimeDomain* requesting_time_domain, + base::TimeTicks run_time) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + if (next_delayed_do_work_.run_time() != run_time) + return; + + DCHECK_EQ(next_delayed_do_work_.time_domain(), requesting_time_domain); + cancelable_delayed_do_work_closure_.Cancel(); + next_delayed_do_work_.Clear(); +} + void TaskQueueManager::DoWork(bool delayed) { DCHECK(main_thread_checker_.CalledOnValidThread()); TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", "delayed", @@ -274,10 +290,9 @@ queues_to_delete_.clear(); // This must be done before running any tasks because they could invoke a - // nested message loop and we risk having a stale - // |next_scheduled_delayed_do_work_time_|. + // nested message loop and we risk having a stale |next_delayed_do_work_|. if (delayed) - next_scheduled_delayed_do_work_time_ = base::TimeTicks(); + next_delayed_do_work_.Clear(); for (int i = 0; i < work_batch_size_; i++) { IncomingImmediateWorkMap queues_to_reload; @@ -339,7 +354,7 @@ { MoveableAutoLock lock(any_thread_lock_); - base::Optional<base::TimeDelta> next_delay = + base::Optional<NextTaskDelay> next_delay = ComputeDelayTillNextTaskLocked(&lazy_now); any_thread().do_work_running_count--; @@ -353,11 +368,10 @@ } void TaskQueueManager::PostDoWorkContinuationLocked( - base::Optional<base::TimeDelta> next_delay, + base::Optional<NextTaskDelay> next_delay, LazyNow* lazy_now, MoveableAutoLock&& lock) { DCHECK(main_thread_checker_.CalledOnValidThread()); - base::TimeDelta delay; { MoveableAutoLock auto_lock(std::move(lock)); @@ -365,8 +379,8 @@ // If there are no tasks left then we don't need to post a continuation. if (!next_delay) { // If there's a pending delayed DoWork, cancel it because it's not needed. - if (!next_scheduled_delayed_do_work_time_.is_null()) { - next_scheduled_delayed_do_work_time_ = base::TimeTicks(); + if (next_delayed_do_work_) { + next_delayed_do_work_.Clear(); cancelable_delayed_do_work_closure_.Cancel(); } return; @@ -376,42 +390,37 @@ if (any_thread().immediate_do_work_posted_count > 0) return; - delay = next_delay.value(); - - // This isn't supposed to happen, but in case it does convert to - // non-delayed. - if (delay < base::TimeDelta()) - delay = base::TimeDelta(); - - if (delay.is_zero()) { + if (next_delay->delay() <= base::TimeDelta()) { // If a delayed DoWork is pending then we don't need to post a // continuation because it should run immediately. - if (!next_scheduled_delayed_do_work_time_.is_null() && - next_scheduled_delayed_do_work_time_ <= lazy_now->Now()) { + if (next_delayed_do_work_ && + next_delayed_do_work_.run_time() <= lazy_now->Now()) { return; } any_thread().immediate_do_work_posted_count++; - } else { - base::TimeTicks run_time = lazy_now->Now() + delay; - if (next_scheduled_delayed_do_work_time_ == run_time) - return; - - next_scheduled_delayed_do_work_time_ = run_time; } } // We avoid holding |any_thread_lock_| while posting the task. - if (delay.is_zero()) { + if (next_delay->delay() <= base::TimeDelta()) { delegate_->PostTask(FROM_HERE, immediate_do_work_closure_); } else { + base::TimeTicks run_time = lazy_now->Now() + next_delay->delay(); + + if (next_delayed_do_work_.run_time() == run_time) + return; + + next_delayed_do_work_ = + NextDelayedDoWork(run_time, next_delay->time_domain()); cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_); - delegate_->PostDelayedTask( - FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay); + delegate_->PostDelayedTask(FROM_HERE, + cancelable_delayed_do_work_closure_.callback(), + next_delay->delay()); } } -base::Optional<base::TimeDelta> +base::Optional<TaskQueueManager::NextTaskDelay> TaskQueueManager::ComputeDelayTillNextTaskLocked(LazyNow* lazy_now) { DCHECK(main_thread_checker_.CalledOnValidThread()); @@ -420,27 +429,32 @@ // check is equavalent to calling ReloadEmptyWorkQueues first. for (const auto& pair : any_thread().has_incoming_immediate_work) { if (pair.first->CouldTaskRun(pair.second)) - return base::TimeDelta(); + return NextTaskDelay(); } // If the selector has non-empty queues we trivially know there is immediate // work to be done. if (!selector_.EnabledWorkQueuesEmpty()) - return base::TimeDelta(); + return NextTaskDelay(); // Otherwise we need to find the shortest delay, if any. NB we don't need to // call WakeupReadyDelayedQueues because it's assumed DelayTillNextTask will // return base::TimeDelta>() if the delayed task is due to run now. - base::Optional<base::TimeDelta> next_continuation; + base::Optional<NextTaskDelay> delay_till_next_task; for (TimeDomain* time_domain : time_domains_) { - base::Optional<base::TimeDelta> continuation = + base::Optional<base::TimeDelta> delay = time_domain->DelayTillNextTask(lazy_now); - if (!continuation) + if (!delay) continue; - if (!next_continuation || next_continuation.value() > continuation.value()) - next_continuation = continuation; + + NextTaskDelay task_delay = (delay.value() == base::TimeDelta()) + ? NextTaskDelay() + : NextTaskDelay(delay.value(), time_domain); + + if (!delay_till_next_task || delay_till_next_task > task_delay) + delay_till_next_task = task_delay; } - return next_continuation; + return delay_till_next_task; } bool TaskQueueManager::SelectWorkQueueToService(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h index 21c4aedc..fa568cc6 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h +++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -74,8 +74,14 @@ // runner. These delayed tasks are de-duplicated. Must be called on the thread // this class was created on. void MaybeScheduleDelayedWork(const tracked_objects::Location& from_here, + TimeDomain* requesting_time_domain, base::TimeTicks now, - base::TimeDelta delay); + base::TimeTicks run_time); + + // Cancels a delayed task to process work at |run_time|, previously requested + // with MaybeScheduleDelayedWork. + void CancelDelayedWork(TimeDomain* requesting_time_domain, + base::TimeTicks run_time); // Set the number of tasks executed in a single invocation of the task queue // manager. Increasing the batch size can reduce the overhead of yielding @@ -149,11 +155,73 @@ // need them, you can turn them off. void SetRecordTaskDelayHistograms(bool record_task_delay_histograms); - private: + protected: friend class LazyNow; friend class internal::TaskQueueImpl; friend class TaskQueueManagerTest; + // Intermediate data structure, used to compute NextDelayedDoWork. + class NextTaskDelay { + public: + NextTaskDelay() : time_domain_(nullptr) {} + + using AllowAnyDelayForTesting = int; + + NextTaskDelay(base::TimeDelta delay, TimeDomain* time_domain) + : delay_(delay), time_domain_(time_domain) { + DCHECK_GT(delay, base::TimeDelta()); + DCHECK(time_domain); + } + + NextTaskDelay(base::TimeDelta delay, + TimeDomain* time_domain, + AllowAnyDelayForTesting) + : delay_(delay), time_domain_(time_domain) { + DCHECK(time_domain); + } + + base::TimeDelta delay() const { return delay_; } + TimeDomain* time_domain() const { return time_domain_; } + + bool operator>(const NextTaskDelay& other) const { + return delay_ > other.delay_; + } + + bool operator<(const NextTaskDelay& other) const { + return delay_ < other.delay_; + } + + private: + base::TimeDelta delay_; + TimeDomain* time_domain_; + }; + + private: + // Represents a scheduled delayed DoWork (if any). Only public for testing. + class NextDelayedDoWork { + public: + NextDelayedDoWork() : time_domain_(nullptr) {} + NextDelayedDoWork(base::TimeTicks run_time, TimeDomain* time_domain) + : run_time_(run_time), time_domain_(time_domain) { + DCHECK_NE(run_time, base::TimeTicks()); + DCHECK(time_domain); + } + + base::TimeTicks run_time() const { return run_time_; } + TimeDomain* time_domain() const { return time_domain_; } + + void Clear() { + run_time_ = base::TimeTicks(); + time_domain_ = nullptr; + } + + explicit operator bool() const { return !run_time_.is_null(); } + + private: + base::TimeTicks run_time_; + TimeDomain* time_domain_; + }; + class DeletionSentinel : public base::RefCounted<DeletionSentinel> { private: friend class base::RefCounted<DeletionSentinel>; @@ -178,7 +246,7 @@ void DoWork(bool delayed); // Post a DoWork continuation if |next_delay| is not empty. - void PostDoWorkContinuationLocked(base::Optional<base::TimeDelta> next_delay, + void PostDoWorkContinuationLocked(base::Optional<NextTaskDelay> next_delay, LazyNow* lazy_now, MoveableAutoLock&& lock); @@ -216,7 +284,7 @@ // Calls DelayTillNextTask on all time domains and returns the smallest delay // requested if any. - base::Optional<base::TimeDelta> ComputeDelayTillNextTaskLocked( + base::Optional<NextTaskDelay> ComputeDelayTillNextTaskLocked( LazyNow* lazy_now); void MaybeRecordTaskDelayHistograms( @@ -293,7 +361,7 @@ return any_thread_; } - base::TimeTicks next_scheduled_delayed_do_work_time_; + NextDelayedDoWork next_delayed_do_work_; bool record_task_delay_histograms_;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc index a7f7454..a39f1b6 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -42,12 +42,17 @@ return base::TimeDelta(); // Makes DoWork post an immediate continuation. } - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override { + void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override { // De-dupe DoWorks. if (NumberOfScheduledWakeups() == 1u) RequestDoWork(); } + void CancelWakeupAt(base::TimeTicks run_time) override { + // We didn't post a delayed task in RequestWakeupAt so there's no need to do + // anything here. + } + const char* GetName() const override { return "PerfTestTimeDomain"; } DISALLOW_COPY_AND_ASSIGN(PerfTestTimeDomain);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc index fc290a3..ed91bfa 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -32,9 +32,11 @@ #include "platform/scheduler/base/work_queue_sets.h" #include "testing/gmock/include/gmock/gmock.h" +using testing::AnyNumber; using testing::Contains; using testing::ElementsAre; using testing::ElementsAreArray; +using testing::Mock; using testing::Not; using testing::_; using blink::scheduler::internal::EnqueueOrder; @@ -42,6 +44,21 @@ namespace blink { namespace scheduler { +class TaskQueueManagerForTest : public TaskQueueManager { + public: + TaskQueueManagerForTest( + scoped_refptr<TaskQueueManagerDelegate> delegate, + const char* tracing_category, + const char* disabled_by_default_tracing_category, + const char* disabled_by_default_verbose_tracing_category) + : TaskQueueManager(delegate, + tracing_category, + disabled_by_default_tracing_category, + disabled_by_default_verbose_tracing_category) {} + + using TaskQueueManager::NextTaskDelay; +}; + class MessageLoopTaskRunner : public TaskQueueManagerDelegateForTest { public: static scoped_refptr<MessageLoopTaskRunner> Create( @@ -85,7 +102,7 @@ test_task_runner_.get(), base::MakeUnique<TestTimeSource>(now_src_.get())); - manager_ = base::MakeUnique<TaskQueueManager>( + manager_ = base::MakeUnique<TaskQueueManagerForTest>( main_task_runner_, "test.scheduler", "test.scheduler", "test.scheduler.debug"); @@ -106,7 +123,7 @@ message_loop_.reset(new base::MessageLoop()); // A null clock triggers some assertions. now_src_->Advance(base::TimeDelta::FromMicroseconds(1000)); - manager_ = base::MakeUnique<TaskQueueManager>( + manager_ = base::MakeUnique<TaskQueueManagerForTest>( MessageLoopTaskRunner::Create( base::WrapUnique(new TestTimeSource(now_src_.get()))), "test.scheduler", "test.scheduler", "test.scheduler.debug"); @@ -120,12 +137,14 @@ manager_->WakeupReadyDelayedQueues(&lazy_now); } - base::Optional<base::TimeDelta> ComputeDelayTillNextTask(LazyNow* lazy_now) { + using NextTaskDelay = TaskQueueManagerForTest::NextTaskDelay; + + base::Optional<NextTaskDelay> ComputeDelayTillNextTask(LazyNow* lazy_now) { base::AutoLock lock(manager_->any_thread_lock_); return manager_->ComputeDelayTillNextTaskLocked(lazy_now); } - void PostDoWorkContinuation(base::Optional<base::TimeDelta> next_delay, + void PostDoWorkContinuation(base::Optional<NextTaskDelay> next_delay, LazyNow* lazy_now) { MoveableAutoLock lock(manager_->any_thread_lock_); return manager_->PostDoWorkContinuationLocked(next_delay, lazy_now, @@ -137,8 +156,8 @@ return manager_->any_thread().immediate_do_work_posted_count; } - base::TimeTicks next_scheduled_delayed_do_work_time() const { - return manager_->next_scheduled_delayed_do_work_time_; + base::TimeTicks next_delayed_do_work_time() const { + return manager_->next_delayed_do_work_.run_time(); } EnqueueOrder GetNextSequenceNumber() const { @@ -175,7 +194,7 @@ std::unique_ptr<base::SimpleTestTickClock> now_src_; scoped_refptr<TaskQueueManagerDelegateForTest> main_task_runner_; scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner_; - std::unique_ptr<TaskQueueManager> manager_; + std::unique_ptr<TaskQueueManagerForTest> manager_; std::vector<scoped_refptr<internal::TaskQueueImpl>> runners_; TestTaskTimeObserver test_task_time_observer_; }; @@ -204,7 +223,7 @@ TestCountUsesTimeSource* test_count_uses_time_source = new TestCountUsesTimeSource(); - manager_ = base::MakeUnique<TaskQueueManager>( + manager_ = base::MakeUnique<TaskQueueManagerForTest>( MessageLoopTaskRunner::Create( base::WrapUnique(test_count_uses_time_source)), "test.scheduler", "test.scheduler", "test.scheduler.debug"); @@ -237,7 +256,7 @@ TestCountUsesTimeSource* test_count_uses_time_source = new TestCountUsesTimeSource(); - manager_ = base::MakeUnique<TaskQueueManager>( + manager_ = base::MakeUnique<TaskQueueManagerForTest>( MessageLoopTaskRunner::Create( base::WrapUnique(test_count_uses_time_source)), "test.scheduler", "test.scheduler", "test.scheduler.debug"); @@ -382,16 +401,11 @@ EXPECT_TRUE(runners_[0]->HasPendingImmediateWork()); // Move the task into the |delayed_work_queue|. - EXPECT_TRUE(runners_[0]->delayed_work_queue()->Empty()); - std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = - runners_[0]->CreateQueueEnabledVoter(); - voter->SetQueueEnabled(false); - test_task_runner_->RunUntilIdle(); + WakeupReadyDelayedQueues(LazyNow(now_src_.get())); EXPECT_FALSE(runners_[0]->delayed_work_queue()->Empty()); EXPECT_TRUE(runners_[0]->HasPendingImmediateWork()); // Run the task, making the queue empty. - voter->SetQueueEnabled(true); test_task_runner_->RunUntilIdle(); EXPECT_FALSE(runners_[0]->HasPendingImmediateWork()); } @@ -1676,6 +1690,137 @@ } namespace { +class MockTimeDomainObserver : public TimeDomain::Observer { + public: + ~MockTimeDomainObserver() override {} + + MOCK_METHOD1(OnTimeDomainHasImmediateWork, void(TaskQueue*)); + MOCK_METHOD1(OnTimeDomainHasDelayedWork, void(TaskQueue*)); +}; +} // namespace + +TEST_F(TaskQueueManagerTest, TimeDomainObserver_ImmediateTask) { + Initialize(1u); + + MockTimeDomainObserver observer; + std::unique_ptr<VirtualTimeDomain> domain( + new VirtualTimeDomain(&observer, manager_->delegate()->NowTicks())); + manager_->RegisterTimeDomain(domain.get()); + runners_[0]->SetTimeDomain(domain.get()); + + // We should get a notification when a task is posted on an empty queue. + EXPECT_CALL(observer, OnTimeDomainHasImmediateWork(runners_[0].get())); + runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); + Mock::VerifyAndClearExpectations(&observer); + + // But not subsequently. + EXPECT_CALL(observer, OnTimeDomainHasImmediateWork(_)).Times(0); + runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); + Mock::VerifyAndClearExpectations(&observer); + + // Unless the immediate work queue is emptied. + runners_[0]->ReloadImmediateWorkQueueIfEmpty(); + EXPECT_CALL(observer, OnTimeDomainHasImmediateWork(runners_[0].get())); + runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); + + // Tidy up. + runners_[0]->UnregisterTaskQueue(); + manager_->UnregisterTimeDomain(domain.get()); +} + +TEST_F(TaskQueueManagerTest, TimeDomainObserver_DelayedTask) { + Initialize(1u); + + MockTimeDomainObserver observer; + std::unique_ptr<VirtualTimeDomain> domain( + new VirtualTimeDomain(&observer, manager_->delegate()->NowTicks())); + manager_->RegisterTimeDomain(domain.get()); + runners_[0]->SetTimeDomain(domain.get()); + + // We should get a notification when a delayed task is posted on an empty + // queue. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get())); + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromSeconds(10)); + Mock::VerifyAndClearExpectations(&observer); + + // We should not get a notification for a longer delay. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0); + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromSeconds(100)); + Mock::VerifyAndClearExpectations(&observer); + + // We should get a notification for a shorter delay. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get())); + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromSeconds(1)); + Mock::VerifyAndClearExpectations(&observer); + + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = + runners_[0]->CreateQueueEnabledVoter(); + voter->SetQueueEnabled(false); + + // When a queue has been enabled, we may get a notification if the + // TimeDomain's next scheduled wakeup has changed. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get())); + voter->SetQueueEnabled(true); + + // Tidy up. + runners_[0]->UnregisterTaskQueue(); + manager_->UnregisterTimeDomain(domain.get()); +} + +TEST_F(TaskQueueManagerTest, TimeDomainObserver_DelayedTaskMultipleQueues) { + Initialize(2u); + + MockTimeDomainObserver observer; + std::unique_ptr<VirtualTimeDomain> domain( + new VirtualTimeDomain(&observer, manager_->delegate()->NowTicks())); + manager_->RegisterTimeDomain(domain.get()); + runners_[0]->SetTimeDomain(domain.get()); + runners_[1]->SetTimeDomain(domain.get()); + + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get())).Times(1); + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[1].get())).Times(1); + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromSeconds(1)); + runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromSeconds(10)); + testing::Mock::VerifyAndClearExpectations(&observer); + + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 = + runners_[0]->CreateQueueEnabledVoter(); + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 = + runners_[1]->CreateQueueEnabledVoter(); + + // Disabling a queue should not trigger a notification. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0); + voter0->SetQueueEnabled(false); + Mock::VerifyAndClearExpectations(&observer); + + // Re-enabling it should should also trigger a notification. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[0].get())); + voter0->SetQueueEnabled(true); + Mock::VerifyAndClearExpectations(&observer); + + // Disabling a queue should not trigger a notification. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(0); + voter1->SetQueueEnabled(false); + Mock::VerifyAndClearExpectations(&observer); + + // Re-enabling it should should trigger a notification. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(runners_[1].get())); + voter1->SetQueueEnabled(true); + Mock::VerifyAndClearExpectations(&observer); + + // Tidy up. + EXPECT_CALL(observer, OnTimeDomainHasDelayedWork(_)).Times(AnyNumber()); + runners_[0]->UnregisterTaskQueue(); + runners_[1]->UnregisterTaskQueue(); + manager_->UnregisterTimeDomain(domain.get()); +} + +namespace { void ChromiumRunloopInspectionTask( scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner) { EXPECT_EQ(1u, test_task_runner->NumPendingTasks()); @@ -2321,10 +2466,6 @@ TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask) { Initialize(2u); - std::unique_ptr<RealTimeDomain> domain2(new RealTimeDomain("test")); - manager_->RegisterTimeDomain(domain2.get()); - runners_[1]->SetTimeDomain(domain2.get()); - LazyNow lazy_now(now_src_.get()); EXPECT_FALSE(static_cast<bool>(ComputeDelayTillNextTask(&lazy_now))); @@ -2332,27 +2473,23 @@ base::TimeDelta::FromSeconds(10)); EXPECT_EQ(base::TimeDelta::FromSeconds(10), - ComputeDelayTillNextTask(&lazy_now).value()); + ComputeDelayTillNextTask(&lazy_now)->delay()); runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), base::TimeDelta::FromSeconds(15)); EXPECT_EQ(base::TimeDelta::FromSeconds(10), - ComputeDelayTillNextTask(&lazy_now).value()); + ComputeDelayTillNextTask(&lazy_now)->delay()); runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), base::TimeDelta::FromSeconds(5)); EXPECT_EQ(base::TimeDelta::FromSeconds(5), - ComputeDelayTillNextTask(&lazy_now).value()); + ComputeDelayTillNextTask(&lazy_now)->delay()); runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); - EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now).value()); - - // Tidy up. - runners_[1]->UnregisterTaskQueue(); - manager_->UnregisterTimeDomain(domain2.get()); + EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now)->delay()); } TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask_Disabled) { @@ -2385,7 +2522,7 @@ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::NOW); LazyNow lazy_now(now_src_.get()); - EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now).value()); + EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now)->delay()); } TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask_DelayedTaskReady) { @@ -2397,44 +2534,63 @@ now_src_->Advance(base::TimeDelta::FromSeconds(10)); LazyNow lazy_now(now_src_.get()); - EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now).value()); + EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now)->delay()); } TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_NoMoreWork) { Initialize(1u); LazyNow lazy_now(now_src_.get()); - PostDoWorkContinuation(base::Optional<base::TimeDelta>(), &lazy_now); + PostDoWorkContinuation(base::Optional<NextTaskDelay>(), &lazy_now); EXPECT_EQ(0u, test_task_runner_->NumPendingTasks()); EXPECT_EQ(0, immediate_do_work_posted_count()); - EXPECT_TRUE(next_scheduled_delayed_do_work_time().is_null()); + EXPECT_TRUE(next_delayed_do_work_time().is_null()); } TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_ImmediateWork) { Initialize(1u); LazyNow lazy_now(now_src_.get()); - PostDoWorkContinuation(base::TimeDelta(), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(), &lazy_now); EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); EXPECT_EQ(1, immediate_do_work_posted_count()); - EXPECT_TRUE(next_scheduled_delayed_do_work_time().is_null()); + EXPECT_TRUE(next_delayed_do_work_time().is_null()); +} + +TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWorkInThePast) { + Initialize(1u); + + LazyNow lazy_now(now_src_.get()); + // Note this isn't supposed to happen in practice. + PostDoWorkContinuation( + NextTaskDelay(base::TimeDelta::FromSeconds(-1), + runners_[0]->GetTimeDomain(), + NextTaskDelay::AllowAnyDelayForTesting()), + &lazy_now); + + EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); + EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); + EXPECT_EQ(1, immediate_do_work_posted_count()); + EXPECT_TRUE(next_delayed_do_work_time().is_null()); } TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWork) { Initialize(1u); LazyNow lazy_now(now_src_.get()); - PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), + runners_[0]->GetTimeDomain()), + &lazy_now); EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); EXPECT_EQ(base::TimeDelta::FromSeconds(1), test_task_runner_->DelayToNextTaskTime()); EXPECT_EQ(0, immediate_do_work_posted_count()); EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(1), - next_scheduled_delayed_do_work_time()); + next_delayed_do_work_time()); } TEST_F(TaskQueueManagerTest, @@ -2447,29 +2603,35 @@ EXPECT_EQ(1, immediate_do_work_posted_count()); LazyNow lazy_now(now_src_.get()); - PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), + runners_[0]->GetTimeDomain()), + &lazy_now); // Test that a delayed task didn't get posted. EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); EXPECT_EQ(1, immediate_do_work_posted_count()); - EXPECT_TRUE(next_scheduled_delayed_do_work_time().is_null()); + EXPECT_TRUE(next_delayed_do_work_time().is_null()); } TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWorkTimeChanges) { Initialize(1u); LazyNow lazy_now(now_src_.get()); - PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), + runners_[0]->GetTimeDomain()), + &lazy_now); EXPECT_TRUE(test_task_runner_->HasPendingTasks()); EXPECT_EQ(0, immediate_do_work_posted_count()); EXPECT_EQ(base::TimeDelta::FromSeconds(1), test_task_runner_->DelayToNextTaskTime()); EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(1), - next_scheduled_delayed_do_work_time()); + next_delayed_do_work_time()); - PostDoWorkContinuation(base::TimeDelta::FromSeconds(10), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(10), + runners_[0]->GetTimeDomain()), + &lazy_now); // This should have resulted in the previous task getting canceled and a new // one getting posted. @@ -2480,7 +2642,7 @@ test_task_runner_->DelayToNextTaskTime()); EXPECT_EQ(0, immediate_do_work_posted_count()); EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(10), - next_scheduled_delayed_do_work_time()); + next_delayed_do_work_time()); } TEST_F(TaskQueueManagerTest, @@ -2488,18 +2650,20 @@ Initialize(1u); LazyNow lazy_now(now_src_.get()); - PostDoWorkContinuation(base::TimeDelta::FromSeconds(1), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), + runners_[0]->GetTimeDomain()), + &lazy_now); now_src_->Advance(base::TimeDelta::FromSeconds(1)); lazy_now = LazyNow(now_src_.get()); - PostDoWorkContinuation(base::TimeDelta(), &lazy_now); + PostDoWorkContinuation(NextTaskDelay(), &lazy_now); // Because the delayed DoWork was pending we don't expect an immediate DoWork // to get posted. EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); EXPECT_EQ(0, immediate_do_work_posted_count()); - EXPECT_EQ(lazy_now.Now(), next_scheduled_delayed_do_work_time()); + EXPECT_EQ(lazy_now.Now(), next_delayed_do_work_time()); } namespace { @@ -2600,5 +2764,98 @@ EXPECT_TRUE(runners_[0]->CouldTaskRun(enqueue_order)); } +TEST_F(TaskQueueManagerTest, DelayedDoWorkNotPostedForDisabledQueue) { + Initialize(1u); + + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromMilliseconds(1)); + ASSERT_TRUE(test_task_runner_->HasPendingTasks()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), + test_task_runner_->DelayToNextTaskTime()); + + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = + runners_[0]->CreateQueueEnabledVoter(); + voter->SetQueueEnabled(false); + + EXPECT_TRUE(test_task_runner_->HasPendingTasks()); + test_task_runner_->RemoveCancelledTasks(); + EXPECT_FALSE(test_task_runner_->HasPendingTasks()); + + voter->SetQueueEnabled(true); + ASSERT_TRUE(test_task_runner_->HasPendingTasks()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), + test_task_runner_->DelayToNextTaskTime()); +} + +TEST_F(TaskQueueManagerTest, DisablingQueuesChangesDelayTillNextDoWork) { + Initialize(3u); + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromMilliseconds(1)); + runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromMilliseconds(10)); + runners_[2]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), + base::TimeDelta::FromMilliseconds(100)); + + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 = + runners_[0]->CreateQueueEnabledVoter(); + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 = + runners_[1]->CreateQueueEnabledVoter(); + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter2 = + runners_[2]->CreateQueueEnabledVoter(); + + ASSERT_TRUE(test_task_runner_->HasPendingTasks()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), + test_task_runner_->DelayToNextTaskTime()); + + voter0->SetQueueEnabled(false); + test_task_runner_->RemoveCancelledTasks(); + ASSERT_TRUE(test_task_runner_->HasPendingTasks()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(10), + test_task_runner_->DelayToNextTaskTime()); + + voter1->SetQueueEnabled(false); + test_task_runner_->RemoveCancelledTasks(); + ASSERT_TRUE(test_task_runner_->HasPendingTasks()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(100), + test_task_runner_->DelayToNextTaskTime()); + + voter2->SetQueueEnabled(false); + test_task_runner_->RemoveCancelledTasks(); + EXPECT_FALSE(test_task_runner_->HasPendingTasks()); +} + +TEST_F(TaskQueueManagerTest, GetNextScheduledWakeUp) { + Initialize(1u); + + EXPECT_EQ(base::nullopt, runners_[0]->GetNextScheduledWakeUp()); + + base::TimeTicks start_time = manager_->delegate()->NowTicks(); + base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10); + base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(2); + + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1); + EXPECT_EQ(start_time + delay1, runners_[0]->GetNextScheduledWakeUp()); + + runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay2); + EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); + + // We don't have wakeups scheduled for disabled queues. + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = + runners_[0]->CreateQueueEnabledVoter(); + voter->SetQueueEnabled(false); + EXPECT_EQ(base::nullopt, runners_[0]->GetNextScheduledWakeUp()); + + voter->SetQueueEnabled(true); + EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); + + // Immediate tasks shouldn't make any difference. + runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); + EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); + + // Neither should fences. + runners_[0]->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); + EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); +} + } // namespace scheduler } // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc index 2e12e70..cbc6e9d 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
@@ -408,6 +408,8 @@ MockObserver mock_observer; selector.SetTaskQueueSelectorObserver(&mock_observer); + EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1); + scoped_refptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting()); selector.AddQueue(task_queue.get()); std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = @@ -467,6 +469,8 @@ EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue)); testing::Mock::VerifyAndClearExpectations(&mock_observer); + EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2); + voter.reset(); selector.EnableQueue(task_queue.get());
diff --git a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.cc b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.cc index 187364e..0cb6b5d 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker.cc
@@ -14,7 +14,7 @@ base::TimeDelta reporting_interval, base::TimeDelta waiting_period) : time_(now), - thread_state_(ThreadState::ACTIVE), + thread_state_(ThreadState::PAUSED), last_state_change_time_(now), waiting_period_(waiting_period), reporting_interval_(reporting_interval),
diff --git a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc index 23072d43..aa3a0955 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc
@@ -33,6 +33,7 @@ ThreadLoadTracker thread_load_tracker( SecondsToTime(1), base::Bind(&AddToVector, base::Unretained(&result)), base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(10)); + thread_load_tracker.Resume(SecondsToTime(1)); // We should discard first ten seconds of information. thread_load_tracker.RecordTaskTime(SecondsToTime(1), SecondsToTime(3)); @@ -67,6 +68,7 @@ ThreadLoadTracker thread_load_tracker( SecondsToTime(1), base::Bind(&AddToVector, base::Unretained(&result)), base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(10)); + thread_load_tracker.Resume(SecondsToTime(1)); thread_load_tracker.RecordTaskTime(SecondsToTime(3), SecondsToTime(4)); thread_load_tracker.Pause(SecondsToTime(5)); @@ -91,5 +93,25 @@ std::make_pair(SecondsToTime(27), 0.1))); } +TEST(ThreadLoadTrackerTest, DisabledByDefault) { + std::vector<std::pair<base::TimeTicks, double>> result; + ThreadLoadTracker thread_load_tracker( + SecondsToTime(1), base::Bind(&AddToVector, base::Unretained(&result)), + base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(10)); + + // ThreadLoadTracker should be disabled and these tasks should be + // ignored. + thread_load_tracker.RecordTaskTime(SecondsToTime(13), SecondsToTime(14)); + thread_load_tracker.RecordTaskTime(SecondsToTime(15), SecondsToTime(16)); + + thread_load_tracker.Resume(SecondsToTime(17)); + + thread_load_tracker.RecordTaskTime(SecondsToTime(28), SecondsToTime(29)); + + EXPECT_THAT(result, ElementsAre(std::make_pair(SecondsToTime(27), 0), + std::make_pair(SecondsToTime(28), 0), + std::make_pair(SecondsToTime(29), 1))); +} + } // namespace scheduler } // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc index 919543e9..161830e 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.cc
@@ -52,10 +52,8 @@ queue->set_scheduled_time_domain_wakeup(delayed_run_time); // If |queue| is the first wakeup then request the wakeup. - if (delayed_wakeup_queue_.min().queue == queue) { - base::TimeDelta delay = std::max(base::TimeDelta(), delayed_run_time - now); - RequestWakeup(now, delay); - } + if (delayed_wakeup_queue_.min().queue == queue) + RequestWakeupAt(now, delayed_run_time); if (observer_) observer_->OnTimeDomainHasDelayedWork(queue); @@ -75,9 +73,18 @@ return; DCHECK_NE(queue->scheduled_time_domain_wakeup(), base::TimeTicks()); + DCHECK(!delayed_wakeup_queue_.empty()); + base::TimeTicks prev_first_wakeup = delayed_wakeup_queue_.min().time; // O(log n) delayed_wakeup_queue_.erase(queue->heap_handle()); + + if (delayed_wakeup_queue_.empty()) { + CancelWakeupAt(prev_first_wakeup); + } else if (prev_first_wakeup != delayed_wakeup_queue_.min().time) { + CancelWakeupAt(prev_first_wakeup); + RequestWakeupAt(Now(), delayed_wakeup_queue_.min().time); + } } void TimeDomain::WakeupReadyDelayedQueues(LazyNow* lazy_now) {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h index 09e3f70a..c2b828a8b 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/time_domain.h +++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain.h
@@ -111,12 +111,18 @@ virtual void OnRegisterWithTaskQueueManager( TaskQueueManager* task_queue_manager) = 0; - // The implementaion will secedule task processing to run with |delay| with - // respect to the TimeDomain's time source. Always called on the main thread. + // The implementation will schedule task processing to run at time |run_time| + // within the TimeDomain's time line. Only called from the main thread. // NOTE this is only called by ScheduleDelayedWork if the scheduled runtime // is sooner than any previously sheduled work or if there is no other // scheduled work. - virtual void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) = 0; + virtual void RequestWakeupAt(base::TimeTicks now, + base::TimeTicks run_time) = 0; + + // The implementation will cancel a wake up previously requested by + // RequestWakeupAt. It's expected this will be a NOP for most virtual time + // domains. + virtual void CancelWakeupAt(base::TimeTicks run_time) = 0; // For implementation specific tracing. virtual void AsValueIntoInternal(
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc index f8eaf85..c8b6acb 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
@@ -53,7 +53,10 @@ void OnRegisterWithTaskQueueManager( TaskQueueManager* task_queue_manager) override {} - MOCK_METHOD2(RequestWakeup, void(base::TimeTicks now, base::TimeDelta delay)); + MOCK_METHOD2(RequestWakeupAt, + void(base::TimeTicks now, base::TimeTicks run_time)); + + MOCK_METHOD1(CancelWakeupAt, void(base::TimeTicks run_time)); void SetNow(base::TimeTicks now) { now_ = now; } @@ -89,7 +92,7 @@ TEST_F(TimeDomainTest, ScheduleDelayedWork) { base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); base::TimeTicks delayed_runtime = time_domain_->Now() + delay; - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay)); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime)); base::TimeTicks now = time_domain_->Now(); time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay, now); @@ -100,6 +103,9 @@ TaskQueue* next_task_queue; EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); EXPECT_EQ(task_queue_.get(), next_task_queue); + Mock::VerifyAndClearExpectations(time_domain_.get()); + + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(AnyNumber()); } TEST_F(TimeDomainTest, ScheduleDelayedWorkSupersedesPreviousWakeup) { @@ -107,7 +113,7 @@ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(100); base::TimeTicks delayed_runtime1 = time_domain_->Now() + delay1; base::TimeTicks delayed_runtime2 = time_domain_->Now() + delay2; - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay1)); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime1)); base::TimeTicks now = time_domain_->Now(); time_domain_->ScheduleDelayedWork(task_queue_.get(), delayed_runtime1, now); @@ -119,14 +125,17 @@ // Now scheduler a later wakeup, which should replace the previously requested // one. - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay2)); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime2)); time_domain_->ScheduleDelayedWork(task_queue_.get(), delayed_runtime2, now); EXPECT_TRUE(time_domain_->NextScheduledRunTime(&next_scheduled_runtime)); EXPECT_EQ(delayed_runtime2, next_scheduled_runtime); + Mock::VerifyAndClearExpectations(time_domain_.get()); + + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(AnyNumber()); } -TEST_F(TimeDomainTest, RequestWakeup_OnlyCalledForEarlierTasks) { +TEST_F(TimeDomainTest, RequestWakeupAt_OnlyCalledForEarlierTasks) { scoped_refptr<internal::TaskQueueImpl> task_queue2 = make_scoped_refptr( new internal::TaskQueueImpl(nullptr, time_domain_.get(), TaskQueue::Spec(TaskQueue::QueueType::TEST), @@ -147,23 +156,27 @@ base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(30); base::TimeDelta delay4 = base::TimeDelta::FromMilliseconds(1); - // RequestWakeup should always be called if there are no other wakeups. - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay1)); + // RequestWakeupAt should always be called if there are no other wakeups. base::TimeTicks now = time_domain_->Now(); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, now + delay1)); time_domain_->ScheduleDelayedWork(task_queue_.get(), now + delay1, now); Mock::VerifyAndClearExpectations(time_domain_.get()); - // RequestWakeup should not be called when scheduling later tasks. - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(0); + // RequestWakeupAt should not be called when scheduling later tasks. + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)).Times(0); time_domain_->ScheduleDelayedWork(task_queue2.get(), now + delay2, now); time_domain_->ScheduleDelayedWork(task_queue3.get(), now + delay3, now); - // RequestWakeup should be called when scheduling earlier tasks. + // RequestWakeupAt should be called when scheduling earlier tasks. Mock::VerifyAndClearExpectations(time_domain_.get()); - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay4)); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, now + delay4)); time_domain_->ScheduleDelayedWork(task_queue4.get(), now + delay4, now); + Mock::VerifyAndClearExpectations(time_domain_.get()); + + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)); + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(2); task_queue2->UnregisterTaskQueue(); task_queue3->UnregisterTaskQueue(); task_queue4->UnregisterTaskQueue(); @@ -175,31 +188,40 @@ TaskQueue::Spec(TaskQueue::QueueType::TEST), "test.category", "test.category")); - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(1); base::TimeTicks now = time_domain_->Now(); - time_domain_->ScheduleDelayedWork( - task_queue_.get(), now + base::TimeDelta::FromMilliseconds(10), now); - time_domain_->ScheduleDelayedWork( - task_queue2_.get(), now + base::TimeDelta::FromMilliseconds(100), now); + base::TimeTicks wakeup1 = now + base::TimeDelta::FromMilliseconds(10); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, wakeup1)).Times(1); + time_domain_->ScheduleDelayedWork(task_queue_.get(), wakeup1, now); + base::TimeTicks wakeup2 = now + base::TimeDelta::FromMilliseconds(100); + time_domain_->ScheduleDelayedWork(task_queue2_.get(), wakeup2, now); TaskQueue* next_task_queue; EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); EXPECT_EQ(task_queue_.get(), next_task_queue); + testing::Mock::VerifyAndClearExpectations(time_domain_.get()); + + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(wakeup1)).Times(1); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, wakeup2)).Times(1); + time_domain_->UnregisterQueue(task_queue_.get()); task_queue_ = scoped_refptr<internal::TaskQueueImpl>(); EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); EXPECT_EQ(task_queue2_.get(), next_task_queue); + testing::Mock::VerifyAndClearExpectations(time_domain_.get()); + + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(wakeup2)).Times(1); + time_domain_->UnregisterQueue(task_queue2_.get()); EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); } TEST_F(TimeDomainTest, WakeupReadyDelayedQueues) { base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50); - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, delay)); base::TimeTicks now = time_domain_->Now(); base::TimeTicks delayed_runtime = now + delay; + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, delayed_runtime)); time_domain_->ScheduleDelayedWork(task_queue_.get(), delayed_runtime, now); base::TimeTicks next_run_time; @@ -219,17 +241,62 @@ TEST_F(TimeDomainTest, CancelDelayedWork) { base::TimeTicks now = time_domain_->Now(); - time_domain_->ScheduleDelayedWork( - task_queue_.get(), now + base::TimeDelta::FromMilliseconds(20), now); + base::TimeTicks run_time = now + base::TimeDelta::FromMilliseconds(20); + + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, run_time)); + time_domain_->ScheduleDelayedWork(task_queue_.get(), run_time, now); TaskQueue* next_task_queue; EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); EXPECT_EQ(task_queue_.get(), next_task_queue); + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(run_time)); time_domain_->CancelDelayedWork(task_queue_.get()); EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); } +TEST_F(TimeDomainTest, CancelDelayedWork_TwoQueues) { + scoped_refptr<internal::TaskQueueImpl> task_queue2 = make_scoped_refptr( + new internal::TaskQueueImpl(nullptr, time_domain_.get(), + TaskQueue::Spec(TaskQueue::QueueType::TEST), + "test.category", "test.category")); + + base::TimeTicks now = time_domain_->Now(); + base::TimeTicks run_time1 = now + base::TimeDelta::FromMilliseconds(20); + base::TimeTicks run_time2 = now + base::TimeDelta::FromMilliseconds(40); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, run_time1)); + time_domain_->ScheduleDelayedWork(task_queue_.get(), run_time1, now); + Mock::VerifyAndClearExpectations(time_domain_.get()); + + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)).Times(0); + time_domain_->ScheduleDelayedWork(task_queue2.get(), run_time2, now); + Mock::VerifyAndClearExpectations(time_domain_.get()); + + TaskQueue* next_task_queue; + EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); + EXPECT_EQ(task_queue_.get(), next_task_queue); + + base::TimeTicks next_run_time; + ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time)); + EXPECT_EQ(run_time1, next_run_time); + + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(run_time1)); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, run_time2)); + time_domain_->CancelDelayedWork(task_queue_.get()); + EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue)); + EXPECT_EQ(task_queue2.get(), next_task_queue); + + ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time)); + EXPECT_EQ(run_time2, next_run_time); + + Mock::VerifyAndClearExpectations(time_domain_.get()); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)).Times(AnyNumber()); + EXPECT_CALL(*time_domain_.get(), CancelWakeupAt(_)).Times(AnyNumber()); + + // Tidy up. + task_queue2->UnregisterTaskQueue(); +} + namespace { class MockObserver : public TimeDomain::Observer { public: @@ -257,7 +324,7 @@ TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasDelayedWork) { EXPECT_CALL(*observer_, OnTimeDomainHasDelayedWork(task_queue_.get())); - EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)); + EXPECT_CALL(*time_domain_.get(), RequestWakeupAt(_, _)); base::TimeTicks now = time_domain_->Now(); time_domain_->ScheduleDelayedWork( task_queue_.get(), now + base::TimeDelta::FromMilliseconds(10), now);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc index a90b3be2..56c7d37 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc +++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.cc
@@ -34,13 +34,17 @@ return now_; } -void VirtualTimeDomain::RequestWakeup(base::TimeTicks now, - base::TimeDelta delay) { +void VirtualTimeDomain::RequestWakeupAt(base::TimeTicks now, + base::TimeTicks run_time) { // We don't need to do anything here because the caller of AdvanceTo is // responsible for calling TaskQueueManager::MaybeScheduleImmediateWork if // needed. } +void VirtualTimeDomain::CancelWakeupAt(base::TimeTicks run_time) { + // We ignore this because RequestWakeupAt is a NOP. +} + base::Optional<base::TimeDelta> VirtualTimeDomain::DelayTillNextTask( LazyNow* lazy_now) { return base::nullopt;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h index a841226..a1503f0 100644 --- a/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h +++ b/third_party/WebKit/Source/platform/scheduler/base/virtual_time_domain.h
@@ -32,7 +32,8 @@ protected: void OnRegisterWithTaskQueueManager( TaskQueueManager* task_queue_manager) override; - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override; + void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override; + void CancelWakeupAt(base::TimeTicks run_time) override; void AsValueIntoInternal( base::trace_event::TracedValue* state) const override;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc index 04dd639..57a44ed 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
@@ -24,14 +24,18 @@ return base::TimeDelta(); // Makes DoWork post an immediate continuation. } -void AutoAdvancingVirtualTimeDomain::RequestWakeup(base::TimeTicks now, - base::TimeDelta delay) { +void AutoAdvancingVirtualTimeDomain::RequestWakeupAt(base::TimeTicks now, + base::TimeTicks run_time) { // Avoid posting pointless DoWorks. I.e. if the time domain has more then one // scheduled wake up then we don't need to do anything. if (can_advance_virtual_time_ && NumberOfScheduledWakeups() == 1u) RequestDoWork(); } +void AutoAdvancingVirtualTimeDomain::CancelWakeupAt(base::TimeTicks run_time) { + // We ignore this because RequestWakeupAt doesn't post a delayed task. +} + void AutoAdvancingVirtualTimeDomain::SetCanAdvanceVirtualTime( bool can_advance_virtual_time) { can_advance_virtual_time_ = can_advance_virtual_time;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h index 0ca186d..9cdd5d8 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h +++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
@@ -28,7 +28,8 @@ // TimeDomain implementation: base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override; - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override; + void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override; + void CancelWakeupAt(base::TimeTicks run_time) override; const char* GetName() const override; // Controls whether or not virtual time is allowed to advance, when the
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc index 53800cc..9e53673 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
@@ -224,7 +224,9 @@ in_idle_period_for_testing(false), use_virtual_time(false), is_audio_playing(false), - rail_mode_observer(nullptr) {} + rail_mode_observer(nullptr) { + foreground_main_thread_load_tracker.Resume(now); +} RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc index d5fdb53..79cef1c 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
@@ -979,5 +979,41 @@ base::TimeDelta::FromMilliseconds(300))); } +TEST_F(TaskQueueThrottlerTest, DisabledQueueThenEnabledQueue) { + std::vector<base::TimeTicks> run_times; + + scoped_refptr<TaskQueue> second_queue = + scheduler_->NewTimerTaskRunner(TaskQueue::QueueType::TEST); + + task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get()); + task_queue_throttler_->IncreaseThrottleRefCount(second_queue.get()); + + timer_queue_->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(100)); + second_queue->PostDelayedTask(FROM_HERE, + base::Bind(&TestTask, &run_times, clock_.get()), + base::TimeDelta::FromMilliseconds(200)); + + std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = + timer_queue_->CreateQueueEnabledVoter(); + voter->SetQueueEnabled(false); + + clock_->Advance(base::TimeDelta::FromMilliseconds(250)); + + mock_task_runner_->RunUntilIdle(); + + EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1000))); + + voter->SetQueueEnabled(true); + mock_task_runner_->RunUntilIdle(); + + EXPECT_THAT( + run_times, + ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000), + base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000))); +} + } // namespace scheduler } // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc index a859d09..348d034 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.cc
@@ -17,12 +17,16 @@ return "ThrottledTimeDomain"; } -void ThrottledTimeDomain::RequestWakeup(base::TimeTicks now, - base::TimeDelta delay) { +void ThrottledTimeDomain::RequestWakeupAt(base::TimeTicks now, + base::TimeTicks run_time) { // We assume the owner (i.e. TaskQueueThrottler) will manage wakeups on our // behalf. } +void ThrottledTimeDomain::CancelWakeupAt(base::TimeTicks run_time) { + // We ignore this because RequestWakeupAt is a NOP. +} + base::Optional<base::TimeDelta> ThrottledTimeDomain::DelayTillNextTask( LazyNow* lazy_now) { base::TimeTicks next_run_time;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h index c9d73fa..4faaf5b 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h +++ b/third_party/WebKit/Source/platform/scheduler/renderer/throttled_time_domain.h
@@ -21,7 +21,8 @@ // TimeDomain implementation: const char* GetName() const override; - void RequestWakeup(base::TimeTicks now, base::TimeDelta delay) override; + void RequestWakeupAt(base::TimeTicks now, base::TimeTicks run_time) override; + void CancelWakeupAt(base::TimeTicks run_time) override; base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override; using TimeDomain::WakeupReadyDelayedQueues;
diff --git a/third_party/WebKit/Source/wtf/Vector.h b/third_party/WebKit/Source/wtf/Vector.h index 9483a550..77f86ed 100644 --- a/third_party/WebKit/Source/wtf/Vector.h +++ b/third_party/WebKit/Source/wtf/Vector.h
@@ -836,49 +836,262 @@ using OffsetRange = typename Base::OffsetRange; public: - typedef T ValueType; - typedef T value_type; + using ValueType = T; + using value_type = T; - typedef T* iterator; - typedef const T* const_iterator; - typedef std::reverse_iterator<iterator> reverse_iterator; - typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; - Vector() { - static_assert(!std::is_polymorphic<T>::value || - !VectorTraits<T>::canInitializeWithMemset, - "Cannot initialize with memset if there is a vtable"); - static_assert(Allocator::isGarbageCollected || - !AllowsOnlyPlacementNew<T>::value || - !IsTraceable<T>::value, - "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that " - "have trace methods into an off-heap Vector"); - static_assert(Allocator::isGarbageCollected || - !IsPointerToGarbageCollectedType<T>::value, - "Cannot put raw pointers to garbage-collected classes into " - "an off-heap Vector. Use HeapVector<Member<T>> instead."); + // Create an empty vector. + inline Vector(); + // Create a vector containing the specified number of default-initialized + // elements. + inline explicit Vector(size_t); + // Create a vector containing the specified number of elements, each of which + // is copy initialized from the specified value. + inline Vector(size_t, const T&); - ANNOTATE_NEW_BUFFER(begin(), capacity(), 0); - m_size = 0; + // Copying. + Vector(const Vector&); + template <size_t otherCapacity> + explicit Vector(const Vector<T, otherCapacity, Allocator>&); + + Vector& operator=(const Vector&); + template <size_t otherCapacity> + Vector& operator=(const Vector<T, otherCapacity, Allocator>&); + + // Moving. + Vector(Vector&&); + Vector& operator=(Vector&&); + + // Construct with an initializer list. You can do e.g. + // Vector<int> v({1, 2, 3}); + // or + // v = {4, 5, 6}; + Vector(std::initializer_list<T> elements); + Vector& operator=(std::initializer_list<T> elements); + + // Basic inquiry about the vector's state. + // + // capacity() is the maximum number of elements that the Vector can hold + // without a reallocation. It can be zero. + size_t size() const { return m_size; } + size_t capacity() const { return Base::capacity(); } + bool isEmpty() const { return !size(); } + + // at() and operator[]: Obtain the reference of the element that is located + // at the given index. The reference may be invalidated on a reallocation. + // + // at() can be used in cases like: + // pointerToVector->at(1); + // instead of: + // (*pointerToVector)[1]; + T& at(size_t i) { + RELEASE_ASSERT(i < size()); + return Base::buffer()[i]; + } + const T& at(size_t i) const { + RELEASE_ASSERT(i < size()); + return Base::buffer()[i]; } - explicit Vector(size_t size) : Base(size) { - static_assert(!std::is_polymorphic<T>::value || - !VectorTraits<T>::canInitializeWithMemset, - "Cannot initialize with memset if there is a vtable"); - static_assert(Allocator::isGarbageCollected || - !AllowsOnlyPlacementNew<T>::value || - !IsTraceable<T>::value, - "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that " - "have trace methods into an off-heap Vector"); - static_assert(Allocator::isGarbageCollected || - !IsPointerToGarbageCollectedType<T>::value, - "Cannot put raw pointers to garbage-collected classes into " - "an off-heap Vector. Use HeapVector<Member<T>> instead."); + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } - ANNOTATE_NEW_BUFFER(begin(), capacity(), size); - m_size = size; - TypeOperations::initialize(begin(), end()); + // Return a pointer to the front of the backing buffer. Those pointers get + // invalidated on a reallocation. + T* data() { return Base::buffer(); } + const T* data() const { return Base::buffer(); } + + // Iterators and reverse iterators. They are invalidated on a reallocation. + iterator begin() { return data(); } + iterator end() { return begin() + m_size; } + const_iterator begin() const { return data(); } + const_iterator end() const { return begin() + m_size; } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Quick access to the first and the last element. It is invalid to call + // these functions when the vector is empty. + T& front() { return at(0); } + const T& front() const { return at(0); } + T& back() { return at(size() - 1); } + const T& back() const { return at(size() - 1); } + + // Searching. + // + // Comparisons are done in terms of compareElement(), which is usually + // operator==(). find() and reverseFind() returns an index of the element + // that is found first. If no match is found, kNotFound will be returned. + template <typename U> + bool contains(const U&) const; + template <typename U> + size_t find(const U&) const; + template <typename U> + size_t reverseFind(const U&) const; + + // Resize the vector to the specified size. + // + // These three functions are essentially similar. They differ in that + // (1) shrink() has a DCHECK to make sure the specified size is not more than + // size(), and (2) grow() has a DCHECK to make sure the specified size is + // not less than size(). + // + // When a vector shrinks, the extra elements in the back will be destructed. + // All the iterators pointing to a to-be-destructed element will be + // invalidated. + // + // When a vector grows, new elements will be added in the back, and they + // will be default-initialized. A reallocation may happen in this case. + void shrink(size_t); + void grow(size_t); + void resize(size_t); + + // Increase the capacity of the vector to at least |newCapacity|. The + // elements in the vector are not affected. This function does not shrink + // the size of the backing buffer, even if |newCapacity| is small. This + // function may cause a reallocation. + void reserveCapacity(size_t newCapacity); + + // This is similar to reserveCapacity() but must be called immediately after + // the vector is default-constructed. + void reserveInitialCapacity(size_t initialCapacity); + + // Shrink the backing buffer so it can contain exactly |size()| elements. + // This function may cause a reallocation. + void shrinkToFit() { shrinkCapacity(size()); } + + // Shrink the backing buffer if at least 50% of the vector's capacity is + // unused. If it shrinks, the new buffer contains roughly 25% of unused + // space. This function may cause a reallocation. + void shrinkToReasonableCapacity() { + if (size() * 2 < capacity()) + shrinkCapacity(size() + size() / 4 + 1); + } + + // Remove all the elements. This function actually releases the backing + // buffer, thus any iterators will get invalidated (including begin()). + void clear() { shrinkCapacity(0); } + + // Insertion to the back. All of these functions except uncheckedAppend() may + // cause a reallocation. + // + // push_back(value) + // Insert a single element to the back. + // emplace_back(args...) + // Insert a single element constructed as T(args...) to the back. The + // element is constructed directly on the backing buffer with placement + // new. + // append(buffer, size) + // appendVector(vector) + // appendRange(begin, end) + // Insert multiple elements represented by (1) |buffer| and |size| + // (for append), (2) |vector| (for appendVector), or (3) a pair of + // iterators (for appendRange) to the back. The elements will be copied. + // uncheckedAppend(value) + // Insert a single element like push_back(), but this function assumes + // the vector has enough capacity such that it can store the new element + // without a reallocation. Using this function could improve the + // performance when you append many elements repeatedly. + template <typename U> + void push_back(U&&); + template <typename... Args> + T& emplace_back(Args&&...); + template <typename U> + void append(const U*, size_t); + template <typename U, size_t otherCapacity, typename V> + void appendVector(const Vector<U, otherCapacity, V>&); + template <typename Iterator> + void appendRange(Iterator begin, Iterator end); + template <typename U> + void uncheckedAppend(U&&); + + // Insertion to an arbitrary position. All of these functions will take + // O(size())-time. All of the elements after |position| will be moved to + // the new locations. |position| must be no more than size(). All of these + // functions may cause a reallocation. In any case, all the iterators + // pointing to an element after |position| will be invalidated. + // + // insert(position, value) + // Insert a single element at |position|. + // insert(position, buffer, size) + // insert(position, vector) + // Insert multiple elements represented by either |buffer| and |size| + // or |vector| at |position|. The elements will be copied. + // + // TODO(yutak): Why not insertVector()? + template <typename U> + void insert(size_t position, U&&); + template <typename U> + void insert(size_t position, const U*, size_t); + template <typename U, size_t otherCapacity, typename OtherAllocator> + void insert(size_t position, const Vector<U, otherCapacity, OtherAllocator>&); + + // Insertion to the front. All of these functions will take O(size())-time. + // All of the elements in the vector will be moved to the new locations. + // All of these functions may cause a reallocation. In any case, all the + // iterators pointing to any element in the vector will be invalidated. + // + // prepend(value) + // Insert a single element to the front. + // prepend(buffer, size) + // prependVector(vector) + // Insert multiple elements represented by either |buffer| and |size| or + // |vector| to the front. The elements will be copied. + template <typename U> + void prepend(U&&); + template <typename U> + void prepend(const U*, size_t); + template <typename U, size_t otherCapacity, typename OtherAllocator> + void prependVector(const Vector<U, otherCapacity, OtherAllocator>&); + + // Remove an element or elements at the specified position. These functions + // take O(size())-time. All of the elements after the removed ones will be + // moved to the new locations. All the iterators pointing to any element + // after |position| will be invalidated. + void remove(size_t position); + void remove(size_t position, size_t length); + + // Remove the last element. Unlike remove(), (1) this function is fast, and + // (2) only iterators pointing to the last element will be invalidated. Other + // references will remain valid. + void pop_back() { + DCHECK(!isEmpty()); + shrink(size() - 1); + } + + // Filling the vector with the same value. If the vector has shrinked or + // growed as a result of this call, those events may invalidate some + // iterators. See comments for shrink() and grow(). + // + // fill(value, size) will resize the Vector to |size|, and then copy-assign + // or copy-initialize all the elements. + // + // fill(value) is a synonym for fill(value, size()). + void fill(const T&, size_t); + void fill(const T& val) { fill(val, size()); } + + // Swap two vectors quickly. + void swap(Vector& other) { + Base::swapVectorBuffer(other, OffsetRange(), OffsetRange()); + } + + // Reverse the contents. + void reverse(); + + // Maximum element count supported; allocating a vector + // buffer with a larger count will fail. + static size_t maxCapacity() { + return Allocator::template maxElementCountInBackingStore<T>(); } // Off-GC-heap vectors: Destructor should be called. @@ -902,135 +1115,6 @@ void finalizeGarbageCollectedObject() { finalize(); } - Vector(const Vector&); - template <size_t otherCapacity> - explicit Vector(const Vector<T, otherCapacity, Allocator>&); - - Vector& operator=(const Vector&); - template <size_t otherCapacity> - Vector& operator=(const Vector<T, otherCapacity, Allocator>&); - - Vector(Vector&&); - Vector& operator=(Vector&&); - - Vector(std::initializer_list<T> elements); - Vector& operator=(std::initializer_list<T> elements); - - size_t size() const { return m_size; } - size_t capacity() const { return Base::capacity(); } - bool isEmpty() const { return !size(); } - - T& at(size_t i) { - RELEASE_ASSERT(i < size()); - return Base::buffer()[i]; - } - const T& at(size_t i) const { - RELEASE_ASSERT(i < size()); - return Base::buffer()[i]; - } - - T& operator[](size_t i) { return at(i); } - const T& operator[](size_t i) const { return at(i); } - - T* data() { return Base::buffer(); } - const T* data() const { return Base::buffer(); } - - iterator begin() { return data(); } - iterator end() { return begin() + m_size; } - const_iterator begin() const { return data(); } - const_iterator end() const { return begin() + m_size; } - - reverse_iterator rbegin() { return reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - T& front() { return at(0); } - const T& front() const { return at(0); } - T& back() { return at(size() - 1); } - const T& back() const { return at(size() - 1); } - - template <typename U> - bool contains(const U&) const; - template <typename U> - size_t find(const U&) const; - template <typename U> - size_t reverseFind(const U&) const; - - void shrink(size_t); - void grow(size_t); - void resize(size_t); - void reserveCapacity(size_t newCapacity); - void reserveInitialCapacity(size_t initialCapacity); - void shrinkToFit() { shrinkCapacity(size()); } - void shrinkToReasonableCapacity() { - if (size() * 2 < capacity()) - shrinkCapacity(size() + size() / 4 + 1); - } - - void clear() { shrinkCapacity(0); } - - template <typename U> - void append(const U*, size_t); - template <typename U> - void push_back(U&&); - template <typename... Args> - T& emplace_back(Args&&...); - template <typename U> - void uncheckedAppend(U&& val); - template <typename U, size_t otherCapacity, typename V> - void appendVector(const Vector<U, otherCapacity, V>&); - - template <typename U> - void insert(size_t position, const U*, size_t); - template <typename U> - void insert(size_t position, U&&); - template <typename U, size_t c, typename V> - void insert(size_t position, const Vector<U, c, V>&); - - template <typename U> - void prepend(const U*, size_t); - template <typename U> - void prepend(U&&); - template <typename U, size_t c, typename V> - void prependVector(const Vector<U, c, V>&); - - void remove(size_t position); - void remove(size_t position, size_t length); - - void pop_back() { - DCHECK(!isEmpty()); - shrink(size() - 1); - } - - Vector(size_t size, const T& val) : Base(size) { - ANNOTATE_NEW_BUFFER(begin(), capacity(), size); - m_size = size; - TypeOperations::uninitializedFill(begin(), end(), val); - } - - void fill(const T&, size_t); - void fill(const T& val) { fill(val, size()); } - - template <typename Iterator> - void appendRange(Iterator start, Iterator end); - - void swap(Vector& other) { - Base::swapVectorBuffer(other, OffsetRange(), OffsetRange()); - } - - void reverse(); - - // Maximum element count supported; allocating a vector - // buffer with a larger count will fail. - static size_t maxCapacity() { - return Allocator::template maxElementCountInBackingStore<T>(); - } - template <typename VisitorDispatcher> void trace(VisitorDispatcher); @@ -1071,6 +1155,67 @@ // template <typename T, size_t inlineCapacity, typename Allocator> +inline Vector<T, inlineCapacity, Allocator>::Vector() { + static_assert(!std::is_polymorphic<T>::value || + !VectorTraits<T>::canInitializeWithMemset, + "Cannot initialize with memset if there is a vtable"); + static_assert(Allocator::isGarbageCollected || + !AllowsOnlyPlacementNew<T>::value || !IsTraceable<T>::value, + "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that " + "have trace methods into an off-heap Vector"); + static_assert(Allocator::isGarbageCollected || + !IsPointerToGarbageCollectedType<T>::value, + "Cannot put raw pointers to garbage-collected classes into " + "an off-heap Vector. Use HeapVector<Member<T>> instead."); + + ANNOTATE_NEW_BUFFER(begin(), capacity(), 0); + m_size = 0; +} + +template <typename T, size_t inlineCapacity, typename Allocator> +inline Vector<T, inlineCapacity, Allocator>::Vector(size_t size) : Base(size) { + static_assert(!std::is_polymorphic<T>::value || + !VectorTraits<T>::canInitializeWithMemset, + "Cannot initialize with memset if there is a vtable"); + static_assert(Allocator::isGarbageCollected || + !AllowsOnlyPlacementNew<T>::value || !IsTraceable<T>::value, + "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that " + "have trace methods into an off-heap Vector"); + static_assert(Allocator::isGarbageCollected || + !IsPointerToGarbageCollectedType<T>::value, + "Cannot put raw pointers to garbage-collected classes into " + "an off-heap Vector. Use HeapVector<Member<T>> instead."); + + ANNOTATE_NEW_BUFFER(begin(), capacity(), size); + m_size = size; + TypeOperations::initialize(begin(), end()); +} + +template <typename T, size_t inlineCapacity, typename Allocator> +inline Vector<T, inlineCapacity, Allocator>::Vector(size_t size, const T& val) + : Base(size) { + // TODO(yutak): Introduce these assertions. Some use sites call this function + // in the context where T is an incomplete type. + // + // static_assert(!std::is_polymorphic<T>::value || + // !VectorTraits<T>::canInitializeWithMemset, + // "Cannot initialize with memset if there is a vtable"); + // static_assert(Allocator::isGarbageCollected || + // !AllowsOnlyPlacementNew<T>::value || + // !IsTraceable<T>::value, + // "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that " + // "have trace methods into an off-heap Vector"); + // static_assert(Allocator::isGarbageCollected || + // !IsPointerToGarbageCollectedType<T>::value, + // "Cannot put raw pointers to garbage-collected classes into " + // "an off-heap Vector. Use HeapVector<Member<T>> instead."); + + ANNOTATE_NEW_BUFFER(begin(), capacity(), size); + m_size = size; + TypeOperations::uninitializedFill(begin(), end(), val); +} + +template <typename T, size_t inlineCapacity, typename Allocator> Vector<T, inlineCapacity, Allocator>::Vector(const Vector& other) : Base(other.capacity()) { ANNOTATE_NEW_BUFFER(begin(), capacity(), other.size()); @@ -1231,14 +1376,6 @@ } template <typename T, size_t inlineCapacity, typename Allocator> -template <typename Iterator> -void Vector<T, inlineCapacity, Allocator>::appendRange(Iterator start, - Iterator end) { - for (Iterator it = start; it != end; ++it) - push_back(*it); -} - -template <typename T, size_t inlineCapacity, typename Allocator> void Vector<T, inlineCapacity, Allocator>::expandCapacity( size_t newMinCapacity) { size_t oldCapacity = capacity(); @@ -1407,24 +1544,6 @@ template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> -void Vector<T, inlineCapacity, Allocator>::append(const U* data, - size_t dataSize) { - DCHECK(Allocator::isAllocationAllowed()); - size_t newSize = m_size + dataSize; - if (newSize > capacity()) { - data = expandCapacity(newSize, data); - DCHECK(begin()); - } - RELEASE_ASSERT(newSize >= m_size); - T* dest = end(); - ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, newSize); - VectorCopier<VectorTraits<T>::canCopyWithMemcpy, T>::uninitializedCopy( - data, &data[dataSize], dest); - m_size = newSize; -} - -template <typename T, size_t inlineCapacity, typename Allocator> -template <typename U> ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::push_back(U&& val) { DCHECK(Allocator::isAllocationAllowed()); if (LIKELY(size() != capacity())) { @@ -1456,6 +1575,24 @@ template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> +void Vector<T, inlineCapacity, Allocator>::append(const U* data, + size_t dataSize) { + DCHECK(Allocator::isAllocationAllowed()); + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + DCHECK(begin()); + } + RELEASE_ASSERT(newSize >= m_size); + T* dest = end(); + ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, newSize); + VectorCopier<VectorTraits<T>::canCopyWithMemcpy, T>::uninitializedCopy( + data, &data[dataSize], dest); + m_size = newSize; +} + +template <typename T, size_t inlineCapacity, typename Allocator> +template <typename U> NEVER_INLINE void Vector<T, inlineCapacity, Allocator>::appendSlowCase( U&& val) { DCHECK_EQ(size(), capacity()); @@ -1469,9 +1606,23 @@ ++m_size; } +template <typename T, size_t inlineCapacity, typename Allocator> +template <typename U, size_t otherCapacity, typename OtherAllocator> +inline void Vector<T, inlineCapacity, Allocator>::appendVector( + const Vector<U, otherCapacity, OtherAllocator>& val) { + append(val.begin(), val.size()); +} + +template <typename T, size_t inlineCapacity, typename Allocator> +template <typename Iterator> +void Vector<T, inlineCapacity, Allocator>::appendRange(Iterator begin, + Iterator end) { + for (Iterator it = begin; it != end; ++it) + push_back(*it); +} + // This version of append saves a branch in the case where you know that the // vector's capacity is large enough for the append to succeed. - template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::uncheckedAppend( @@ -1488,10 +1639,21 @@ } template <typename T, size_t inlineCapacity, typename Allocator> -template <typename U, size_t otherCapacity, typename OtherAllocator> -inline void Vector<T, inlineCapacity, Allocator>::appendVector( - const Vector<U, otherCapacity, OtherAllocator>& val) { - append(val.begin(), val.size()); +template <typename U> +inline void Vector<T, inlineCapacity, Allocator>::insert(size_t position, + U&& val) { + DCHECK(Allocator::isAllocationAllowed()); + RELEASE_ASSERT(position <= size()); + typename std::remove_reference<U>::type* data = &val; + if (size() == capacity()) { + data = expandCapacity(size() + 1, data); + DCHECK(begin()); + } + ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1); + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + 1); + new (NotNull, spot) T(std::forward<U>(*data)); + ++m_size; } template <typename T, size_t inlineCapacity, typename Allocator> @@ -1516,29 +1678,17 @@ } template <typename T, size_t inlineCapacity, typename Allocator> -template <typename U> -inline void Vector<T, inlineCapacity, Allocator>::insert(size_t position, - U&& val) { - DCHECK(Allocator::isAllocationAllowed()); - RELEASE_ASSERT(position <= size()); - typename std::remove_reference<U>::type* data = &val; - if (size() == capacity()) { - data = expandCapacity(size() + 1, data); - DCHECK(begin()); - } - ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1); - T* spot = begin() + position; - TypeOperations::moveOverlapping(spot, end(), spot + 1); - new (NotNull, spot) T(std::forward<U>(*data)); - ++m_size; +template <typename U, size_t otherCapacity, typename OtherAllocator> +inline void Vector<T, inlineCapacity, Allocator>::insert( + size_t position, + const Vector<U, otherCapacity, OtherAllocator>& val) { + insert(position, val.begin(), val.size()); } template <typename T, size_t inlineCapacity, typename Allocator> -template <typename U, size_t c, typename OtherAllocator> -inline void Vector<T, inlineCapacity, Allocator>::insert( - size_t position, - const Vector<U, c, OtherAllocator>& val) { - insert(position, val.begin(), val.size()); +template <typename U> +inline void Vector<T, inlineCapacity, Allocator>::prepend(U&& val) { + insert(0, std::forward<U>(val)); } template <typename T, size_t inlineCapacity, typename Allocator> @@ -1549,15 +1699,9 @@ } template <typename T, size_t inlineCapacity, typename Allocator> -template <typename U> -inline void Vector<T, inlineCapacity, Allocator>::prepend(U&& val) { - insert(0, std::forward<U>(val)); -} - -template <typename T, size_t inlineCapacity, typename Allocator> -template <typename U, size_t c, typename V> +template <typename U, size_t otherCapacity, typename OtherAllocator> inline void Vector<T, inlineCapacity, Allocator>::prependVector( - const Vector<U, c, V>& val) { + const Vector<U, otherCapacity, OtherAllocator>& val) { insert(0, val.begin(), val.size()); }
diff --git a/third_party/WebKit/public/platform/scheduler/base/task_queue.h b/third_party/WebKit/public/platform/scheduler/base/task_queue.h index 07335fb0..c57d237 100644 --- a/third_party/WebKit/public/platform/scheduler/base/task_queue.h +++ b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
@@ -150,8 +150,9 @@ // NOTE: this must be called on the thread this TaskQueue was created by. virtual bool HasPendingImmediateWork() const = 0; - // Returns requested run time of next delayed task, which is not ready - // to run. If there are no such tasks, returns base::nullopt. + // Returns requested run time of next scheduled wakeup for a delayed task + // which is not ready to run. If there are no such tasks or the queue is + // disabled (by a QueueEnabledVoter) it returns base::nullopt. // NOTE: this must be called on the thread this TaskQueue was created by. virtual base::Optional<base::TimeTicks> GetNextScheduledWakeUp() = 0;
diff --git a/third_party/freetype-android/README.chromium b/third_party/freetype-android/README.chromium index 90889607..a37fbc1 100644 --- a/third_party/freetype-android/README.chromium +++ b/third_party/freetype-android/README.chromium
@@ -1,7 +1,7 @@ Name: FreeType URL: http://www.freetype.org/ -Version: VER-2-7-0-updates -Revision: c38be52bf8de3b1699d74932b849bf150265819e +Version: VER-2-7-1-updates +Revision: 66725768cdf758cfb3f9abf03cbf5e5a77f42088 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent JPEG Group) licenses" License File: src/docs/FTL.TXT
diff --git a/third_party/freetype-android/include/freetype-android-config/ftoption.h b/third_party/freetype-android/include/freetype-android-config/ftoption.h index bcd1307..f419cc1 100644 --- a/third_party/freetype-android/include/freetype-android-config/ftoption.h +++ b/third_party/freetype-android/include/freetype-android-config/ftoption.h
@@ -4,7 +4,7 @@ /* */ /* User-selectable configuration macros (specification only). */ /* */ -/* Copyright 1996-2016 by */ +/* Copyright 1996-2017 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -82,8 +82,8 @@ /* to control the various font drivers and modules. The controllable */ /* properties are listed in the section `Controlling FreeType Modules' */ /* in the reference's table of contents; currently there are properties */ - /* for the auto-hinter (file `ftautoh.h'), CFF (file `ftcffdrv.h'), and */ - /* TrueType (file `ftttdrv.h'). */ + /* for the auto-hinter (file `ftautoh.h'), CFF (file `ftcffdrv.h'), */ + /* TrueType (file `ftttdrv.h'), and PCF (file `ftpcfdrv.h'). */ /* */ /* `FREETYPE_PROPERTIES' has the following syntax form (broken here into */ /* multiple lines for better readability). */ @@ -640,17 +640,21 @@ /* [1] for a technical overview on what this means. See `ttinterp.h' */ /* for more details on the LEAN option. */ /* */ - /* There are three options. */ + /* There are three possible values. */ /* */ - /* 1. This option is associated with the `Infinality' moniker. */ - /* Contributed by an individual nicknamed Infinality with the goal of */ + /* Value 1: */ + /* This value is associated with the `Infinality' moniker, */ + /* contributed by an individual nicknamed Infinality with the goal of */ /* making TrueType fonts render better than on Windows. A high */ /* amount of configurability and flexibility, down to rules for */ /* single glyphs in fonts, but also very slow. Its experimental and */ /* slow nature and the original developer losing interest meant that */ /* this option was never enabled in default builds. */ /* */ - /* 2. The new default mode for the TrueType driver. The Infinality code */ + /* The corresponding interpreter version is v38. */ + /* */ + /* Value 2: */ + /* The new default mode for the TrueType driver. The Infinality code */ /* base was stripped to the bare minimum and all configurability */ /* removed in the name of speed and simplicity. The configurability */ /* was mainly aimed at legacy fonts like Arial, Times New Roman, or */ @@ -660,14 +664,19 @@ /* that modern and web fonts render well while legacy fonts render */ /* okay. */ /* */ - /* 3. Compile both. */ + /* The corresponding interpreter version is v40. */ + /* */ + /* Value 3: */ + /* Compile both, making both v38 and v40 available (the latter is the */ + /* default). */ /* */ /* By undefining these, you get rendering behavior like on Windows */ /* without ClearType, i.e., Windows XP without ClearType enabled and */ /* Win9x (interpreter version v35). Or not, depending on how much */ /* hinting blood and testing tears the font designer put into a given */ /* font. If you define one or both subpixel hinting options, you can */ - /* switch between between v35 and the ones you define. */ + /* switch between between v35 and the ones you define (using */ + /* `FT_Property_Set'). */ /* */ /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ /* defined. */ @@ -835,6 +844,33 @@ /*************************************************************************/ /*************************************************************************/ /**** ****/ + /**** P C F D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* There are many PCF fonts just called `Fixed' which look completely */ + /* different, and which have nothing to do with each other. When */ + /* selecting `Fixed' in KDE or Gnome one gets results that appear rather */ + /* random, the style changes often if one changes the size and one */ + /* cannot select some fonts at all. This option makes the PCF module */ + /* prepend the foundry name (plus a space) to the family name. */ + /* */ + /* We also check whether we have `wide' characters; all put together, we */ + /* get family names like `Sony Fixed' or `Misc Fixed Wide'. */ + /* */ + /* If this option is activated, it can be controlled with the */ + /* `no-long-family-names' property of the pcf driver module. */ + /* */ +/* #define PCF_CONFIG_OPTION_LONG_FAMILY_NAMES */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ /**** ****/ /*************************************************************************/
diff --git a/third_party/re2/BUILD.gn b/third_party/re2/BUILD.gn index 4ced4b2..b96f754 100644 --- a/third_party/re2/BUILD.gn +++ b/third_party/re2/BUILD.gn
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//testing/libfuzzer/fuzzer_test.gni") + config("re2_config") { include_dirs = [ "src" ] } @@ -57,3 +59,12 @@ configs += [ "//build/config/compiler:no_chromium_code" ] public_configs = [ ":re2_config" ] } + +fuzzer_test("third_party_re2_fuzzer") { + sources = [ + "src/re2/fuzzing/re2_fuzzer.cc", + ] + deps = [ + ":re2", + ] +}
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index f633c57..0cf99454 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -14996,7 +14996,7 @@ </histogram> <histogram name="Event.Latency.OS" units="microseconds"> - <owner>sahel@chromium.org</owner> + <owner>tdresser@chromium.org</owner> <summary>Time between input event received by OS and sent to Chrome.</summary> </histogram> @@ -44199,7 +44199,7 @@ <histogram name="PageLoad.InputTiming.NavigationToFirstNonScroll.AfterPaint" units="ms"> - <owner>sahel@chromium.org</owner> + <owner>tdresser@chromium.org</owner> <summary> Measures the time to first non-scroll input after the first paint. </summary> @@ -44207,7 +44207,7 @@ <histogram name="PageLoad.InputTiming.NavigationToFirstScroll.AfterPaint" units="ms"> - <owner>sahel@chromium.org</owner> + <owner>tdresser@chromium.org</owner> <summary> Measures the time to first scroll input after the first paint. </summary>
diff --git a/tools/perf/benchmarks/v8_browsing.py b/tools/perf/benchmarks/v8_browsing.py index 6f2f08b..f5c4c8c 100644 --- a/tools/perf/benchmarks/v8_browsing.py +++ b/tools/perf/benchmarks/v8_browsing.py
@@ -78,8 +78,11 @@ if 'memory:chrome' in value.name: return ('renderer_processes' in value.name and not _IGNORED_MEMORY_STATS_RE.search(value.name)) - return (_V8_GC_HIGH_LEVEL_STATS_RE.search(value.name) and - not _IGNORED_V8_STATS_RE.search(value.name)) + if 'v8-gc' in value.name: + return (_V8_GC_HIGH_LEVEL_STATS_RE.search(value.name) and + not _IGNORED_V8_STATS_RE.search(value.name)) + # Allow all other non-GC metrics. + return 'v8' in value.name @classmethod def ShouldTearDownStateAfterEachStoryRun(cls):