diff --git a/DEPS b/DEPS index 0f70c3e..c104238 100644 --- a/DEPS +++ b/DEPS
@@ -90,11 +90,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': '5c9369eb14f567b0d6348b0d63c14d0a8d966c56', + 'skia_revision': 'a070ed7fccd8bc9c5f314e1a4ac090c497862836', # 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': '69a2f03ac7decea3c079584433b88a08e2e86feb', + 'v8_revision': '24871c6b1ea211f8ee58cf17d4d09161362342b3', # 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. @@ -102,7 +102,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '0086de17943b9fae9bf0bf79d4b8fe52ce275e66', + 'angle_revision': 'b221486a8dfd90214d2755edd61290b3f5e8c782', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. @@ -114,7 +114,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '9b8b217e6f8f2ff03b09d6075e689804928c7b3c', + 'pdfium_revision': '52ab95aa3edbabbe90dcefcc54f3b6dace7ac53d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -150,7 +150,7 @@ # 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': 'dc970d3e1f7b3da5a2849de70ff253acdb70148f', + 'catapult_revision': '846cec5c4d22e5f0e6f995a6af30eff5dbccebb6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -260,7 +260,7 @@ }, 'src/ios/third_party/material_components_ios/src': { - 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a0c6b4d6bbade404fb51a654c0c6529422e2c477', + 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '34aabeea14450bc357b81b5e6206c86d346fffe6', 'condition': 'checkout_ios', }, @@ -489,7 +489,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'aa6cca46950beff30c9a0e98cf03ff20a7b5cd92', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '74d7d171a121135f3a16daba6485e63e2fe6cc9f', 'condition': 'checkout_linux', }, @@ -504,7 +504,7 @@ # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '1f64aefc280ef03010d5dd1cb22954ed3bfc0056', + 'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '43880122c4f3dfce062613e5b1d2865647c03a5a', 'condition': 'checkout_linux', }, @@ -514,7 +514,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3b9bb8131796023882d6f48b0c391f95a43c2e1e', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '97ce05851cb5559459058706a69e81ddaed54f1e', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -549,7 +549,7 @@ }, 'src/third_party/ffmpeg': - Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '16ee25d20583df326539f70180287acc39e53651', + Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '156e91a4f377b985b6455155a8d4ba0f7608a96a', 'src/third_party/flac': Var('chromium_git') + '/chromium/deps/flac.git' + '@' + '7d0f5b3a173ffe98db08057d1f52b7787569e0a6', @@ -584,7 +584,7 @@ }, 'src/third_party/googletest/src': - Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + '4bd8c4638ada823a8da2569735cc0a9402fb8052', + Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + 'a6f06bf2fd3b832822cd4e9e554b7d47f32ec084', # GNU binutils assembler for x86-32. 'src/third_party/gnu_binutils': { @@ -624,6 +624,11 @@ 'condition': 'checkout_android', }, + 'src/third_party/arcore-android-sdk/src': { + 'url': Var('chromium_git') + '/external/github.com/google-ar/arcore-android-sdk.git' + '@' + '772bed8e2e1bc525a0d10441fa71168a9a87eb69', + 'condition': 'checkout_android', + }, + 'src/third_party/hamcrest': { 'packages': [ { @@ -954,7 +959,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c0541da63f571512c49758cbc0767117997a270', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'ff61273c010c6bc1641369eddfe939d5e56cb8c4', # commit position 21742 + Var('webrtc_git') + '/src.git' + '@' + 'e782abab190e0fb7afb501c470cf3d7fa8d5e7a9', # commit position 21742 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -988,7 +993,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7e2468888f998876aaec501dcbb42a9a0569649d', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3fd804ac57040882275c005e11c2ecc19f30d7cc', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index f1759228..3d4b12c 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn
@@ -897,6 +897,7 @@ "//components/navigation_interception/android:navigation_interception_java", "//components/policy/android:policy_java", "//components/safe_browsing/android:safe_browsing_java", + "//components/variations:load_seed_result_enum_java", "//components/variations/android:variations_java", "//components/version_info/android:version_constants_java", "//components/web_contents_delegate_android:web_contents_delegate_android_java",
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java index 2c0ccb3c..61c9809 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java +++ b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
@@ -26,7 +26,7 @@ private static final String OPT_IN_META_DATA_STR = "android.webkit.WebView.EnableSafeBrowsing"; private static final boolean DEFAULT_USER_OPT_IN = false; - private static Boolean sSafeBrowsingUserOptIn; + private static volatile Boolean sSafeBrowsingUserOptIn; // Used to record the UMA histogram SafeBrowsing.WebView.AppOptIn. Since these values are // persisted to logs, they should never be renumbered nor reused.
diff --git a/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java b/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java index 63f862c..a934de6 100644 --- a/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java +++ b/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java
@@ -51,6 +51,7 @@ return false; } + // Overriding implementations may call "callback" asynchronously, on any thread. public void querySafeBrowsingUserConsent( Context context, @NonNull final Callback<Boolean> callback) { // User opt-in preference depends on a SafetyNet API.
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java index df8bf1d..00616a2 100644 --- a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java +++ b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
@@ -12,6 +12,7 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.SystemClock; import org.chromium.android_webview.services.IVariationsSeedServer; import org.chromium.android_webview.services.VariationsSeedServer; @@ -19,6 +20,9 @@ import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.VisibleForTesting; +import org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample; +import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample; +import org.chromium.components.variations.LoadSeedResult; import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo; import java.io.File; @@ -72,9 +76,15 @@ // is exceeded, proceed with variations disabled. private static final long SEED_LOAD_TIMEOUT_MILLIS = 20; + private static void recordLoadSeedResult(int result) { + EnumeratedHistogramSample histogram = new EnumeratedHistogramSample( + "Variations.SeedLoadResult", LoadSeedResult.ENUM_SIZE); + histogram.record(result); + } + // AGSA will notify us to enable variations by touching this file. // TODO(paulmiller): Remove this after completing the experiment. - private static boolean isEnabledByExperiment() { + private static boolean checkEnabledByExperiment() { File filesDir = ContextUtils.getApplicationContext().getFilesDir(); File experimentFile = new File(new File(filesDir, "webview"), "finch-exp"); return experimentFile.exists(); @@ -97,12 +107,19 @@ // - mNeedNewSeed: Should we request a new seed from the service? // - mCurrentSeedDate: The "date" field of our local seed, converted to milliseconds since // epoch, or Long.MIN_VALUE if we have no seed. + // - mEnabledByExperiment: Whether variations enabled by the AGSA experiment. If so, and + // variations is not already enabled by CommandLine, then it should be made enabled by + // CommandLine. This is volatile because it's set inside mLoadTask on a background thread, + // but read in isVariationsEnabled() on the main thread. TODO(paulmiller): Remove this + // after completing the experiment. private boolean mFoundNewSeed; private boolean mNeedNewSeed; private long mCurrentSeedDate = Long.MIN_VALUE; + private volatile boolean mEnabledByExperiment; private FutureTask<SeedInfo> mLoadTask = new FutureTask<>(() -> { - if (!(mEnabledByCmd || isEnabledByExperiment())) return null; + mEnabledByExperiment = checkEnabledByExperiment(); + if (!(mEnabledByCmd || mEnabledByExperiment)) return null; File newSeedFile = VariationsUtils.getNewSeedFile(); File oldSeedFile = VariationsUtils.getSeedFile(); @@ -187,6 +204,12 @@ throws InterruptedException, ExecutionException, TimeoutException { return mLoadTask.get(timeout, unit); } + + // mEnabledByExperiment is set in mLoadTask, so isVariationsEnabled() should only be called + // after run() returns. + public boolean isVariationsEnabled() { + return mEnabledByCmd || mEnabledByExperiment; + } } // Connects to VariationsSeedServer service. Sends a file descriptor for our local copy of the @@ -229,6 +252,36 @@ private SeedLoadAndUpdateRunnable mRunnable; + private SeedInfo getSeedBlockingAndLog() { + long start = SystemClock.elapsedRealtime(); + try { + try { + return mRunnable.get(SEED_LOAD_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } finally { + if (mRunnable.isVariationsEnabled()) { + long end = SystemClock.elapsedRealtime(); + TimesHistogramSample histogram = new TimesHistogramSample( + "Variations.SeedLoadBlockingTime", TimeUnit.MILLISECONDS); + histogram.record(end - start); + } + } + } catch (TimeoutException e) { + if (mRunnable.isVariationsEnabled()) { + recordLoadSeedResult(LoadSeedResult.LOAD_TIMED_OUT); + } + } catch (InterruptedException e) { + if (mRunnable.isVariationsEnabled()) { + recordLoadSeedResult(LoadSeedResult.LOAD_INTERRUPTED); + } + } catch (ExecutionException e) { + if (mRunnable.isVariationsEnabled()) { + recordLoadSeedResult(LoadSeedResult.LOAD_OTHER_FAILURE); + } + } + Log.e(TAG, "Failed loading variations seed. Variations disabled."); + return null; + } + @VisibleForTesting // Overridden by tests to wait until all work is done. protected void onBackgroundWorkFinished() {} @@ -276,24 +329,16 @@ // Block on loading the seed with a timeout. Then if a seed was successfully loaded, initialize // variations. public void finishVariationsInit() { - try { - SeedInfo seed = mRunnable.get(SEED_LOAD_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - if (seed != null) { - // If variations was not enabled by CommandLine, but we got a seed anyway, that - // means variations is enabled by the AGSA experiment. So enable it by CommandLine - // as well. - if (!isEnabledByCmd()) { - CommandLine.getInstance().appendSwitch(AwSwitches.ENABLE_WEBVIEW_VARIATIONS); - } + SeedInfo seed = getSeedBlockingAndLog(); - // TODO(paulmiller): Once we have actual seeds, this would be the place to do - // VariationsSeedBridge.setVariationsFirstRunSeed() with the loaded seed. - } - } catch (TimeoutException e) { - // TODO(paulmiller): Log seed load time and success rate in UMA. - Log.w(TAG, "Timeout out waiting for variations seed - variations disabled"); - } catch (InterruptedException | ExecutionException e) { - Log.e(TAG, "Failed loading variations seed - variations disabled", e); + // If enabled by experiment but not cmd, then also enable by cmd. + // isVariationsEnabled() must not be called before getSeedBlockingAndLog() returns. + // TODO(paulmiller): Remove this after completing the experiment. + if (mRunnable.isVariationsEnabled() && !isEnabledByCmd()) { + CommandLine.getInstance().appendSwitch(AwSwitches.ENABLE_WEBVIEW_VARIATIONS); } + + // TODO(paulmiller): Once we have actual seeds, this would be the place to do: + // if (seed != null) { VariationsSeedBridge.setVariationsFirstRunSeed(seed); } } }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java index 70a84d01..3e5bfc7 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
@@ -21,6 +21,7 @@ import org.chromium.android_webview.services.ServiceInit; import org.chromium.android_webview.test.services.MockVariationsSeedServer; import org.chromium.base.ContextUtils; +import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.parameter.SkipCommandLineParameterization; @@ -129,11 +130,13 @@ InstrumentationRegistry.getInstrumentation() .getTargetContext().getApplicationContext()); ServiceInit.setPrivateDataDirectorySuffix(); + RecordHistogram.setDisabledForTests(true); deleteSeeds(); } @After public void tearDown() throws IOException { + RecordHistogram.setDisabledForTests(false); deleteSeeds(); }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 8993d51..281f6bb 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -100,6 +100,8 @@ "assistant/model/assistant_interaction_model_impl.h", "assistant/ui/assistant_bubble.cc", "assistant/ui/assistant_bubble.h", + "assistant/ui/assistant_bubble_view.cc", + "assistant/ui/assistant_bubble_view.h", "autoclick/autoclick_controller.cc", "autoclick/autoclick_controller.h", "cancel_mode.cc", @@ -122,6 +124,8 @@ "disconnected_app_handler.h", "display/ash_display_controller.cc", "display/ash_display_controller.h", + "display/cros_display_config.cc", + "display/cros_display_config.h", "display/cursor_window_controller.cc", "display/cursor_window_controller.h", "display/display_animator.cc", @@ -605,7 +609,6 @@ "system/network/network_list.cc", "system/network/network_list.h", "system/network/network_observer.h", - "system/network/network_portal_detector_observer.h", "system/network/network_row_title_view.cc", "system/network/network_row_title_view.h", "system/network/network_state_list_detailed_view.cc", @@ -724,6 +727,8 @@ "system/session/logout_confirmation_controller.h", "system/session/logout_confirmation_dialog.cc", "system/session/logout_confirmation_dialog.h", + "system/session/session_limit_notification_controller.cc", + "system/session/session_limit_notification_controller.h", "system/session/tray_session_length_limit.cc", "system/session/tray_session_length_limit.h", "system/status_area_layout_manager.cc", @@ -1516,6 +1521,7 @@ "detachable_base/detachable_base_handler_unittest.cc", "detachable_base/detachable_base_notification_controller_unittest.cc", "dip_unittest.cc", + "display/cros_display_config_unittest.cc", "display/cursor_window_controller_unittest.cc", "display/display_color_manager_unittest.cc", "display/display_configuration_controller_unittest.cc", @@ -1663,6 +1669,7 @@ "system/screen_security/screen_tray_item_unittest.cc", "system/session/logout_button_tray_unittest.cc", "system/session/logout_confirmation_controller_unittest.cc", + "system/session/session_limit_notification_controller_unittest.cc", "system/session/tray_session_length_limit_unittest.cc", "system/status_area_widget_unittest.cc", "system/supervised/supervised_notification_controller_unittest.cc",
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index 62bd9bbb..a37a1d68 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -181,8 +181,12 @@ void AppListControllerImpl::SetItemMetadata(const std::string& id, AppListItemMetadataPtr data) { app_list::AppListItem* item = model_.FindItem(id); - if (item) + if (item) { + // data may not contain valid position. Preserve it in this case. + if (!data->position.IsValid()) + data->position = item->position(); item->SetMetadata(std::move(data)); + } } void AppListControllerImpl::SetItemIcon(const std::string& id,
diff --git a/ash/assistant/ash_assistant_controller.cc b/ash/assistant/ash_assistant_controller.cc index d0132d59..f1a84f6a 100644 --- a/ash/assistant/ash_assistant_controller.cc +++ b/ash/assistant/ash_assistant_controller.cc
@@ -24,14 +24,11 @@ : assistant_controller_binding_(this), assistant_event_subscriber_binding_(this), assistant_bubble_(std::make_unique<AssistantBubble>(this)) { - Shell::Get()->AddShellObserver(this); } AshAssistantController::~AshAssistantController() { assistant_controller_binding_.Close(); assistant_event_subscriber_binding_.Close(); - - Shell::Get()->RemoveShellObserver(this); } void AshAssistantController::BindRequest( @@ -115,9 +112,6 @@ } void AshAssistantController::OnHtmlResponse(const std::string& response) { - if (!is_app_list_shown_) - return; - assistant_interaction_model_.AddUiElement( std::make_unique<app_list::AssistantCardElement>(response)); } @@ -135,59 +129,38 @@ void AshAssistantController::OnSuggestionsResponse( const std::vector<std::string>& response) { - if (!is_app_list_shown_) - return; - assistant_interaction_model_.AddSuggestions(response); } void AshAssistantController::OnTextResponse(const std::string& response) { - if (!is_app_list_shown_) - return; - assistant_interaction_model_.AddUiElement( std::make_unique<app_list::AssistantTextElement>(response)); } void AshAssistantController::OnSpeechRecognitionStarted() { - if (!is_app_list_shown_) - return; - assistant_interaction_model_.ClearInteraction(); } void AshAssistantController::OnSpeechRecognitionIntermediateResult( const std::string& high_confidence_text, const std::string& low_confidence_text) { - if (!is_app_list_shown_) - return; - assistant_interaction_model_.SetQuery( {high_confidence_text, low_confidence_text}); } void AshAssistantController::OnSpeechRecognitionEndOfUtterance() { - if (!is_app_list_shown_) - return; - // TODO(dmblack): Handle. NOTIMPLEMENTED(); } void AshAssistantController::OnSpeechRecognitionFinalResult( const std::string& final_result) { - if (!is_app_list_shown_) - return; - app_list::Query query; query.high_confidence_text = final_result; assistant_interaction_model_.SetQuery(query); } void AshAssistantController::OnSpeechLevelUpdated(float speech_level) { - if (!is_app_list_shown_) - return; - // TODO(dmblack): Handle. NOTIMPLEMENTED(); } @@ -197,13 +170,4 @@ OnInteractionDismissed(); } -// TODO(b/77637813): Remove when pulling Assistant out of launcher. -void AshAssistantController::OnAppListVisibilityChanged( - bool shown, - aura::Window* root_window) { - is_app_list_shown_ = shown; - if (!is_app_list_shown_) - assistant_interaction_model_.ClearInteraction(); -} - } // namespace ash
diff --git a/ash/assistant/ash_assistant_controller.h b/ash/assistant/ash_assistant_controller.h index 0c007db3..8e65fb12 100644 --- a/ash/assistant/ash_assistant_controller.h +++ b/ash/assistant/ash_assistant_controller.h
@@ -12,7 +12,6 @@ #include "ash/assistant/model/assistant_interaction_model_impl.h" #include "ash/public/interfaces/ash_assistant_controller.mojom.h" #include "ash/public/interfaces/assistant_card_renderer.mojom.h" -#include "ash/shell_observer.h" #include "base/macros.h" #include "base/timer/timer.h" #include "chromeos/services/assistant/public/mojom/assistant.mojom.h" @@ -23,10 +22,6 @@ class AssistantInteractionModelObserver; } // namespace app_list -namespace aura { -class Window; -} // namespace aura - namespace base { class UnguessableToken; } // namespace base @@ -38,8 +33,7 @@ class AshAssistantController : public app_list::AssistantController, public mojom::AshAssistantController, - public chromeos::assistant::mojom::AssistantEventSubscriber, - public ShellObserver { + public chromeos::assistant::mojom::AssistantEventSubscriber { public: AshAssistantController(); ~AshAssistantController() override; @@ -83,10 +77,6 @@ void SetAssistantCardRenderer( mojom::AssistantCardRendererPtr assistant_card_renderer) override; - // ShellObserver: - void OnAppListVisibilityChanged(bool shown, - aura::Window* root_window) override; - private: void OnInteractionDismissed(); @@ -101,9 +91,6 @@ std::unique_ptr<AssistantBubble> assistant_bubble_; base::OneShotTimer assistant_bubble_timer_; - // TODO(b/77637813): Remove when pulling Assistant out of launcher. - bool is_app_list_shown_ = false; - DISALLOW_COPY_AND_ASSIGN(AshAssistantController); };
diff --git a/ash/assistant/ui/assistant_bubble.cc b/ash/assistant/ui/assistant_bubble.cc index 5cbefd7..f9b6635 100644 --- a/ash/assistant/ui/assistant_bubble.cc +++ b/ash/assistant/ui/assistant_bubble.cc
@@ -7,6 +7,7 @@ #include <memory> #include "ash/assistant/ash_assistant_controller.h" +#include "ash/assistant/ui/assistant_bubble_view.h" #include "base/strings/utf_string_conversions.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -29,8 +30,8 @@ class AssistantContainerView : public views::BubbleDialogDelegateView { public: - explicit AssistantContainerView( - AshAssistantController* assistant_controller) { + explicit AssistantContainerView(AshAssistantController* assistant_controller) + : assistant_controller_(assistant_controller) { set_accept_events(true); SetAnchor(); set_arrow(views::BubbleBorder::Arrow::BOTTOM_LEFT); @@ -71,12 +72,7 @@ private: void InitLayout() { SetLayoutManager(std::make_unique<views::FillLayout>()); - - // TODO(dmblack): Replace w/ actual bubble view. - views::View* bubble_view = new views::View(); - bubble_view->SetPreferredSize(gfx::Size(360, 48)); - - AddChildView(bubble_view); + AddChildView(new AssistantBubbleView(assistant_controller_)); } void SetAnchor() { @@ -93,6 +89,8 @@ SetAnchorRect(anchor); } + app_list::AssistantController* assistant_controller_; // Owned by Shell. + DISALLOW_COPY_AND_ASSIGN(AssistantContainerView); };
diff --git a/ui/app_list/views/assistant_bubble_view.cc b/ash/assistant/ui/assistant_bubble_view.cc similarity index 90% rename from ui/app_list/views/assistant_bubble_view.cc rename to ash/assistant/ui/assistant_bubble_view.cc index b485e79..de36762 100644 --- a/ui/app_list/views/assistant_bubble_view.cc +++ b/ash/assistant/ui/assistant_bubble_view.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/app_list/views/assistant_bubble_view.h" +#include "ash/assistant/ui/assistant_bubble_view.h" #include "base/callback.h" #include "base/strings/utf_string_conversions.h" @@ -18,13 +18,11 @@ #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" -namespace app_list { +namespace ash { namespace { // Appearance. -constexpr SkColor kBackgroundColor = SK_ColorWHITE; -constexpr int kCornerRadiusDip = 16; constexpr int kPaddingDip = 8; constexpr int kPreferredWidthDip = 364; constexpr int kSpacingDip = 8; @@ -79,7 +77,7 @@ ~InteractionContainer() override = default; - void SetQuery(const Query& query) { + void SetQuery(const app_list::Query& query) { // TODO(dmblack): Represent high confidence and low confidence portions of // the query with different colors. interaction_label_->SetText(base::UTF8ToUTF16(query.high_confidence_text) + @@ -174,8 +172,9 @@ void EmbedCard(const base::UnguessableToken& embed_token) { // When the card has been rendered in the same process, its view is // available in the AnswerCardContentsRegistry's token-to-view map. - if (AnswerCardContentsRegistry::Get()) { - AddChildView(AnswerCardContentsRegistry::Get()->GetView(embed_token)); + if (app_list::AnswerCardContentsRegistry::Get()) { + AddChildView( + app_list::AnswerCardContentsRegistry::Get()->GetView(embed_token)); } // TODO(dmblack): Handle Mash case. } @@ -204,7 +203,7 @@ class SuggestionsContainer : public views::View { public: - explicit SuggestionsContainer(SuggestionChipListener* listener) + explicit SuggestionsContainer(app_list::SuggestionChipListener* listener) : suggestion_chip_listener_(listener) {} ~SuggestionsContainer() override = default; @@ -253,8 +252,8 @@ void AddSuggestions(const std::vector<std::string>& suggestions) { for (const std::string& suggestion : suggestions) { - AddChildView(new SuggestionChipView(base::UTF8ToUTF16(suggestion), - suggestion_chip_listener_)); + AddChildView(new app_list::SuggestionChipView( + base::UTF8ToUTF16(suggestion), suggestion_chip_listener_)); } PreferredSizeChanged(); } @@ -265,7 +264,7 @@ } private: - SuggestionChipListener* const suggestion_chip_listener_; + app_list::SuggestionChipListener* const suggestion_chip_listener_; DISALLOW_COPY_AND_ASSIGN(SuggestionsContainer); }; @@ -275,7 +274,7 @@ // AssistantBubbleView --------------------------------------------------------- AssistantBubbleView::AssistantBubbleView( - AssistantController* assistant_controller) + app_list::AssistantController* assistant_controller) : assistant_controller_(assistant_controller), interaction_container_(new InteractionContainer()), ui_element_container_(new UiElementContainer()), @@ -308,9 +307,6 @@ } void AssistantBubbleView::InitLayout() { - SetBackground(std::make_unique<RoundRectBackground>(kBackgroundColor, - kCornerRadiusDip)); - SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::Orientation::kVertical, gfx::Insets(kPaddingDip, 0), kSpacingDip)); @@ -342,14 +338,15 @@ void AssistantBubbleView::ProcessPendingUiElements() { while (!is_processing_ui_element_ && !pending_ui_element_list_.empty()) { - const AssistantUiElement* ui_element = pending_ui_element_list_.front(); + const app_list::AssistantUiElement* ui_element = + pending_ui_element_list_.front(); pending_ui_element_list_.pop_front(); OnUiElementAdded(ui_element); } } void AssistantBubbleView::OnUiElementAdded( - const AssistantUiElement* ui_element) { + const app_list::AssistantUiElement* ui_element) { // If we are processing a UI element we need to pend the incoming element // instead of handling it immediately. if (is_processing_ui_element_) { @@ -358,11 +355,13 @@ } switch (ui_element->GetType()) { - case AssistantUiElementType::kCard: - OnCardAdded(static_cast<const AssistantCardElement*>(ui_element)); + case app_list::AssistantUiElementType::kCard: + OnCardAdded( + static_cast<const app_list::AssistantCardElement*>(ui_element)); break; - case AssistantUiElementType::kText: - OnTextAdded(static_cast<const AssistantTextElement*>(ui_element)); + case app_list::AssistantUiElementType::kText: + OnTextAdded( + static_cast<const app_list::AssistantTextElement*>(ui_element)); break; } } @@ -382,7 +381,7 @@ } void AssistantBubbleView::OnCardAdded( - const AssistantCardElement* card_element) { + const app_list::AssistantCardElement* card_element) { DCHECK(!is_processing_ui_element_); // We need to pend any further UI elements until the card has been rendered. @@ -432,7 +431,7 @@ } } -void AssistantBubbleView::OnQueryChanged(const Query& query) { +void AssistantBubbleView::OnQueryChanged(const app_list::Query& query) { interaction_container_->SetQuery(query); } @@ -452,17 +451,17 @@ } void AssistantBubbleView::OnSuggestionChipPressed( - SuggestionChipView* suggestion_chip_view) { + app_list::SuggestionChipView* suggestion_chip_view) { assistant_controller_->OnSuggestionChipPressed( base::UTF16ToUTF8(suggestion_chip_view->GetText())); } void AssistantBubbleView::OnTextAdded( - const AssistantTextElement* text_element) { + const app_list::AssistantTextElement* text_element) { DCHECK(!is_processing_ui_element_); ui_element_container_->AddText(text_element->GetText()); ui_element_container_->SetVisible(true); } -} // namespace app_list +} // namespace ash
diff --git a/ui/app_list/views/assistant_bubble_view.h b/ash/assistant/ui/assistant_bubble_view.h similarity index 71% rename from ui/app_list/views/assistant_bubble_view.h rename to ash/assistant/ui/assistant_bubble_view.h index d01fc90..b011631e 100644 --- a/ui/app_list/views/assistant_bubble_view.h +++ b/ash/assistant/ui/assistant_bubble_view.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_APP_LIST_VIEWS_ASSISTANT_BUBBLE_VIEW_H_ -#define UI_APP_LIST_VIEWS_ASSISTANT_BUBBLE_VIEW_H_ +#ifndef ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_ +#define ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_ #include <deque> #include <memory> @@ -16,11 +16,13 @@ #include "ui/views/view.h" namespace app_list { - class AssistantCardElement; class AssistantController; class AssistantTextElement; class AssistantUiElement; +} // namespace app_list + +namespace ash { namespace { class InteractionContainer; @@ -29,10 +31,11 @@ } // namespace class AssistantBubbleView : public views::View, - public AssistantInteractionModelObserver, - public SuggestionChipListener { + public app_list::AssistantInteractionModelObserver, + public app_list::SuggestionChipListener { public: - explicit AssistantBubbleView(AssistantController* assistant_controller); + explicit AssistantBubbleView( + app_list::AssistantController* assistant_controller); ~AssistantBubbleView() override; // views::View: @@ -40,17 +43,18 @@ void ChildPreferredSizeChanged(views::View* child) override; void ChildVisibilityChanged(views::View* child) override; - // AssistantInteractionModelObserver: - void OnUiElementAdded(const AssistantUiElement* ui_element) override; + // app_list::AssistantInteractionModelObserver: + void OnUiElementAdded( + const app_list::AssistantUiElement* ui_element) override; void OnUiElementsCleared() override; - void OnQueryChanged(const Query& query) override; + void OnQueryChanged(const app_list::Query& query) override; void OnQueryCleared() override; void OnSuggestionsAdded(const std::vector<std::string>& suggestions) override; void OnSuggestionsCleared() override; - // SuggestionChipListener: + // app_list::SuggestionChipListener: void OnSuggestionChipPressed( - SuggestionChipView* suggestion_chip_view) override; + app_list::SuggestionChipView* suggestion_chip_view) override; private: void InitLayout(); @@ -63,12 +67,12 @@ void SetProcessingUiElement(bool is_processing); void ProcessPendingUiElements(); - void OnCardAdded(const AssistantCardElement* card_element); + void OnCardAdded(const app_list::AssistantCardElement* card_element); void OnCardReady(const base::UnguessableToken& embed_token); void OnReleaseCards(); - void OnTextAdded(const AssistantTextElement* text_element); + void OnTextAdded(const app_list::AssistantTextElement* text_element); - AssistantController* assistant_controller_; // Owned by Shell. + app_list::AssistantController* assistant_controller_; // Owned by Shell. InteractionContainer* interaction_container_; // Owned by view hierarchy. UiElementContainer* ui_element_container_; // Owned by view hierarchy. SuggestionsContainer* suggestions_container_; // Owned by view hierarchy. @@ -77,7 +81,7 @@ std::vector<base::UnguessableToken> id_token_list_; // Owned by AssistantInteractionModel. - std::deque<const AssistantUiElement*> pending_ui_element_list_; + std::deque<const app_list::AssistantUiElement*> pending_ui_element_list_; // Whether a UI element is currently being processed. If true, new UI elements // are added to |pending_ui_element_list_| and processed later. @@ -89,6 +93,6 @@ DISALLOW_COPY_AND_ASSIGN(AssistantBubbleView); }; -} // namespace app_list +} // namespace ash -#endif // UI_APP_LIST_VIEWS_ASSISTANT_BUBBLE_VIEW_H_ +#endif // ASH_ASSISTANT_UI_ASSISTANT_BUBBLE_VIEW_H_
diff --git a/ash/display/cros_display_config.cc b/ash/display/cros_display_config.cc new file mode 100644 index 0000000..efb17783 --- /dev/null +++ b/ash/display/cros_display_config.cc
@@ -0,0 +1,893 @@ +// Copyright 2018 The Chromium 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 "ash/display/cros_display_config.h" + +#include "ash/display/display_configuration_controller.h" +#include "ash/display/display_prefs.h" +#include "ash/display/overscan_calibrator.h" +#include "ash/display/resolution_notification_controller.h" +#include "ash/display/screen_orientation_controller.h" +#include "ash/display/touch_calibrator_controller.h" +#include "ash/display/window_tree_host_manager.h" +#include "ash/shell.h" +#include "ash/touch/ash_touch_transform_controller.h" +#include "ash/wm/tablet_mode/tablet_mode_controller.h" +#include "base/optional.h" +#include "base/strings/string_number_conversions.h" +#include "ui/display/display.h" +#include "ui/display/display_layout.h" +#include "ui/display/display_layout_builder.h" +#include "ui/display/manager/display_manager.h" +#include "ui/display/manager/display_util.h" +#include "ui/display/mojo/display_struct_traits.h" +#include "ui/display/screen.h" + +namespace ash { + +namespace { + +// Maximum allowed bounds origin absolute value. +constexpr int kMaxBoundsOrigin = 200 * 1000; + +// This is the default range of display width in logical pixels allowed after +// applying display zoom. +constexpr int kDefaultMaxZoomWidth = 4096; +constexpr int kDefaultMinZoomWidth = 640; + +display::DisplayManager* GetDisplayManager() { + return Shell::Get()->display_manager(); +} + +int64_t GetDisplayId(const std::string& display_id_str) { + int64_t display_id; + if (!base::StringToInt64(display_id_str, &display_id)) + display_id = display::kInvalidDisplayId; + return display_id; +} + +// Gets the display with the provided string id. +display::Display GetDisplay(const std::string& display_id_str) { + int64_t display_id = GetDisplayId(display_id_str); + if (display_id == display::kInvalidDisplayId) + return display::Display(); + display::DisplayManager* display_manager = GetDisplayManager(); + if (display_manager->IsInUnifiedMode() && + display_id != display::kUnifiedDisplayId) { + // In unified desktop mode, the mirroring displays, which constitue the + // combined unified display, are contained in the software mirroring + // displays list in the display manager. + // The active list of displays only contains a single unified display entry + // with id |kUnifiedDisplayId|. + return display_manager->GetMirroringDisplayById(display_id); + } + return display_manager->GetDisplayForId(display_id); +} + +mojom::DisplayLayoutPosition GetMojomDisplayLayoutPosition( + display::DisplayPlacement::Position position) { + switch (position) { + case display::DisplayPlacement::TOP: + return mojom::DisplayLayoutPosition::kTop; + case display::DisplayPlacement::RIGHT: + return mojom::DisplayLayoutPosition::kRight; + case display::DisplayPlacement::BOTTOM: + return mojom::DisplayLayoutPosition::kBottom; + case display::DisplayPlacement::LEFT: + return mojom::DisplayLayoutPosition::kLeft; + } + NOTREACHED(); + return mojom::DisplayLayoutPosition::kLeft; +} + +display::DisplayPlacement::Position GetDisplayPlacementPosition( + mojom::DisplayLayoutPosition position) { + switch (position) { + case mojom::DisplayLayoutPosition::kTop: + return display::DisplayPlacement::TOP; + case mojom::DisplayLayoutPosition::kRight: + return display::DisplayPlacement::RIGHT; + case mojom::DisplayLayoutPosition::kBottom: + return display::DisplayPlacement::BOTTOM; + case mojom::DisplayLayoutPosition::kLeft: + return display::DisplayPlacement::LEFT; + } + NOTREACHED(); + return display::DisplayPlacement::LEFT; +} + +std::vector<mojom::DisplayLayoutPtr> GetDisplayLayouts() { + auto layouts = std::vector<mojom::DisplayLayoutPtr>(); + display::Screen* screen = display::Screen::GetScreen(); + const std::vector<display::Display>& displays = screen->GetAllDisplays(); + display::DisplayManager* display_manager = GetDisplayManager(); + for (const display::Display& display : displays) { + const display::DisplayPlacement placement = + display_manager->GetCurrentResolvedDisplayLayout().FindPlacementById( + display.id()); + if (placement.display_id == display::kInvalidDisplayId) + continue; + auto layout = mojom::DisplayLayout::New(); + layout->id = base::Int64ToString(placement.display_id); + layout->parent_id = base::Int64ToString(placement.parent_display_id); + layout->position = GetMojomDisplayLayoutPosition(placement.position); + layout->offset = placement.offset; + layouts.emplace_back(std::move(layout)); + } + return layouts; +} + +std::vector<mojom::DisplayLayoutPtr> GetDisplayUnifiedLayouts() { + auto layouts = std::vector<mojom::DisplayLayoutPtr>(); + display::DisplayManager* display_manager = GetDisplayManager(); + + const display::UnifiedDesktopLayoutMatrix& matrix = + display_manager->current_unified_desktop_matrix(); + for (size_t row_index = 0; row_index < matrix.size(); ++row_index) { + const auto& row = matrix[row_index]; + for (size_t column_index = 0; column_index < row.size(); ++column_index) { + if (column_index == 0 && row_index == 0) { + // No placement for the primary display. + continue; + } + auto layout = mojom::DisplayLayout::New(); + const int64_t display_id = row[column_index]; + // Parent display is either the one in the above row, or the one on the + // left in the same row. + const int64_t parent_id = column_index == 0 + ? matrix[row_index - 1][column_index] + : row[column_index - 1]; + layout->id = base::Int64ToString(display_id); + layout->parent_id = base::Int64ToString(parent_id); + layout->position = column_index == 0 + ? mojom::DisplayLayoutPosition::kBottom + : mojom::DisplayLayoutPosition::kRight; + layout->offset = 0; + layouts.emplace_back(std::move(layout)); + } + } + return layouts; +} + +mojom::DisplayConfigResult SetDisplayLayoutMode( + const mojom::DisplayLayoutInfo& info) { + display::DisplayManager* display_manager = GetDisplayManager(); + if (info.layout_mode == mojom::DisplayLayoutMode::kNormal) { + display_manager->SetDefaultMultiDisplayModeForCurrentDisplays( + display::DisplayManager::EXTENDED); + display_manager->SetMirrorMode(display::MirrorMode::kOff, base::nullopt); + return mojom::DisplayConfigResult::kSuccess; + } + + if (info.layout_mode == mojom::DisplayLayoutMode::kUnified) { + if (!display_manager->unified_desktop_enabled()) + return mojom::DisplayConfigResult::kUnifiedNotEnabledError; + display_manager->SetDefaultMultiDisplayModeForCurrentDisplays( + display::DisplayManager::UNIFIED); + display_manager->SetMirrorMode(display::MirrorMode::kOff, base::nullopt); + return mojom::DisplayConfigResult::kSuccess; + } + + DCHECK(info.layout_mode == mojom::DisplayLayoutMode::kMirrored); + + // 'Normal' mirror mode. + if (!info.mirror_source_id) { + display_manager->SetMirrorMode(display::MirrorMode::kNormal, base::nullopt); + return mojom::DisplayConfigResult::kSuccess; + } + + // 'Mixed' mirror mode. + display::Display source = GetDisplay(*info.mirror_source_id); + if (source.id() == display::kInvalidDisplayId) + return mojom::DisplayConfigResult::kMirrorModeSourceIdError; + display::DisplayIdList destination_ids; + if (info.mirror_destination_ids) { + for (const std::string& id_str : *info.mirror_destination_ids) { + int64_t destination_id = GetDisplayId(id_str); + if (destination_id == display::kInvalidDisplayId) + return mojom::DisplayConfigResult::kMirrorModeDestIdError; + destination_ids.emplace_back(destination_id); + } + } else { + const std::vector<display::Display>& displays = + display::Screen::GetScreen()->GetAllDisplays(); + for (const display::Display& display : displays) + destination_ids.emplace_back(display.id()); + } + base::Optional<display::MixedMirrorModeParams> mixed_params( + base::in_place, source.id(), destination_ids); + const display::MixedMirrorModeParamsErrors error_type = + display::ValidateParamsForMixedMirrorMode( + display_manager->GetCurrentDisplayIdList(), *mixed_params); + switch (error_type) { + case display::MixedMirrorModeParamsErrors::kErrorSingleDisplay: + return mojom::DisplayConfigResult::kMirrorModeSingleDisplayError; + case display::MixedMirrorModeParamsErrors::kErrorSourceIdNotFound: + return mojom::DisplayConfigResult::kMirrorModeSourceIdError; + case display::MixedMirrorModeParamsErrors::kErrorDestinationIdsEmpty: + case display::MixedMirrorModeParamsErrors::kErrorDestinationIdNotFound: + case display::MixedMirrorModeParamsErrors::kErrorDuplicateId: + return mojom::DisplayConfigResult::kMirrorModeDestIdError; + case display::MixedMirrorModeParamsErrors::kSuccess: + break; + } + display_manager->SetMirrorMode(display::MirrorMode::kMixed, mixed_params); + return mojom::DisplayConfigResult::kSuccess; +} + +mojom::DisplayModePtr GetDisplayMode( + const display::ManagedDisplayInfo& display_info, + const display::ManagedDisplayMode& display_mode) { + auto result = mojom::DisplayMode::New(); + bool is_internal = display::Display::HasInternalDisplay() && + display::Display::InternalDisplayId() == display_info.id(); + gfx::Size size_dip = display_mode.GetSizeInDIP(is_internal); + result->size = size_dip; + result->size_in_native_pixels = display_mode.size(); + result->ui_scale = display_mode.ui_scale(); + result->device_scale_factor = display_mode.device_scale_factor(); + result->refresh_rate = display_mode.refresh_rate(); + result->is_native = display_mode.native(); + return result; +} + +mojom::DisplayUnitInfoPtr GetDisplayUnitInfo(const display::Display& display, + int64_t primary_id) { + display::DisplayManager* display_manager = GetDisplayManager(); + const display::ManagedDisplayInfo& display_info = + display_manager->GetDisplayInfo(display.id()); + + auto info = mojom::DisplayUnitInfo::New(); + info->id = base::Int64ToString(display.id()); + info->name = display_manager->GetDisplayNameForId(display.id()); + + if (!display_info.manufacturer_id().empty() || + !display_info.product_id().empty() || + (display_info.year_of_manufacture() != + display::kInvalidYearOfManufacture)) { + info->edid = mojom::Edid::New(); + info->edid->manufacturer_id = display_info.manufacturer_id(); + info->edid->product_id = display_info.product_id(); + info->edid->year_of_manufacture = display_info.year_of_manufacture(); + } + + info->is_primary = display.id() == primary_id; + info->is_internal = display.IsInternal(); + info->is_enabled = true; + bool has_accelerometer_support = + display.accelerometer_support() == + display::Display::AccelerometerSupport::AVAILABLE; + info->is_tablet_mode = + has_accelerometer_support && Shell::Get() + ->tablet_mode_controller() + ->IsTabletModeWindowManagerEnabled(); + info->has_touch_support = + display.touch_support() == display::Display::TouchSupport::AVAILABLE; + info->has_accelerometer_support = has_accelerometer_support; + + const float device_dpi = display_info.device_dpi(); + info->dpi_x = device_dpi * display.size().width() / + display_info.bounds_in_native().width(); + info->dpi_y = device_dpi * display.size().height() / + display_info.bounds_in_native().height(); + + info->rotation = display::mojom::Rotation(display.rotation()); + info->bounds = display.bounds(); + info->overscan = display_manager->GetOverscanInsets(display.id()); + info->work_area = display.work_area(); + + int display_mode_index = 0; + display::ManagedDisplayMode active_mode; + bool has_active_mode = display_manager->GetActiveModeForDisplayId( + display_info.id(), &active_mode); + for (const display::ManagedDisplayMode& display_mode : + display_info.display_modes()) { + info->available_display_modes.emplace_back( + GetDisplayMode(display_info, display_mode)); + if (has_active_mode && display_mode.IsEquivalent(active_mode)) + info->selected_display_mode_index = display_mode_index; + ++display_mode_index; + } + + info->display_zoom_factor = display_info.zoom_factor(); + if (has_active_mode) { + info->available_display_zoom_factors = + display::GetDisplayZoomFactors(active_mode); + } else { + info->available_display_zoom_factors.push_back(display_info.zoom_factor()); + } + + return info; +} + +// Validates that DisplayProperties are valid with the current DisplayManager +// configuration. Returns an error on failure. +mojom::DisplayConfigResult ValidateDisplayProperties( + const mojom::DisplayConfigProperties& properties, + const display::Display& display) { + display::DisplayManager* display_manager = GetDisplayManager(); + + int64_t id = display.id(); + if (id == display::kInvalidDisplayId) + return mojom::DisplayConfigResult::kInvalidDisplayIdError; + + // Overscan cannot be changed for the internal display, and should be at most + // half of the screen size. + if (properties.overscan) { + if (display.IsInternal()) + return mojom::DisplayConfigResult::kNotSupportedOnInternalDisplayError; + + if (properties.overscan->left() < 0 || properties.overscan->top() < 0 || + properties.overscan->right() < 0 || properties.overscan->bottom() < 0) { + DLOG(ERROR) << "Negative overscan"; + return mojom::DisplayConfigResult::kPropertyValueOutOfRangeError; + } + + const gfx::Insets overscan = display_manager->GetOverscanInsets(id); + int screen_width = display.bounds().width() + overscan.width(); + int screen_height = display.bounds().height() + overscan.height(); + + if ((properties.overscan->left() + properties.overscan->right()) * 2 > + screen_width || + (properties.overscan->top() + properties.overscan->bottom()) * 2 > + screen_height) { + DLOG(ERROR) << "Overscan: " << properties.overscan->ToString() + << " exceeds bounds: " << screen_width << "x" + << screen_height; + return mojom::DisplayConfigResult::kPropertyValueOutOfRangeError; + } + } + + // The bounds cannot be changed for the primary display and should be inside + // a reasonable bounds. + if (properties.bounds_origin) { + const display::Display& primary = + display::Screen::GetScreen()->GetPrimaryDisplay(); + if (id == primary.id() || properties.set_primary) + return mojom::DisplayConfigResult::kNotSupportedOnInternalDisplayError; + if (properties.bounds_origin->x() > kMaxBoundsOrigin || + properties.bounds_origin->x() < -kMaxBoundsOrigin || + properties.bounds_origin->y() > kMaxBoundsOrigin || + properties.bounds_origin->y() < -kMaxBoundsOrigin) { + DLOG(ERROR) << "Bounds origin out of range"; + return mojom::DisplayConfigResult::kPropertyValueOutOfRangeError; + } + } + + if (properties.display_zoom_factor > 0) { + display::ManagedDisplayMode current_mode; + if (!display_manager->GetActiveModeForDisplayId(id, ¤t_mode)) { + DLOG(ERROR) << "No active mode for display: " << id; + return mojom::DisplayConfigResult::kInvalidDisplayIdError; + } + // This check is added to limit the range of display zoom that can be + // applied via the system display API. The said range is such that when a + // display zoom is applied, the final logical width in pixels should lie + // within the range of 640 pixels and 4096 pixels. + const int max_allowed_width = + std::max(kDefaultMaxZoomWidth, current_mode.size().width()); + const int min_allowed_width = + std::min(kDefaultMinZoomWidth, current_mode.size().width()); + int current_width = static_cast<float>(current_mode.size().width()) / + current_mode.device_scale_factor(); + if (current_width / properties.display_zoom_factor > max_allowed_width || + current_width / properties.display_zoom_factor < min_allowed_width) { + DLOG(ERROR) << "Display zoom factor out of range"; + return mojom::DisplayConfigResult::kPropertyValueOutOfRangeError; + } + } + + return mojom::DisplayConfigResult::kSuccess; +} + +// Sets the display layout for the target display in reference to the primary +// display. +void SetDisplayLayoutFromBounds(const gfx::Rect& primary_display_bounds, + int64_t primary_display_id, + const gfx::Rect& target_display_bounds, + int64_t target_display_id) { + display::DisplayPlacement placement( + display::DisplayLayout::CreatePlacementForRectangles( + primary_display_bounds, target_display_bounds)); + placement.display_id = target_display_id; + placement.parent_display_id = primary_display_id; + + std::unique_ptr<display::DisplayLayout> layout(new display::DisplayLayout); + layout->placement_list.push_back(placement); + layout->primary_id = primary_display_id; + + Shell::Get()->display_configuration_controller()->SetDisplayLayout( + std::move(layout)); +} + +// Attempts to set the display mode for display |id|. +mojom::DisplayConfigResult SetDisplayMode( + int64_t id, + const mojom::DisplayMode& display_mode) { + display::DisplayManager* display_manager = GetDisplayManager(); + + display::ManagedDisplayMode current_mode; + if (!display_manager->GetActiveModeForDisplayId(id, ¤t_mode)) + return mojom::DisplayConfigResult::kInvalidDisplayIdError; + + display::ManagedDisplayMode new_mode( + display_mode.size_in_native_pixels, current_mode.refresh_rate(), + current_mode.is_interlaced(), display_mode.is_native, + display_mode.ui_scale, display_mode.device_scale_factor); + + if (!new_mode.IsEquivalent(current_mode)) { + // For the internal display, the display mode will be applied directly. + // Otherwise a confirm/revert notification will be prepared first, and the + // display mode will be applied. If the user accepts the mode change by + // dismissing the notification, StoreDisplayPrefs() will be called back to + // persist the new preferences. + if (!Shell::Get() + ->resolution_notification_controller() + ->PrepareNotificationAndSetDisplayMode( + id, current_mode, new_mode, base::BindOnce([]() { + Shell::Get()->display_prefs()->StoreDisplayPrefs(); + }))) { + return mojom::DisplayConfigResult::kSetDisplayModeError; + } + } + + return mojom::DisplayConfigResult::kSuccess; +} + +display::TouchCalibrationData::CalibrationPointPair GetCalibrationPair( + const mojom::TouchCalibrationPair& pair) { + return std::make_pair(pair.display_point, pair.touch_point); +} + +} // namespace + +class CrosDisplayConfig::DisplayObserver : public display::DisplayObserver { + public: + explicit DisplayObserver(CrosDisplayConfig* owner) : owner_(owner) { + display::Screen::GetScreen()->AddObserver(this); + } + ~DisplayObserver() override { + display::Screen::GetScreen()->RemoveObserver(this); + } + + // display::DisplayObserver: + void OnDisplayAdded(const display::Display& new_display) override { + owner_->NotifyObserversDisplayConfigChanged(); + } + void OnDisplayRemoved(const display::Display& old_display) override { + owner_->NotifyObserversDisplayConfigChanged(); + } + void OnDisplayMetricsChanged(const display::Display& display, + uint32_t metrics) override { + owner_->NotifyObserversDisplayConfigChanged(); + } + + private: + CrosDisplayConfig* owner_; + + DISALLOW_COPY_AND_ASSIGN(DisplayObserver); +}; + +CrosDisplayConfig::CrosDisplayConfig() + : display_observer_(std::make_unique<DisplayObserver>(this)) {} + +CrosDisplayConfig::~CrosDisplayConfig() {} + +void CrosDisplayConfig::BindRequest( + mojom::CrosDisplayConfigControllerRequest request) { + bindings_.AddBinding(this, std::move(request)); +} + +void CrosDisplayConfig::AddObserver( + mojom::CrosDisplayConfigObserverAssociatedPtrInfo observer) { + mojom::CrosDisplayConfigObserverAssociatedPtr observer_ptr; + observer_ptr.Bind(std::move(observer)); + observers_.AddPtr(std::move(observer_ptr)); +} + +void CrosDisplayConfig::GetDisplayLayoutInfo( + GetDisplayLayoutInfoCallback callback) { + display::DisplayManager* display_manager = GetDisplayManager(); + + auto info = mojom::DisplayLayoutInfo::New(); + if (display_manager->IsInUnifiedMode()) { + info->layout_mode = mojom::DisplayLayoutMode::kUnified; + } else if (display_manager->IsInMirrorMode()) { + info->layout_mode = mojom::DisplayLayoutMode::kMirrored; + info->mirror_source_id = + base::Int64ToString(display_manager->mirroring_source_id()); + info->mirror_destination_ids = std::vector<std::string>(); + for (int64_t id : display_manager->GetMirroringDestinationDisplayIdList()) + info->mirror_destination_ids->emplace_back(base::Int64ToString(id)); + } else { + info->layout_mode = mojom::DisplayLayoutMode::kNormal; + } + + if (display_manager->IsInUnifiedMode()) { + info->layouts = GetDisplayUnifiedLayouts(); + } else if (display_manager->num_connected_displays() > 1) { + info->layouts = GetDisplayLayouts(); + } + + std::move(callback).Run(std::move(info)); +} + +mojom::DisplayConfigResult SetDisplayLayouts( + const std::vector<mojom::DisplayLayoutPtr>& layouts) { + display::DisplayManager* display_manager = GetDisplayManager(); + display::DisplayLayoutBuilder builder( + display_manager->GetCurrentResolvedDisplayLayout()); + int64_t root_id = display::kInvalidDisplayId; + std::set<int64_t> layout_ids; + builder.ClearPlacements(); + for (const mojom::DisplayLayoutPtr& layout_ptr : layouts) { + const mojom::DisplayLayout& layout = *layout_ptr; + display::Display display = GetDisplay(layout.id); + if (display.id() == display::kInvalidDisplayId) { + LOG(ERROR) << "Display layout has invalid id: " << layout.id; + return mojom::DisplayConfigResult::kInvalidDisplayIdError; + } + display::Display parent = GetDisplay(layout.parent_id); + if (parent.id() == display::kInvalidDisplayId) { + if (root_id != display::kInvalidDisplayId) { + LOG(ERROR) << "Display layout has invalid parent: " << layout.parent_id; + return mojom::DisplayConfigResult::kInvalidDisplayLayoutError; + } + root_id = display.id(); + continue; // No placement for root (primary) display. + } + layout_ids.insert(display.id()); + display::DisplayPlacement::Position position = + GetDisplayPlacementPosition(layout.position); + builder.AddDisplayPlacement(display.id(), parent.id(), position, + layout.offset); + } + + const display::DisplayIdList display_ids = + display_manager->GetCurrentDisplayIdList(); + std::unique_ptr<display::DisplayLayout> layout = builder.Build(); + if (display_manager->IsInUnifiedMode()) { + if (root_id == display::kInvalidDisplayId) { + // Look for a display with no layout info to use as the root. + for (int64_t id : display_ids) { + if (!base::ContainsKey(layout_ids, id)) { + root_id = id; + break; + } + } + if (root_id == display::kInvalidDisplayId) { + LOG(ERROR) << "Invalid unified layout: No root id"; + return mojom::DisplayConfigResult::kInvalidDisplayLayoutError; + } + } + layout->primary_id = root_id; + display::UnifiedDesktopLayoutMatrix matrix; + if (!display::BuildUnifiedDesktopMatrix(display_ids, *layout, &matrix)) { + LOG(ERROR) << "Invalid unified layout: No proper conversion to a matrix"; + return mojom::DisplayConfigResult::kInvalidDisplayLayoutError; + } + Shell::Get() + ->display_configuration_controller() + ->SetUnifiedDesktopLayoutMatrix(matrix); + } else { + if (!display::DisplayLayout::Validate(display_ids, *layout)) { + return mojom::DisplayConfigResult::kInvalidDisplayLayoutError; + } + Shell::Get()->display_configuration_controller()->SetDisplayLayout( + std::move(layout)); + } + return mojom::DisplayConfigResult::kSuccess; +} + +void CrosDisplayConfig::SetDisplayLayoutInfo( + mojom::DisplayLayoutInfoPtr info, + SetDisplayLayoutInfoCallback callback) { + mojom::DisplayConfigResult result = SetDisplayLayoutMode(*info); + if (result != mojom::DisplayConfigResult::kSuccess) { + std::move(callback).Run(result); + return; + } + if (info->layouts) { + result = SetDisplayLayouts(*info->layouts); + if (result != mojom::DisplayConfigResult::kSuccess) { + std::move(callback).Run(result); + return; + } + } + std::move(callback).Run(mojom::DisplayConfigResult::kSuccess); +} + +void CrosDisplayConfig::GetDisplayUnitInfoList( + bool single_unified, + GetDisplayUnitInfoListCallback callback) { + std::vector<mojom::DisplayUnitInfoPtr> info_list; + display::DisplayManager* display_manager = GetDisplayManager(); + + std::vector<display::Display> displays; + int64_t primary_id; + if (!display_manager->IsInUnifiedMode()) { + displays = display::Screen::GetScreen()->GetAllDisplays(); + primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); + } else if (single_unified) { + for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) + displays.push_back(display_manager->GetDisplayAt(i)); + primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); + } else { + displays = display_manager->software_mirroring_display_list(); + primary_id = + display_manager->GetPrimaryMirroringDisplayForUnifiedDesktop()->id(); + } + + for (const display::Display& display : displays) + info_list.emplace_back(GetDisplayUnitInfo(display, primary_id)); + std::move(callback).Run(std::move(info_list)); +} + +void CrosDisplayConfig::SetDisplayProperties( + const std::string& id, + mojom::DisplayConfigPropertiesPtr properties, + SetDisplayPropertiesCallback callback) { + const display::Display display = GetDisplay(id); + mojom::DisplayConfigResult result = + ValidateDisplayProperties(*properties, display); + if (result != mojom::DisplayConfigResult::kSuccess) { + std::move(callback).Run(result); + return; + } + + display::DisplayManager* display_manager = GetDisplayManager(); + DisplayConfigurationController* display_configuration_controller = + Shell::Get()->display_configuration_controller(); + const display::Display& primary = + display::Screen::GetScreen()->GetPrimaryDisplay(); + + if (properties->set_primary && display.id() != primary.id()) + display_configuration_controller->SetPrimaryDisplayId(display.id()); + + if (properties->overscan) + display_manager->SetOverscanInsets(display.id(), *properties->overscan); + + if (properties->rotation) { + display::Display::Rotation rotation = + display::Display::Rotation(properties->rotation->rotation); + if (Shell::Get() + ->tablet_mode_controller() + ->IsTabletModeWindowManagerEnabled() && + display.id() == display::Display::InternalDisplayId()) { + Shell::Get()->screen_orientation_controller()->SetLockToRotation( + rotation); + } else { + display_configuration_controller->SetDisplayRotation( + display.id(), rotation, display::Display::RotationSource::USER); + } + } + + if (properties->bounds_origin && + *properties->bounds_origin != display.bounds().origin()) { + gfx::Rect display_bounds = display.bounds(); + display_bounds.Offset( + properties->bounds_origin->x() - display.bounds().x(), + properties->bounds_origin->y() - display.bounds().y()); + SetDisplayLayoutFromBounds(primary.bounds(), primary.id(), display_bounds, + display.id()); + } + + if (properties->display_zoom_factor > 0) { + display_manager->UpdateZoomFactor(display.id(), + properties->display_zoom_factor); + } + + // Set the display mode. Note: if this returns an error, other properties + // will have already been applied. TODO(stevenjb): Validate the display mode + // before applying any properties. + if (properties->display_mode) { + result = SetDisplayMode(display.id(), *properties->display_mode); + if (result != mojom::DisplayConfigResult::kSuccess) { + std::move(callback).Run(result); + return; + } + } + + std::move(callback).Run(mojom::DisplayConfigResult::kSuccess); +} + +void CrosDisplayConfig::SetUnifiedDesktopEnabled(bool enabled) { + GetDisplayManager()->SetUnifiedDesktopEnabled(enabled); +} + +void CrosDisplayConfig::OverscanCalibration( + const std::string& display_id, + mojom::DisplayConfigOperation op, + const base::Optional<gfx::Insets>& delta, + OverscanCalibrationCallback callback) { + display::Display display = GetDisplay(display_id); + if (display.id() == display::kInvalidDisplayId) { + std::move(callback).Run(mojom::DisplayConfigResult::kInvalidDisplayIdError); + return; + } + + OverscanCalibrator* calibrator = GetOverscanCalibrator(display_id); + if (!calibrator && op != mojom::DisplayConfigOperation::kStart) { + LOG(ERROR) << "Calibrator does not exist for op=" << op; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationNotAvailableError); + return; + } + switch (op) { + case mojom::DisplayConfigOperation::kStart: { + DVLOG(1) << "OverscanCalibrationStart: " << display_id; + gfx::Insets insets = + Shell::Get()->window_tree_host_manager()->GetOverscanInsets( + display.id()); + if (calibrator) + DVLOG(1) << "Replacing existing calibrator for id: " << display_id; + overscan_calibrators_[display_id] = + std::make_unique<OverscanCalibrator>(display, insets); + break; + } + case mojom::DisplayConfigOperation::kAdjust: + DVLOG(1) << "OverscanCalibrationAdjust: " << display_id; + if (!delta) { + LOG(ERROR) << "Delta not provided for for adjust: " << display_id; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationFailedError); + return; + } + calibrator->UpdateInsets(calibrator->insets() + *delta); + break; + case mojom::DisplayConfigOperation::kReset: + DVLOG(1) << "OverscanCalibrationReset: " << display_id; + calibrator->Reset(); + break; + case mojom::DisplayConfigOperation::kComplete: + DVLOG(1) << "OverscanCalibrationComplete: " << display_id; + calibrator->Commit(); + overscan_calibrators_[display_id].reset(); + break; + case mojom::DisplayConfigOperation::kShowNative: + LOG(ERROR) << "Operation not supported: " << op; + std::move(callback).Run( + mojom::DisplayConfigResult::kInvalidOperationError); + return; + return; + } + std::move(callback).Run(mojom::DisplayConfigResult::kSuccess); +} + +void CrosDisplayConfig::TouchCalibration(const std::string& display_id, + mojom::DisplayConfigOperation op, + mojom::TouchCalibrationPtr calibration, + TouchCalibrationCallback callback) { + display::Display display = GetDisplay(display_id); + if (display.id() == display::kInvalidDisplayId) { + std::move(callback).Run(mojom::DisplayConfigResult::kInvalidDisplayIdError); + return; + } + if (display.IsInternal()) { + LOG(ERROR) << "Internal display cannot be calibrated for touch: " + << display_id; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationNotAvailableError); + return; + } + if (!display::HasExternalTouchscreenDevice()) { + LOG(ERROR) + << "Touch calibration called with no external touch screen device."; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationNotAvailableError); + return; + } + + if (op == mojom::DisplayConfigOperation::kStart || + op == mojom::DisplayConfigOperation::kShowNative) { + if (touch_calibrator_ && touch_calibrator_->IsCalibrating()) { + LOG(ERROR) << "Touch calibration already active."; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationInProgressError); + return; + } + if (!touch_calibrator_) + touch_calibrator_ = std::make_unique<ash::TouchCalibratorController>(); + if (op == mojom::DisplayConfigOperation::kShowNative) { + // For native calibration, |callback| is not run until calibration + // completes. + touch_calibrator_->StartCalibration( + display, /*is_custom_calibration=*/false, + base::BindOnce( + [](TouchCalibrationCallback callback, bool result) { + std::move(callback).Run( + result + ? mojom::DisplayConfigResult::kSuccess + : mojom::DisplayConfigResult::kCalibrationFailedError); + }, + std::move(callback))); + return; + } + // For custom calibration, start calibration and run |callback| now. + touch_calibrator_->StartCalibration(display, /*is_custom_calibration=*/true, + base::OnceCallback<void(bool)>()); + std::move(callback).Run(mojom::DisplayConfigResult::kSuccess); + return; + } + + if (op == mojom::DisplayConfigOperation::kReset) { + ash::Shell::Get()->display_manager()->ClearTouchCalibrationData( + display.id(), base::nullopt); + std::move(callback).Run(mojom::DisplayConfigResult::kSuccess); + return; + } + + if (op != mojom::DisplayConfigOperation::kComplete) { + LOG(ERROR) << "Unknown operation: " << op; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationNotStartedError); + return; + } + + if (!touch_calibrator_) { + LOG(ERROR) << "Touch calibration not active."; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationNotStartedError); + return; + } + + if (!calibration || calibration->pairs.size() != 4) { + LOG(ERROR) << "Touch calibration requires four calibration pairs."; + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationInvalidDataError); + return; + } + + ash::Shell::Get()->touch_transformer_controller()->SetForCalibration(false); + + display::TouchCalibrationData::CalibrationPointPairQuad calibration_points; + calibration_points[0] = GetCalibrationPair(*calibration->pairs[0]); + calibration_points[1] = GetCalibrationPair(*calibration->pairs[1]); + calibration_points[2] = GetCalibrationPair(*calibration->pairs[2]); + calibration_points[3] = GetCalibrationPair(*calibration->pairs[3]); + + gfx::Size bounds = calibration->bounds; + for (size_t row = 0; row < calibration_points.size(); row++) { + // Coordinates for display and touch point cannot be negative. + if (calibration_points[row].first.x() < 0 || + calibration_points[row].first.y() < 0 || + calibration_points[row].second.x() < 0 || + calibration_points[row].second.y() < 0) { + LOG(ERROR) + << "Display points and touch points cannot have negative coordinates"; + touch_calibrator_->StopCalibrationAndResetParams(); + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationInvalidDataError); + return; + } + // Coordinates for display points cannot be greater than the screen + // bounds. + if (calibration_points[row].first.x() > bounds.width() || + calibration_points[row].first.y() > bounds.height()) { + LOG(ERROR) << "Display point coordinates cannot be more than size of the " + "display."; + touch_calibrator_->StopCalibrationAndResetParams(); + std::move(callback).Run( + mojom::DisplayConfigResult::kCalibrationInvalidDataError); + return; + } + } + + touch_calibrator_->CompleteCalibration(calibration_points, bounds); + std::move(callback).Run(mojom::DisplayConfigResult::kSuccess); +} + +void CrosDisplayConfig::NotifyObserversDisplayConfigChanged() { + observers_.ForAllPtrs([](mojom::CrosDisplayConfigObserver* observer) { + observer->OnDisplayConfigChanged(); + }); +} + +OverscanCalibrator* CrosDisplayConfig::GetOverscanCalibrator( + const std::string& id) { + auto iter = overscan_calibrators_.find(id); + return iter == overscan_calibrators_.end() ? nullptr : iter->second.get(); +} + +} // namespace ash
diff --git a/ash/display/cros_display_config.h b/ash/display/cros_display_config.h new file mode 100644 index 0000000..b2904bb9 --- /dev/null +++ b/ash/display/cros_display_config.h
@@ -0,0 +1,73 @@ +// Copyright 2018 The Chromium 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 ASH_DISPLAY_CROS_DISPLAY_CONFIG_H_ +#define ASH_DISPLAY_CROS_DISPLAY_CONFIG_H_ + +#include <map> +#include <memory> +#include <string> + +#include "ash/ash_export.h" +#include "ash/public/interfaces/cros_display_config.mojom.h" +#include "base/macros.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" + +namespace ash { + +class OverscanCalibrator; +class TouchCalibratorController; + +// ASH_EXPORT for use in chrome unit_tests for DisplayInfoProviderChromeOS. +class ASH_EXPORT CrosDisplayConfig : public mojom::CrosDisplayConfigController { + public: + CrosDisplayConfig(); + ~CrosDisplayConfig() override; + + void BindRequest(mojom::CrosDisplayConfigControllerRequest request); + + // mojom::CrosDisplayConfigController: + void AddObserver( + mojom::CrosDisplayConfigObserverAssociatedPtrInfo observer) override; + void GetDisplayLayoutInfo(GetDisplayLayoutInfoCallback callback) override; + void SetDisplayLayoutInfo(mojom::DisplayLayoutInfoPtr info, + SetDisplayLayoutInfoCallback callback) override; + void GetDisplayUnitInfoList(bool single_unified, + GetDisplayUnitInfoListCallback callback) override; + void SetDisplayProperties(const std::string& id, + mojom::DisplayConfigPropertiesPtr properties, + SetDisplayPropertiesCallback callback) override; + void SetUnifiedDesktopEnabled(bool enabled) override; + void OverscanCalibration(const std::string& display_id, + mojom::DisplayConfigOperation op, + const base::Optional<gfx::Insets>& delta, + OverscanCalibrationCallback callback) override; + void TouchCalibration(const std::string& display_id, + mojom::DisplayConfigOperation op, + mojom::TouchCalibrationPtr calibration, + TouchCalibrationCallback callback) override; + + TouchCalibratorController* touch_calibrator_for_test() { + return touch_calibrator_.get(); + } + + private: + class DisplayObserver; + void NotifyObserversDisplayConfigChanged(); + OverscanCalibrator* GetOverscanCalibrator(const std::string& id); + + std::unique_ptr<DisplayObserver> display_observer_; + mojo::BindingSet<mojom::CrosDisplayConfigController> bindings_; + mojo::AssociatedInterfacePtrSet<mojom::CrosDisplayConfigObserver> observers_; + std::map<std::string, std::unique_ptr<OverscanCalibrator>> + overscan_calibrators_; + std::unique_ptr<TouchCalibratorController> touch_calibrator_; + + DISALLOW_COPY_AND_ASSIGN(CrosDisplayConfig); +}; + +} // namespace ash + +#endif // ASH_DISPLAY_CROS_DISPLAY_CONFIG_H_
diff --git a/ash/display/cros_display_config_unittest.cc b/ash/display/cros_display_config_unittest.cc new file mode 100644 index 0000000..37cc7a7 --- /dev/null +++ b/ash/display/cros_display_config_unittest.cc
@@ -0,0 +1,735 @@ +// Copyright 2018 The Chromium 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 "ash/display/cros_display_config.h" + +#include "ash/display/cros_display_config.h" +#include "ash/display/touch_calibrator_controller.h" +#include "ash/public/interfaces/cros_display_config.mojom.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/touch/ash_touch_transform_controller.h" +#include "base/command_line.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "services/ui/public/cpp/input_devices/input_device_client_test_api.h" +#include "ui/display/display_switches.h" +#include "ui/display/manager/display_manager.h" +#include "ui/display/manager/test/touch_transform_controller_test_api.h" +#include "ui/display/manager/touch_transform_setter.h" +#include "ui/display/test/display_manager_test_api.h" +#include "ui/events/devices/touch_device_transform.h" +#include "ui/events/devices/touchscreen_device.h" + +namespace ash { + +namespace { + +void SetResult(mojom::DisplayConfigResult* result_ptr, + base::OnceClosure callback, + mojom::DisplayConfigResult result) { + *result_ptr = result; + std::move(callback).Run(); +} + +void InitExternalTouchDevices(int64_t display_id) { + ui::TouchscreenDevice touchdevice( + 123, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, + std::string("test external touch device"), gfx::Size(1000, 1000), 1); + ui::InputDeviceClientTestApi().SetTouchscreenDevices({touchdevice}); + + std::vector<ui::TouchDeviceTransform> transforms; + ui::TouchDeviceTransform touch_device_transform; + touch_device_transform.display_id = display_id; + touch_device_transform.device_id = touchdevice.id; + transforms.push_back(touch_device_transform); + display::test::TouchTransformControllerTestApi( + ash::Shell::Get()->touch_transformer_controller()) + .touch_transform_setter() + ->ConfigureTouchDevices(transforms); +} + +class TestObserver : public mojom::CrosDisplayConfigObserver { + public: + TestObserver() = default; + + // mojom::CrosDisplayConfigObserver: + void OnDisplayConfigChanged() override { display_changes_++; } + + int display_changes() const { return display_changes_; } + void reset_display_changes() { display_changes_ = 0; } + + private: + int display_changes_ = 0; + + DISALLOW_COPY_AND_ASSIGN(TestObserver); +}; + +class CrosDisplayConfigTest : public AshTestBase { + public: + CrosDisplayConfigTest() {} + ~CrosDisplayConfigTest() override{}; + + void SetUp() override { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kUseFirstDisplayAsInternal); + AshTestBase::SetUp(); + CHECK(display::Screen::GetScreen()); + cros_display_config_ = Shell::Get()->cros_display_config(); + } + + mojom::DisplayLayoutInfoPtr GetDisplayLayoutInfo() { + mojom::DisplayLayoutInfoPtr display_layout_info; + base::RunLoop run_loop; + cros_display_config_->GetDisplayLayoutInfo(base::BindOnce( + [](mojom::DisplayLayoutInfoPtr* result_ptr, base::OnceClosure callback, + mojom::DisplayLayoutInfoPtr result) { + *result_ptr = std::move(result); + std::move(callback).Run(); + }, + &display_layout_info, run_loop.QuitClosure())); + run_loop.Run(); + return display_layout_info; + } + + mojom::DisplayConfigResult SetDisplayLayoutInfo( + mojom::DisplayLayoutInfoPtr display_layout_info) { + mojom::DisplayConfigResult result; + base::RunLoop run_loop; + cros_display_config_->SetDisplayLayoutInfo( + std::move(display_layout_info), + base::BindOnce(&SetResult, &result, run_loop.QuitClosure())); + run_loop.Run(); + return result; + } + + std::vector<mojom::DisplayUnitInfoPtr> GetDisplayUnitInfoList() { + std::vector<mojom::DisplayUnitInfoPtr> display_info_list; + base::RunLoop run_loop; + cros_display_config_->GetDisplayUnitInfoList( + /*single_unified=*/false, + base::BindOnce( + [](std::vector<mojom::DisplayUnitInfoPtr>* result_ptr, + base::OnceClosure callback, + std::vector<mojom::DisplayUnitInfoPtr> result) { + *result_ptr = std::move(result); + std::move(callback).Run(); + }, + &display_info_list, run_loop.QuitClosure())); + run_loop.Run(); + return display_info_list; + } + + mojom::DisplayConfigResult SetDisplayProperties( + const std::string& id, + mojom::DisplayConfigPropertiesPtr properties) { + mojom::DisplayConfigResult result; + base::RunLoop run_loop; + cros_display_config_->SetDisplayProperties( + id, std::move(properties), + base::BindOnce(&SetResult, &result, run_loop.QuitClosure())); + run_loop.Run(); + return result; + } + + bool OverscanCalibration(int64_t id, + mojom::DisplayConfigOperation op, + const base::Optional<gfx::Insets>& delta) { + mojom::DisplayConfigResult result; + base::RunLoop run_loop; + cros_display_config()->OverscanCalibration( + base::Int64ToString(id), op, delta, + base::BindOnce(&SetResult, &result, run_loop.QuitClosure())); + run_loop.Run(); + return result == mojom::DisplayConfigResult::kSuccess; + } + + bool DisplayExists(int64_t display_id) { + const display::Display& display = + display_manager()->GetDisplayForId(display_id); + return display.id() != display::kInvalidDisplayId; + } + + mojom::TouchCalibrationPtr GetDefaultCalibration() { + auto calibration = mojom::TouchCalibration::New(); + for (int i = 0; i < 4; ++i) + calibration->pairs.emplace_back(mojom::TouchCalibrationPair::New()); + return calibration; + } + + bool StartTouchCalibration(const std::string& display_id) { + return CallTouchCalibration( + display_id, ash::mojom::DisplayConfigOperation::kStart, nullptr); + } + + bool CompleteCustomTouchCalibration(const std::string& display_id, + mojom::TouchCalibrationPtr calibration) { + return CallTouchCalibration(display_id, + ash::mojom::DisplayConfigOperation::kComplete, + std::move(calibration)); + } + + bool CallTouchCalibration(const std::string& id, + ash::mojom::DisplayConfigOperation op, + ash::mojom::TouchCalibrationPtr calibration) { + mojom::DisplayConfigResult result; + base::RunLoop run_loop; + cros_display_config_->TouchCalibration( + id, op, std::move(calibration), + base::BindOnce(&SetResult, &result, run_loop.QuitClosure())); + run_loop.Run(); + return result == mojom::DisplayConfigResult::kSuccess; + } + + bool IsTouchCalibrationActive() { + TouchCalibratorController* touch_calibrator = + cros_display_config_->touch_calibrator_for_test(); + return touch_calibrator && touch_calibrator->IsCalibrating(); + } + + CrosDisplayConfig* cros_display_config() { return cros_display_config_; } + + private: + CrosDisplayConfig* cros_display_config_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(CrosDisplayConfigTest); +}; + +} // namespace + +TEST_F(CrosDisplayConfigTest, OnDisplayConfigChanged) { + TestObserver observer; + mojom::CrosDisplayConfigObserverAssociatedPtr observer_ptr; + mojo::AssociatedBinding<mojom::CrosDisplayConfigObserver> binding( + &observer, mojo::MakeRequestAssociatedWithDedicatedPipe(&observer_ptr)); + cros_display_config()->AddObserver(observer_ptr.PassInterface()); + base::RunLoop().RunUntilIdle(); + + // Adding one display should trigger one notification. + UpdateDisplay("500x400"); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, observer.display_changes()); + observer.reset_display_changes(); + + // Adding two displays should trigger two notification. + UpdateDisplay("500x400,500x400"); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, observer.display_changes()); +} + +TEST_F(CrosDisplayConfigTest, GetDisplayLayoutInfo) { + UpdateDisplay("500x400,500x400,500x400"); + std::vector<display::Display> displays = + display::Screen::GetScreen()->GetAllDisplays(); + ASSERT_EQ(3u, displays.size()); + + mojom::DisplayLayoutInfoPtr display_layout_info = GetDisplayLayoutInfo(); + + ASSERT_TRUE(display_layout_info); + const std::vector<mojom::DisplayLayoutPtr>& layouts = + *display_layout_info->layouts; + ASSERT_EQ(2u, layouts.size()); + + EXPECT_EQ(base::Int64ToString(displays[1].id()), layouts[0]->id); + EXPECT_EQ(base::Int64ToString(displays[0].id()), layouts[0]->parent_id); + EXPECT_EQ(mojom::DisplayLayoutPosition::kRight, layouts[0]->position); + EXPECT_EQ(0, layouts[0]->offset); + + EXPECT_EQ(base::Int64ToString(displays[2].id()), layouts[1]->id); + EXPECT_EQ(layouts[0]->id, layouts[1]->parent_id); + EXPECT_EQ(mojom::DisplayLayoutPosition::kRight, layouts[1]->position); + EXPECT_EQ(0, layouts[1]->offset); +} + +TEST_F(CrosDisplayConfigTest, SetLayoutUnified) { + UpdateDisplay("500x400,500x400"); + EXPECT_FALSE(display_manager()->IsInUnifiedMode()); + + // Enable unified desktop. Enables unified mode. + cros_display_config()->SetUnifiedDesktopEnabled(true); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(display_manager()->IsInUnifiedMode()); + + // Disable unified mode. + auto properties = mojom::DisplayLayoutInfo::New(); + properties->layout_mode = mojom::DisplayLayoutMode::kNormal; + mojom::DisplayConfigResult result = + SetDisplayLayoutInfo(std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_FALSE(display_manager()->IsInUnifiedMode()); + + // Enable unified mode. + properties = mojom::DisplayLayoutInfo::New(); + properties->layout_mode = mojom::DisplayLayoutMode::kUnified; + result = SetDisplayLayoutInfo(std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_TRUE(display_manager()->IsInUnifiedMode()); + + // Restore extended mode. + cros_display_config()->SetUnifiedDesktopEnabled(false); + EXPECT_FALSE(display_manager()->IsInUnifiedMode()); +} + +TEST_F(CrosDisplayConfigTest, SetLayoutMirroredDefault) { + UpdateDisplay("500x400,500x400,500x400"); + + auto properties = mojom::DisplayLayoutInfo::New(); + properties->layout_mode = mojom::DisplayLayoutMode::kMirrored; + mojom::DisplayConfigResult result = + SetDisplayLayoutInfo(std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_TRUE(display_manager()->IsInMirrorMode()); + display::DisplayIdList id_list = + display_manager()->GetMirroringDestinationDisplayIdList(); + ASSERT_EQ(2u, id_list.size()); + + properties = mojom::DisplayLayoutInfo::New(); + properties->layout_mode = mojom::DisplayLayoutMode::kNormal; + result = SetDisplayLayoutInfo(std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_FALSE(display_manager()->IsInMirrorMode()); +} + +TEST_F(CrosDisplayConfigTest, SetLayoutMirroredMixed) { + UpdateDisplay("500x400,500x400,500x400,500x400"); + + std::vector<display::Display> displays = + display::Screen::GetScreen()->GetAllDisplays(); + ASSERT_EQ(4u, displays.size()); + + auto properties = mojom::DisplayLayoutInfo::New(); + properties->layout_mode = mojom::DisplayLayoutMode::kMirrored; + properties->mirror_source_id = base::Int64ToString(displays[0].id()); + properties->mirror_destination_ids = + base::make_optional<std::vector<std::string>>( + {base::Int64ToString(displays[1].id()), + base::Int64ToString(displays[3].id())}); + mojom::DisplayConfigResult result = + SetDisplayLayoutInfo(std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_TRUE(display_manager()->IsInMirrorMode()); + display::DisplayIdList id_list = + display_manager()->GetMirroringDestinationDisplayIdList(); + ASSERT_EQ(2u, id_list.size()); + EXPECT_TRUE(base::ContainsValue(id_list, displays[1].id())); + EXPECT_TRUE(base::ContainsValue(id_list, displays[3].id())); +} + +TEST_F(CrosDisplayConfigTest, GetDisplayUnitInfoListBasic) { + UpdateDisplay("500x600,400x520"); + std::vector<mojom::DisplayUnitInfoPtr> result = GetDisplayUnitInfoList(); + ASSERT_EQ(2u, result.size()); + + int64_t display_id; + ASSERT_TRUE(base::StringToInt64(result[0]->id, &display_id)); + ASSERT_TRUE(DisplayExists(display_id)); + const mojom::DisplayUnitInfo& info_0 = *result[0]; + EXPECT_TRUE(info_0.is_primary); + EXPECT_TRUE(info_0.is_internal); + EXPECT_TRUE(info_0.is_enabled); + EXPECT_FALSE(info_0.is_tablet_mode); + EXPECT_FALSE(info_0.has_touch_support); + EXPECT_FALSE(info_0.has_accelerometer_support); + EXPECT_EQ(96, info_0.dpi_x); + EXPECT_EQ(96, info_0.dpi_y); + EXPECT_EQ(display::mojom::Rotation::VALUE_0, info_0.rotation); + EXPECT_EQ("0,0 500x600", info_0.bounds.ToString()); + EXPECT_EQ("0,0,0,0", info_0.overscan.ToString()); + + ASSERT_TRUE(base::StringToInt64(result[1]->id, &display_id)); + ASSERT_TRUE(DisplayExists(display_id)); + const mojom::DisplayUnitInfo& info_1 = *result[1]; + EXPECT_EQ(display_manager()->GetDisplayNameForId(display_id), info_1.name); + // Second display is left of the primary display whose width 500. + EXPECT_EQ("500,0 400x520", info_1.bounds.ToString()); + EXPECT_EQ("0,0,0,0", info_1.overscan.ToString()); + EXPECT_EQ(display::mojom::Rotation::VALUE_0, info_1.rotation); + EXPECT_FALSE(info_1.is_primary); + EXPECT_FALSE(info_1.is_internal); + EXPECT_TRUE(info_1.is_enabled); + EXPECT_EQ(96, info_1.dpi_x); + EXPECT_EQ(96, info_1.dpi_y); +} + +TEST_F(CrosDisplayConfigTest, GetDisplayUnitInfoListModes) { + UpdateDisplay("1024x512,1024x512"); + std::vector<mojom::DisplayUnitInfoPtr> result = GetDisplayUnitInfoList(); + ASSERT_EQ(2u, result.size()); + + const mojom::DisplayUnitInfo& info_0 = *result[0]; + EXPECT_EQ(3, info_0.selected_display_mode_index); + ASSERT_EQ(5u, info_0.available_display_modes.size()); + + const std::vector<mojom::DisplayModePtr>& modes = + info_0.available_display_modes; + // Test native/selected mode. + EXPECT_EQ("1024x512", modes[3]->size.ToString()); + EXPECT_EQ("1024x512", modes[3]->size_in_native_pixels.ToString()); + EXPECT_EQ(1.0, modes[3]->ui_scale); + EXPECT_EQ(1.0, modes[3]->device_scale_factor); + EXPECT_TRUE(modes[3]->is_native); + EXPECT_EQ("1024x512", modes[3]->size.ToString()); + + // Test sizes of other modes. + EXPECT_EQ("512x256", modes[0]->size.ToString()); + EXPECT_EQ("1024x512", modes[0]->size_in_native_pixels.ToString()); + EXPECT_EQ("640x320", modes[1]->size.ToString()); + EXPECT_EQ("819x409", modes[2]->size.ToString()); + EXPECT_EQ("1152x576", modes[4]->size.ToString()); + EXPECT_EQ("1024x512", modes[4]->size_in_native_pixels.ToString()); + + // External display does not have any display modes. + const mojom::DisplayUnitInfo& info_1 = *result[1]; + EXPECT_EQ(0, info_1.selected_display_mode_index); + ASSERT_EQ(0u, info_1.available_display_modes.size()); +} + +TEST_F(CrosDisplayConfigTest, GetDisplayUnitInfoListZoomFactor) { + UpdateDisplay("1024x512,1024x512"); + std::vector<mojom::DisplayUnitInfoPtr> result = GetDisplayUnitInfoList(); + ASSERT_EQ(2u, result.size()); + + const mojom::DisplayUnitInfo& info_0 = *result[0]; + EXPECT_EQ(1.0, info_0.display_zoom_factor); + const std::vector<double>& zoom_factors = + info_0.available_display_zoom_factors; + EXPECT_EQ(9u, zoom_factors.size()); + EXPECT_FLOAT_EQ(0.6, zoom_factors[0]); + EXPECT_FLOAT_EQ(0.7, zoom_factors[1]); + EXPECT_FLOAT_EQ(0.8, zoom_factors[2]); + EXPECT_FLOAT_EQ(0.9, zoom_factors[3]); + EXPECT_FLOAT_EQ(1.0, zoom_factors[4]); + EXPECT_FLOAT_EQ(1.1, zoom_factors[5]); + EXPECT_FLOAT_EQ(1.2, zoom_factors[6]); + EXPECT_FLOAT_EQ(1.3, zoom_factors[7]); + EXPECT_FLOAT_EQ(1.4, zoom_factors[8]); +} + +TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesPrimary) { + UpdateDisplay("1200x600,600x1000"); + int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); + int64_t secondary_id = display_manager()->GetSecondaryDisplay().id(); + ASSERT_NE(primary_id, secondary_id); + + auto properties = mojom::DisplayConfigProperties::New(); + properties->set_primary = true; + mojom::DisplayConfigResult result = SetDisplayProperties( + base::Int64ToString(secondary_id), std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + + // secondary display should now be primary. + primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); + EXPECT_EQ(primary_id, secondary_id); +} + +TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesOverscan) { + UpdateDisplay("1200x600,600x1000*2"); + const display::Display& secondary = display_manager()->GetSecondaryDisplay(); + + auto properties = mojom::DisplayConfigProperties::New(); + properties->overscan = gfx::Insets({199, 20, 51, 130}); + mojom::DisplayConfigResult result = SetDisplayProperties( + base::Int64ToString(secondary.id()), std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("1200,0 150x250", secondary.bounds().ToString()); + const gfx::Insets overscan = + display_manager()->GetOverscanInsets(secondary.id()); + EXPECT_EQ(199, overscan.top()); + EXPECT_EQ(20, overscan.left()); + EXPECT_EQ(51, overscan.bottom()); + EXPECT_EQ(130, overscan.right()); +} + +TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesRotation) { + UpdateDisplay("1200x600,600x1000*2"); + const display::Display& secondary = display_manager()->GetSecondaryDisplay(); + + mojom::DisplayConfigResult result; + + auto properties = mojom::DisplayConfigProperties::New(); + properties->rotation = + mojom::DisplayRotation::New(display::mojom::Rotation::VALUE_90); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("1200,0 500x300", secondary.bounds().ToString()); + EXPECT_EQ(display::Display::ROTATE_90, secondary.rotation()); + + properties = mojom::DisplayConfigProperties::New(); + properties->rotation = + mojom::DisplayRotation::New(display::mojom::Rotation::VALUE_270); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("1200,0 500x300", secondary.bounds().ToString()); + EXPECT_EQ(display::Display::ROTATE_270, secondary.rotation()); + + // Test setting primary and rotating. + properties = mojom::DisplayConfigProperties::New(); + properties->set_primary = true; + properties->rotation = + mojom::DisplayRotation::New(display::mojom::Rotation::VALUE_180); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + const display::Display& primary = + display::Screen::GetScreen()->GetPrimaryDisplay(); + EXPECT_EQ(secondary.id(), primary.id()); + EXPECT_EQ("0,0 300x500", primary.bounds().ToString()); + EXPECT_EQ(display::Display::ROTATE_180, primary.rotation()); +} + +TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesBoundsOrigin) { + UpdateDisplay("1200x600,520x400"); + const display::Display& secondary = display_manager()->GetSecondaryDisplay(); + + mojom::DisplayConfigResult result; + + auto properties = mojom::DisplayConfigProperties::New(); + properties->bounds_origin = gfx::Point({-520, 50}); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("-520,50 520x400", secondary.bounds().ToString()); + + properties = mojom::DisplayConfigProperties::New(); + properties->bounds_origin = gfx::Point({1200, 100}); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("1200,100 520x400", secondary.bounds().ToString()); + + properties = mojom::DisplayConfigProperties::New(); + properties->bounds_origin = gfx::Point({1100, -400}); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("1100,-400 520x400", secondary.bounds().ToString()); + + properties = mojom::DisplayConfigProperties::New(); + properties->bounds_origin = gfx::Point({-350, 600}); + result = SetDisplayProperties(base::Int64ToString(secondary.id()), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ("-350,600 520x400", secondary.bounds().ToString()); +} + +TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesDisplayZoomFactor) { + UpdateDisplay("1200x600, 1600x1000#1600x1000"); + display::DisplayIdList display_id_list = + display_manager()->GetCurrentDisplayIdList(); + + const float zoom_factor_1 = 1.23f; + const float zoom_factor_2 = 2.34f; + + display_manager()->UpdateZoomFactor(display_id_list[0], zoom_factor_2); + display_manager()->UpdateZoomFactor(display_id_list[1], zoom_factor_1); + + EXPECT_EQ( + zoom_factor_2, + display_manager()->GetDisplayInfo(display_id_list[0]).zoom_factor()); + EXPECT_EQ( + zoom_factor_1, + display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor()); + + // Set zoom factor for display 0, should not affect display 1. + auto properties = mojom::DisplayConfigProperties::New(); + properties->display_zoom_factor = zoom_factor_1; + mojom::DisplayConfigResult result = SetDisplayProperties( + base::Int64ToString(display_id_list[0]), std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ( + zoom_factor_1, + display_manager()->GetDisplayInfo(display_id_list[0]).zoom_factor()); + EXPECT_EQ( + zoom_factor_1, + display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor()); + + // Set zoom factor for display 1. + properties = mojom::DisplayConfigProperties::New(); + properties->display_zoom_factor = zoom_factor_2; + result = SetDisplayProperties(base::Int64ToString(display_id_list[1]), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kSuccess, result); + EXPECT_EQ( + zoom_factor_1, + display_manager()->GetDisplayInfo(display_id_list[0]).zoom_factor()); + EXPECT_EQ( + zoom_factor_2, + display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor()); + + // Invalid zoom factor should fail. + const float invalid_zoom_factor = 0.01f; + properties = mojom::DisplayConfigProperties::New(); + properties->display_zoom_factor = invalid_zoom_factor; + result = SetDisplayProperties(base::Int64ToString(display_id_list[1]), + std::move(properties)); + EXPECT_EQ(mojom::DisplayConfigResult::kPropertyValueOutOfRangeError, result); + EXPECT_EQ( + zoom_factor_2, + display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor()); +} + +TEST_F(CrosDisplayConfigTest, SetDisplayMode) { + UpdateDisplay("1024x512,1024x512"); + std::vector<mojom::DisplayUnitInfoPtr> result = GetDisplayUnitInfoList(); + ASSERT_EQ(2u, result.size()); + EXPECT_EQ(3, result[0]->selected_display_mode_index); + ASSERT_EQ(5u, result[0]->available_display_modes.size()); + + auto properties = mojom::DisplayConfigProperties::New(); + auto display_mode = result[0]->available_display_modes[2].Clone(); + properties->display_mode = std::move(display_mode); + ASSERT_EQ(mojom::DisplayConfigResult::kSuccess, + SetDisplayProperties(result[0]->id, std::move(properties))); + + result = GetDisplayUnitInfoList(); + ASSERT_EQ(2u, result.size()); + EXPECT_EQ(2, result[0]->selected_display_mode_index); +} + +TEST_F(CrosDisplayConfigTest, OverscanCalibration) { + UpdateDisplay("1200x600"); + int64_t id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); + ASSERT_NE(display::kInvalidDisplayId, id); + + // Test that kAdjust succeeds after kComplete call. + EXPECT_TRUE(OverscanCalibration(id, mojom::DisplayConfigOperation::kStart, + base::nullopt)); + EXPECT_EQ(gfx::Insets(0, 0, 0, 0), display_manager()->GetOverscanInsets(id)); + + gfx::Insets insets(10, 10, 10, 10); + EXPECT_TRUE( + OverscanCalibration(id, mojom::DisplayConfigOperation::kAdjust, insets)); + // Adjust has no effect until Complete. + EXPECT_EQ(gfx::Insets(0, 0, 0, 0), display_manager()->GetOverscanInsets(id)); + + EXPECT_TRUE(OverscanCalibration(id, mojom::DisplayConfigOperation::kComplete, + base::nullopt)); + gfx::Insets overscan = display_manager()->GetOverscanInsets(id); + EXPECT_EQ(insets, overscan) + << "Overscan: " << overscan.ToString() << " != " << insets.ToString(); + + // Test that kReset clears restores previous insets. + + // Start clears any overscan values. + EXPECT_TRUE(OverscanCalibration(id, mojom::DisplayConfigOperation::kStart, + base::nullopt)); + EXPECT_EQ(gfx::Insets(0, 0, 0, 0), display_manager()->GetOverscanInsets(id)); + + // Reset + Complete restores previously set insets. + EXPECT_TRUE(OverscanCalibration(id, mojom::DisplayConfigOperation::kReset, + base::nullopt)); + EXPECT_EQ(gfx::Insets(0, 0, 0, 0), display_manager()->GetOverscanInsets(id)); + EXPECT_TRUE(OverscanCalibration(id, mojom::DisplayConfigOperation::kComplete, + base::nullopt)); + EXPECT_EQ(insets, display_manager()->GetOverscanInsets(id)); + + // Additional complete call should fail. + EXPECT_FALSE(OverscanCalibration(id, mojom::DisplayConfigOperation::kComplete, + base::nullopt)); +} + +TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationInternal) { + UpdateDisplay("1200x600,600x1000*2"); + const int64_t internal_display_id = + display::test::DisplayManagerTestApi(display_manager()) + .SetFirstDisplayAsInternalDisplay(); + + InitExternalTouchDevices(internal_display_id); + + EXPECT_FALSE(StartTouchCalibration(base::Int64ToString(internal_display_id))); + EXPECT_FALSE(IsTouchCalibrationActive()); +} + +TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationWithoutStart) { + UpdateDisplay("1200x600,600x1000*2"); + EXPECT_FALSE(IsTouchCalibrationActive()); +} + +TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationNonTouchDisplay) { + UpdateDisplay("1200x600,600x1000*2"); + + const int64_t internal_display_id = + display::test::DisplayManagerTestApi(display_manager()) + .SetFirstDisplayAsInternalDisplay(); + + display::DisplayIdList display_id_list = + display_manager()->GetCurrentDisplayIdList(); + + // Pick the non internal display Id. + const int64_t display_id = display_id_list[0] == internal_display_id + ? display_id_list[1] + : display_id_list[0]; + + ui::InputDeviceClientTestApi().SetTouchscreenDevices({}); + std::string id = base::Int64ToString(display_id); + + // Since no external touch devices are present, the calibration should fail. + EXPECT_FALSE(StartTouchCalibration(id)); + + // If an external touch device is present, the calibration should proceed. + InitExternalTouchDevices(display_id); + EXPECT_TRUE(StartTouchCalibration(id)); + EXPECT_TRUE(IsTouchCalibrationActive()); +} + +TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationInvalidPoints) { + UpdateDisplay("1200x600,600x1000*2"); + + const int64_t internal_display_id = + display::test::DisplayManagerTestApi(display_manager()) + .SetFirstDisplayAsInternalDisplay(); + + display::DisplayIdList display_id_list = + display_manager()->GetCurrentDisplayIdList(); + + // Pick the non internal display Id. + const int64_t display_id = display_id_list[0] == internal_display_id + ? display_id_list[1] + : display_id_list[0]; + + InitExternalTouchDevices(display_id); + + std::string id = base::Int64ToString(display_id); + + EXPECT_TRUE(StartTouchCalibration(id)); + mojom::TouchCalibrationPtr calibration = GetDefaultCalibration(); + calibration->pairs[0]->display_point.set_x(-1); + EXPECT_FALSE(CompleteCustomTouchCalibration(id, std::move(calibration))); + + EXPECT_TRUE(StartTouchCalibration(id)); + calibration = GetDefaultCalibration(); + calibration->bounds.set_width(1); + calibration->pairs[0]->display_point.set_x(2); + EXPECT_FALSE(CompleteCustomTouchCalibration(id, std::move(calibration))); +} + +TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationSuccess) { + UpdateDisplay("1200x600,600x1000*2"); + + const int64_t internal_display_id = + display::test::DisplayManagerTestApi(display_manager()) + .SetFirstDisplayAsInternalDisplay(); + + display::DisplayIdList display_id_list = + display_manager()->GetCurrentDisplayIdList(); + + // Pick the non internal display Id. + const int64_t display_id = display_id_list[0] == internal_display_id + ? display_id_list[1] + : display_id_list[0]; + + InitExternalTouchDevices(display_id); + + std::string id = base::Int64ToString(display_id); + + EXPECT_TRUE(StartTouchCalibration(id)); + EXPECT_TRUE(IsTouchCalibrationActive()); + mojom::TouchCalibrationPtr calibration = GetDefaultCalibration(); + EXPECT_TRUE(CompleteCustomTouchCalibration(id, std::move(calibration))); +} + +} // namespace ash
diff --git a/ash/display/cursor_window_controller.cc b/ash/display/cursor_window_controller.cc index 943eb9a7..d7aa1a6 100644 --- a/ash/display/cursor_window_controller.cc +++ b/ash/display/cursor_window_controller.cc
@@ -6,6 +6,7 @@ #include "ash/ash_constants.h" #include "ash/components/cursor/cursor_view.h" +#include "ash/display/display_color_manager.h" #include "ash/display/mirror_window_controller.h" #include "ash/display/window_tree_host_manager.h" #include "ash/magnifier/magnification_controller.h" @@ -16,6 +17,7 @@ #include "ash/session/session_controller.h" #include "ash/shell.h" #include "base/command_line.h" +#include "base/metrics/histogram_macros.h" #include "components/prefs/pref_service.h" #include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/env.h" @@ -128,7 +130,8 @@ // During startup, we may not have a preference service yet. We need to check // display manager state first so that we don't accidentally ignore it while // early outing when there isn't a PrefService yet. - display::DisplayManager* display_manager = Shell::Get()->display_manager(); + Shell* shell = Shell::Get(); + display::DisplayManager* display_manager = shell->display_manager(); if ((display_manager->is_multi_mirroring_enabled() && display_manager->IsInSoftwareMirrorMode()) || display_manager->IsInUnifiedMode() || @@ -136,19 +139,33 @@ return true; } - if (ash::Shell::Get()->magnification_controller()->IsEnabled()) + if (shell->magnification_controller()->IsEnabled()) return true; - PrefService* prefs = - Shell::Get()->session_controller()->GetActivePrefService(); + PrefService* prefs = shell->session_controller()->GetActivePrefService(); if (!prefs) { // The active pref service can be null early in startup. return false; } + + if (prefs->GetBoolean(prefs::kNightLightEnabled)) { + // All or some displays don't support setting a CRTC matrix, which means + // Night Light is using the composited color matrix, and hence software + // cursor should be used. + // TODO(afakhry): Instead of switching to the composited cursor on all + // displays if any of them don't support a CRTC matrix, we should provide + // the functionality to turn on the composited cursor on a per-display basis + // (i.e. use it only on the displays that don't support CRTC matrices). + const DisplayColorManager::DisplayCtmSupport displays_ctm_support = + shell->display_color_manager()->displays_ctm_support(); + UMA_HISTOGRAM_ENUMERATION("Ash.NightLight.DisplayCrtcCtmSupport", + displays_ctm_support); + return displays_ctm_support != DisplayColorManager::DisplayCtmSupport::kAll; + } + return prefs->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) || prefs->GetBoolean(prefs::kAccessibilityHighContrastEnabled) || - prefs->GetBoolean(prefs::kDockedMagnifierEnabled) || - prefs->GetBoolean(prefs::kNightLightEnabled); + prefs->GetBoolean(prefs::kDockedMagnifierEnabled); } void CursorWindowController::SetCursorCompositingEnabled(bool enabled) {
diff --git a/ash/display/display_color_manager.cc b/ash/display/display_color_manager.cc index aec12f87..3fd0bb5 100644 --- a/ash/display/display_color_manager.cc +++ b/ash/display/display_color_manager.cc
@@ -166,6 +166,10 @@ SkMatrix44 SkMatrix44FromColorMatrixVector( const std::vector<float>& matrix_vector) { + if (matrix_vector.empty()) + return SkMatrix44::I(); + + DCHECK_EQ(matrix_vector.size(), 9u); SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); matrix.set3x3RowMajorf(matrix_vector.data()); return matrix; @@ -181,6 +185,7 @@ sequenced_task_runner_(base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), base::TaskPriority::USER_VISIBLE, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})), + displays_ctm_support_(DisplayCtmSupport::kNone), screen_to_observe_(screen_to_observe), weak_ptr_factory_(this) { configurator_->AddObserver(this); @@ -206,10 +211,12 @@ return false; } - displays_color_matrix_map_.emplace(display_id, color_matrix); + // Always overwrite any existing matrix for this display. + displays_color_matrix_map_[display_id] = color_matrix; const auto iter = calibration_map_.find(display_snapshot->product_code()); SkMatrix44 combined_matrix = color_matrix; - if (iter != calibration_map_.end() && iter->second) { + if (iter != calibration_map_.end()) { + DCHECK(iter->second); combined_matrix.preConcat( SkMatrix44FromColorMatrixVector(iter->second->correction_matrix)); } @@ -224,31 +231,34 @@ void DisplayColorManager::OnDisplayModeChanged( const display::DisplayConfigurator::DisplayStateList& display_states) { + size_t displays_with_ctm_support_count = 0; for (const display::DisplaySnapshot* state : display_states) { UMA_HISTOGRAM_BOOLEAN("Ash.DisplayColorManager.ValidDisplayColorSpace", state->color_space().IsValid()); - // Always reset the configuration before setting a new one, because some - // drivers hold on to it across screen changes, http://crrev.com/1914343003. - configurator_->SetColorCorrection( - state->display_id(), std::vector<display::GammaRampRGBEntry>(), - std::vector<display::GammaRampRGBEntry>(), std::vector<float>()); + if (state->has_color_correction_matrix()) + ++displays_with_ctm_support_count; UMA_HISTOGRAM_BOOLEAN("Ash.DisplayColorManager.HasColorCorrectionMatrix", state->has_color_correction_matrix()); - if (calibration_map_[state->product_code()]) { - ApplyDisplayColorCalibration(state->display_id(), state->product_code()); - } else { - const bool valid_product_code = - state->product_code() != - display::DisplaySnapshot::kInvalidProductCode; - // TODO(mcasas): correct UMA s/Id/Code/, https://crbug.com/821393. - UMA_HISTOGRAM_BOOLEAN("Ash.DisplayColorManager.ValidProductId", - valid_product_code); - if (valid_product_code) - LoadCalibrationForDisplay(state); + const int64_t display_id = state->display_id(); + const auto calibration_iter = calibration_map_.find(state->product_code()); + if (calibration_iter != calibration_map_.end()) { + DCHECK(calibration_iter->second); + ApplyDisplayColorCalibration(display_id, *(calibration_iter->second)); + } else if (!LoadCalibrationForDisplay(state)) { + // Failed to start loading ICC profile. Reset calibration or reapply an + // existing color matrix we have for this display. + ResetDisplayColorCalibration(display_id); } } + + if (!displays_with_ctm_support_count) + displays_ctm_support_ = DisplayCtmSupport::kNone; + else if (displays_with_ctm_support_count == display_states.size()) + displays_ctm_support_ = DisplayCtmSupport::kAll; + else + displays_ctm_support_ = DisplayCtmSupport::kMixed; } void DisplayColorManager::OnDisplayRemoved( @@ -256,42 +266,46 @@ displays_color_matrix_map_.erase(old_display.id()); } -void DisplayColorManager::ApplyDisplayColorCalibration(int64_t display_id, - int64_t product_code) { - const auto calibration_iter = calibration_map_.find(product_code); - if (calibration_iter == calibration_map_.end() || !calibration_iter->second) - return; - +void DisplayColorManager::ApplyDisplayColorCalibration( + int64_t display_id, + const ColorCalibrationData& calibration_data) { const auto color_matrix_iter = displays_color_matrix_map_.find(display_id); - ColorCalibrationData* data = calibration_iter->second.get(); - const std::vector<float>* final_matrix = &data->correction_matrix; + const std::vector<float>* final_matrix = &calibration_data.correction_matrix; if (color_matrix_iter != displays_color_matrix_map_.end()) { SkMatrix44 combined_matrix = color_matrix_iter->second; - combined_matrix.preConcat( - SkMatrix44FromColorMatrixVector(data->correction_matrix)); + combined_matrix.preConcat(SkMatrix44FromColorMatrixVector(*final_matrix)); ColorMatrixVectorFromSkMatrix44(combined_matrix, &matrix_buffer_); final_matrix = &matrix_buffer_; } - if (!configurator_->SetColorCorrection(display_id, data->degamma_lut, - data->gamma_lut, *final_matrix)) { + if (!configurator_->SetColorCorrection( + display_id, calibration_data.degamma_lut, calibration_data.gamma_lut, + *final_matrix)) { LOG(WARNING) << "Error applying color correction data"; } } -void DisplayColorManager::LoadCalibrationForDisplay( +bool DisplayColorManager::LoadCalibrationForDisplay( const display::DisplaySnapshot* display) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (display->display_id() == display::kInvalidDisplayId) { LOG(WARNING) << "Trying to load calibration data for invalid display id"; - return; + return false; } // TODO: enable QuirksManager for mash. http://crbug.com/728748. Some tests // don't create the Shell when running this code, hence the // Shell::HasInstance() conditional. if (Shell::HasInstance() && Shell::GetAshConfig() == Config::MASH) - return; + return false; + + const bool valid_product_code = + display->product_code() != display::DisplaySnapshot::kInvalidProductCode; + // TODO(mcasas): correct UMA s/Id/Code/, https://crbug.com/821393. + UMA_HISTOGRAM_BOOLEAN("Ash.DisplayColorManager.ValidProductId", + valid_product_code); + if (!valid_product_code) + return false; quirks::QuirksManager::Get()->RequestIccProfilePath( display->product_code(), display->display_name(), @@ -299,6 +313,7 @@ weak_ptr_factory_.GetWeakPtr(), display->display_id(), display->product_code(), display->has_color_correction_matrix(), display->type())); + return true; } void DisplayColorManager::FinishLoadCalibrationForDisplay( @@ -314,16 +329,17 @@ if (path.empty()) { VLOG(1) << "No ICC file found with product id: " << product_string << " for display id: " << display_id; + ResetDisplayColorCalibration(display_id); return; - } else { - UMA_HISTOGRAM_BOOLEAN("Ash.DisplayColorManager.IccFileDownloaded", - file_downloaded); } + UMA_HISTOGRAM_BOOLEAN("Ash.DisplayColorManager.IccFileDownloaded", + file_downloaded); if (file_downloaded && type == display::DISPLAY_CONNECTION_TYPE_INTERNAL) { VLOG(1) << "Downloaded ICC file with product id: " << product_string << " for internal display id: " << display_id << ". Profile will be applied on next startup."; + ResetDisplayColorCalibration(display_id); return; } @@ -343,12 +359,35 @@ int64_t product_id, std::unique_ptr<ColorCalibrationData> data) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Apply the received |data| if valid or reset color calibration. if (data) { + ApplyDisplayColorCalibration(display_id, *data); calibration_map_[product_id] = std::move(data); - ApplyDisplayColorCalibration(display_id, product_id); + } else { + ResetDisplayColorCalibration(display_id); } } +void DisplayColorManager::ResetDisplayColorCalibration(int64_t display_id) { + // We must call this in every potential failure point at loading the ICC + // profile of the displays when the displays have been reconfigured. This is + // due to the following reason: + // With the DRM drivers on ChromeOS, the color management tables and matrices + // are stored at the pipe level (part of the display hardware that is + // configurable regardless of the actual connector it is attached to). This + // allows display configuration to remain active while different processes are + // using the driver (for example switching VT). + // + // As a result, when an external screen is connected to a Chromebook, a given + // color configuration might be applied to it and remain stored in the driver + // after the screen is disconnected. If another external screen is now + // connected the previously applied color management will remain if there is + // not a profile for that display. + // + // For more details, please refer to https://crrev.com/1914343003. + ApplyDisplayColorCalibration(display_id, {} /* calibration_data */); +} + DisplayColorManager::ColorCalibrationData::ColorCalibrationData() = default; DisplayColorManager::ColorCalibrationData::~ColorCalibrationData() = default;
diff --git a/ash/display/display_color_manager.h b/ash/display/display_color_manager.h index 4010f61..c103dde 100644 --- a/ash/display/display_color_manager.h +++ b/ash/display/display_color_manager.h
@@ -7,7 +7,6 @@ #include <stdint.h> -#include <map> #include <memory> #include <vector> @@ -40,10 +39,28 @@ : public display::DisplayConfigurator::Observer, public display::DisplayObserver { public: + // The type of CRTC color transform matrix (CTM) support for the currently + // connected displays. + // WARNING: These values are persisted to logs. Entries should not be + // renumbered and numeric values should never be reused. + enum class DisplayCtmSupport { + // All connected displays don't support CRTC CTMs. + kNone = 0, + // Mixed support; some displays support CRTC CTMs while others don't. + kMixed = 1, + // All connected displays support CRTC CTMs. + kAll = 2, + kMaxValue = kAll, + }; + DisplayColorManager(display::DisplayConfigurator* configurator, display::Screen* screen_to_observe); ~DisplayColorManager() override; + DisplayCtmSupport displays_ctm_support() const { + return displays_ctm_support_; + } + // Sets the given |color_matrix| on the display hardware of |display_id|, // combining the given matrix with any available color calibration matrix for // this display. This doesn't affect gamma or degamma values. @@ -87,8 +104,21 @@ private: friend class DisplayColorManagerTest; - void ApplyDisplayColorCalibration(int64_t display_id, int64_t product_code); - void LoadCalibrationForDisplay(const display::DisplaySnapshot* display); + void ApplyDisplayColorCalibration( + int64_t display_id, + const ColorCalibrationData& calibration_data); + + // Attempts to start requesting the ICC profile for |display|. Returns true if + // it was successful at initiating the request, false otherwise. + bool LoadCalibrationForDisplay(const display::DisplaySnapshot* display); + + // Applies an empty color calibration data, potentially with a color + // matrix from |displays_color_matrix_map_| (if any for this display is + // available). This is needed in cases we fail to load ICC profiles for + // displays and we won't be getting any calibration data for them. We must + // reset their configuration because some drivers hold on to it across screen + // changes, https://crrev.com/1914343003. + void ResetDisplayColorCalibration(int64_t display_id); display::DisplayConfigurator* configurator_; @@ -103,10 +133,13 @@ // Maps a display's color calibration data by the display's product code as // the key. - std::map<int64_t, std::unique_ptr<ColorCalibrationData>> calibration_map_; + base::flat_map<int64_t, std::unique_ptr<ColorCalibrationData>> + calibration_map_; SEQUENCE_CHECKER(sequence_checker_); scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; + DisplayCtmSupport displays_ctm_support_; + // This is null in DisplayColorManagerTest. display::Screen* screen_to_observe_;
diff --git a/ash/display/display_color_manager_unittest.cc b/ash/display/display_color_manager_unittest.cc index 6102b7b..2f2ce3f 100644 --- a/ash/display/display_color_manager_unittest.cc +++ b/ash/display/display_color_manager_unittest.cc
@@ -172,8 +172,8 @@ configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); - EXPECT_TRUE( - base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); + // Clear initial configuration log. + log_->GetActionsAndClear(); WaitOnColorCalibration(); EXPECT_TRUE(base::MatchPattern(log_->GetActionsAndClear(), kSetGammaAction)); @@ -195,8 +195,8 @@ log_->GetActionsAndClear(); configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); - EXPECT_TRUE( - base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); + // Clear initial configuration log. + log_->GetActionsAndClear(); WaitOnColorCalibration(); EXPECT_TRUE(base::MatchPattern(log_->GetActionsAndClear(), kSetGammaAction)); @@ -217,8 +217,8 @@ configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); - EXPECT_TRUE( - base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); + // Clear initial configuration log. + log_->GetActionsAndClear(); WaitOnColorCalibration(); EXPECT_TRUE( @@ -241,17 +241,19 @@ configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); + WaitOnColorCalibration(); + // DisplayColorManager::ResetDisplayColorCalibration() will be called since + // this display has no CTM support. EXPECT_TRUE( base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); - WaitOnColorCalibration(); - EXPECT_STREQ("", log_->GetActionsAndClear().c_str()); - // Attempt to set a color matrix. SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); matrix.set(1, 1, 0.7); matrix.set(2, 2, 0.3); EXPECT_FALSE(color_manager_->SetDisplayColorMatrix(kDisplayId, matrix)); + EXPECT_EQ(color_manager_->displays_ctm_support(), + DisplayColorManager::DisplayCtmSupport::kNone); EXPECT_STREQ("", log_->GetActionsAndClear().c_str()); } @@ -282,11 +284,72 @@ matrix.set(1, 1, 0.7); matrix.set(2, 2, 0.3); EXPECT_TRUE(color_manager_->SetDisplayColorMatrix(kDisplayId, matrix)); + EXPECT_EQ(color_manager_->displays_ctm_support(), + DisplayColorManager::DisplayCtmSupport::kAll); // This display has no color calibration data. Gamma/degamma won't be // affected. Color matrix is applied as is. EXPECT_TRUE(base::MatchPattern( log_->GetActionsAndClear(), "set_color_correction(id=123,ctm[0]=1*ctm[4]=0.7*ctm[8]=0.3*)")); + + // Reconfiguring with the same displays snapshots will reapply the matrix. + native_display_delegate_->set_outputs(outputs); + configurator_.OnConfigurationChanged(); + EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); + EXPECT_TRUE(base::MatchPattern( + log_->GetActionsAndClear(), + "*set_color_correction(id=123,ctm[0]=1*ctm[4]=0.7*ctm[8]=0.3*)")); +} + +TEST_F(DisplayColorManagerTest, SetDisplayColorMatrixWithMixedCTMSupport) { + constexpr int64_t kDisplayWithCtmId = 123; + std::unique_ptr<display::DisplaySnapshot> snapshot1 = + display::FakeDisplaySnapshot::Builder() + .SetId(kDisplayWithCtmId) + .SetNativeMode(kDisplaySize) + .SetCurrentMode(kDisplaySize) + .SetType(display::DISPLAY_CONNECTION_TYPE_INTERNAL) + .SetHasColorCorrectionMatrix(true) + .SetProductCode(0x0) // Non-existent product code. + .Build(); + constexpr int64_t kDisplayNoCtmId = 456; + std::unique_ptr<display::DisplaySnapshot> snapshot2 = + display::FakeDisplaySnapshot::Builder() + .SetId(kDisplayNoCtmId) + .SetNativeMode(kDisplaySize) + .SetCurrentMode(kDisplaySize) + .SetType(display::DISPLAY_CONNECTION_TYPE_HDMI) + .SetHasColorCorrectionMatrix(false) + .SetProductCode(0x0) // Non-existent product code. + .Build(); + + std::vector<display::DisplaySnapshot*> outputs( + {snapshot1.get(), snapshot2.get()}); + native_display_delegate_->set_outputs(outputs); + + configurator_.OnConfigurationChanged(); + EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); + + // No need to wait for calibration here, these displays don't have icc files. + log_->GetActionsAndClear(); + + EXPECT_EQ(color_manager_->displays_ctm_support(), + DisplayColorManager::DisplayCtmSupport::kMixed); + + // Attempt to set a color matrix. + SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); + matrix.set(1, 1, 0.7); + matrix.set(2, 2, 0.3); + EXPECT_TRUE(color_manager_->SetDisplayColorMatrix(kDisplayWithCtmId, matrix)); + // This display has no color calibration data. Gamma/degamma won't be + // affected. Color matrix is applied as is. + EXPECT_TRUE(base::MatchPattern( + log_->GetActionsAndClear(), + "set_color_correction(id=123,ctm[0]=1*ctm[4]=0.7*ctm[8]=0.3*)")); + + // No matrix will be applied to this display. + EXPECT_FALSE(color_manager_->SetDisplayColorMatrix(kDisplayNoCtmId, matrix)); + EXPECT_STREQ("", log_->GetActionsAndClear().c_str()); } TEST_F(DisplayColorManagerTest, @@ -315,11 +378,23 @@ matrix.set(1, 1, 0.7); matrix.set(2, 2, 0.3); EXPECT_TRUE(color_manager_->SetDisplayColorMatrix(kDisplayId, matrix)); + EXPECT_EQ(color_manager_->displays_ctm_support(), + DisplayColorManager::DisplayCtmSupport::kAll); // The applied matrix is the combination of this color matrix and the color // calibration matrix. Gamma/degamma won't be affected. EXPECT_TRUE(base::MatchPattern( log_->GetActionsAndClear(), "set_color_correction(id=123,ctm[0]=0.01*ctm[4]=0.5*ctm[8]=0.04*)")); + + // Reconfiguring with the same displays snapshots will reapply the same + // product matrix as well as gamma/degamma from the calibration data. + native_display_delegate_->set_outputs(outputs); + configurator_.OnConfigurationChanged(); + EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); + EXPECT_TRUE( + base::MatchPattern(log_->GetActionsAndClear(), + "*set_color_correction(id=123,degamma[0]*gamma[0]*," + "ctm[0]=0.01*ctm[4]=0.5*ctm[8]=0.04*)")); } TEST_F(DisplayColorManagerTest, FullWithoutPlatformCTM) { @@ -337,11 +412,12 @@ configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); - EXPECT_TRUE( - base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); + // Clear initial configuration log. + log_->GetActionsAndClear(); WaitOnColorCalibration(); - EXPECT_STREQ("", log_->GetActionsAndClear().c_str()); + EXPECT_TRUE( + base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); } TEST_F(DisplayColorManagerTest, NoMatchProductID) { @@ -359,11 +435,13 @@ configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); + // DisplayColorManager::ResetDisplayColorCalibration() will be called since + // the product code is invalid. EXPECT_TRUE( base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); - // NOTE: If product_id == 0, there is no thread switching in Quirks or Display - // code, so we shouldn't call WaitOnColorCalibration(). + // NOTE: If product_code == 0, there is no thread switching in Quirks or + // Display code, so we shouldn't call WaitOnColorCalibration(). EXPECT_STREQ("", log_->GetActionsAndClear().c_str()); } @@ -382,11 +460,14 @@ configurator_.OnConfigurationChanged(); EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); - EXPECT_TRUE( - base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); + // Clear initial configuration log. + log_->GetActionsAndClear(); WaitOnColorCalibration(); - EXPECT_STREQ("", log_->GetActionsAndClear().c_str()); + // DisplayColorManager::ResetDisplayColorCalibration() will be called since + // there is no vcgt table. + EXPECT_TRUE( + base::MatchPattern(log_->GetActionsAndClear(), kResetGammaAction)); } } // namespace ash
diff --git a/ash/display/window_tree_host_manager.h b/ash/display/window_tree_host_manager.h index fefcd64..cf6ccd9 100644 --- a/ash/display/window_tree_host_manager.h +++ b/ash/display/window_tree_host_manager.h
@@ -125,8 +125,8 @@ // Returns the root window for |display_id|. aura::Window* GetRootWindowForDisplayId(int64_t id); - // Returns AshWTH for given display |id|. Call results in CHECK failure - // if the WTH does not exist. + // Returns AshWTH for given display |id|. Returns nullptr if the WTH does not + // exist. AshWindowTreeHost* GetAshWindowTreeHostForDisplayId(int64_t id); // Sets the primary display by display id. This re-assigns the current primary
diff --git a/ash/manifest.json b/ash/manifest.json index 13adbaa..94bfd70 100644 --- a/ash/manifest.json +++ b/ash/manifest.json
@@ -16,6 +16,7 @@ "ash::mojom::AshAssistantController", "ash::mojom::AshMessageCenterController", "ash::mojom::CastConfig", + "ash::mojom::CrosDisplayConfigController", "ash::mojom::DockedMagnifierController", "ash::mojom::FirstRunHelper", "ash::mojom::HighlighterController",
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc index 65e148f..a41da5b 100644 --- a/ash/mojo_interface_factory.cc +++ b/ash/mojo_interface_factory.cc
@@ -13,6 +13,7 @@ #include "ash/assistant/ash_assistant_controller.h" #include "ash/cast_config_controller.h" #include "ash/display/ash_display_controller.h" +#include "ash/display/cros_display_config.h" #include "ash/first_run/first_run_helper.h" #include "ash/highlighter/highlighter_controller.h" #include "ash/ime/ime_controller.h" @@ -82,6 +83,11 @@ Shell::Get()->ash_display_controller()->BindRequest(std::move(request)); } +void BindCrosDisplayConfigControllerRequestOnMainThread( + mojom::CrosDisplayConfigControllerRequest request) { + Shell::Get()->cros_display_config()->BindRequest(std::move(request)); +} + void BindAshMessageCenterControllerRequestOnMainThread( mojom::AshMessageCenterControllerRequest request) { Shell::Get()->message_center_controller()->BindRequest(std::move(request)); @@ -216,6 +222,9 @@ base::Bind(&BindAshDisplayControllerRequestOnMainThread), main_thread_task_runner); registry->AddInterface( + base::BindRepeating(&BindCrosDisplayConfigControllerRequestOnMainThread), + main_thread_task_runner); + registry->AddInterface( base::Bind(&BindAshMessageCenterControllerRequestOnMainThread), main_thread_task_runner); registry->AddInterface(base::Bind(&BindCastConfigOnMainThread),
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn index 24906ab0..9848360b 100644 --- a/ash/public/interfaces/BUILD.gn +++ b/ash/public/interfaces/BUILD.gn
@@ -22,6 +22,7 @@ "assistant_card_renderer.mojom", "cast_config.mojom", "constants.mojom", + "cros_display_config.mojom", "docked_magnifier_controller.mojom", "event_properties.mojom", "first_run_helper.mojom", @@ -70,6 +71,7 @@ "//skia/public/interfaces", "//ui/accessibility:ax_enums_mojo", "//ui/base/ime/chromeos/public/interfaces", + "//ui/display/mojo:interfaces", "//ui/events/mojo:interfaces", "//ui/gfx/geometry/mojo", "//ui/gfx/image/mojo:interfaces",
diff --git a/ash/public/interfaces/cros_display_config.mojom b/ash/public/interfaces/cros_display_config.mojom new file mode 100644 index 0000000..d2cf42c --- /dev/null +++ b/ash/public/interfaces/cros_display_config.mojom
@@ -0,0 +1,275 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module ash.mojom; + +import "ui/gfx/geometry/mojo/geometry.mojom"; +import "ui/display/mojo/display.mojom"; + +// All points, bounds, and insets are in display pixels unless otherwise +// sepcified. + +// SetDisplayLayoutInfo or SetDisplayProperties result. +enum DisplayConfigResult { + kSuccess = 0, + kInvalidOperationError, + kInvalidDisplayIdError, + kUnifiedNotEnabledError, + kPropertyValueOutOfRangeError, + kNotSupportedOnInternalDisplayError, + kNegativeValueError, + kSetDisplayModeError, + kInvalidDisplayLayoutError, + kMirrorModeSingleDisplayError, + kMirrorModeSourceIdError, + kMirrorModeDestIdError, + kCalibrationNotAvailableError, + kCalibrationNotStartedError, + kCalibrationInProgressError, + kCalibrationInvalidDataError, + kCalibrationFailedError, +}; + +// Describes how the displays are laid out. +enum DisplayLayoutMode { + // In normal mode displays are laid out as described by + // DisplayLayoutInfo.layouts. + kNormal = 0, + // In unified desktop mode, a single desktop will be stretched across all + // available displays. + kUnified, + // In mirrored mode, the display defined by DisplayLayoutInfo.mirrorSourceId + // will be mirrored in the displays defined by + // DisplayLayoutInfo.mirrorDestinationIds, or in all other displays if + // mirrorDestinationIds is empty. + kMirrored +}; + +// Describes a display edge. +enum DisplayLayoutPosition { + kTop = 0, + kRight, + kBottom, + kLeft +}; + +// Describes an overscan or touch calibration operation. +enum DisplayConfigOperation { + kStart = 0, + kAdjust, + kReset, + kComplete, + kShowNative, +}; + +// Defines a pair of display + touch points used for touch calibration. +struct TouchCalibrationPair { + // The coordinates of the display point. + gfx.mojom.Point display_point; + // The coordinates of the touch point corresponding to the display point. + gfx.mojom.Point touch_point; +}; + +// Defines the data required for touch calibration. +struct TouchCalibration { + // Must contain exactly four pairs of touch calibration points. + array<TouchCalibrationPair> pairs; + // Width and height of the display area when the touch calibration was + // performed. + gfx.mojom.Size bounds; +}; + +// Defines the layout of a single display. +struct DisplayLayout { + // The unique identifier of the display. + string id; + // The unique identifier of the parent display. Empty for the root display. + string parent_id; + // The edge of the display that is shared with the parent display. Ignored for + // the root display. + DisplayLayoutPosition position; + // The offset of the display along the connected edge. 0 indicates that + // the topmost or leftmost corner is aligned. + int32 offset; +}; + +// Defines the layout mode and details. +struct DisplayLayoutInfo { + // The layout mode to use, see DisplayLayoutMode for details. + DisplayLayoutMode layout_mode; + // Ignored if If layout_mode is not kMirrored. Otherwise, if provided, + // specifies the unique identifier of the source display for mirroring. If + // not provided, mirror_destination_ids will be ignored and default ('normal') + // mirrored mode will be enabled. + string? mirror_source_id; + // Ignored if layout_mode is not kMirrored. Otherwise, if provided, specifies + // the unique identifiers of the displays to mirror the source display. If not + // provided or empty, all displays will mirror the source display. + array<string>? mirror_destination_ids; + // An array of layouts describing a directed graph of displays. Required if + // layout_mode is kNormal or kMirrored and not all displays are mirrored + // ('mixed' mode). Ignored if layout_mode is kUnified. + array<DisplayLayout>? layouts; +}; + +// EDID extracted parameters. Field description refers to "VESA ENHANCED +// EXTENDED DISPLAY IDENTIFICATION DATA STANDARD (Defines EDID Structure +// Version 1, Revision 4)" Release A, Revision 2 September 25, 2006. +// https://www.vesa.org/vesa-standards +struct Edid { + // Three character manufacturer code, Sec. 3.4.1 page 21. + string manufacturer_id; + // Two byte manufacturer-assigned code, Sec. 3.4.2 page 21. + string product_id; + // Year of manufacture. Sec. 3.4.4 page 22. + int32 year_of_manufacture; +}; + +// Struct wrapper so that the property can be optional. +struct DisplayRotation { + display.mojom.Rotation rotation; +}; + +// Defines the properties for a display mode, i.e. a valid size and scale. +struct DisplayMode { + // The display mode size in device independent (user visible) pixels. + gfx.mojom.Size size; + // The display mode size in native pixels. + gfx.mojom.Size size_in_native_pixels; + // The display mode UI scale factor. + double ui_scale; + // The display mode device scale factor. + double device_scale_factor; + // The display mode refresh rate in hertz. + double refresh_rate; + // True if the mode is the display's native mode. + bool is_native; +}; + +// Defines the properties of an individual display, returned by +// GetDisplayLayoutInfo. +struct DisplayUnitInfo { + // The unique identifier of the display. + string id; + // The user-friendly name (e.g. "Acme LCD monitor"). + string name; + // EDID properties when available. + Edid? edid; + // True if this is the primary display. + bool is_primary; + // True if this is an internal display. + bool is_internal; + // True if this display is enabled. + bool is_enabled; + // True when the display is in tablet mode. + bool is_tablet_mode; + // True if this display has a touch input device associated with it. + bool has_touch_support; + // True if this display has an accelerometer associated with it. + bool has_accelerometer_support; + // The number of pixels per inch along the x-axis. + double dpi_x; + // The number of pixels per inch along the y-axis. + double dpi_y; + // The display's clockwise rotation. + display.mojom.Rotation rotation; + // The display's logical bounds. + gfx.mojom.Rect bounds; + // The display's ovserscan insets within its screen's bounds. + gfx.mojom.Insets overscan; + // The usable work area of the display within the display bounds. Excludes + // areas of the display reserved for the OS, e.g. the taskbar and launcher. + gfx.mojom.Rect work_area; + // The index of the selected display mode. + int32 selected_display_mode_index; + // The list of available display modes. + array<DisplayMode> available_display_modes; + // The ratio between the display's current and default zoom. i.e. 1.0 is + // is equivalent to 100% zoom, and value 1.5 is equivalent to 150% zoom. + double display_zoom_factor; + // The list of allowed zoom factor values for the display. + array<double> available_display_zoom_factors; +}; + +// Properties for configuring an individual display, used in +// SetDisplayProperties. +struct DisplayConfigProperties { + // If true, makes the display primary. No-op if set to false. + bool set_primary; + // If provided, sets the display's overscan insets to the provided value. + // Note: overscan values may not be negative or larger than a half of the + // screen's size. Overscan cannot be changed on the internal monitor. + gfx.mojom.Insets? overscan; + // If provided updates the display's rotation. + DisplayRotation? rotation; + // If provided, updates the display's logical bounds origin. Note: when + // updating the display origin, some constraints will be applied. so the final + // bounds origin may be different than the one set. The actual bounds will be + // reflected in DisplayUnitInfo. Cannot be changed on the primary display (or + // if set_primary is true). + gfx.mojom.Point? bounds_origin; + // If non zero, updates the zoom associated with the display. This zoom + // performs relayout and repaint thus resulting in a better quality zoom than + // just performing a pixel by pixel stretch enlargement. + double display_zoom_factor; + // Optional DisplayMode properties to set. This should match one of the + // modes listed in DisplayUnitInfo.available_display_modes. Other custom + // modes may or may not be valid. + DisplayMode? display_mode; +}; + +// Interface for configuring displays in Chrome OS. Currently this is +// implemented in Ash through classes owned by ash::Shell, but the interface +// should not have any Ash specific dependencies. +interface CrosDisplayConfigController { + // Observers are notified when the display layout or any display properties + // change. + AddObserver(associated CrosDisplayConfigObserver observer); + + // Returns the display layout info, including the list of layouts. + GetDisplayLayoutInfo() => (DisplayLayoutInfo info); + + // Sets the layout mode, mirroring, and layouts. Returns kSuccess if the + // layout is valid or an error value otherwise. + SetDisplayLayoutInfo(DisplayLayoutInfo info) => (DisplayConfigResult result); + + // Returns the properties for all displays. If |single_unified| is true, a + // single display will be returned if the display layout is in unifed mode. + GetDisplayUnitInfoList(bool single_unified) => + (array<DisplayUnitInfo> info_list); + + // Sets |properties| for individual display with identifier |id|. Returns + // Success if the properties are valid or an error value otherwise. + SetDisplayProperties(string id, DisplayConfigProperties properties) => + (DisplayConfigResult result); + + // Enables or disables unified desktop mode. If the current display mode is + // kMirrored the mode will not be changed, if it is kNormal then the mode will + // be set to kUnified. + SetUnifiedDesktopEnabled(bool enabled); + + // Starts, updates, completes, or resets overscan calibration for the display + // with identifier |display_id|. If |op| is kAdjust, |delta| describes the + // amount to change the overscan value. Runs the callback after performing the + // operation or on error. + OverscanCalibration(string display_id, + DisplayConfigOperation op, + gfx.mojom.Insets? delta) => (DisplayConfigResult result); + + // Starts, completes, or resets touch calibration for the display with + // identifier |display_id|. If |op| is kShowNative shows the native + // calibration UI. Runs the callback after performing the operation or on + // error. + TouchCalibration(string display_id, + DisplayConfigOperation op, + TouchCalibration? calibration) => + (DisplayConfigResult result); +}; + +// Interface for clients needing to be informed when the display configuration +// changes. +interface CrosDisplayConfigObserver { + // Called any time the display configuration changes. + OnDisplayConfigChanged(); +};
diff --git a/ash/shell.cc b/ash/shell.cc index a32534c9..c5cf3e5 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -26,6 +26,7 @@ #include "ash/detachable_base/detachable_base_handler.h" #include "ash/detachable_base/detachable_base_notification_controller.h" #include "ash/display/ash_display_controller.h" +#include "ash/display/cros_display_config.h" #include "ash/display/cursor_window_controller.h" #include "ash/display/display_color_manager.h" #include "ash/display/display_configuration_controller.h" @@ -721,6 +722,7 @@ shell_delegate_->PreShutdown(); + cros_display_config_.reset(); display_configuration_observer_.reset(); display_prefs_.reset(); @@ -947,8 +949,6 @@ // These controllers call Shell::Get() in their constructors, so they cannot // be in the member initialization list. - if (switches::IsNightLightEnabled()) - night_light_controller_ = std::make_unique<NightLightController>(); touch_devices_controller_ = std::make_unique<TouchDevicesController>(); bluetooth_power_controller_ = std::make_unique<BluetoothPowerController>(); detachable_base_handler_ = std::make_unique<DetachableBaseHandler>(this); @@ -1013,6 +1013,11 @@ context_factory_private); } + // Night Light depends on the display manager, the display color manager, and + // aura::Env, so initialize it after all have been initialized. + if (switches::IsNightLightEnabled()) + night_light_controller_ = std::make_unique<NightLightController>(); + // The WindowModalityController needs to be at the front of the input event // pretarget handler list to ensure that it processes input events when modal // windows are active. @@ -1288,6 +1293,8 @@ display_configuration_observer_ = std::make_unique<DisplayConfigurationObserver>(); + cros_display_config_ = std::make_unique<CrosDisplayConfig>(); + persistent_window_controller_ = std::make_unique<PersistentWindowController>();
diff --git a/ash/shell.h b/ash/shell.h index 2dcfec2..b79178c 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -95,6 +95,7 @@ class BluetoothPowerController; class BrightnessControlDelegate; class CastConfigController; +class CrosDisplayConfig; class DetachableBaseHandler; class DetachableBaseNotificationController; class DisplayColorManager; @@ -357,6 +358,9 @@ return brightness_control_delegate_.get(); } CastConfigController* cast_config() { return cast_config_.get(); } + CrosDisplayConfig* cros_display_config() { + return cros_display_config_.get(); + } // Returns nullptr in mash which has no global cursor manager. ::wm::CursorManager* cursor_manager() { return cursor_manager_.get(); } @@ -375,6 +379,9 @@ display::DisplayConfigurator* display_configurator() { return display_configurator_.get(); } + DisplayColorManager* display_color_manager() { + return display_color_manager_.get(); + } DisplayErrorObserver* display_error_observer() { return display_error_observer_.get(); } @@ -700,6 +707,7 @@ std::unique_ptr<BacklightsForcedOffSetter> backlights_forced_off_setter_; std::unique_ptr<BrightnessControlDelegate> brightness_control_delegate_; std::unique_ptr<CastConfigController> cast_config_; + std::unique_ptr<CrosDisplayConfig> cros_display_config_; std::unique_ptr<DetachableBaseHandler> detachable_base_handler_; std::unique_ptr<DetachableBaseNotificationController> detachable_base_notification_controller_;
diff --git a/ash/system/network/network_feature_pod_button.cc b/ash/system/network/network_feature_pod_button.cc index 2c6e361..975a85e 100644 --- a/ash/system/network/network_feature_pod_button.cc +++ b/ash/system/network/network_feature_pod_button.cc
@@ -27,14 +27,11 @@ FeaturePodControllerBase* controller) : FeaturePodButton(controller) { network_state_observer_ = std::make_unique<TrayNetworkStateObserver>(this); - Shell::Get()->system_tray_notifier()->AddNetworkPortalDetectorObserver(this); Update(); } NetworkFeaturePodButton::~NetworkFeaturePodButton() { network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); - Shell::Get()->system_tray_notifier()->RemoveNetworkPortalDetectorObserver( - this); } void NetworkFeaturePodButton::NetworkIconChanged() { @@ -45,10 +42,6 @@ Update(); } -void NetworkFeaturePodButton::OnCaptivePortalDetected(const std::string& guid) { - Update(); -} - void NetworkFeaturePodButton::Update() { gfx::ImageSkia image; base::string16 label;
diff --git a/ash/system/network/network_feature_pod_button.h b/ash/system/network/network_feature_pod_button.h index 81dfaf6..1ca674b8 100644 --- a/ash/system/network/network_feature_pod_button.h +++ b/ash/system/network/network_feature_pod_button.h
@@ -6,7 +6,6 @@ #define ASH_SYSTEM_NETWORK_NETWORK_FEATURE_POD_BUTTON_H_ #include "ash/system/network/network_icon_animation_observer.h" -#include "ash/system/network/network_portal_detector_observer.h" #include "ash/system/network/tray_network_state_observer.h" #include "ash/system/unified/feature_pod_button.h" @@ -16,8 +15,7 @@ // animation to implement network connecting animation on feature pod button. class NetworkFeaturePodButton : public FeaturePodButton, public network_icon::AnimationObserver, - public TrayNetworkStateObserver::Delegate, - public NetworkPortalDetectorObserver { + public TrayNetworkStateObserver::Delegate { public: explicit NetworkFeaturePodButton(FeaturePodControllerBase* controller); ~NetworkFeaturePodButton() override; @@ -28,9 +26,6 @@ // TrayNetworkStateObserver::Delegate: void NetworkStateChanged(bool notify_a11y) override; - // NetworkPortalDetectorObserver: - void OnCaptivePortalDetected(const std::string& guid) override; - private: void Update();
diff --git a/ash/system/network/network_icon_unittest.cc b/ash/system/network/network_icon_unittest.cc index 32287ede..340ff75 100644 --- a/ash/system/network/network_icon_unittest.cc +++ b/ash/system/network/network_icon_unittest.cc
@@ -199,7 +199,7 @@ dbus_manager->GetShillDeviceClient()->GetTestInterface(); device_test->SetDeviceProperty("/device/cellular1", shill::kScanningProperty, - base::Value(true)); + base::Value(true), /*notify_changed=*/true); base::RunLoop().RunUntilIdle(); ASSERT_TRUE( handler_->GetScanningByType(chromeos::NetworkTypePattern::Cellular()));
diff --git a/ash/system/network/network_portal_detector_observer.h b/ash/system/network/network_portal_detector_observer.h deleted file mode 100644 index aa2b394..0000000 --- a/ash/system/network/network_portal_detector_observer.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_SYSTEM_NETWORK_NETWORK_PORTAL_DETECTOR_OBSERVER_H_ -#define ASH_SYSTEM_NETWORK_NETWORK_PORTAL_DETECTOR_OBSERVER_H_ - -#include <string> - -namespace ash { - -class NetworkPortalDetectorObserver { - public: - virtual ~NetworkPortalDetectorObserver() {} - - // Called when captive portal is detected for the network associated with - // |guid|. - virtual void OnCaptivePortalDetected(const std::string& guid) = 0; -}; - -} // namespace ash - -#endif // ASH_SYSTEM_NETWORK_NETWORK_PORTAL_DETECTOR_OBSERVER_H_
diff --git a/ash/system/network/tray_network.cc b/ash/system/network/tray_network.cc index 7aa9861..c638e7c0 100644 --- a/ash/system/network/tray_network.cc +++ b/ash/system/network/tray_network.cc
@@ -239,13 +239,11 @@ network_state_observer_.reset(new TrayNetworkStateObserver(this)); SystemTrayNotifier* notifier = Shell::Get()->system_tray_notifier(); notifier->AddNetworkObserver(this); - notifier->AddNetworkPortalDetectorObserver(this); } TrayNetwork::~TrayNetwork() { SystemTrayNotifier* notifier = Shell::Get()->system_tray_notifier(); notifier->RemoveNetworkObserver(this); - notifier->RemoveNetworkPortalDetectorObserver(this); } views::View* TrayNetwork::CreateTrayView(LoginStatus status) { @@ -306,10 +304,6 @@ message_center->AddNotification(tray::CreateNotification(!enabled)); } -void TrayNetwork::OnCaptivePortalDetected(const std::string& /* guid */) { - NetworkStateChanged(true /* notify_a11y */); -} - void TrayNetwork::NetworkStateChanged(bool notify_a11y) { if (tray_) { tray_->UpdateNetworkStateHandlerIcon();
diff --git a/ash/system/network/tray_network.h b/ash/system/network/tray_network.h index 3eecb71..7d5859e6 100644 --- a/ash/system/network/tray_network.h +++ b/ash/system/network/tray_network.h
@@ -9,7 +9,6 @@ #include <set> #include "ash/system/network/network_observer.h" -#include "ash/system/network/network_portal_detector_observer.h" #include "ash/system/network/tray_network_state_observer.h" #include "ash/system/tray/system_tray_item.h" #include "base/macros.h" @@ -24,7 +23,6 @@ class TrayNetwork : public SystemTrayItem, public NetworkObserver, - public NetworkPortalDetectorObserver, public TrayNetworkStateObserver::Delegate { public: explicit TrayNetwork(SystemTray* system_tray); @@ -43,9 +41,6 @@ // NetworkObserver void RequestToggleWifi() override; - // NetworkPortalDetectorObserver - void OnCaptivePortalDetected(const std::string& guid) override; - // TrayNetworkStateObserver::Delegate void NetworkStateChanged(bool notify_a11y) override;
diff --git a/ash/system/network/tray_network_state_observer.cc b/ash/system/network/tray_network_state_observer.cc index 270ec120..6174b791d 100644 --- a/ash/system/network/tray_network_state_observer.cc +++ b/ash/system/network/tray_network_state_observer.cc
@@ -38,14 +38,20 @@ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION) { update_frequency_ = 0; // Send updates immediately for tests. } + // TODO(mash): Figure out what to do about NetworkHandler and + // NetworkPortalDetector. if (NetworkHandler::IsInitialized()) { NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE); wifi_enabled_ = IsWifiEnabled(); } + if (chromeos::network_portal_detector::IsInitialized()) + chromeos::network_portal_detector::GetInstance()->AddObserver(this); } TrayNetworkStateObserver::~TrayNetworkStateObserver() { + if (chromeos::network_portal_detector::IsInitialized()) + chromeos::network_portal_detector::GetInstance()->RemoveObserver(this); if (NetworkHandler::IsInitialized()) { NetworkHandler::Get()->network_state_handler()->RemoveObserver(this, FROM_HERE); @@ -89,6 +95,12 @@ SignalUpdate(false /* notify_a11y */); } +void TrayNetworkStateObserver::OnPortalDetectionCompleted( + const chromeos::NetworkState* network, + const chromeos::NetworkPortalDetector::CaptivePortalState& state) { + SignalUpdate(true /* notify_a11y */); +} + void TrayNetworkStateObserver::SignalUpdate(bool notify_a11y) { bool old_state = wifi_enabled_; wifi_enabled_ = IsWifiEnabled();
diff --git a/ash/system/network/tray_network_state_observer.h b/ash/system/network/tray_network_state_observer.h index 8810399..f560c79c 100644 --- a/ash/system/network/tray_network_state_observer.h +++ b/ash/system/network/tray_network_state_observer.h
@@ -8,10 +8,13 @@ #include "base/macros.h" #include "base/timer/timer.h" #include "chromeos/network/network_state_handler_observer.h" +#include "chromeos/network/portal_detector/network_portal_detector.h" namespace ash { -class TrayNetworkStateObserver : public chromeos::NetworkStateHandlerObserver { +class TrayNetworkStateObserver + : public chromeos::NetworkStateHandlerObserver, + public chromeos::NetworkPortalDetector::Observer { public: class Delegate { public: @@ -27,7 +30,7 @@ ~TrayNetworkStateObserver() override; - // NetworkStateHandlerObserver overrides. + // NetworkStateHandlerObserver void NetworkListChanged() override; void DeviceListChanged() override; void DefaultNetworkChanged(const chromeos::NetworkState* network) override; @@ -36,6 +39,12 @@ void NetworkPropertiesUpdated(const chromeos::NetworkState* network) override; void DevicePropertiesUpdated(const chromeos::DeviceState* device) override; + // NetworkPortalDetector::Observer + void OnPortalDetectionCompleted( + const chromeos::NetworkState* network, + const chromeos::NetworkPortalDetector::CaptivePortalState& state) + override; + private: void SignalUpdate(bool notify_a11y); void SendNetworkStateChanged(bool notify_a11y);
diff --git a/ash/system/night_light/night_light_controller.cc b/ash/system/night_light/night_light_controller.cc index f30d717d..3ce4e73 100644 --- a/ash/system/night_light/night_light_controller.cc +++ b/ash/system/night_light/night_light_controller.cc
@@ -7,9 +7,10 @@ #include <cmath> #include <memory> +#include "ash/display/display_color_manager.h" +#include "ash/display/window_tree_host_manager.h" #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/ash_switches.h" -#include "ash/root_window_controller.h" #include "ash/session/session_controller.h" #include "ash/shell.h" #include "base/i18n/time_formatting.h" @@ -21,9 +22,13 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "third_party/icu/source/i18n/astro.h" +#include "ui/aura/env.h" +#include "ui/aura/window_tree_host.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/display/manager/display_manager.h" +#include "ui/display/types/display_constants.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/linear_animation.h" @@ -105,9 +110,8 @@ return std::floor(5 * temperature); } -// Applies the given |temperature| value by converting it to the corresponding -// color matrix that will be set on the output display. -void ApplyTemperatureToCompositors(float temperature) { +// Returns the color matrix that corresponds to the given |temperature|. +SkMatrix44 MatrixFromTemperature(float temperature) { SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); if (temperature != 0.0f) { const float blue_scale = @@ -118,10 +122,66 @@ matrix.set(1, 1, green_scale); matrix.set(2, 2, blue_scale); } + return matrix; +} - for (auto* controller : RootWindowController::root_window_controllers()) { - if (controller->GetHost()->compositor()) - controller->GetHost()->compositor()->SetDisplayColorMatrix(matrix); +// Applies the given |temperature| to the display associated with the given +// |host|. This is useful for when we have a host and not a display ID. +void ApplyTemperatureToHost(aura::WindowTreeHost* host, float temperature) { + DCHECK(host); + const int64_t display_id = host->GetDisplayId(); + DCHECK_NE(display_id, display::kInvalidDisplayId); + if (display_id == display::kUnifiedDisplayId) { + // In Unified Desktop mode, applying the color matrix to either the CRTC or + // the compositor of the mirroring (actual) displays is sufficient. If we + // apply it to the compositor of the Unified host (since there's no actual + // display CRTC for |display::kUnifiedDisplayId|), then we'll end up with + // double the temperature. + return; + } + + const SkMatrix44 matrix = MatrixFromTemperature(temperature); + if (!Shell::Get()->display_color_manager()->SetDisplayColorMatrix( + host->GetDisplayId(), matrix)) { + // This display doesn't support CRTC matrix. Fall back to the composited + // matrix. + if (host->compositor()) + host->compositor()->SetDisplayColorMatrix(matrix); + } +} + +// Applies the given |temperature| value by converting it to the corresponding +// color matrix that will be set on the output displays. +void ApplyTemperatureToAllDisplays(float temperature) { + const SkMatrix44 matrix = MatrixFromTemperature(temperature); + + Shell* shell = Shell::Get(); + DisplayColorManager* color_manager = shell->display_color_manager(); + WindowTreeHostManager* wth_manager = shell->window_tree_host_manager(); + for (int64_t display_id : + shell->display_manager()->GetCurrentDisplayIdList()) { + DCHECK_NE(display_id, display::kUnifiedDisplayId); + + if (color_manager->SetDisplayColorMatrix(display_id, matrix)) { + // Setting the hardware CRTC matrix was successful. + continue; + } + + // This display doesn't support CRTC matrix. Fall back to the composited + // matrix. + aura::Window* root_window = + wth_manager->GetRootWindowForDisplayId(display_id); + if (!root_window) { + // Some displays' hosts may have not being initialized yet. In this case + // NightLightController::OnHostInitialized() will take care of those + // hosts. + continue; + } + + auto* host = root_window->GetHost(); + DCHECK(host); + if (host->compositor()) + host->compositor()->SetDisplayColorMatrix(matrix); } } @@ -153,7 +213,7 @@ // Animations are disabled. Apply the target temperature directly to the // compositors. current_temperature_ = target_temperature_; - ApplyTemperatureToCompositors(target_temperature_); + ApplyTemperatureToAllDisplays(target_temperature_); Stop(); return; } @@ -180,7 +240,7 @@ Stop(); } - ApplyTemperatureToCompositors(current_temperature_); + ApplyTemperatureToAllDisplays(current_temperature_); } float start_temperature_ = 0.0f; @@ -195,7 +255,7 @@ temperature_animation_(std::make_unique<ColorTemperatureAnimation>()), binding_(this) { Shell::Get()->session_controller()->AddObserver(this); - Shell::Get()->window_tree_host_manager()->AddObserver(this); + aura::Env::GetInstance()->AddObserver(this); chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( this); } @@ -203,7 +263,7 @@ NightLightController::~NightLightController() { chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver( this); - Shell::Get()->window_tree_host_manager()->RemoveObserver(this); + aura::Env::GetInstance()->RemoveObserver(this); Shell::Get()->session_controller()->RemoveObserver(this); } @@ -344,12 +404,11 @@ SetEnabled(!GetEnabled(), AnimationDuration::kShort); } -void NightLightController::OnDisplayConfigurationChanged() { - // Since the color temperature matrix is a per-display output surface - // property, then when displays are added, removed, mirrored, extended, or - // switched to Unified Mode, we need to apply the current temperature - // immediately without animation. - ApplyTemperatureToCompositors(GetEnabled() ? GetColorTemperature() : 0.0f); +void NightLightController::OnHostInitialized(aura::WindowTreeHost* host) { + // This newly initialized |host| could be of a newly added display, or of a + // newly created mirroring display (either for mirroring or unified). we need + // to apply the current temperature immediately without animation. + ApplyTemperatureToHost(host, GetEnabled() ? GetColorTemperature() : 0.0f); } void NightLightController::OnActiveUserPrefServiceChanged(
diff --git a/ash/system/night_light/night_light_controller.h b/ash/system/night_light/night_light_controller.h index a4671ea..1748a9d 100644 --- a/ash/system/night_light/night_light_controller.h +++ b/ash/system/night_light/night_light_controller.h
@@ -8,7 +8,6 @@ #include <memory> #include "ash/ash_export.h" -#include "ash/display/window_tree_host_manager.h" #include "ash/public/interfaces/night_light_controller.mojom.h" #include "ash/session/session_observer.h" #include "ash/system/night_light/time_of_day.h" @@ -18,6 +17,7 @@ #include "chromeos/dbus/power_manager_client.h" #include "components/prefs/pref_change_registrar.h" #include "mojo/public/cpp/bindings/binding.h" +#include "ui/aura/env_observer.h" class PrefRegistrySimple; class PrefService; @@ -27,10 +27,17 @@ class ColorTemperatureAnimation; // Controls the NightLight feature that adjusts the color temperature of the -// screen. +// screen. It uses the display's hardware CRTC (Cathode Ray Tube Controller) +// color transform matrix (CTM) when possible for efficiency, and can fall back +// to setting a color matrix on the compositor if the display doesn't support +// color transformation. +// For Unified Desktop mode, the color matrix is set on the mirroring actual +// displays' hosts, rather than on the Unified host, so that we can use the +// CRTC matrix if available (the Unified host doesn't correspond to an actual +// display). class ASH_EXPORT NightLightController : public mojom::NightLightController, - public WindowTreeHostManager::Observer, + public aura::EnvObserver, public SessionObserver, public chromeos::PowerManagerClient::Observer { public: @@ -118,8 +125,9 @@ // AnimationDurationType::kShort. void Toggle(); - // ash::WindowTreeHostManager::Observer: - void OnDisplayConfigurationChanged() override; + // aura::EnvObserver: + void OnWindowInitialized(aura::Window* window) override {} + void OnHostInitialized(aura::WindowTreeHost* host) override; // SessionObserver: void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc index 03b3eb7..8f390be 100644 --- a/ash/system/night_light/night_light_controller_unittest.cc +++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -6,7 +6,11 @@ #include <cmath> #include <limits> +#include <sstream> +#include <string> +#include "ash/display/cursor_window_controller.h" +#include "ash/display/window_tree_host_manager.h" #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/config.h" @@ -23,8 +27,14 @@ #include "base/command_line.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/strings/pattern.h" #include "components/prefs/pref_service.h" #include "ui/compositor/layer.h" +#include "ui/display/manager/display_change_observer.h" +#include "ui/display/manager/display_manager.h" +#include "ui/display/manager/fake_display_snapshot.h" +#include "ui/display/manager/test/action_logger_util.h" +#include "ui/display/manager/test/test_native_display_delegate.h" namespace ash { @@ -37,22 +47,36 @@ return Shell::Get()->night_light_controller(); } +// Tests that the given display with |display_id| has the expected color matrix +// on its compositor that corresponds to the given |expected_temperature|. +void TestDisplayCompositorTemperature(int64_t display_id, + float expected_temperature) { + WindowTreeHostManager* wth_manager = Shell::Get()->window_tree_host_manager(); + aura::Window* root_window = + wth_manager->GetRootWindowForDisplayId(display_id); + DCHECK(root_window); + aura::WindowTreeHost* host = root_window->GetHost(); + DCHECK(host); + ui::Compositor* compositor = host->compositor(); + DCHECK(compositor); + + const SkMatrix44& matrix = compositor->display_color_matrix(); + const float blue_scale = matrix.get(2, 2); + const float green_scale = matrix.get(1, 1); + EXPECT_FLOAT_EQ( + expected_temperature, + NightLightController::TemperatureFromBlueColorScale(blue_scale)); + EXPECT_FLOAT_EQ( + expected_temperature, + NightLightController::TemperatureFromGreenColorScale(green_scale)); +} + // Tests that the display color matrices of all compositors correctly correspond // to the given |expected_temperature|. void TestCompositorsTemperature(float expected_temperature) { - for (auto* controller : RootWindowController::root_window_controllers()) { - ui::Compositor* compositor = controller->GetHost()->compositor(); - if (compositor) { - const SkMatrix44& matrix = compositor->display_color_matrix(); - const float blue_scale = matrix.get(2, 2); - const float green_scale = matrix.get(1, 1); - EXPECT_FLOAT_EQ( - expected_temperature, - NightLightController::TemperatureFromBlueColorScale(blue_scale)); - EXPECT_FLOAT_EQ( - expected_temperature, - NightLightController::TemperatureFromGreenColorScale(green_scale)); - } + for (int64_t display_id : + Shell::Get()->display_manager()->GetCurrentDisplayIdList()) { + TestDisplayCompositorTemperature(display_id, expected_temperature); } } @@ -256,23 +280,30 @@ // should still have the same temperature. display_manager()->SetMirrorMode(display::MirrorMode::kNormal, base::nullopt); EXPECT_TRUE(display_manager()->IsInMirrorMode()); + base::RunLoop().RunUntilIdle(); TestCompositorsTemperature(temperature); // Exit mirror mode, temperature is still applied. display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt); EXPECT_FALSE(display_manager()->IsInMirrorMode()); + base::RunLoop().RunUntilIdle(); TestCompositorsTemperature(temperature); // Enter unified mode, temperature is still applied. display_manager()->SetUnifiedDesktopEnabled(true); EXPECT_TRUE(display_manager()->IsInUnifiedMode()); + base::RunLoop().RunUntilIdle(); TestCompositorsTemperature(temperature); + // The unified host should never have a temperature on its compositor. + TestDisplayCompositorTemperature(display::kUnifiedDisplayId, 0.0f); // Exit unified mode, and remove the display, temperature should remain the // same. display_manager()->SetUnifiedDesktopEnabled(false); EXPECT_FALSE(display_manager()->IsInUnifiedMode()); + base::RunLoop().RunUntilIdle(); TestCompositorsTemperature(temperature); + display_manager()->AddRemoveDisplay(); base::RunLoop().RunUntilIdle(); ASSERT_EQ(1u, RootWindowController::root_window_controllers().size()); @@ -737,6 +768,224 @@ controller->timer().GetCurrentDelay()); } +// Fixture for testing behavior of Night Light when displays support hardware +// CRTC matrices. +class NightLightCrtcTest : public NightLightTest { + public: + NightLightCrtcTest() + : logger_(std::make_unique<display::test::ActionLogger>()) {} + ~NightLightCrtcTest() override = default; + + static constexpr gfx::Size kDisplaySize{1024, 768}; + static constexpr int64_t kId1 = 123; + static constexpr int64_t kId2 = 456; + + display::DisplayConfigurator* display_configurator() { + return Shell::Get()->display_configurator(); + } + + // NightLightTest: + void SetUp() override { + NightLightTest::SetUp(); + + native_display_delegate_ = + new display::test::TestNativeDisplayDelegate(logger_.get()); + display_configurator()->SetDelegateForTesting( + std::unique_ptr<display::NativeDisplayDelegate>( + native_display_delegate_)); + display_change_observer_ = std::make_unique<display::DisplayChangeObserver>( + display_configurator(), display_manager()); + test_api_ = std::make_unique<display::DisplayConfigurator::TestApi>( + display_configurator()); + } + + void TearDown() override { + // DisplayChangeObserver access InputDeviceManager in its destructor, so + // destroy it first. + display_change_observer_ = nullptr; + NightLightTest::TearDown(); + } + + // Builds two displays snapshots into |owned_snapshots_| and return a list of + // unowned pointers to them. |color_matrix_support| should contain exactly 2 + // boolean values that correspond to the color correction matrix support for + // both displays. + std::vector<display::DisplaySnapshot*> BuildAndGetDisplaySnapshots( + const std::vector<bool>& color_matrix_support) { + DCHECK_EQ(2u, color_matrix_support.size()); + owned_snapshots_.clear(); + owned_snapshots_.emplace_back( + display::FakeDisplaySnapshot::Builder() + .SetId(kId1) + .SetNativeMode(kDisplaySize) + .SetCurrentMode(kDisplaySize) + .SetHasColorCorrectionMatrix(color_matrix_support[0]) + .SetType(display::DISPLAY_CONNECTION_TYPE_INTERNAL) + .Build()); + owned_snapshots_.back()->set_origin({0, 0}); + owned_snapshots_.emplace_back( + display::FakeDisplaySnapshot::Builder() + .SetId(kId2) + .SetNativeMode(kDisplaySize) + .SetCurrentMode(kDisplaySize) + .SetHasColorCorrectionMatrix(color_matrix_support[1]) + .Build()); + owned_snapshots_.back()->set_origin({1030, 0}); + std::vector<display::DisplaySnapshot*> outputs = { + owned_snapshots_[0].get(), owned_snapshots_[1].get()}; + return outputs; + } + + // Updates the display configurator and display manager with the given list of + // display snapshots. + void UpdateDisplays(const std::vector<display::DisplaySnapshot*>& outputs) { + native_display_delegate_->set_outputs(outputs); + display_configurator()->OnConfigurationChanged(); + EXPECT_TRUE(test_api_->TriggerConfigureTimeout()); + display_change_observer_->GetStateForDisplayIds(outputs); + display_change_observer_->OnDisplayModeChanged(outputs); + } + + // Returns true if the software cursor is turned on. + bool IsCursorCompositingEnabled() const { + return Shell::Get() + ->window_tree_host_manager() + ->cursor_window_controller() + ->ShouldEnableCursorCompositing(); + } + + std::string GetLoggerActionsAndClear() { + return logger_->GetActionsAndClear(); + } + + bool VerifyCrtcMatrix(int64_t display_id, + float temperature, + const std::string& logger_actions_string) const { + constexpr float kRedScale = 1.0f; + const float blue_scale = + NightLightController::BlueColorScaleFromTemperature(temperature); + const float green_scale = + NightLightController::GreenColorScaleFromTemperature(temperature); + std::stringstream pattern_stream; + pattern_stream << "*set_color_correction(id=" << display_id + << ",ctm[0]=" << kRedScale << "*ctm[4]=" << green_scale + << "*ctm[8]=" << blue_scale << "*)*"; + return base::MatchPattern(logger_actions_string, pattern_stream.str()); + } + + private: + std::unique_ptr<display::test::ActionLogger> logger_; + // Not owned. + display::test::TestNativeDisplayDelegate* native_display_delegate_; + std::unique_ptr<display::DisplayChangeObserver> display_change_observer_; + std::unique_ptr<display::DisplayConfigurator::TestApi> test_api_; + + std::vector<std::unique_ptr<display::DisplaySnapshot>> owned_snapshots_; + + DISALLOW_COPY_AND_ASSIGN(NightLightCrtcTest); +}; + +// static +constexpr gfx::Size NightLightCrtcTest::kDisplaySize; + +// All displays support CRTC matrices. +TEST_F(NightLightCrtcTest, TestAllDisplaysSupportCrtcMatrix) { + // Create two displays with both having support for CRTC matrices. + std::vector<display::DisplaySnapshot*> outputs = + BuildAndGetDisplaySnapshots({true, true}); + UpdateDisplays(outputs); + + EXPECT_EQ(2u, display_manager()->GetNumDisplays()); + ASSERT_EQ(2u, RootWindowController::root_window_controllers().size()); + + // Turn on Night Light: + NightLightController* controller = GetController(); + SetNightLightEnabled(true); + float temperature = 0.2f; + GetLoggerActionsAndClear(); + controller->SetColorTemperature(temperature); + EXPECT_EQ(temperature, controller->GetColorTemperature()); + + // Since both displays support CRTC matrices, no compositor matrix should be + // set (i.e. compositor matrix is identity which corresponds to the zero + // temperature). + TestCompositorsTemperature(0.0f); + // Hence software cursor should not be used. + EXPECT_FALSE(IsCursorCompositingEnabled()); + // Verify correct matrix has been set on both crtcs. + std::string logger_actions = GetLoggerActionsAndClear(); + EXPECT_TRUE(VerifyCrtcMatrix(kId1, temperature, logger_actions)); + EXPECT_TRUE(VerifyCrtcMatrix(kId2, temperature, logger_actions)); + + // Setting a new temperature is applied. + temperature = 0.65f; + controller->SetColorTemperature(temperature); + EXPECT_EQ(temperature, controller->GetColorTemperature()); + TestCompositorsTemperature(0.0f); + EXPECT_FALSE(IsCursorCompositingEnabled()); + logger_actions = GetLoggerActionsAndClear(); + EXPECT_TRUE(VerifyCrtcMatrix(kId1, temperature, logger_actions)); + EXPECT_TRUE(VerifyCrtcMatrix(kId2, temperature, logger_actions)); +} + +// One display supports CRTC matrix and the other doesn't. +TEST_F(NightLightCrtcTest, TestMixedCrtcMatrixSupport) { + std::vector<display::DisplaySnapshot*> outputs = + BuildAndGetDisplaySnapshots({true, false}); + UpdateDisplays(outputs); + + EXPECT_EQ(2u, display_manager()->GetNumDisplays()); + ASSERT_EQ(2u, RootWindowController::root_window_controllers().size()); + + // Turn on Night Light: + NightLightController* controller = GetController(); + SetNightLightEnabled(true); + const float temperature = 0.2f; + GetLoggerActionsAndClear(); + controller->SetColorTemperature(temperature); + EXPECT_EQ(temperature, controller->GetColorTemperature()); + + // The first display supports CRTC matrix, so its compositor has identity + // matrix. + TestDisplayCompositorTemperature(kId1, 0.0f); + // However, the second display doesn't support CRTC matrix, Night Light is + // using the compositor matrix on this display. + TestDisplayCompositorTemperature(kId2, temperature); + // With mixed CTRC support, software cursor must be on. + EXPECT_TRUE(IsCursorCompositingEnabled()); + // Verify correct matrix has been set on both crtcs. + const std::string logger_actions = GetLoggerActionsAndClear(); + EXPECT_TRUE(VerifyCrtcMatrix(kId1, temperature, logger_actions)); + EXPECT_FALSE(VerifyCrtcMatrix(kId2, temperature, logger_actions)); +} + +// All displays don't support CRTC matrices. +TEST_F(NightLightCrtcTest, TestNoCrtcMatrixSupport) { + std::vector<display::DisplaySnapshot*> outputs = + BuildAndGetDisplaySnapshots({false, false}); + UpdateDisplays(outputs); + + EXPECT_EQ(2u, display_manager()->GetNumDisplays()); + ASSERT_EQ(2u, RootWindowController::root_window_controllers().size()); + + // Turn on Night Light: + NightLightController* controller = GetController(); + SetNightLightEnabled(true); + const float temperature = 0.2f; + GetLoggerActionsAndClear(); + controller->SetColorTemperature(temperature); + EXPECT_EQ(temperature, controller->GetColorTemperature()); + + // Compositor matrices are used on both displays. + TestCompositorsTemperature(temperature); + // With no CTRC support, software cursor must be on. + EXPECT_TRUE(IsCursorCompositingEnabled()); + // No CRTC matrices have been set. + const std::string logger_actions = GetLoggerActionsAndClear(); + EXPECT_FALSE(VerifyCrtcMatrix(kId1, temperature, logger_actions)); + EXPECT_FALSE(VerifyCrtcMatrix(kId2, temperature, logger_actions)); +} + } // namespace } // namespace ash
diff --git a/ash/system/session/session_limit_notification_controller.cc b/ash/system/session/session_limit_notification_controller.cc new file mode 100644 index 0000000..96e3b94 --- /dev/null +++ b/ash/system/session/session_limit_notification_controller.cc
@@ -0,0 +1,119 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/system/session/session_limit_notification_controller.h" + +#include "ash/resources/vector_icons/vector_icons.h" +#include "ash/session/session_controller.h" +#include "ash/shell.h" +#include "ash/strings/grit/ash_strings.h" +#include "ash/system/model/session_length_limit_model.h" +#include "ash/system/model/system_tray_model.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/l10n/time_format.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/public/cpp/notification.h" + +namespace ash { + +namespace { + +const char kNotifierSessionLengthTimeout[] = "ash.session-length-timeout"; + +// A notification is shown to the user only if the remaining session time falls +// under this threshold. e.g. If the user has several days left in their +// session, there is no use displaying a notification right now. +constexpr base::TimeDelta kNotificationThreshold = + base::TimeDelta::FromMinutes(60); + +} // namespace + +// static +const char SessionLimitNotificationController::kNotificationId[] = + "chrome://session/timeout"; + +SessionLimitNotificationController::SessionLimitNotificationController() + : model_(Shell::Get()->system_tray_model()->session_length_limit()) { + model_->AddObserver(this); + OnSessionLengthLimitUpdated(); +} + +SessionLimitNotificationController::~SessionLimitNotificationController() { + model_->RemoveObserver(this); +} + +void SessionLimitNotificationController::OnSessionLengthLimitUpdated() { + // Don't show notification until the user is logged in. + if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) + return; + + UpdateNotification(); + last_limit_state_ = model_->limit_state(); +} + +void SessionLimitNotificationController::UpdateNotification() { + message_center::MessageCenter* message_center = + message_center::MessageCenter::Get(); + + // If state hasn't changed and the notification has already been acknowledged, + // we won't re-create it. We consider a notification to be acknowledged if it + // was shown before, but is no longer visible. + if (model_->limit_state() == last_limit_state_ && + has_notification_been_shown_ && + !message_center->FindVisibleNotificationById(kNotificationId)) { + return; + } + + // After state change, any possibly existing notification is removed to make + // sure it is re-shown even if it had been acknowledged by the user before + // (and in the rare case of state change towards LIMIT_NONE to make the + // notification disappear). + if (model_->limit_state() != last_limit_state_ && + message_center->FindVisibleNotificationById(kNotificationId)) { + message_center::MessageCenter::Get()->RemoveNotification( + kNotificationId, false /* by_user */); + } + + // If the session is unlimited or if the remaining time is too far off into + // the future, there is nothing more to do. + if (model_->limit_state() == SessionLengthLimitModel::LIMIT_NONE || + model_->remaining_session_time() > kNotificationThreshold) { + return; + } + + message_center::RichNotificationData data; + data.should_make_spoken_feedback_for_popup_updates = + (model_->limit_state() != last_limit_state_); + std::unique_ptr<message_center::Notification> notification = + message_center::Notification::CreateSystemNotification( + message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, + ComposeNotificationTitle(), + l10n_util::GetStringUTF16( + IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT_MESSAGE), + gfx::Image(), base::string16() /* display_source */, GURL(), + message_center::NotifierId( + message_center::NotifierId::SYSTEM_COMPONENT, + kNotifierSessionLengthTimeout), + data, nullptr /* delegate */, kNotificationTimerIcon, + message_center::SystemNotificationWarningLevel::NORMAL); + notification->SetSystemPriority(); + if (message_center->FindVisibleNotificationById(kNotificationId)) { + message_center->UpdateNotification(kNotificationId, + std::move(notification)); + } else { + message_center->AddNotification(std::move(notification)); + } + has_notification_been_shown_ = true; +} + +base::string16 SessionLimitNotificationController::ComposeNotificationTitle() + const { + return l10n_util::GetStringFUTF16( + IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT_TITLE, + ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, + ui::TimeFormat::LENGTH_SHORT, + model_->remaining_session_time())); +} + +} // namespace ash
diff --git a/ash/system/session/session_limit_notification_controller.h b/ash/system/session/session_limit_notification_controller.h new file mode 100644 index 0000000..a5c4d83 --- /dev/null +++ b/ash/system/session/session_limit_notification_controller.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SYSTEM_SESSION_SESSION_LIMIT_NOTIFICATION_CONTROLLER_H_ +#define ASH_SYSTEM_SESSION_SESSION_LIMIT_NOTIFICATION_CONTROLLER_H_ + +#include "ash/system/model/session_length_limit_model.h" + +namespace ash { + +class ASH_EXPORT SessionLimitNotificationController + : public SessionLengthLimitModel::Observer { + public: + SessionLimitNotificationController(); + ~SessionLimitNotificationController() override; + + // SessionLengthLimitModel::Observer: + void OnSessionLengthLimitUpdated() override; + + private: + friend class SessionLimitNotificationControllerTest; + + void UpdateNotification(); + + base::string16 ComposeNotificationTitle() const; + + static const char kNotificationId[]; + + // Unowned. + SessionLengthLimitModel* const model_; + + // LimitState of the last time OnSessionLengthLimitUpdate() is called. + SessionLengthLimitModel::LimitState last_limit_state_ = + SessionLengthLimitModel::LIMIT_NONE; + + bool has_notification_been_shown_ = false; + + DISALLOW_COPY_AND_ASSIGN(SessionLimitNotificationController); +}; + +} // namespace ash + +#endif // ASH_SYSTEM_SESSION_SESSION_LIMIT_NOTIFICATION_CONTROLLER_H_
diff --git a/ash/system/session/session_limit_notification_controller_unittest.cc b/ash/system/session/session_limit_notification_controller_unittest.cc new file mode 100644 index 0000000..5441f311 --- /dev/null +++ b/ash/system/session/session_limit_notification_controller_unittest.cc
@@ -0,0 +1,204 @@ +// Copyright 2018 The Chromium 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 "ash/system/session/session_limit_notification_controller.h" + +#include "ash/session/session_controller.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/public/cpp/notification.h" +#include "ui/message_center/public/cpp/notification_types.h" + +namespace ash { + +class SessionLimitNotificationControllerTest : public AshTestBase { + public: + SessionLimitNotificationControllerTest() = default; + ~SessionLimitNotificationControllerTest() override = default; + + protected: + static const int kNotificationThresholdInMinutes = 60; + + void UpdateSessionLengthLimitInMin(int mins) { + Shell::Get()->session_controller()->SetSessionLengthLimit( + base::TimeDelta::FromMinutes(mins), base::TimeTicks::Now()); + } + + message_center::Notification* GetNotification() { + const message_center::NotificationList::Notifications& notifications = + message_center::MessageCenter::Get()->GetVisibleNotifications(); + for (message_center::NotificationList::Notifications::const_iterator iter = + notifications.begin(); + iter != notifications.end(); ++iter) { + if ((*iter)->id() == SessionLimitNotificationController::kNotificationId) + return *iter; + } + return nullptr; + } + + void ClearSessionLengthLimit() { + Shell::Get()->session_controller()->SetSessionLengthLimit( + base::TimeDelta(), base::TimeTicks()); + } + + void RemoveNotification() { + message_center::MessageCenter::Get()->RemoveNotification( + SessionLimitNotificationController::kNotificationId, + false /* by_user */); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SessionLimitNotificationControllerTest); +}; + +TEST_F(SessionLimitNotificationControllerTest, Notification) { + // No notifications when no session limit. + EXPECT_FALSE(GetNotification()); + + // Limit is 15 min. + UpdateSessionLengthLimitInMin(15); + message_center::Notification* notification = GetNotification(); + EXPECT_TRUE(notification); + EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); + base::string16 first_title = notification->title(); + // Should read the content. + EXPECT_TRUE(notification->rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + + // Limit is 10 min. + UpdateSessionLengthLimitInMin(10); + notification = GetNotification(); + EXPECT_TRUE(notification); + EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); + // The title should be updated. + EXPECT_NE(first_title, notification->title()); + // Should NOT read, because just update the remaining time. + EXPECT_FALSE(notification->rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + + // Limit is 3 min. + UpdateSessionLengthLimitInMin(3); + notification = GetNotification(); + EXPECT_TRUE(notification); + EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); + // Should read the content again because the state has changed. + EXPECT_TRUE(notification->rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + + // Session length limit is updated to longer: 15 min. + UpdateSessionLengthLimitInMin(15); + notification = GetNotification(); + EXPECT_TRUE(notification); + EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); + // Should read again because an increase of the remaining time is noteworthy. + EXPECT_TRUE(notification->rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + + // Clears the limit: the notification should be gone. + ClearSessionLengthLimit(); + EXPECT_FALSE(GetNotification()); +} + +TEST_F(SessionLimitNotificationControllerTest, FarOffNotificationHidden) { + // Test that notification is not shown if the session end time is far off into + // the future, but an item should be present in system tray bubble. + + // Notification should be absent. + EXPECT_FALSE(GetNotification()); + UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes + 10); + EXPECT_FALSE(GetNotification()); + + RemoveNotification(); +} + +TEST_F(SessionLimitNotificationControllerTest, + NotificationShownAfterThreshold) { + // Test that a notification is shown when time runs under the notification + // display threshold. + + // Start with a generous session length. We should not get a notification. + UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes + 10); + EXPECT_FALSE(GetNotification()); + + // Update the session length now, without changing limit_state_. + UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes - 1); + + // A notification should be displayed now. + EXPECT_TRUE(GetNotification()); + + RemoveNotification(); +} + +TEST_F(SessionLimitNotificationControllerTest, RemoveNotification) { + // Limit is 15 min. + UpdateSessionLengthLimitInMin(15); + EXPECT_TRUE(GetNotification()); + + // Removes the notification. + RemoveNotification(); + EXPECT_FALSE(GetNotification()); + + // Limit is 10 min. The notification should not re-appear. + UpdateSessionLengthLimitInMin(10); + EXPECT_FALSE(GetNotification()); + + // Limit is 3 min. The notification should re-appear and should be re-read + // because of state change. + UpdateSessionLengthLimitInMin(3); + message_center::Notification* notification = GetNotification(); + EXPECT_TRUE(notification); + EXPECT_TRUE(notification->rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + + RemoveNotification(); + + // Session length limit is updated to longer state. Notification should + // re-appear and be re-read. + UpdateSessionLengthLimitInMin(15); + notification = GetNotification(); + EXPECT_TRUE(notification); + EXPECT_TRUE(notification->rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + + RemoveNotification(); +} + +class SessionLimitNotificationControllerLoginTest + : public SessionLimitNotificationControllerTest { + public: + SessionLimitNotificationControllerLoginTest() { set_start_session(false); } + + private: + DISALLOW_COPY_AND_ASSIGN(SessionLimitNotificationControllerLoginTest); +}; + +TEST_F(SessionLimitNotificationControllerLoginTest, + NotificationShownAfterLogin) { + UpdateSessionLengthLimitInMin(15); + + // No notifications before login. + EXPECT_FALSE(GetNotification()); + + // Notification is shown after login. + CreateUserSessions(1); + EXPECT_TRUE(GetNotification()); + + RemoveNotification(); +} + +TEST_F(SessionLimitNotificationControllerLoginTest, + FarOffNotificationHiddenAfterLogin) { + // Test that notification is not shown if the session end time is far off into + // the future, but an item should be present in system tray bubble. + + // Notification should be absent. + UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes + 10); + CreateUserSessions(1); + EXPECT_FALSE(GetNotification()); + + RemoveNotification(); +} + +} // namespace ash
diff --git a/ash/system/session/tray_session_length_limit.cc b/ash/system/session/tray_session_length_limit.cc index a73ba9c5..9a557ec 100644 --- a/ash/system/session/tray_session_length_limit.cc +++ b/ash/system/session/tray_session_length_limit.cc
@@ -16,33 +16,15 @@ #include "ash/system/model/system_tray_model.h" #include "ash/system/tray/label_tray_view.h" #include "ash/system/tray/system_tray.h" -#include "ash/system/tray/system_tray_notifier.h" #include "ash/system/tray/tray_constants.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/time_format.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/public/cpp/notification.h" #include "ui/views/view.h" namespace ash { -namespace { - -const char kNotifierSessionLengthTimeout[] = "ash.session-length-timeout"; - -// A notification is shown to the user only if the remaining session time falls -// under this threshold. e.g. If the user has several days left in their -// session, there is no use displaying a notification right now. -constexpr base::TimeDelta kNotificationThreshold = - base::TimeDelta::FromMinutes(60); - -} // namespace - -// static -const char TraySessionLengthLimit::kNotificationId[] = - "chrome://session/timeout"; TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray) : SystemTrayItem(system_tray, UMA_SESSION_LENGTH_LIMIT), @@ -71,68 +53,11 @@ } void TraySessionLengthLimit::OnSessionLengthLimitUpdated() { - // Don't show notification or tray item until the user is logged in. + // Don't show tray item until the user is logged in. if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted()) return; - UpdateNotification(); UpdateTrayBubbleView(); - last_limit_state_ = model_->limit_state(); -} - -void TraySessionLengthLimit::UpdateNotification() { - message_center::MessageCenter* message_center = - message_center::MessageCenter::Get(); - - // If state hasn't changed and the notification has already been acknowledged, - // we won't re-create it. We consider a notification to be acknowledged if it - // was shown before, but is no longer visible. - if (model_->limit_state() == last_limit_state_ && - has_notification_been_shown_ && - !message_center->FindVisibleNotificationById(kNotificationId)) { - return; - } - - // After state change, any possibly existing notification is removed to make - // sure it is re-shown even if it had been acknowledged by the user before - // (and in the rare case of state change towards LIMIT_NONE to make the - // notification disappear). - if (model_->limit_state() != last_limit_state_ && - message_center->FindVisibleNotificationById(kNotificationId)) { - message_center::MessageCenter::Get()->RemoveNotification( - kNotificationId, false /* by_user */); - } - - // If the session is unlimited or if the remaining time is too far off into - // the future, there is nothing more to do. - if (model_->limit_state() == SessionLengthLimitModel::LIMIT_NONE || - model_->remaining_session_time() > kNotificationThreshold) { - return; - } - - message_center::RichNotificationData data; - data.should_make_spoken_feedback_for_popup_updates = - (model_->limit_state() != last_limit_state_); - std::unique_ptr<message_center::Notification> notification = - message_center::Notification::CreateSystemNotification( - message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, - ComposeNotificationTitle(), - l10n_util::GetStringUTF16( - IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT_MESSAGE), - gfx::Image(), base::string16() /* display_source */, GURL(), - message_center::NotifierId( - message_center::NotifierId::SYSTEM_COMPONENT, - kNotifierSessionLengthTimeout), - data, nullptr /* delegate */, kNotificationTimerIcon, - message_center::SystemNotificationWarningLevel::NORMAL); - notification->SetSystemPriority(); - if (message_center->FindVisibleNotificationById(kNotificationId)) { - message_center->UpdateNotification(kNotificationId, - std::move(notification)); - } else { - message_center->AddNotification(std::move(notification)); - } - has_notification_been_shown_ = true; } void TraySessionLengthLimit::UpdateTrayBubbleView() const { @@ -145,14 +70,6 @@ tray_bubble_view_->Layout(); } -base::string16 TraySessionLengthLimit::ComposeNotificationTitle() const { - return l10n_util::GetStringFUTF16( - IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT_TITLE, - ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, - ui::TimeFormat::LENGTH_SHORT, - model_->remaining_session_time())); -} - base::string16 TraySessionLengthLimit::ComposeTrayBubbleMessage() const { return l10n_util::GetStringFUTF16( IDS_ASH_STATUS_TRAY_BUBBLE_SESSION_LENGTH_LIMIT,
diff --git a/ash/system/session/tray_session_length_limit.h b/ash/system/session/tray_session_length_limit.h index 52d5c0c..454622d2b 100644 --- a/ash/system/session/tray_session_length_limit.h +++ b/ash/system/session/tray_session_length_limit.h
@@ -34,24 +34,13 @@ private: friend class TraySessionLengthLimitTest; - static const char kNotificationId[]; - - void UpdateNotification(); void UpdateTrayBubbleView() const; - // These require that the state has been updated before. - base::string16 ComposeNotificationTitle() const; base::string16 ComposeTrayBubbleMessage() const; // Unowned. SessionLengthLimitModel* const model_; - // LimitState of the last time OnSessionLengthLimitUpdate() is called. - SessionLengthLimitModel::LimitState last_limit_state_ = - SessionLengthLimitModel::LIMIT_NONE; - - bool has_notification_been_shown_ = false; - LabelTrayView* tray_bubble_view_ = nullptr; DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimit);
diff --git a/ash/system/session/tray_session_length_limit_unittest.cc b/ash/system/session/tray_session_length_limit_unittest.cc index 4ea7183d..b83cafd 100644 --- a/ash/system/session/tray_session_length_limit_unittest.cc +++ b/ash/system/session/tray_session_length_limit_unittest.cc
@@ -11,9 +11,6 @@ #include "ash/system/tray/system_tray_test_api.h" #include "ash/test/ash_test_base.h" #include "base/time/time.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/public/cpp/notification.h" -#include "ui/message_center/public/cpp/notification_types.h" namespace ash { @@ -23,8 +20,6 @@ ~TraySessionLengthLimitTest() override = default; protected: - static const int kNotificationThresholdInMinutes = 60; - LabelTrayView* GetSessionLengthLimitTrayView() { return SystemTrayTestApi(GetPrimarySystemTray()) .tray_session_length_limit() @@ -36,28 +31,6 @@ base::TimeDelta::FromMinutes(mins), base::TimeTicks::Now()); } - message_center::Notification* GetNotification() { - const message_center::NotificationList::Notifications& notifications = - message_center::MessageCenter::Get()->GetVisibleNotifications(); - for (message_center::NotificationList::Notifications::const_iterator iter = - notifications.begin(); - iter != notifications.end(); ++iter) { - if ((*iter)->id() == TraySessionLengthLimit::kNotificationId) - return *iter; - } - return nullptr; - } - - void ClearSessionLengthLimit() { - Shell::Get()->session_controller()->SetSessionLengthLimit( - base::TimeDelta(), base::TimeTicks()); - } - - void RemoveNotification() { - message_center::MessageCenter::Get()->RemoveNotification( - TraySessionLengthLimit::kNotificationId, false /* by_user */); - } - private: DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimitTest); }; @@ -84,162 +57,4 @@ system_tray->CloseBubble(); } -TEST_F(TraySessionLengthLimitTest, Notification) { - // No notifications when no session limit. - EXPECT_FALSE(GetNotification()); - - // Limit is 15 min. - UpdateSessionLengthLimitInMin(15); - message_center::Notification* notification = GetNotification(); - EXPECT_TRUE(notification); - EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); - base::string16 first_title = notification->title(); - // Should read the content. - EXPECT_TRUE(notification->rich_notification_data() - .should_make_spoken_feedback_for_popup_updates); - - // Limit is 10 min. - UpdateSessionLengthLimitInMin(10); - notification = GetNotification(); - EXPECT_TRUE(notification); - EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); - // The title should be updated. - EXPECT_NE(first_title, notification->title()); - // Should NOT read, because just update the remaining time. - EXPECT_FALSE(notification->rich_notification_data() - .should_make_spoken_feedback_for_popup_updates); - - // Limit is 3 min. - UpdateSessionLengthLimitInMin(3); - notification = GetNotification(); - EXPECT_TRUE(notification); - EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); - // Should read the content again because the state has changed. - EXPECT_TRUE(notification->rich_notification_data() - .should_make_spoken_feedback_for_popup_updates); - - // Session length limit is updated to longer: 15 min. - UpdateSessionLengthLimitInMin(15); - notification = GetNotification(); - EXPECT_TRUE(notification); - EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority()); - // Should read again because an increase of the remaining time is noteworthy. - EXPECT_TRUE(notification->rich_notification_data() - .should_make_spoken_feedback_for_popup_updates); - - // Clears the limit: the notification should be gone. - ClearSessionLengthLimit(); - EXPECT_FALSE(GetNotification()); -} - -TEST_F(TraySessionLengthLimitTest, FarOffNotificationHidden) { - // Test that notification is not shown if the session end time is far off into - // the future, but an item should be present in system tray bubble. - - // Notification should be absent. - EXPECT_FALSE(GetNotification()); - UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes + 10); - EXPECT_FALSE(GetNotification()); - - // However, an item should be present in the system tray bubble. - SystemTray* system_tray = GetPrimarySystemTray(); - system_tray->ShowDefaultView(BUBBLE_CREATE_NEW, false /* show_by_click */); - ASSERT_TRUE(GetSessionLengthLimitTrayView()); - EXPECT_TRUE(GetSessionLengthLimitTrayView()->visible()); - system_tray->CloseBubble(); - - RemoveNotification(); -} - -TEST_F(TraySessionLengthLimitTest, NotificationShownAfterThreshold) { - // Test that a notification is shown when time runs under the notification - // display threshold. - - // Start with a generous session length. We should not get a notification. - UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes + 10); - EXPECT_FALSE(GetNotification()); - - // Update the session length now, without changing limit_state_. - UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes - 1); - - // A notification should be displayed now. - EXPECT_TRUE(GetNotification()); - - RemoveNotification(); -} - -TEST_F(TraySessionLengthLimitTest, RemoveNotification) { - // Limit is 15 min. - UpdateSessionLengthLimitInMin(15); - EXPECT_TRUE(GetNotification()); - - // Removes the notification. - RemoveNotification(); - EXPECT_FALSE(GetNotification()); - - // Limit is 10 min. The notification should not re-appear. - UpdateSessionLengthLimitInMin(10); - EXPECT_FALSE(GetNotification()); - - // Limit is 3 min. The notification should re-appear and should be re-read - // because of state change. - UpdateSessionLengthLimitInMin(3); - message_center::Notification* notification = GetNotification(); - EXPECT_TRUE(notification); - EXPECT_TRUE(notification->rich_notification_data() - .should_make_spoken_feedback_for_popup_updates); - - RemoveNotification(); - - // Session length limit is updated to longer state. Notification should - // re-appear and be re-read. - UpdateSessionLengthLimitInMin(15); - notification = GetNotification(); - EXPECT_TRUE(notification); - EXPECT_TRUE(notification->rich_notification_data() - .should_make_spoken_feedback_for_popup_updates); - - RemoveNotification(); -} - -class TraySessionLengthLimitLoginTest : public TraySessionLengthLimitTest { - public: - TraySessionLengthLimitLoginTest() { set_start_session(false); } - - private: - DISALLOW_COPY_AND_ASSIGN(TraySessionLengthLimitLoginTest); -}; - -TEST_F(TraySessionLengthLimitLoginTest, NotificationShownAfterLogin) { - UpdateSessionLengthLimitInMin(15); - - // No notifications before login. - EXPECT_FALSE(GetNotification()); - - // Notification is shown after login. - CreateUserSessions(1); - EXPECT_TRUE(GetNotification()); - - RemoveNotification(); -} - -TEST_F(TraySessionLengthLimitLoginTest, FarOffNotificationHiddenAfterLogin) { - // Test that notification is not shown if the session end time is far off into - // the future, but an item should be present in system tray bubble. - - // Notification should be absent. - UpdateSessionLengthLimitInMin(kNotificationThresholdInMinutes + 10); - CreateUserSessions(1); - EXPECT_FALSE(GetNotification()); - - // However, an item should be present in the system tray bubble. - SystemTray* system_tray = GetPrimarySystemTray(); - system_tray->ShowDefaultView(BUBBLE_CREATE_NEW, false /* show_by_click */); - ASSERT_TRUE(GetSessionLengthLimitTrayView()); - EXPECT_TRUE(GetSessionLengthLimitTrayView()->visible()); - system_tray->CloseBubble(); - - RemoveNotification(); -} - } // namespace ash
diff --git a/ash/system/system_notification_controller.cc b/ash/system/system_notification_controller.cc index 83a6ac6..7804e9d 100644 --- a/ash/system/system_notification_controller.cc +++ b/ash/system/system_notification_controller.cc
@@ -7,6 +7,7 @@ #include "ash/system/caps_lock_notification_controller.h" #include "ash/system/power/power_notification_controller.h" #include "ash/system/screen_security/screen_security_notification_controller.h" +#include "ash/system/session/session_limit_notification_controller.h" #include "ash/system/supervised/supervised_notification_controller.h" #include "ui/message_center/message_center.h" @@ -18,6 +19,7 @@ message_center::MessageCenter::Get())), screen_security_( std::make_unique<ScreenSecurityNotificationController>()), + session_limit_(std::make_unique<SessionLimitNotificationController>()), supervised_(std::make_unique<SupervisedNotificationController>()) {} SystemNotificationController::~SystemNotificationController() = default;
diff --git a/ash/system/system_notification_controller.h b/ash/system/system_notification_controller.h index bfeaa7d7..be7e9b9 100644 --- a/ash/system/system_notification_controller.h +++ b/ash/system/system_notification_controller.h
@@ -14,6 +14,7 @@ class CapsLockNotificationController; class PowerNotificationController; class ScreenSecurityNotificationController; +class SessionLimitNotificationController; class SupervisedNotificationController; // Class that owns individual notification controllers. @@ -26,6 +27,7 @@ const std::unique_ptr<CapsLockNotificationController> caps_lock_; const std::unique_ptr<PowerNotificationController> power_; const std::unique_ptr<ScreenSecurityNotificationController> screen_security_; + const std::unique_ptr<SessionLimitNotificationController> session_limit_; const std::unique_ptr<SupervisedNotificationController> supervised_; DISALLOW_COPY_AND_ASSIGN(SystemNotificationController);
diff --git a/ash/system/tray/system_tray_notifier.cc b/ash/system/tray/system_tray_notifier.cc index 7e995d4..7ccd33a 100644 --- a/ash/system/tray/system_tray_notifier.cc +++ b/ash/system/tray/system_tray_notifier.cc
@@ -7,7 +7,6 @@ #include "ash/system/bluetooth/bluetooth_observer.h" #include "ash/system/ime/ime_observer.h" #include "ash/system/network/network_observer.h" -#include "ash/system/network/network_portal_detector_observer.h" #include "ash/system/screen_security/screen_capture_observer.h" #include "ash/system/screen_security/screen_share_observer.h" #include "ash/system/system_tray_focus_observer.h" @@ -69,22 +68,6 @@ observer.RequestToggleWifi(); } -void SystemTrayNotifier::AddNetworkPortalDetectorObserver( - NetworkPortalDetectorObserver* observer) { - network_portal_detector_observers_.AddObserver(observer); -} - -void SystemTrayNotifier::RemoveNetworkPortalDetectorObserver( - NetworkPortalDetectorObserver* observer) { - network_portal_detector_observers_.RemoveObserver(observer); -} - -void SystemTrayNotifier::NotifyOnCaptivePortalDetected( - const std::string& guid) { - for (auto& observer : network_portal_detector_observers_) - observer.OnCaptivePortalDetected(guid); -} - void SystemTrayNotifier::AddScreenCaptureObserver( ScreenCaptureObserver* observer) { screen_capture_observers_.AddObserver(observer);
diff --git a/ash/system/tray/system_tray_notifier.h b/ash/system/tray/system_tray_notifier.h index 9b8cf88..1a916f5 100644 --- a/ash/system/tray/system_tray_notifier.h +++ b/ash/system/tray/system_tray_notifier.h
@@ -19,7 +19,6 @@ class BluetoothObserver; class IMEObserver; class NetworkObserver; -class NetworkPortalDetectorObserver; class ScreenCaptureObserver; class ScreenShareObserver; class SystemTrayFocusObserver; @@ -52,13 +51,6 @@ void RemoveNetworkObserver(NetworkObserver* observer); void NotifyRequestToggleWifi(); - // Network portal detector. - void AddNetworkPortalDetectorObserver( - NetworkPortalDetectorObserver* observer); - void RemoveNetworkPortalDetectorObserver( - NetworkPortalDetectorObserver* observer); - void NotifyOnCaptivePortalDetected(const std::string& guid); - // Screen capture. void AddScreenCaptureObserver(ScreenCaptureObserver* observer); void RemoveScreenCaptureObserver(ScreenCaptureObserver* observer); @@ -87,8 +79,6 @@ base::ObserverList<BluetoothObserver> bluetooth_observers_; base::ObserverList<IMEObserver> ime_observers_; base::ObserverList<NetworkObserver> network_observers_; - base::ObserverList<NetworkPortalDetectorObserver> - network_portal_detector_observers_; base::ObserverList<ScreenCaptureObserver> screen_capture_observers_; base::ObserverList<ScreenShareObserver> screen_share_observers_; base::ObserverList<SystemTrayFocusObserver> system_tray_focus_observers_;
diff --git a/base/BUILD.gn b/base/BUILD.gn index 3c7062e2..e01eed4 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1324,10 +1324,15 @@ "files/memory_mapped_file_posix.cc", "fuchsia/async_dispatcher.cc", "fuchsia/async_dispatcher.h", + "fuchsia/component_context.cc", + "fuchsia/component_context.h", "fuchsia/default_job.cc", "fuchsia/default_job.h", + "fuchsia/fidl_interface_request.cc", + "fuchsia/fidl_interface_request.h", "fuchsia/fuchsia_logging.cc", "fuchsia/fuchsia_logging.h", + "fuchsia/scoped_zx_handle.cc", "fuchsia/scoped_zx_handle.h", "memory/platform_shared_memory_region_fuchsia.cc", "memory/protected_memory_posix.cc", @@ -1378,7 +1383,11 @@ libs = [ "launchpad" ] public_deps += [ "//third_party/fuchsia-sdk:async" ] - deps += [ "//third_party/fuchsia-sdk:async_default" ] + deps += [ + "//third_party/fuchsia-sdk:async_default", + "//third_party/fuchsia-sdk:fdio", + "//third_party/fuchsia-sdk:zx", + ] } else { sources += [ "message_loop/message_pump_libevent.cc",
diff --git a/base/fuchsia/component_context.cc b/base/fuchsia/component_context.cc new file mode 100644 index 0000000..2c26305e --- /dev/null +++ b/base/fuchsia/component_context.cc
@@ -0,0 +1,47 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/fuchsia/component_context.h" + +#include <fdio/util.h> + +#include "base/no_destructor.h" + +namespace base { +namespace fuchsia { + +namespace { + +// static +ScopedZxHandle ConnectToServiceRoot() { + ScopedZxHandle h1; + ScopedZxHandle h2; + zx_status_t result = zx_channel_create(0, h1.receive(), h2.receive()); + ZX_CHECK(result == ZX_OK, result) << "zx_channel_create()"; + result = fdio_service_connect("/svc/.", h1.release()); + ZX_CHECK(result == ZX_OK, result) << "Failed to open /svc"; + return h2; +} + +} // namespace + +ComponentContext::ComponentContext() : service_root_(ConnectToServiceRoot()) {} +ComponentContext::~ComponentContext() = default; + +// static +ComponentContext* ComponentContext::GetDefault() { + static base::NoDestructor<ComponentContext> component_context; + return component_context.get(); +} + +void ComponentContext::ConnectToService(FidlInterfaceRequest request) { + DCHECK(request.is_valid()); + zx_status_t result = + fdio_service_connect_at(service_root_.get(), request.interface_name(), + request.TakeChannel().release()); + ZX_CHECK(result == ZX_OK, result) << "fdio_service_connect_at()"; +} + +} // namespace fuchsia +} // namespace base \ No newline at end of file
diff --git a/base/fuchsia/component_context.h b/base/fuchsia/component_context.h new file mode 100644 index 0000000..5de4da3 --- /dev/null +++ b/base/fuchsia/component_context.h
@@ -0,0 +1,68 @@ +// Copyright 2018 The Chromium 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 BASE_FUCHSIA_COMPONENT_CONTEXT_H_ +#define BASE_FUCHSIA_COMPONENT_CONTEXT_H_ + +#include "base/fuchsia/fidl_interface_request.h" +#include "base/fuchsia/scoped_zx_handle.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" + +namespace fidl { + +template <typename Interface> +class InterfacePtr; + +template <typename Interface> +class SynchronousInterfacePtr; + +} // namespace fidl + +namespace base { +namespace fuchsia { + +// Provides access to the component's environment and allows it to publish +// outgoing services. +class ComponentContext { + public: + ComponentContext(); + ~ComponentContext(); + + // Returns default ComponentContext instance for the current process. The + // default context uses /srv namespace to connect to environment services and + // publishes outgoing services to the directory provided by the process + // creator. + static ComponentContext* GetDefault(); + + // Satisfies the interface |request| by binding the channel to a service. + void ConnectToService(FidlInterfaceRequest request); + + // Same as above, but returns interface pointer instead of taking a request. + template <typename Interface> + fidl::InterfacePtr<Interface> ConnectToService() { + fidl::InterfacePtr<Interface> result; + ConnectToService(FidlInterfaceRequest(&result)); + return result; + } + + // Connects to an environment service and returns synchronous interface + // implementation. + template <typename Interface> + fidl::SynchronousInterfacePtr<Interface> ConnectToServiceSync() { + fidl::SynchronousInterfacePtr<Interface> result; + ConnectToService(FidlInterfaceRequest(&result)); + return result; + } + + private: + ScopedZxHandle service_root_; + + DISALLOW_COPY_AND_ASSIGN(ComponentContext); +}; + +} // namespace fuchsia +} // namespace base + +#endif // BASE_FUCHSIA_COMPONENT_CONTEXT_H_ \ No newline at end of file
diff --git a/base/fuchsia/fidl_interface_request.cc b/base/fuchsia/fidl_interface_request.cc new file mode 100644 index 0000000..34d6d32 --- /dev/null +++ b/base/fuchsia/fidl_interface_request.cc
@@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/fuchsia/fidl_interface_request.h" + +namespace base { +namespace fuchsia { + +FidlInterfaceRequest::FidlInterfaceRequest(FidlInterfaceRequest&& moved) = + default; + +FidlInterfaceRequest::FidlInterfaceRequest(const char* interface_name, + ScopedZxHandle channel) + : interface_name_(interface_name), channel_(std::move(channel)) {} +FidlInterfaceRequest::~FidlInterfaceRequest() = default; + +ScopedZxHandle FidlInterfaceRequest::TakeChannel() { + DCHECK(channel_); + return std::move(channel_); +} + +} // namespace fuchsia +} // namespace base \ No newline at end of file
diff --git a/base/fuchsia/fidl_interface_request.h b/base/fuchsia/fidl_interface_request.h new file mode 100644 index 0000000..b034c5f --- /dev/null +++ b/base/fuchsia/fidl_interface_request.h
@@ -0,0 +1,76 @@ +// Copyright 2018 The Chromium 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 BASE_FUCHSIA_FIDL_INTERFACE_REQUEST_H_ +#define BASE_FUCHSIA_FIDL_INTERFACE_REQUEST_H_ + +#include "base/fuchsia/scoped_zx_handle.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" + +namespace fidl { + +template <typename Interface> +class InterfaceRequest; + +template <typename Interface> +class InterfacePtr; + +template <typename Interface> +class SynchronousInterfacePtr; + +} // namespace fidl + +namespace base { +namespace fuchsia { + +// A request for a FIDL interface. FidlInterfaceRequest contains interface name +// and channel handle. Interface consumers create FidlInterfaceRequest when they +// need to connect to a service. FidlInterfaceRequest is resolved when the +// channel is passed to the service implementation, e.g. through +// ComponentContext. +class FidlInterfaceRequest { + public: + template <typename Interface> + explicit FidlInterfaceRequest(fidl::InterfaceRequest<Interface> request) + : FidlInterfaceRequest( + Interface::Name_, + ScopedZxHandle::FromZxChannel(request.TakeChannel())) {} + + // Creates a new request for |Interface| and binds the client end to the + // |stub|. |stub| can be used immediately after the request is created, even + // before the request is passed to the service that implements the interface. + template <typename Interface> + explicit FidlInterfaceRequest(fidl::InterfacePtr<Interface>* stub) + : FidlInterfaceRequest(stub->NewRequest()) {} + + template <typename Interface> + explicit FidlInterfaceRequest(fidl::SynchronousInterfacePtr<Interface>* stub) + : FidlInterfaceRequest(stub->NewRequest()) {} + + FidlInterfaceRequest(FidlInterfaceRequest&&); + ~FidlInterfaceRequest(); + + bool is_valid() const { return interface_name_ && channel_; } + + const char* interface_name() const { return interface_name_; } + + // Extracts the channel handle to be passed to service implementation. The + // request becomes invalid after this call, i.e. TakeChannel() can be called + // only once. + ScopedZxHandle TakeChannel(); + + private: + FidlInterfaceRequest(const char* interface_name, ScopedZxHandle channel); + + const char* interface_name_; + ScopedZxHandle channel_; + + DISALLOW_COPY_AND_ASSIGN(FidlInterfaceRequest); +}; + +} // namespace fuchsia +} // namespace base + +#endif // BASE_FUCHSIA_FIDL_INTERFACE_REQUEST_H_ \ No newline at end of file
diff --git a/base/fuchsia/scoped_zx_handle.cc b/base/fuchsia/scoped_zx_handle.cc new file mode 100644 index 0000000..7379b2f --- /dev/null +++ b/base/fuchsia/scoped_zx_handle.cc
@@ -0,0 +1,16 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/fuchsia/scoped_zx_handle.h" + +#include <lib/zx/channel.h> + +namespace base { + +// static +ScopedZxHandle ScopedZxHandle::FromZxChannel(zx::channel channel) { + return ScopedZxHandle(channel.release()); +} + +} // namespace base
diff --git a/base/fuchsia/scoped_zx_handle.h b/base/fuchsia/scoped_zx_handle.h index fd42bc7..2e30827 100644 --- a/base/fuchsia/scoped_zx_handle.h +++ b/base/fuchsia/scoped_zx_handle.h
@@ -11,6 +11,10 @@ #include "base/fuchsia/fuchsia_logging.h" #include "base/scoped_generic.h" +namespace zx { +class channel; +} + namespace base { namespace internal { @@ -25,8 +29,17 @@ } // namespace internal -using ScopedZxHandle = - ScopedGeneric<zx_handle_t, internal::ScopedZxHandleTraits>; +class ScopedZxHandle + : public ScopedGeneric<zx_handle_t, internal::ScopedZxHandleTraits> { + public: + ScopedZxHandle() = default; + explicit ScopedZxHandle(zx_handle_t value) : ScopedGeneric(value) {} + + explicit operator bool() const { return get() != ZX_HANDLE_INVALID; } + + // Helper to converts zx::channel to ScopedZxHandle. + static ScopedZxHandle FromZxChannel(zx::channel channel); +}; } // namespace base
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index b18b030..6192031 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc
@@ -543,6 +543,14 @@ WriteAsciiImpl(true, "\n", output); } +void Histogram::ValidateHistogramContents() const { + CHECK(unlogged_samples_); + CHECK(unlogged_samples_->bucket_ranges()); + CHECK(logged_samples_); + CHECK(logged_samples_->bucket_ranges()); + CHECK_NE(0U, logged_samples_->id()); +} + void Histogram::SerializeInfoImpl(Pickle* pickle) const { DCHECK(bucket_ranges()->HasValidChecksum()); pickle->WriteString(histogram_name());
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 4c4b150e..91d2547 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h
@@ -208,6 +208,10 @@ void WriteHTMLGraph(std::string* output) const override; void WriteAscii(std::string* output) const override; + // Validates the histogram contents and CHECKs on errors. + // TODO(bcwhite): Remove this after https://crbug/836875. + void ValidateHistogramContents() const override; + protected: // This class, defined entirely within the .cc file, contains all the // common logic for building a Histogram and can be overridden by more
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc index d8ae483b..defe808 100644 --- a/base/metrics/histogram_base.cc +++ b/base/metrics/histogram_base.cc
@@ -130,6 +130,8 @@ return NO_INCONSISTENCIES; } +void HistogramBase::ValidateHistogramContents() const {} + void HistogramBase::WriteJSON(std::string* output, JSONVerbosityLevel verbosity_level) const { Count count;
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h index 1971a65..f379880 100644 --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h
@@ -230,6 +230,9 @@ virtual void WriteHTMLGraph(std::string* output) const = 0; virtual void WriteAscii(std::string* output) const = 0; + // TODO(bcwhite): Remove this after https://crbug/836875. + virtual void ValidateHistogramContents() const; + // Produce a JSON representation of the histogram with |verbosity_level| as // the serialization verbosity. This is implemented with the help of // GetParameters and GetCountAndBucketData; overwrite them to customize the
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc index 705e325..c1b804e 100644 --- a/base/metrics/histogram_snapshot_manager.cc +++ b/base/metrics/histogram_snapshot_manager.cc
@@ -55,11 +55,13 @@ } void HistogramSnapshotManager::PrepareDelta(HistogramBase* histogram) { + histogram->ValidateHistogramContents(); PrepareSamples(histogram, histogram->SnapshotDelta()); } void HistogramSnapshotManager::PrepareFinalDelta( const HistogramBase* histogram) { + histogram->ValidateHistogramContents(); PrepareSamples(histogram, histogram->SnapshotFinalDelta()); }
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc index 010ee9c..c6bcd2d 100644 --- a/base/observer_list_unittest.cc +++ b/base/observer_list_unittest.cc
@@ -20,6 +20,7 @@ #include "base/synchronization/waitable_event.h" #include "base/task_scheduler/post_task.h" #include "base/task_scheduler/task_scheduler.h" +#include "base/test/gtest_util.h" #include "base/test/scoped_task_environment.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_restrictions.h" @@ -1244,18 +1245,14 @@ Adder a(1); non_reentrant_observer_list.AddObserver(&a); - ::testing::StrictMock<MockLogAssertHandler> handler; - EXPECT_CALL(handler, HandleLogAssert(_, _, _, _)).Times(1); - { - logging::ScopedLogAssertHandler scoped_handler_b(base::BindRepeating( - &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler))); + EXPECT_DCHECK_DEATH({ for (const Foo& a : non_reentrant_observer_list) { for (const Foo& b : non_reentrant_observer_list) { std::ignore = a; std::ignore = b; } } - } + }); } TEST(ObserverListTest, ReentrantObserverList) {
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc index 5d41b12f..a8cddf0 100644 --- a/base/profiler/stack_sampling_profiler.cc +++ b/base/profiler/stack_sampling_profiler.cc
@@ -235,14 +235,11 @@ // Get task runner that is usable from the sampling thread itself. scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread(); - // Finishes a collection and reports collected data via callback. Returns - // the new collection params, if a new collection should be started. The - // collection's |finished| waitable event will be signalled if no new params - // are available or |allow_collection_restart| is false. The |collection| + // Finishes a collection and reports collected data via callback. The + // collection's |finished| waitable event will be signalled. The |collection| // should already have been removed from |active_collections_| by the caller, // as this is needed to avoid flakyness in unit tests. - Optional<SamplingParams> FinishCollection(CollectionContext* collection, - bool allow_collection_restart); + void FinishCollection(CollectionContext* collection); // Records a single sample of a collection. void RecordSample(CollectionContext* collection); @@ -496,10 +493,8 @@ return Thread::task_runner(); } -Optional<StackSamplingProfiler::SamplingParams> -StackSamplingProfiler::SamplingThread::FinishCollection( - CollectionContext* collection, - bool allow_collection_restart) { +void StackSamplingProfiler::SamplingThread::FinishCollection( + CollectionContext* collection) { DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId()); DCHECK_EQ(0u, active_collections_.count(collection->profiler_id)); @@ -521,15 +516,10 @@ WaitableEvent* finished = collection->finished; // Run the associated callback, passing the collected profiles. - Optional<SamplingParams> new_params = callback.Run(std::move(profiles)); - if (!allow_collection_restart) - new_params.reset(); + callback.Run(std::move(profiles)); - // Signal that this collection is finished if it shouldn't be rescheduled. - if (!new_params.has_value()) - finished->Signal(); - - return new_params; + // Signal that this collection is finished. + finished->Signal(); } void StackSamplingProfiler::SamplingThread::RecordSample( @@ -620,7 +610,7 @@ size_t count = active_collections_.erase(id); DCHECK_EQ(1U, count); - FinishCollection(collection.get(), false); + FinishCollection(collection.get()); ScheduleShutdownIfIdle(); } @@ -660,22 +650,9 @@ size_t count = active_collections_.erase(id); DCHECK_EQ(1U, count); - // All capturing has completed so finish the collection. If no new params - // are returned, a new collection should not be started. - Optional<SamplingParams> new_params = FinishCollection(collection, true); - if (!new_params.has_value()) { - // By not adding it to the task queue, the collection will "expire" (i.e. - // no further work will be done). - ScheduleShutdownIfIdle(); - return; - } - - // Restart the collection with the new params. Keep the same id so the - // Stop() operation continues to work. - auto new_collection = std::make_unique<SamplingThread::CollectionContext>( - id, collection->target, new_params.value(), collection->callback, - collection->finished, std::move(collection->native_sampler)); - AddCollectionTask(std::move(new_collection)); + // All capturing has completed so finish the collection. + FinishCollection(collection); + ScheduleShutdownIfIdle(); } void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) {
diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h index c0493cf..2f9ade5 100644 --- a/base/profiler/stack_sampling_profiler.h +++ b/base/profiler/stack_sampling_profiler.h
@@ -16,7 +16,6 @@ #include "base/callback.h" #include "base/files/file_path.h" #include "base/macros.h" -#include "base/optional.h" #include "base/strings/string16.h" #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" @@ -216,18 +215,13 @@ // are move-only. Other threads, including the UI thread, may block on // callback completion so this should run as quickly as possible. // - // After collection completion, the callback may instruct the profiler to do - // additional collection(s) by returning a SamplingParams object to indicate - // collection should be started again. - // // IMPORTANT NOTE: The callback is invoked on a thread the profiler // constructs, rather than on the thread used to construct the profiler and // set the callback, and thus the callback must be callable on any thread. For // threads with message loops that create StackSamplingProfilers, posting a // task to the message loop with the moved (i.e. std::move) profiles is the // thread-safe callback implementation. - using CompletedCallback = - Callback<Optional<SamplingParams>(CallStackProfiles)>; + using CompletedCallback = Callback<void(CallStackProfiles)>; // Creates a profiler for the CURRENT thread that sends completed profiles // to |callback|. An optional |test_delegate| can be supplied by tests.
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc index 94b8a6e..8fc25c9 100644 --- a/base/profiler/stack_sampling_profiler_unittest.cc +++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -324,48 +324,19 @@ } // Called on the profiler thread when complete, to collect profiles. -Optional<StackSamplingProfiler::SamplingParams> SaveProfiles( - CallStackProfiles* profiles, - CallStackProfiles pending_profiles) { +void SaveProfiles(CallStackProfiles* profiles, + CallStackProfiles pending_profiles) { *profiles = std::move(pending_profiles); - return Optional<StackSamplingProfiler::SamplingParams>(); } // Called on the profiler thread when complete. Collects profiles produced by // the profiler, and signals an event to allow the main thread to know that that // the profiler is done. -Optional<StackSamplingProfiler::SamplingParams> SaveProfilesAndSignalEvent( - CallStackProfiles* profiles, - WaitableEvent* event, - CallStackProfiles pending_profiles) { +void SaveProfilesAndSignalEvent(CallStackProfiles* profiles, + WaitableEvent* event, + CallStackProfiles pending_profiles) { *profiles = std::move(pending_profiles); event->Signal(); - return Optional<StackSamplingProfiler::SamplingParams>(); -} - -// Similar to SaveProfilesAndSignalEvent(), but will schedule subsequent -// |extra_collection_count| collections. -Optional<StackSamplingProfiler::SamplingParams> SaveProfilesAndReschedule( - std::vector<CallStackProfiles>* profiles, - WaitableEvent* event, - size_t extra_collection_count, - CallStackProfiles pending_profiles) { - profiles->push_back(std::move(pending_profiles)); - - event->Signal(); - - // Note: size() is guaranted to be >= 1 due to the push_back() call above. - if (profiles->size() - 1 == extra_collection_count) - return Optional<StackSamplingProfiler::SamplingParams>(); - - StackSamplingProfiler::SamplingParams sampling_params; - sampling_params.initial_delay = base::TimeDelta::FromMilliseconds(100); - sampling_params.bursts = 1; - sampling_params.samples_per_burst = 1; - // Below are unused: - sampling_params.burst_interval = base::TimeDelta::FromMilliseconds(0); - sampling_params.sampling_interval = base::TimeDelta::FromMilliseconds(0); - return sampling_params; } // Executes the function with the target thread running and executing within @@ -1050,60 +1021,6 @@ }); } -PROFILER_TEST_F(StackSamplingProfilerTest, RescheduledByCallback) { - WithTargetThread([](PlatformThreadId target_thread_id) { - SamplingParams params; - params.sampling_interval = TimeDelta::FromMilliseconds(0); - params.samples_per_burst = 1; - - std::vector<CallStackProfiles> profiles; - WaitableEvent sampling_completed(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - const StackSamplingProfiler::CompletedCallback callback = - Bind(&SaveProfilesAndReschedule, Unretained(&profiles), - Unretained(&sampling_completed), 1); - StackSamplingProfiler profiler(target_thread_id, params, callback); - - // Start once and wait for it to be completed. - profiler.Start(); - sampling_completed.Wait(); - ASSERT_EQ(1u, profiles.size()); - ASSERT_EQ(1u, profiles[0].size()); - - // Now, wait for the second callback call. - sampling_completed.Wait(); - profiler.Stop(); - ASSERT_EQ(2u, profiles.size()); - ASSERT_EQ(1u, profiles[1].size()); - }); -} - -PROFILER_TEST_F(StackSamplingProfilerTest, RescheduledByCallback_Shutdown) { - WithTargetThread([](PlatformThreadId target_thread_id) { - SamplingParams params; - params.sampling_interval = TimeDelta::FromMilliseconds(0); - params.samples_per_burst = 1; - - std::vector<CallStackProfiles> profiles; - WaitableEvent sampling_completed(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - const StackSamplingProfiler::CompletedCallback callback = - Bind(&SaveProfilesAndReschedule, Unretained(&profiles), - Unretained(&sampling_completed), 1000000); - StackSamplingProfiler profiler(target_thread_id, params, callback); - - // Start once and wait for it to be completed. - profiler.Start(); - sampling_completed.Wait(); - ASSERT_EQ(1u, profiles.size()); - ASSERT_EQ(1u, profiles[0].size()); - - // Now, instead of waiting for more callback calls, simply let the sampling - // profiler object go out of scope, running its destructor. This should - // work gracefully without crashing or hanging. - }); -} - // Checks that the different profilers may be run. PROFILER_TEST_F(StackSamplingProfilerTest, CanRunMultipleProfilers) { SamplingParams params;
diff --git a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc index 6b7a91b..51ac854 100644 --- a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc +++ b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc
@@ -1532,17 +1532,16 @@ task_tracker_.FlushForTesting(); } -// Test appears to live-lock with hundreds of worker threads actively being -// created and destroyed, under Fuchsia (see https://crbug.com/816575). -#if defined(OS_FUCHSIA) -#define MAYBE_RacyCleanup DISABLED_RacyCleanup -#else -#define MAYBE_RacyCleanup RacyCleanup -#endif // Verify that worker detachement doesn't race with worker cleanup, regression // test for https://crbug.com/810464. -TEST(TaskSchedulerWorkerPoolTest, MAYBE_RacyCleanup) { +TEST(TaskSchedulerWorkerPoolTest, RacyCleanup) { +#if defined(OS_FUCHSIA) + // Fuchsia + QEMU doesn't deal well with *many* threads being + // created/destroyed at once: https://crbug.com/816575. + constexpr size_t kWorkerCapacity = 16; +#else // defined(OS_FUCHSIA) constexpr size_t kWorkerCapacity = 256; +#endif // defined(OS_FUCHSIA) constexpr TimeDelta kReclaimTimeForRacyCleanupTest = TimeDelta::FromMilliseconds(10);
diff --git a/base/test/test_mock_time_task_runner_unittest.cc b/base/test/test_mock_time_task_runner_unittest.cc index cf334ccc0..06530ca 100644 --- a/base/test/test_mock_time_task_runner_unittest.cc +++ b/base/test/test_mock_time_task_runner_unittest.cc
@@ -84,7 +84,7 @@ auto unbound_mock_time_task_runner = MakeRefCounted<TestMockTimeTaskRunner>(); EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); - EXPECT_DCHECK_DEATH({ RunLoop().RunUntilIdle(); }); + EXPECT_DEATH_IF_SUPPORTED({ RunLoop().RunUntilIdle(); }, ""); } TEST(TestMockTimeTaskRunnerTest, RunLoopDriveableWhenBound) {
diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h index 58137f00..4bf3e83 100644 --- a/base/trace_event/trace_event_impl.h +++ b/base/trace_event/trace_event_impl.h
@@ -129,7 +129,7 @@ const char* scope() const { return scope_; } unsigned long long id() const { return id_; } unsigned int flags() const { return flags_; } - + unsigned long long bind_id() const { return bind_id_; } // Exposed for unittesting: const std::string* parameter_copy_storage() const {
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc index 005795c..5fd36a5 100644 --- a/base/trace_event/trace_log.cc +++ b/base/trace_event/trace_log.cc
@@ -317,6 +317,12 @@ // find the generation mismatch and delete this buffer soon. } +void TraceLog::SetAddTraceEventOverride( + const AddTraceEventOverrideCallback& override) { + subtle::NoBarrier_Store(&trace_event_override_, + reinterpret_cast<subtle::AtomicWord>(override)); +} + struct TraceLog::RegisteredAsyncObserver { explicit RegisteredAsyncObserver(WeakPtr<AsyncEnabledStateObserver> observer) : observer(observer), task_runner(ThreadTaskRunnerHandle::Get()) {} @@ -357,6 +363,7 @@ thread_shared_chunk_index_(0), generation_(0), use_worker_thread_(false), + trace_event_override_(0), filter_factory_for_testing_(nullptr) { CategoryRegistry::Initialize(); @@ -1239,6 +1246,20 @@ convertable_values); #endif // OS_WIN + AddTraceEventOverrideCallback trace_event_override = + reinterpret_cast<AddTraceEventOverrideCallback>( + subtle::NoBarrier_Load(&trace_event_override_)); + if (trace_event_override) { + TraceEvent new_trace_event; + new_trace_event.Initialize(thread_id, offset_event_timestamp, thread_now, + phase, category_group_enabled, name, scope, id, + bind_id, num_args, arg_names, arg_types, + arg_values, convertable_values, flags); + + trace_event_override(new_trace_event); + return handle; + } + std::string console_message; std::unique_ptr<TraceEvent> filtered_trace_event; bool disabled_by_filters = false;
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h index 06c5848..2c23189 100644 --- a/base/trace_event/trace_log.h +++ b/base/trace_event/trace_log.h
@@ -178,6 +178,12 @@ // Cancels tracing and discards collected data. void CancelTracing(const OutputCallback& cb); + typedef void (*AddTraceEventOverrideCallback)(const TraceEvent&); + // The callback will be called up until the point where the flush is + // finished, i.e. must be callable until OutputCallback is called with + // has_more_events==false. + void SetAddTraceEventOverride(const AddTraceEventOverrideCallback& override); + // Called by TRACE_EVENT* macros, don't call this directly. // The name parameter is a category group for example: // TRACE_EVENT0("renderer,webkit", "WebViewImpl::HandleInputEvent") @@ -509,6 +515,7 @@ ArgumentFilterPredicate argument_filter_predicate_; subtle::AtomicWord generation_; bool use_worker_thread_; + subtle::AtomicWord trace_event_override_; FilterFactoryForTesting filter_factory_for_testing_;
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn index ff75d15..11dba35 100644 --- a/build/config/clang/BUILD.gn +++ b/build/config/clang/BUILD.gn
@@ -35,16 +35,6 @@ "find-bad-constructs", ] - # This option should be safe to use by default, and this also - # works with distributed build systems such as icecc. - # TODO(mostynb@vewd.com): update the plugin and remove these flags. - cflags += [ - "-Xclang", - "-plugin-arg-find-bad-constructs", - "-Xclang", - "no-realpath", - ] - cflags += [ "-Xclang", "-plugin-arg-find-bad-constructs",
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni index a33d421b..2e0643a 100644 --- a/build/config/compiler/compiler.gni +++ b/build/config/compiler/compiler.gni
@@ -162,8 +162,9 @@ # Linux or Fuchsia. # TODO(pcc): Enable lld on more architectures on Linux. E.g. we probably need # to fix some of crbug.com/742655 to enable it on ARM. + # TODO(hans): Enable lld for Win/ASan when the crbug.com/837090 fix is rolled. use_lld = is_clang && - ((is_win && host_os != "win") || is_fuchsia || + ((is_win && !is_asan) || is_fuchsia || (use_thin_lto && target_os != "chromeos") || (is_linux && current_cpu == "x64" && target_os != "chromeos") || (is_android && (current_cpu != "arm" || arm_version >= 7) &&
diff --git a/build/config/fuchsia/BUILD.gn b/build/config/fuchsia/BUILD.gn index f5ef583..ed96b2a 100644 --- a/build/config/fuchsia/BUILD.gn +++ b/build/config/fuchsia/BUILD.gn
@@ -66,10 +66,10 @@ # Writes an extended version of fvm.blk to fvm.extended.blk. blobstore_extended_path = "$root_out_dir/fvm.extended.blk" action("blobstore_extended_fvm") { - # The file is grown by 500MB, which should be large enough to hold packaged + # The file is grown by 1GB, which should be large enough to hold packaged # binaries and assets. The value should be increased if the size becomes a # limitation in the future. - _extend_size = "524288000" # 500MB = 500 * 1024 * 1024 + _extend_size = "1073741824" # 1GB if (current_cpu == "arm64") { _target_dir = "//third_party/fuchsia-sdk/sdk/target/aarch64"
diff --git a/build/config/fuchsia/build_manifest.py b/build/config/fuchsia/build_manifest.py index bbd0ca5a..6528c9dc 100644 --- a/build/config/fuchsia/build_manifest.py +++ b/build/config/fuchsia/build_manifest.py
@@ -18,6 +18,7 @@ import json import os +import re import sys import tempfile @@ -55,10 +56,8 @@ if file_path.startswith(next_root): relative_path = file_path[len(next_root):] - # TODO(fuchsia): The requirements for finding/loading .so are in flux, so - # this ought to be reconsidered at some point. - # See https://crbug.com/732897. - if file_path.endswith('.so'): + # Move all dynamic libraries (ending in .so or .so.<number>) to lib/. + if re.search('.*\.so(\.\d+)?$', file_path): relative_path = 'lib/' + os.path.basename(relative_path) return relative_path @@ -70,8 +69,10 @@ """Finds the stripped version of the binary |bin_path| in the build output directory.""" + # Skip the resolution step for binaries that don't have stripped counterparts, + # like system libraries or other libraries built outside the Chromium build. if not '.unstripped' in bin_path: - raise Exception('File "%s" is not in an .unstripped directory.' % bin_path) + return bin_path return os.path.normpath(os.path.join(bin_path, os.path.pardir, @@ -100,20 +101,21 @@ next_path = next_path.strip() if os.path.isdir(next_path): for root, _, files in os.walk(next_path): - for next_file in files: - if next_file.startswith('.'): + for current_file in files: + if current_file.startswith('.'): continue - expanded_files.add(os.path.abspath(os.path.join(root, next_file))) + expanded_files.add(os.path.abspath( + os.path.join(root, current_file))) else: expanded_files.add(os.path.abspath(next_path)) # Format and write out the manifest contents. app_found = False - for next_file in expanded_files: - if _IsBinary(next_file): - next_file = _GetStrippedPath(next_file) + for current_file in expanded_files: + if _IsBinary(current_file): + current_file = _GetStrippedPath(current_file) - in_package_path = MakePackagePath(os.path.join(out_dir, next_file), + in_package_path = MakePackagePath(os.path.join(out_dir, current_file), [root_dir, out_dir]) if in_package_path == app_filename: in_package_path = 'bin/app' @@ -123,7 +125,14 @@ # environments with differing parent directory structures, # e.g. builder bots and swarming clients. output.write('%s=%s\n' % (in_package_path, - os.path.relpath(next_file, out_dir))) + os.path.relpath(current_file, out_dir))) + + # Use libc.so's dynamic linker by aliasing libc.so to ld.so.1. + # Fuchsia always looks for the linker implementation in ld.so.1. + if os.path.basename(in_package_path) == 'libc.so': + output.write('%s=%s\n' % (os.path.dirname(in_package_path) + '/ld.so.1', + os.path.relpath(current_file, out_dir))) + if not app_found: raise Exception('Could not locate executable inside runtime_deps.')
diff --git a/build/config/fuchsia/config.gni b/build/config/fuchsia/config.gni index 89c56fdb..52ca5e0 100644 --- a/build/config/fuchsia/config.gni +++ b/build/config/fuchsia/config.gni
@@ -8,3 +8,12 @@ # Path to Fuchsia SDK. fuchsia_sdk = "//third_party/fuchsia-sdk/sdk" } + +# Compute the arch-specific path to packages' dynamic library dependencies. +if (current_cpu == "arm64") { + dist_libroot = fuchsia_sdk + "/arch/arm64/dist/" +} else if (current_cpu == "x64") { + dist_libroot = fuchsia_sdk + "/arch/x64/dist/" +} else { + assert(false, "No libraries available for architecture: $current_cpu") +}
diff --git a/build/config/fuchsia/package.gni b/build/config/fuchsia/package.gni index 2d7d7ea6..d71d505 100644 --- a/build/config/fuchsia/package.gni +++ b/build/config/fuchsia/package.gni
@@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/fuchsia/config.gni") +import("//build/config/sysroot.gni") # Creates a Fuchsia .far package file. # @@ -35,7 +36,7 @@ _bundle_target = "${pkg.package_name}__bundle" # Generates a manifest file based on the GN runtime deps - # suitable for "far" tool consumption. + # suitable for "pm" tool consumption. action(_write_manifest_target) { forward_variables_from(invoker, [ @@ -48,6 +49,7 @@ inputs = [ _runtime_deps_file, + "//build/config/fuchsia/sandbox_policy", ] outputs = [ @@ -56,6 +58,11 @@ data_deps = pkg.deps + # Include the SDK's core dynamic libraries in the package. + data = [ + dist_libroot, + ] + args = [ rebase_path("//"), rebase_path(root_out_dir),
diff --git a/build/config/fuchsia/sandbox_policy b/build/config/fuchsia/sandbox_policy index e3352a0..d03f937 100644 --- a/build/config/fuchsia/sandbox_policy +++ b/build/config/fuchsia/sandbox_policy
@@ -1,6 +1,4 @@ { - "features": [ "persistent-storage", - "shell", - "system-temp" ] + "features": [ "persistent-storage", "system-temp" ] }
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy index 26b1774..9a4354d2 100644 --- a/build/config/fuchsia/testing_sandbox_policy +++ b/build/config/fuchsia/testing_sandbox_policy
@@ -1,5 +1,5 @@ { - "features": [ "persistent-storage", "shell", "system-temp" ], + "features": [ "persistent-storage", "system-temp" ], "dev": ["null", "zero"] }
diff --git a/build/fuchsia/create_runner_script.py b/build/fuchsia/create_runner_script.py index a879dfb..a1f9f9c 100755 --- a/build/fuchsia/create_runner_script.py +++ b/build/fuchsia/create_runner_script.py
@@ -24,35 +24,19 @@ import sys def main(): - # Redirect execution to the new scripts, if requested. + # Redirect execution to the new scripts. # Suppress -h/-help behavior so that we don't interfere with the help text # output of the runner scripts. parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--use-new-test-runner', action='store_true', default=False, - help='Uses the runner_v2 scripts for execution.') + help='Transitional flag. To be removed.') args, unknown_args = parser.parse_known_args() - if args.use_new_test_runner: - v2_test_path = sys.argv[0] + '_v2' - os.execv(v2_test_path, - [v2_test_path] + unknown_args) - return 1 - script_directory = os.path.dirname(__file__) - - def ResolvePath(path): - \"\"\"Returns an absolute filepath given a path relative to this script. - \"\"\" - return os.path.abspath(os.path.join(script_directory, path)) - - runner_path = ResolvePath('{runner_path}') - runner_args = {runner_args} - runner_path_args = {runner_path_args} - for arg, path in runner_path_args: - runner_args.extend([arg, ResolvePath(path)]) - - os.execv(runner_path, - [runner_path] + runner_args + sys.argv[1:]) + v2_test_path = sys.argv[0] + '_v2' + os.execv(v2_test_path, + [v2_test_path, '-vv'] + unknown_args) + return 1 if __name__ == '__main__': sys.exit(main())
diff --git a/build/fuchsia/runner_v2/boot_data.py b/build/fuchsia/runner_v2/boot_data.py index fce0da04..7065036f 100644 --- a/build/fuchsia/runner_v2/boot_data.py +++ b/build/fuchsia/runner_v2/boot_data.py
@@ -28,6 +28,9 @@ ControlPersist 1m ControlPath /tmp/ssh-%r@%h:%p""" +FVM_TYPE_QCOW = 'qcow' +FVM_TYPE_SPARSE = 'sparse' + def _TargetCpuToSdkBinPath(target_arch): """Returns the path to the kernel & bootfs .bin files for |target_cpu|.""" @@ -76,6 +79,18 @@ ) +def _MakeQcowDisk(output_dir, disk_path): + """Creates a QEMU copy-on-write version of |disk_path| in the output + directory.""" + + qimg_path = os.path.join(common.SDK_ROOT, 'qemu', 'bin', 'qemu-img') + output_path = os.path.join(output_dir, + os.path.basename(disk_path) + '.qcow2') + subprocess.check_call([qimg_path, 'create', '-q', '-f', 'qcow2', + '-b', disk_path, output_path]) + return output_path + + def GetTargetFile(target_arch, filename): """Computes a path to |filename| in the Fuchsia target directory specific to |target_arch|.""" @@ -87,44 +102,51 @@ return output_dir + '/ssh_config' -def ConfigureDataFVM(output_dir, sparse): - """Builds the FVM image for the persistent /data volume and prepopulates it +def ConfigureDataFVM(output_dir, output_type): + """Builds the FVM image for the /data volume and prepopulates it with SSH keys. output_dir: Path to the output directory which will contain the FVM file. - sparse: If true, then a netboot-friendly sparse file will be generated. + output_type: If FVM_TYPE_QCOW, then returns a path to the qcow2 FVM file, + used for QEMU. - Returns the path to the new FVM file.""" + If FVM_TYPE_SPARSE, then returns a path to the + sparse/compressed FVM file.""" - logging.debug('Building persistent data FVM file.') - data_file = tempfile.NamedTemporaryFile() + logging.debug('Building /data partition FVM file.') + with tempfile.NamedTemporaryFile() as data_file: + # Build up the minfs partition data and install keys into it. + ssh_config, ssh_data = _ProvisionSSH(output_dir) + with tempfile.NamedTemporaryFile() as manifest: + for dest, src in ssh_data: + manifest.write('%s=%s\n' % (dest, src)) + manifest.flush() + minfs_path = os.path.join(common.SDK_ROOT, 'tools', 'minfs') + subprocess.check_call([minfs_path, '%s@1G' % data_file.name, 'create']) + subprocess.check_call([minfs_path, data_file.name, 'manifest', + manifest.name]) - # Build up the minfs partition data and install keys into it. - ssh_config, ssh_data = _ProvisionSSH(output_dir) - manifest = tempfile.NamedTemporaryFile() - for dest, src in ssh_data: - manifest.write('%s=%s\n' % (dest, src)) - manifest.flush() - minfs_path = os.path.join(common.SDK_ROOT, 'tools', 'minfs') - subprocess.check_call([minfs_path, '%s@10m' % data_file.name, 'create']) - subprocess.check_call([minfs_path, data_file.name, 'manifest', manifest.name]) + # Wrap the minfs partition in a FVM container. + fvm_path = os.path.join(common.SDK_ROOT, 'tools', 'fvm') + fvm_output_path = os.path.join(output_dir, 'fvm.data.blk') + if os.path.exists(fvm_output_path): + os.remove(fvm_output_path) - # Wrap the minfs partition in a FVM container. - fvm_path = os.path.join(common.SDK_ROOT, 'tools', 'fvm') - fvm_output_path = os.path.join(output_dir, 'fvm.data.blk') - if os.path.exists(fvm_output_path): - os.remove(fvm_output_path) + if output_type == FVM_TYPE_SPARSE: + cmd = [fvm_path, fvm_output_path, 'sparse', '--compress', 'lz4', + '--data', data_file.name] + else: + cmd = [fvm_path, fvm_output_path, 'create', '--data', data_file.name] - if sparse: - cmd = [fvm_path, fvm_output_path, 'sparse', '--compress', 'lz4', - '--data', data_file.name] - else: - cmd = [fvm_path, fvm_output_path, 'create', '--data', data_file.name] + logging.debug(' '.join(cmd)) + subprocess.check_call(cmd) - logging.debug(' '.join(cmd)) - - subprocess.check_call(cmd) - return fvm_output_path + if output_type == FVM_TYPE_SPARSE: + return fvm_output_path + elif output_type == FVM_TYPE_QCOW: + return _MakeQcowDisk(output_dir, fvm_output_path) + else: + raise Exception('Unknown output_type: %r' % output_type) def GetNodeName(output_dir):
diff --git a/build/fuchsia/runner_v2/device_target.py b/build/fuchsia/runner_v2/device_target.py index b2c4b141..054ad1cf 100644 --- a/build/fuchsia/runner_v2/device_target.py +++ b/build/fuchsia/runner_v2/device_target.py
@@ -70,7 +70,8 @@ logging.info('Netbooting Fuchsia. ' + 'Please ensure that your device is in bootloader mode.') bootserver_path = os.path.join(common.SDK_ROOT, 'tools', 'bootserver') - data_fvm_path = boot_data.ConfigureDataFVM(self._output_dir, True) + data_fvm_path = boot_data.ConfigureDataFVM(self._output_dir, + boot_data.FVM_TYPE_SPARSE) bootserver_command = [bootserver_path, '-1', '--efi',
diff --git a/build/fuchsia/runner_v2/qemu_target.py b/build/fuchsia/runner_v2/qemu_target.py index 16a903f3..05f1539 100644 --- a/build/fuchsia/runner_v2/qemu_target.py +++ b/build/fuchsia/runner_v2/qemu_target.py
@@ -79,8 +79,8 @@ '-drive', 'file=%s,format=qcow2,if=none,id=data,snapshot=on' % os.path.join(self._output_dir, 'fvm.blk.qcow2'), '-drive', 'file=%s,format=qcow2,if=none,id=blobstore,snapshot=on' % - self._MakeQcowDisk(boot_data.ConfigureDataFVM(self._output_dir, - False)), + boot_data.ConfigureDataFVM(self._output_dir, + boot_data.FVM_TYPE_QCOW), '-device', 'virtio-blk-pci,drive=data', '-device', 'virtio-blk-pci,drive=blobstore', @@ -154,13 +154,3 @@ def _GetSshConfigPath(self): return boot_data.GetSSHConfigPath(self._output_dir) - def _MakeQcowDisk(self, disk_path): - """Creates a QEMU copy-on-write version of |disk_path| in the output - directory.""" - - qimg_path = os.path.join(common.SDK_ROOT, 'qemu', 'bin', 'qemu-img') - output_path = os.path.join(self._output_dir, - os.path.basename(disk_path) + '.qcow2') - subprocess.check_call([qimg_path, 'create', '-q', '-f', 'qcow2', - '-b', disk_path, output_path]) - return output_path
diff --git a/build/fuchsia/runner_v2/run_package.py b/build/fuchsia/runner_v2/run_package.py index bcc38c6..1af051e 100644 --- a/build/fuchsia/runner_v2/run_package.py +++ b/build/fuchsia/runner_v2/run_package.py
@@ -38,44 +38,51 @@ logging.info('Copying package to target.') - tmp_path = os.path.join('/tmp', os.path.basename(package_path)) - target.PutFile(package_path, tmp_path) + install_path = os.path.join('/data', os.path.basename(package_path)) + target.PutFile(package_path, install_path) - logging.info('Installing package.') - p = target.RunCommandPiped(['pm', 'install', tmp_path], - stderr=subprocess.PIPE) - output = p.stderr.readlines() - p.wait() - if p.returncode != 0: - # Don't error out if the package already exists on the device. - if len(output) != 1 or 'ErrAlreadyExists' not in output[0]: - raise Exception('Error when installing package: %s' % '\n'.join(output)) + try: + logging.info('Installing package.') + p = target.RunCommandPiped(['pm', 'install', install_path], + stderr=subprocess.PIPE) + output = p.stderr.readlines() + p.wait() - logging.info('Running package.') - command = ['run', package_name] + run_args - process = target.RunCommandPiped(command, - stdin=open(os.devnull, 'r'), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + if p.returncode != 0: + # Don't error out if the package already exists on the device. + if len(output) != 1 or 'ErrAlreadyExists' not in output[0]: + raise Exception('Error when installing package: %s' % '\n'.join(output)) - if symbolizer_config: - # Decorate the process output stream with the symbolizer. - output = FilterStream(process.stdout, package_name, - symbolizer_config, output_dir) - else: - logging.warn('Symbolization is DISABLED.') - output = process.stdout + logging.info('Running application.') + command = ['run', package_name] + run_args + process = target.RunCommandPiped(command, + stdin=open(os.devnull, 'r'), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) - for next_line in output: - print next_line.strip() + if symbolizer_config: + # Decorate the process output stream with the symbolizer. + output = FilterStream(process.stdout, package_name, + symbolizer_config, output_dir) + else: + logging.warn('Symbolization is DISABLED.') + output = process.stdout - process.wait() - if process.returncode == 0: - logging.info('Process exited normally with status code 0.') - else: - # The test runner returns an error status code if *any* tests fail, - # so we should proceed anyway. - logging.warning('Process exited with status code %d.' % - process.returncode) + for next_line in output: + print next_line.strip() + + process.wait() + if process.returncode == 0: + logging.info('Process exited normally with status code 0.') + else: + # The test runner returns an error status code if *any* tests fail, + # so we should proceed anyway. + logging.warning('Process exited with status code %d.' % + process.returncode) + + finally: + logging.info('Removing package source from device.') + target.RunCommand(['rm', install_path]) + return process.returncode
diff --git a/build/fuchsia/sdk.sha1 b/build/fuchsia/sdk.sha1 index 7ef51652..94f4dfe1 100644 --- a/build/fuchsia/sdk.sha1 +++ b/build/fuchsia/sdk.sha1
@@ -1 +1 @@ -b56bbf6095a1d4a9deeefe4ef4e2f17806e932a3 \ No newline at end of file +9aded1cf90cda2da1401a19e972daf6d5d32c277 \ No newline at end of file
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni index 6ede004af..cb0b9b0f 100644 --- a/build/toolchain/toolchain.gni +++ b/build/toolchain/toolchain.gni
@@ -104,7 +104,7 @@ _tool_wrapper_path = rebase_path("//build/toolchain/win/tool_wrapper.py", root_build_dir) - stamp_command = "$python_path $_tool_wrapper_path stamp {{output}}" + stamp_command = "cmd /c type nul > \"{{output}}\"" copy_command = "$python_path $_tool_wrapper_path recursive-mirror {{source}} {{output}}" } else {
diff --git a/build/toolchain/win/tool_wrapper.py b/build/toolchain/win/tool_wrapper.py index 2d46882..b2cb093 100644 --- a/build/toolchain/win/tool_wrapper.py +++ b/build/toolchain/win/tool_wrapper.py
@@ -99,10 +99,6 @@ kvs = [item.split('=', 1) for item in pairs] return dict(kvs) - def ExecStamp(self, path): - """Simple stamp command.""" - open(path, 'w').close() - def ExecDeleteFile(self, path): """Simple file delete command.""" if os.path.exists(path):
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 19828e33..ca681c60 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -583,7 +583,6 @@ "input/scroll_state_unittest.cc", "input/scrollbar_animation_controller_unittest.cc", "input/single_scrollbar_animation_controller_thinning_unittest.cc", - "ipc/cc_param_traits_unittest.cc", "layers/effect_tree_layer_list_iterator_unittest.cc", "layers/heads_up_display_layer_impl_unittest.cc", "layers/heads_up_display_unittest.cc", @@ -729,7 +728,6 @@ ":cc", ":test_support", "//base/test:test_support", - "//cc/ipc", "//cc/paint", "//components/ukm:test_support", "//components/viz/common", @@ -765,7 +763,6 @@ sources = [ "animation/animation_host_perftest.cc", "base/rtree_perftest.cc", - "ipc/cc_serialization_perftest.cc", "layers/layer_perftest.cc", "layers/picture_layer_impl_perftest.cc", "paint/paint_op_perftest.cc", @@ -786,7 +783,6 @@ ":test_support", "//base", "//base/test:test_support", - "//cc/ipc", "//cc/paint", "//components/viz/common", "//components/viz/test:test_support",
diff --git a/cc/PRESUBMIT.py b/cc/PRESUBMIT.py index 3ddbde8..3e27d70 100644 --- a/cc/PRESUBMIT.py +++ b/cc/PRESUBMIT.py
@@ -275,25 +275,6 @@ else: return [] -def CheckIpcUpdatedWithMojo(input_api, output_api): - """Make sure IPC is updated whenever Mojo serialization is updated""" - def match_ipc(affected_file): - match = re.match(r'.*_param_traits.*', affected_file.LocalPath()) - return match is not None - - def match_mojo(affected_file): - mojo_patterns = (r'.*_struct_traits.*', r'.*\.mojom$', r'.*\.typemap$') - matches = (re.match(pattern, affected_file.LocalPath()) - for pattern in mojo_patterns) - return any(matches) - - ipc_files = input_api.AffectedFiles(file_filter=match_ipc) - mojo_files = input_api.AffectedFiles(file_filter=match_mojo) - if mojo_files and not ipc_files: - return [output_api.PresubmitPromptOrNotify( - 'Make sure to update IPC ParamTraits along with mojo types.\n\n'),] - return [] - def CheckChangeOnUpload(input_api, output_api): results = [] results += CheckAsserts(input_api, output_api) @@ -305,7 +286,6 @@ results += CheckNamespace(input_api, output_api) results += CheckForUseOfWrongClock(input_api, output_api) results += FindUselessIfdefs(input_api, output_api) - results += CheckIpcUpdatedWithMojo(input_api, output_api) return results def PostUploadHook(cl, change, output_api):
diff --git a/cc/ipc/BUILD.gn b/cc/ipc/BUILD.gn deleted file mode 100644 index 8692a3c..0000000 --- a/cc/ipc/BUILD.gn +++ /dev/null
@@ -1,40 +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. - -import("//build/config/ui.gni") -import("//cc/cc.gni") -import("//mojo/public/tools/bindings/mojom.gni") - -cc_component("ipc") { - output_name = "cc_ipc" - - defines = [ "CC_IPC_IMPLEMENTATION" ] - - sources = [ - "cc_ipc_export.h", - "cc_param_traits.cc", - "cc_param_traits.h", - "cc_param_traits_macros.h", - ] - - public_deps = [ - "//cc", - "//cc/paint", - "//components/viz/common", - "//skia", - ] - - deps = [ - "//base", - "//gpu/ipc/common", - "//ipc", - "//ui/gfx", - "//ui/gfx/ipc", - "//ui/gfx/ipc/buffer_types", - "//ui/gfx/ipc/color", - "//ui/gfx/ipc/geometry", - "//ui/gfx/ipc/skia", - "//ui/latency/ipc", - ] -}
diff --git a/cc/ipc/DEPS b/cc/ipc/DEPS deleted file mode 100644 index a21ff764..0000000 --- a/cc/ipc/DEPS +++ /dev/null
@@ -1,10 +0,0 @@ -include_rules = [ - "+gpu/ipc/common", - "+mojo/common", - "+mojo/public", - "+skia/public", - "+ui/gfx/geometry/mojo", - "+ui/latency/ipc", - "+ui/latency/mojo", - "+services/viz/public", -]
diff --git a/cc/ipc/OWNERS b/cc/ipc/OWNERS deleted file mode 100644 index 8b2d44f..0000000 --- a/cc/ipc/OWNERS +++ /dev/null
@@ -1,11 +0,0 @@ -per-file *_param_traits*.*=set noparent -per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS - -per-file *_struct_traits*.*=set noparent -per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS - -per-file *.mojom=set noparent -per-file *.mojom=file://ipc/SECURITY_OWNERS - -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/cc/ipc/cc_ipc_export.h b/cc/ipc/cc_ipc_export.h deleted file mode 100644 index 4a3623e0..0000000 --- a/cc/ipc/cc_ipc_export.h +++ /dev/null
@@ -1,29 +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 CC_IPC_CC_IPC_EXPORT_H_ -#define CC_IPC_CC_IPC_EXPORT_H_ - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(CC_IPC_IMPLEMENTATION) -#define CC_IPC_EXPORT __declspec(dllexport) -#else -#define CC_IPC_EXPORT __declspec(dllimport) -#endif // defined(CC_IPC_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(CC_IPC_IMPLEMENTATION) -#define CC_IPC_EXPORT __attribute__((visibility("default"))) -#else -#define CC_IPC_EXPORT -#endif -#endif - -#else // defined(COMPONENT_BUILD) -#define CC_IPC_EXPORT -#endif - -#endif // CC_IPC_CC_IPC_EXPORT_H_
diff --git a/cc/ipc/cc_param_traits.cc b/cc/ipc/cc_param_traits.cc deleted file mode 100644 index c66518e2..0000000 --- a/cc/ipc/cc_param_traits.cc +++ /dev/null
@@ -1,1006 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/ipc/cc_param_traits.h" - -#include <stddef.h> -#include <utility> - -#include "base/numerics/safe_conversions.h" -#include "base/time/time.h" -#include "base/trace_event/trace_event.h" -#include "base/unguessable_token.h" -#include "cc/paint/filter_operations.h" -#include "cc/paint/paint_filter.h" -#include "cc/paint/paint_op_buffer.h" -#include "cc/paint/paint_op_reader.h" -#include "cc/paint/paint_op_writer.h" -#include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/debug_border_draw_quad.h" -#include "components/viz/common/quads/draw_quad.h" -#include "components/viz/common/quads/largest_draw_quad.h" -#include "components/viz/common/quads/render_pass_draw_quad.h" -#include "components/viz/common/quads/solid_color_draw_quad.h" -#include "components/viz/common/quads/surface_draw_quad.h" -#include "components/viz/common/quads/tile_draw_quad.h" -#include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/surfaces/surface_id.h" -#include "ui/gfx/ipc/geometry/gfx_param_traits.h" -#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" - -namespace IPC { - -void ParamTraits<cc::FilterOperation>::Write(base::Pickle* m, - const param_type& p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::FilterOperation::Write"); - WriteParam(m, p.type()); - switch (p.type()) { - case cc::FilterOperation::GRAYSCALE: - case cc::FilterOperation::SEPIA: - case cc::FilterOperation::SATURATE: - case cc::FilterOperation::HUE_ROTATE: - case cc::FilterOperation::INVERT: - case cc::FilterOperation::BRIGHTNESS: - case cc::FilterOperation::SATURATING_BRIGHTNESS: - case cc::FilterOperation::CONTRAST: - case cc::FilterOperation::OPACITY: - WriteParam(m, p.amount()); - break; - case cc::FilterOperation::BLUR: - WriteParam(m, p.amount()); - WriteParam(m, p.blur_tile_mode()); - break; - case cc::FilterOperation::DROP_SHADOW: - WriteParam(m, p.drop_shadow_offset()); - WriteParam(m, p.amount()); - WriteParam(m, p.drop_shadow_color()); - break; - case cc::FilterOperation::COLOR_MATRIX: - for (int i = 0; i < 20; ++i) - WriteParam(m, p.matrix()[i]); - break; - case cc::FilterOperation::ZOOM: - WriteParam(m, p.amount()); - WriteParam(m, p.zoom_inset()); - break; - case cc::FilterOperation::REFERENCE: - WriteParam(m, p.image_filter()); - break; - case cc::FilterOperation::ALPHA_THRESHOLD: - WriteParam(m, p.amount()); - WriteParam(m, p.outer_threshold()); - WriteParam(m, p.shape()); - break; - } -} - -bool ParamTraits<cc::FilterOperation>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::FilterOperation::Read"); - cc::FilterOperation::FilterType type; - float amount; - float outer_threshold; - gfx::Point drop_shadow_offset; - SkColor drop_shadow_color; - SkScalar matrix[20]; - cc::FilterOperation::ShapeRects shape; - int zoom_inset; - SkBlurImageFilter::TileMode tile_mode; - - if (!ReadParam(m, iter, &type)) - return false; - r->set_type(type); - - bool success = false; - switch (type) { - case cc::FilterOperation::GRAYSCALE: - case cc::FilterOperation::SEPIA: - case cc::FilterOperation::SATURATE: - case cc::FilterOperation::HUE_ROTATE: - case cc::FilterOperation::INVERT: - case cc::FilterOperation::BRIGHTNESS: - case cc::FilterOperation::SATURATING_BRIGHTNESS: - case cc::FilterOperation::CONTRAST: - case cc::FilterOperation::OPACITY: - if (ReadParam(m, iter, &amount)) { - r->set_amount(amount); - success = true; - } - break; - case cc::FilterOperation::BLUR: - if (ReadParam(m, iter, &amount) && ReadParam(m, iter, &tile_mode)) { - r->set_amount(amount); - r->set_blur_tile_mode(tile_mode); - success = true; - } - break; - case cc::FilterOperation::DROP_SHADOW: - if (ReadParam(m, iter, &drop_shadow_offset) && - ReadParam(m, iter, &amount) && - ReadParam(m, iter, &drop_shadow_color)) { - r->set_drop_shadow_offset(drop_shadow_offset); - r->set_amount(amount); - r->set_drop_shadow_color(drop_shadow_color); - success = true; - } - break; - case cc::FilterOperation::COLOR_MATRIX: { - int i; - for (i = 0; i < 20; ++i) { - if (!ReadParam(m, iter, &matrix[i])) - break; - } - if (i == 20) { - r->set_matrix(matrix); - success = true; - } - break; - } - case cc::FilterOperation::ZOOM: - if (ReadParam(m, iter, &amount) && ReadParam(m, iter, &zoom_inset) && - amount >= 0.f && zoom_inset >= 0) { - r->set_amount(amount); - r->set_zoom_inset(zoom_inset); - success = true; - } - break; - case cc::FilterOperation::REFERENCE: { - sk_sp<cc::PaintFilter> filter; - if (!ReadParam(m, iter, &filter)) { - success = false; - break; - } - r->set_image_filter(std::move(filter)); - success = true; - break; - } - case cc::FilterOperation::ALPHA_THRESHOLD: - if (ReadParam(m, iter, &amount) && ReadParam(m, iter, &outer_threshold) && - ReadParam(m, iter, &shape) && amount >= 0.f && - outer_threshold >= 0.f) { - r->set_amount(amount); - r->set_outer_threshold(amount); - r->set_shape(shape); - success = true; - } - break; - } - return success; -} - -void ParamTraits<cc::FilterOperation>::Log(const param_type& p, - std::string* l) { - l->append("("); - LogParam(static_cast<unsigned>(p.type()), l); - l->append(", "); - - switch (p.type()) { - case cc::FilterOperation::GRAYSCALE: - case cc::FilterOperation::SEPIA: - case cc::FilterOperation::SATURATE: - case cc::FilterOperation::HUE_ROTATE: - case cc::FilterOperation::INVERT: - case cc::FilterOperation::BRIGHTNESS: - case cc::FilterOperation::SATURATING_BRIGHTNESS: - case cc::FilterOperation::CONTRAST: - case cc::FilterOperation::OPACITY: - LogParam(p.amount(), l); - break; - case cc::FilterOperation::BLUR: - LogParam(p.amount(), l); - l->append(", "); - LogParam(static_cast<int>(p.blur_tile_mode()), l); - break; - case cc::FilterOperation::DROP_SHADOW: - LogParam(p.drop_shadow_offset(), l); - l->append(", "); - LogParam(p.amount(), l); - l->append(", "); - LogParam(p.drop_shadow_color(), l); - break; - case cc::FilterOperation::COLOR_MATRIX: - for (int i = 0; i < 20; ++i) { - if (i) - l->append(", "); - LogParam(p.matrix()[i], l); - } - break; - case cc::FilterOperation::ZOOM: - LogParam(p.amount(), l); - l->append(", "); - LogParam(p.zoom_inset(), l); - break; - case cc::FilterOperation::REFERENCE: - LogParam(p.image_filter(), l); - break; - case cc::FilterOperation::ALPHA_THRESHOLD: - LogParam(p.amount(), l); - l->append(", "); - LogParam(p.outer_threshold(), l); - l->append(", "); - LogParam(p.shape(), l); - break; - } - l->append(")"); -} - -void ParamTraits<cc::FilterOperations>::Write(base::Pickle* m, - const param_type& p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::FilterOperations::Write"); - WriteParam(m, base::checked_cast<uint32_t>(p.size())); - for (std::size_t i = 0; i < p.size(); ++i) { - WriteParam(m, p.at(i)); - } -} - -bool ParamTraits<cc::FilterOperations>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::FilterOperations::Read"); - uint32_t count; - if (!ReadParam(m, iter, &count)) - return false; - - for (std::size_t i = 0; i < count; ++i) { - cc::FilterOperation op = cc::FilterOperation::CreateEmptyFilter(); - if (!ReadParam(m, iter, &op)) - return false; - r->Append(op); - } - return true; -} - -void ParamTraits<cc::FilterOperations>::Log(const param_type& p, - std::string* l) { - l->append("("); - for (std::size_t i = 0; i < p.size(); ++i) { - if (i) - l->append(", "); - LogParam(p.at(i), l); - } - l->append(")"); -} - -void ParamTraits<sk_sp<cc::PaintFilter>>::Write(base::Pickle* m, - const param_type& p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::PaintFilter::Write"); - std::vector<uint8_t> memory; - memory.resize(cc::PaintOpWriter::HeaderBytes() + - cc::PaintFilter::GetFilterSize(p.get())); - cc::PaintOpWriter writer(memory.data(), memory.size(), nullptr, nullptr, - true /* enable_security_constraints */); - writer.Write(p.get()); - if (writer.size() == 0u) - m->WriteData(nullptr, 0); - else - m->WriteData(reinterpret_cast<const char*>(memory.data()), writer.size()); -} - -bool ParamTraits<sk_sp<cc::PaintFilter>>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::PaintFilter::Read"); - const char* data = nullptr; - int length = 0; - if (!iter->ReadData(&data, &length)) - return false; - - if (length <= 0) { - r->reset(); - return true; - } - - cc::PaintOpReader reader(data, length, nullptr, - true /* enable_security_constraints */); - sk_sp<cc::PaintFilter> filter; - reader.Read(&filter); - if (!filter) - return false; - - *r = std::move(filter); - return true; -} - -void ParamTraits<sk_sp<cc::PaintFilter>>::Log(const param_type& p, - std::string* l) { - l->append("("); - auto type = p ? p->type() : cc::PaintFilter::Type::kNullFilter; - LogParam(cc::PaintFilter::TypeToString(type), l); - l->append(")"); -} - -void ParamTraits<viz::RenderPass>::Write(base::Pickle* m, const param_type& p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::RenderPass::Write"); - WriteParam(m, p.id); - WriteParam(m, p.output_rect); - WriteParam(m, p.damage_rect); - WriteParam(m, p.transform_to_root_target); - WriteParam(m, p.filters); - WriteParam(m, p.background_filters); - WriteParam(m, p.color_space); - WriteParam(m, p.has_transparent_background); - WriteParam(m, p.cache_render_pass); - WriteParam(m, p.has_damage_from_contributing_content); - WriteParam(m, p.generate_mipmap); - WriteParam(m, base::checked_cast<uint32_t>(p.quad_list.size())); - - auto shared_quad_state_iter = p.shared_quad_state_list.cbegin(); - auto last_shared_quad_state_iter = p.shared_quad_state_list.cend(); - for (auto* quad : p.quad_list) { - DCHECK(quad->rect.Contains(quad->visible_rect)) - << quad->material << " rect: " << quad->rect.ToString() - << " visible_rect: " << quad->visible_rect.ToString(); - - switch (quad->material) { - case viz::DrawQuad::DEBUG_BORDER: - WriteParam(m, *viz::DebugBorderDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::PICTURE_CONTENT: - NOTREACHED(); - break; - case viz::DrawQuad::TEXTURE_CONTENT: - WriteParam(m, *viz::TextureDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::RENDER_PASS: - WriteParam(m, *viz::RenderPassDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::SOLID_COLOR: - WriteParam(m, *viz::SolidColorDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::SURFACE_CONTENT: - WriteParam(m, *viz::SurfaceDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::TILED_CONTENT: - WriteParam(m, *viz::TileDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::STREAM_VIDEO_CONTENT: - WriteParam(m, *viz::StreamVideoDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::YUV_VIDEO_CONTENT: - WriteParam(m, *viz::YUVVideoDrawQuad::MaterialCast(quad)); - break; - case viz::DrawQuad::INVALID: - break; - } - - // Null shared quad states should not occur. - DCHECK(quad->shared_quad_state); - - // SharedQuadStates should appear in the order they are used by DrawQuads. - // Find the SharedQuadState for this DrawQuad. - while (shared_quad_state_iter != p.shared_quad_state_list.end() && - quad->shared_quad_state != *shared_quad_state_iter) { - ++shared_quad_state_iter; - } - - DCHECK(shared_quad_state_iter != p.shared_quad_state_list.end()); - - if (shared_quad_state_iter != last_shared_quad_state_iter) { - WriteParam(m, true); - WriteParam(m, **shared_quad_state_iter); - last_shared_quad_state_iter = shared_quad_state_iter; - } else { - WriteParam(m, false); - } - } -} - -static size_t ReserveSizeForRenderPassWrite(const viz::RenderPass& p) { - size_t to_reserve = sizeof(viz::RenderPass); - - // Whether the quad points to a new shared quad state for each quad. - to_reserve += p.quad_list.size() * sizeof(bool); - - // Shared quad state is only written when a quad contains a shared quad state - // that has not been written. - to_reserve += p.shared_quad_state_list.size() * sizeof(viz::SharedQuadState); - - // The largest quad type, verified by a unit test. - to_reserve += p.quad_list.size() * viz::LargestDrawQuadSize(); - - to_reserve += - sizeof(uint32_t) + p.filters.size() * sizeof(cc::FilterOperation); - to_reserve += sizeof(uint32_t) + - p.background_filters.size() * sizeof(cc::FilterOperation); - - return to_reserve; -} - -template <typename QuadType> -static viz::DrawQuad* ReadDrawQuad(const base::Pickle* m, - base::PickleIterator* iter, - viz::RenderPass* render_pass) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::ReadDrawQuad"); - QuadType* quad = render_pass->CreateAndAppendDrawQuad<QuadType>(); - if (!ReadParam(m, iter, quad)) - return nullptr; - return quad; -} - -bool ParamTraits<viz::RenderPass>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::RenderPass::Read"); - uint64_t id; - gfx::Rect output_rect; - gfx::Rect damage_rect; - gfx::Transform transform_to_root_target; - cc::FilterOperations filters; - cc::FilterOperations background_filters; - gfx::ColorSpace color_space; - bool has_transparent_background; - bool cache_render_pass; - bool has_damage_from_contributing_content; - bool generate_mipmap; - - uint32_t quad_list_size; - - if (!ReadParam(m, iter, &id) || !ReadParam(m, iter, &output_rect) || - !ReadParam(m, iter, &damage_rect) || - !ReadParam(m, iter, &transform_to_root_target) || - !ReadParam(m, iter, &filters) || - !ReadParam(m, iter, &background_filters) || - !ReadParam(m, iter, &color_space) || - !ReadParam(m, iter, &has_transparent_background) || - !ReadParam(m, iter, &cache_render_pass) || - !ReadParam(m, iter, &has_damage_from_contributing_content) || - !ReadParam(m, iter, &generate_mipmap) || - !ReadParam(m, iter, &quad_list_size)) - return false; - - p->SetAll(id, output_rect, damage_rect, transform_to_root_target, filters, - background_filters, color_space, has_transparent_background, - cache_render_pass, has_damage_from_contributing_content, - generate_mipmap); - - for (uint32_t i = 0; i < quad_list_size; ++i) { - viz::DrawQuad::Material material; - base::PickleIterator temp_iter = *iter; - if (!ReadParam(m, &temp_iter, &material)) - return false; - - viz::DrawQuad* draw_quad = nullptr; - switch (material) { - case viz::DrawQuad::DEBUG_BORDER: - draw_quad = ReadDrawQuad<viz::DebugBorderDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::PICTURE_CONTENT: - NOTREACHED(); - return false; - case viz::DrawQuad::SURFACE_CONTENT: - draw_quad = ReadDrawQuad<viz::SurfaceDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::TEXTURE_CONTENT: - draw_quad = ReadDrawQuad<viz::TextureDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::RENDER_PASS: - draw_quad = ReadDrawQuad<viz::RenderPassDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::SOLID_COLOR: - draw_quad = ReadDrawQuad<viz::SolidColorDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::TILED_CONTENT: - draw_quad = ReadDrawQuad<viz::TileDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::STREAM_VIDEO_CONTENT: - draw_quad = ReadDrawQuad<viz::StreamVideoDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::YUV_VIDEO_CONTENT: - draw_quad = ReadDrawQuad<viz::YUVVideoDrawQuad>(m, iter, p); - break; - case viz::DrawQuad::INVALID: - break; - } - if (!draw_quad) - return false; - if (!draw_quad->rect.Contains(draw_quad->visible_rect)) { - LOG(ERROR) << "Quad with invalid visible rect " << draw_quad->material - << " rect: " << draw_quad->rect.ToString() - << " visible_rect: " << draw_quad->visible_rect.ToString(); - return false; - } - - bool has_new_shared_quad_state; - if (!ReadParam(m, iter, &has_new_shared_quad_state)) - return false; - - // If the quad has a new shared quad state, read it in. - if (has_new_shared_quad_state) { - viz::SharedQuadState* state = p->CreateAndAppendSharedQuadState(); - if (!ReadParam(m, iter, state)) - return false; - } - - draw_quad->shared_quad_state = p->shared_quad_state_list.back(); - } - - return true; -} - -void ParamTraits<viz::RenderPass>::Log(const param_type& p, std::string* l) { - l->append("RenderPass(("); - LogParam(p.id, l); - l->append("), "); - LogParam(p.output_rect, l); - l->append(", "); - LogParam(p.damage_rect, l); - l->append(", "); - LogParam(p.transform_to_root_target, l); - l->append(", "); - LogParam(p.filters, l); - l->append(", "); - LogParam(p.background_filters, l); - l->append(", "); - LogParam(p.color_space, l); - l->append(", "); - LogParam(p.has_transparent_background, l); - l->append(", "); - LogParam(p.cache_render_pass, l); - l->append(", "); - LogParam(p.has_damage_from_contributing_content, l); - l->append(", "); - - l->append("["); - for (auto* shared_quad_state : p.shared_quad_state_list) { - if (shared_quad_state != p.shared_quad_state_list.front()) - l->append(", "); - LogParam(*shared_quad_state, l); - } - l->append("], ["); - for (auto* quad : p.quad_list) { - if (quad != p.quad_list.front()) - l->append(", "); - switch (quad->material) { - case viz::DrawQuad::DEBUG_BORDER: - LogParam(*viz::DebugBorderDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::PICTURE_CONTENT: - NOTREACHED(); - break; - case viz::DrawQuad::TEXTURE_CONTENT: - LogParam(*viz::TextureDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::RENDER_PASS: - LogParam(*viz::RenderPassDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::SOLID_COLOR: - LogParam(*viz::SolidColorDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::SURFACE_CONTENT: - LogParam(*viz::SurfaceDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::TILED_CONTENT: - LogParam(*viz::TileDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::STREAM_VIDEO_CONTENT: - LogParam(*viz::StreamVideoDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::YUV_VIDEO_CONTENT: - LogParam(*viz::YUVVideoDrawQuad::MaterialCast(quad), l); - break; - case viz::DrawQuad::INVALID: - break; - } - } - l->append("])"); -} - -void ParamTraits<viz::FrameDeadline>::Write(base::Pickle* m, - const param_type& p) { - WriteParam(m, p.frame_start_time()); - WriteParam(m, p.deadline_in_frames()); - WriteParam(m, p.frame_interval()); - WriteParam(m, p.use_default_lower_bound_deadline()); -} - -bool ParamTraits<viz::FrameDeadline>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - base::TimeTicks frame_start_time; - if (!ReadParam(m, iter, &frame_start_time)) - return false; - - uint32_t deadline_in_frames; - if (!ReadParam(m, iter, &deadline_in_frames)) - return false; - - base::TimeDelta frame_interval; - if (!ReadParam(m, iter, &frame_interval)) - return false; - - bool use_default_lower_bound_deadline; - if (!ReadParam(m, iter, &use_default_lower_bound_deadline)) - return false; - - *p = viz::FrameDeadline(frame_start_time, deadline_in_frames, frame_interval, - use_default_lower_bound_deadline); - return true; -} - -void ParamTraits<viz::FrameDeadline>::Log(const param_type& p, std::string* l) { - l->append("viz::FrameDeadline("); - LogParam(p.frame_start_time(), l); - l->append(", "); - LogParam(p.deadline_in_frames(), l); - l->append(", "); - LogParam(p.frame_interval(), l); - l->append(", "); - LogParam(p.use_default_lower_bound_deadline(), l); - l->append(")"); -} - -void ParamTraits<viz::FrameSinkId>::Write(base::Pickle* m, - const param_type& p) { - DCHECK(p.is_valid()); - WriteParam(m, p.client_id()); - WriteParam(m, p.sink_id()); -} - -bool ParamTraits<viz::FrameSinkId>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - uint32_t client_id; - if (!ReadParam(m, iter, &client_id)) - return false; - - uint32_t sink_id; - if (!ReadParam(m, iter, &sink_id)) - return false; - - *p = viz::FrameSinkId(client_id, sink_id); - return p->is_valid(); -} - -void ParamTraits<viz::FrameSinkId>::Log(const param_type& p, std::string* l) { - l->append("viz::FrameSinkId("); - LogParam(p.client_id(), l); - l->append(", "); - LogParam(p.sink_id(), l); - l->append(")"); -} - -void ParamTraits<viz::LocalSurfaceId>::Write(base::Pickle* m, - const param_type& p) { - DCHECK(p.is_valid()); - WriteParam(m, p.parent_sequence_number()); - WriteParam(m, p.child_sequence_number()); - WriteParam(m, p.embed_token()); -} - -bool ParamTraits<viz::LocalSurfaceId>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - uint32_t parent_sequence_number; - if (!ReadParam(m, iter, &parent_sequence_number)) - return false; - - uint32_t child_sequence_number; - if (!ReadParam(m, iter, &child_sequence_number)) - return false; - - base::UnguessableToken embed_token; - if (!ReadParam(m, iter, &embed_token)) - return false; - - *p = viz::LocalSurfaceId(parent_sequence_number, child_sequence_number, - embed_token); - return p->is_valid(); -} - -void ParamTraits<viz::LocalSurfaceId>::Log(const param_type& p, - std::string* l) { - l->append("viz::LocalSurfaceId("); - LogParam(p.parent_sequence_number(), l); - l->append(", "); - LogParam(p.child_sequence_number(), l); - l->append(", "); - LogParam(p.embed_token(), l); - l->append(")"); -} - -void ParamTraits<viz::SurfaceId>::Write(base::Pickle* m, const param_type& p) { - WriteParam(m, p.frame_sink_id()); - WriteParam(m, p.local_surface_id()); -} - -bool ParamTraits<viz::SurfaceId>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - viz::FrameSinkId frame_sink_id; - if (!ReadParam(m, iter, &frame_sink_id)) - return false; - - viz::LocalSurfaceId local_surface_id; - if (!ReadParam(m, iter, &local_surface_id)) - return false; - - *p = viz::SurfaceId(frame_sink_id, local_surface_id); - return true; -} - -void ParamTraits<viz::SurfaceId>::Log(const param_type& p, std::string* l) { - l->append("viz::SurfaceId("); - LogParam(p.frame_sink_id(), l); - l->append(", "); - LogParam(p.local_surface_id(), l); - l->append(")"); -} - -void ParamTraits<viz::SurfaceInfo>::Write(base::Pickle* m, - const param_type& p) { - WriteParam(m, p.id()); - WriteParam(m, p.device_scale_factor()); - WriteParam(m, p.size_in_pixels()); -} - -bool ParamTraits<viz::SurfaceInfo>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - viz::SurfaceId surface_id; - if (!ReadParam(m, iter, &surface_id)) - return false; - - float device_scale_factor; - if (!ReadParam(m, iter, &device_scale_factor)) - return false; - - gfx::Size size_in_pixels; - if (!ReadParam(m, iter, &size_in_pixels)) - return false; - - *p = viz::SurfaceInfo(surface_id, device_scale_factor, size_in_pixels); - return p->is_valid(); -} - -void ParamTraits<viz::SurfaceInfo>::Log(const param_type& p, std::string* l) { - l->append("viz::SurfaceInfo("); - LogParam(p.id(), l); - l->append(", "); - LogParam(p.device_scale_factor(), l); - l->append(", "); - LogParam(p.size_in_pixels(), l); - l->append(")"); -} - -void ParamTraits<viz::CompositorFrame>::Write(base::Pickle* m, - const param_type& p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::CompositorFrame::Write"); - WriteParam(m, p.metadata); - size_t to_reserve = 0u; - to_reserve += p.resource_list.size() * sizeof(viz::TransferableResource); - for (const auto& pass : p.render_pass_list) { - to_reserve += sizeof(size_t) * 2; - to_reserve += ReserveSizeForRenderPassWrite(*pass); - } - m->Reserve(to_reserve); - - WriteParam(m, p.resource_list); - WriteParam(m, base::checked_cast<uint32_t>(p.render_pass_list.size())); - for (const auto& pass : p.render_pass_list) { - WriteParam(m, base::checked_cast<uint32_t>(pass->quad_list.size())); - WriteParam( - m, base::checked_cast<uint32_t>(pass->shared_quad_state_list.size())); - WriteParam(m, *pass); - } -} - -bool ParamTraits<viz::CompositorFrame>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::CompositorFrame::Read"); - if (!ReadParam(m, iter, &p->metadata)) - return false; - - const size_t kMaxRenderPasses = 10000; - const size_t kMaxSharedQuadStateListSize = 100000; - const size_t kMaxQuadListSize = 1000000; - - std::set<viz::RenderPassId> pass_id_set; - - uint32_t num_render_passes; - if (!ReadParam(m, iter, &p->resource_list) || - !ReadParam(m, iter, &num_render_passes) || num_render_passes == 0 || - num_render_passes > kMaxRenderPasses) - return false; - for (uint32_t i = 0; i < num_render_passes; ++i) { - uint32_t quad_list_size; - uint32_t shared_quad_state_list_size; - if (!ReadParam(m, iter, &quad_list_size) || - !ReadParam(m, iter, &shared_quad_state_list_size) || - quad_list_size > kMaxQuadListSize || - shared_quad_state_list_size > kMaxSharedQuadStateListSize) - return false; - std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create( - static_cast<size_t>(shared_quad_state_list_size), - static_cast<size_t>(quad_list_size)); - if (!ReadParam(m, iter, render_pass.get())) - return false; - // Validate that each viz::RenderPassDrawQuad points at a valid RenderPass - // earlier in the frame. - for (const auto* quad : render_pass->quad_list) { - if (quad->material != viz::DrawQuad::RENDER_PASS) - continue; - const viz::RenderPassDrawQuad* rpdq = - viz::RenderPassDrawQuad::MaterialCast(quad); - if (!pass_id_set.count(rpdq->render_pass_id)) - return false; - } - pass_id_set.insert(render_pass->id); - p->render_pass_list.push_back(std::move(render_pass)); - } - - if (p->render_pass_list.back()->output_rect.size().IsEmpty()) - return false; - - return true; -} - -void ParamTraits<viz::CompositorFrame>::Log(const param_type& p, - std::string* l) { - l->append("CompositorFrame("); - LogParam(p.metadata, l); - l->append(", "); - LogParam(p.resource_list, l); - l->append(", ["); - for (size_t i = 0; i < p.render_pass_list.size(); ++i) { - if (i) - l->append(", "); - LogParam(*p.render_pass_list[i], l); - } - l->append("])"); -} - -void ParamTraits<viz::DrawQuad::Resources>::Write(base::Pickle* m, - const param_type& p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::DrawQuad::Resources::Write"); - DCHECK_LE(p.count, viz::DrawQuad::Resources::kMaxResourceIdCount); - WriteParam(m, p.count); - for (size_t i = 0; i < p.count; ++i) - WriteParam(m, p.ids[i]); -} - -bool ParamTraits<viz::DrawQuad::Resources>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "ParamTraits::DrawQuad::Resources::Read"); - if (!ReadParam(m, iter, &p->count)) - return false; - if (p->count > viz::DrawQuad::Resources::kMaxResourceIdCount) - return false; - for (size_t i = 0; i < p->count; ++i) { - if (!ReadParam(m, iter, &p->ids[i])) - return false; - } - return true; -} - -void ParamTraits<viz::DrawQuad::Resources>::Log(const param_type& p, - std::string* l) { - l->append("DrawQuad::Resources("); - LogParam(p.count, l); - l->append(", ["); - if (p.count > viz::DrawQuad::Resources::kMaxResourceIdCount) { - l->append("])"); - return; - } - - for (size_t i = 0; i < p.count; ++i) { - LogParam(p.ids[i], l); - if (i < (p.count - 1)) - l->append(", "); - } - l->append("])"); -} - -void ParamTraits<viz::YUVVideoDrawQuad>::Write(base::Pickle* m, - const param_type& p) { - ParamTraits<viz::DrawQuad>::Write(m, p); - WriteParam(m, p.ya_tex_coord_rect); - WriteParam(m, p.uv_tex_coord_rect); - WriteParam(m, p.ya_tex_size); - WriteParam(m, p.uv_tex_size); - WriteParam(m, p.video_color_space); - WriteParam(m, p.resource_offset); - WriteParam(m, p.resource_multiplier); - WriteParam(m, p.bits_per_channel); -} - -bool ParamTraits<viz::YUVVideoDrawQuad>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - return ParamTraits<viz::DrawQuad>::Read(m, iter, p) && - ReadParam(m, iter, &p->ya_tex_coord_rect) && - ReadParam(m, iter, &p->uv_tex_coord_rect) && - ReadParam(m, iter, &p->ya_tex_size) && - ReadParam(m, iter, &p->uv_tex_size) && - ReadParam(m, iter, &p->video_color_space) && - ReadParam(m, iter, &p->resource_offset) && - ReadParam(m, iter, &p->resource_multiplier) && - ReadParam(m, iter, &p->bits_per_channel) && - p->bits_per_channel >= viz::YUVVideoDrawQuad::kMinBitsPerChannel && - p->bits_per_channel <= viz::YUVVideoDrawQuad::kMaxBitsPerChannel; -} - -void ParamTraits<viz::YUVVideoDrawQuad>::Log(const param_type& p, - std::string* l) { - l->append("("); - ParamTraits<viz::DrawQuad>::Log(p, l); - l->append(", "); - LogParam(p.ya_tex_coord_rect, l); - l->append(", "); - LogParam(p.uv_tex_coord_rect, l); - l->append(", "); - LogParam(p.ya_tex_size, l); - l->append(", "); - LogParam(p.uv_tex_size, l); - l->append(", "); - LogParam(p.video_color_space, l); - l->append(", "); - LogParam(p.resource_offset, l); - l->append(", "); - LogParam(p.resource_multiplier, l); - l->append(", "); - LogParam(p.bits_per_channel, l); - l->append("])"); -} - -void ParamTraits<viz::BeginFrameAck>::Write(base::Pickle* m, - const param_type& p) { - m->WriteUInt64(p.sequence_number); - m->WriteUInt64(p.source_id); - // |has_damage| is implicit through IPC message name, so not transmitted. -} - -bool ParamTraits<viz::BeginFrameAck>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - return iter->ReadUInt64(&p->sequence_number) && - p->sequence_number >= viz::BeginFrameArgs::kStartingFrameNumber && - iter->ReadUInt64(&p->source_id); -} - -void ParamTraits<viz::BeginFrameAck>::Log(const param_type& p, std::string* l) { - l->append("("); - LogParam(p.sequence_number, l); - l->append(", "); - LogParam(p.source_id, l); - l->append(")"); -} - -} // namespace IPC - -// Generate param traits write methods. -#include "ipc/param_traits_write_macros.h" -namespace IPC { -#undef CC_IPC_CC_PARAM_TRAITS_MACROS_H_ -#include "cc/ipc/cc_param_traits_macros.h" -} // namespace IPC - -// Generate param traits read methods. -#include "ipc/param_traits_read_macros.h" -namespace IPC { -#undef CC_IPC_CC_PARAM_TRAITS_MACROS_H_ -#include "cc/ipc/cc_param_traits_macros.h" -} // namespace IPC - -// Generate param traits log methods. -#include "ipc/param_traits_log_macros.h" -namespace IPC { -#undef CC_IPC_CC_PARAM_TRAITS_MACROS_H_ -#include "cc/ipc/cc_param_traits_macros.h" -} // namespace IPC
diff --git a/cc/ipc/cc_param_traits.h b/cc/ipc/cc_param_traits.h deleted file mode 100644 index bb575eb..0000000 --- a/cc/ipc/cc_param_traits.h +++ /dev/null
@@ -1,160 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// IPC Messages sent between compositor instances. - -#ifndef CC_IPC_CC_PARAM_TRAITS_H_ -#define CC_IPC_CC_PARAM_TRAITS_H_ - -#include "cc/ipc/cc_ipc_export.h" -#include "cc/ipc/cc_param_traits_macros.h" -#include "cc/paint/filter_operation.h" -#include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/draw_quad.h" -#include "components/viz/common/quads/frame_deadline.h" -#include "components/viz/common/quads/stream_video_draw_quad.h" -#include "components/viz/common/quads/texture_draw_quad.h" -#include "gpu/ipc/common/gpu_command_buffer_traits.h" -#include "ipc/ipc_message_macros.h" - -namespace cc { -class FilterOperations; -class PaintFilter; -} - -namespace IPC { - -template <> -struct ParamTraits<cc::FilterOperation> { - typedef cc::FilterOperation param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct ParamTraits<cc::FilterOperations> { - typedef cc::FilterOperations param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<sk_sp<cc::PaintFilter>> { - typedef sk_sp<cc::PaintFilter> param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::RenderPass> { - typedef viz::RenderPass param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::FrameDeadline> { - typedef viz::FrameDeadline param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::FrameSinkId> { - typedef viz::FrameSinkId param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::LocalSurfaceId> { - typedef viz::LocalSurfaceId param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::SurfaceId> { - typedef viz::SurfaceId param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::SurfaceInfo> { - typedef viz::SurfaceInfo param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::CompositorFrame> { - typedef viz::CompositorFrame param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::DrawQuad::Resources> { - typedef viz::DrawQuad::Resources param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::YUVVideoDrawQuad> { - typedef viz::YUVVideoDrawQuad param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p); - static void Log(const param_type& p, std::string* l); -}; - -template <> -struct CC_IPC_EXPORT ParamTraits<viz::BeginFrameAck> { - typedef viz::BeginFrameAck param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p); - static void Log(const param_type& p, std::string* l); -}; - -} // namespace IPC - -#endif // CC_IPC_CC_PARAM_TRAITS_H_
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h deleted file mode 100644 index 12dd30c7..0000000 --- a/cc/ipc/cc_param_traits_macros.h +++ /dev/null
@@ -1,204 +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 CC_IPC_CC_PARAM_TRAITS_MACROS_H_ -#define CC_IPC_CC_PARAM_TRAITS_MACROS_H_ - -#include "cc/paint/filter_operation.h" -#include "components/viz/common/frame_sinks/begin_frame_args.h" -#include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/debug_border_draw_quad.h" -#include "components/viz/common/quads/draw_quad.h" -#include "components/viz/common/quads/render_pass.h" -#include "components/viz/common/quads/render_pass_draw_quad.h" -#include "components/viz/common/quads/solid_color_draw_quad.h" -#include "components/viz/common/quads/stream_video_draw_quad.h" -#include "components/viz/common/quads/surface_draw_quad.h" -#include "components/viz/common/quads/texture_draw_quad.h" -#include "components/viz/common/quads/tile_draw_quad.h" -#include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/resources/resource_format.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "components/viz/common/surfaces/surface_id.h" -#include "components/viz/common/surfaces/surface_info.h" -#include "ui/gfx/ipc/buffer_types/gfx_param_traits.h" -#include "ui/gfx/ipc/color/gfx_param_traits.h" -#include "ui/gfx/ipc/gfx_param_traits.h" -#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" -#include "ui/latency/ipc/latency_info_param_traits.h" - -#undef IPC_MESSAGE_EXPORT -#define IPC_MESSAGE_EXPORT CC_IPC_EXPORT - -IPC_ENUM_TRAITS_MAX_VALUE(viz::DrawQuad::Material, viz::DrawQuad::MATERIAL_LAST) -IPC_ENUM_TRAITS_MAX_VALUE(cc::FilterOperation::FilterType, - cc::FilterOperation::FILTER_TYPE_LAST) -// TODO(wutao): This trait belongs with skia code. -IPC_ENUM_TRAITS_MAX_VALUE(SkBlurImageFilter::TileMode, - SkBlurImageFilter::kMax_TileMode) -IPC_ENUM_TRAITS_MAX_VALUE(viz::ResourceFormat, viz::RESOURCE_FORMAT_MAX) - -// TODO(fsamuel): This trait belongs with skia code. -IPC_ENUM_TRAITS_MAX_VALUE(SkBlendMode, SkBlendMode::kLastMode) - -IPC_STRUCT_TRAITS_BEGIN(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(material) - IPC_STRUCT_TRAITS_MEMBER(rect) - IPC_STRUCT_TRAITS_MEMBER(visible_rect) - IPC_STRUCT_TRAITS_MEMBER(needs_blending) - IPC_STRUCT_TRAITS_MEMBER(resources) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::DebugBorderDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(color) - IPC_STRUCT_TRAITS_MEMBER(width) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::RenderPassDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(render_pass_id) - IPC_STRUCT_TRAITS_MEMBER(mask_uv_rect) - IPC_STRUCT_TRAITS_MEMBER(mask_texture_size) - IPC_STRUCT_TRAITS_MEMBER(filters_scale) - IPC_STRUCT_TRAITS_MEMBER(filters_origin) - IPC_STRUCT_TRAITS_MEMBER(tex_coord_rect) - IPC_STRUCT_TRAITS_MEMBER(force_anti_aliasing_off) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::SolidColorDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(color) - IPC_STRUCT_TRAITS_MEMBER(force_anti_aliasing_off) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::StreamVideoDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(overlay_resources) - IPC_STRUCT_TRAITS_MEMBER(matrix) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::StreamVideoDrawQuad::OverlayResources) - IPC_STRUCT_TRAITS_MEMBER(size_in_pixels) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::SurfaceDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(primary_surface_id) - IPC_STRUCT_TRAITS_MEMBER(fallback_surface_id) - IPC_STRUCT_TRAITS_MEMBER(default_background_color) - IPC_STRUCT_TRAITS_MEMBER(stretch_content_to_fill_bounds) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::TextureDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(overlay_resources) - IPC_STRUCT_TRAITS_MEMBER(premultiplied_alpha) - IPC_STRUCT_TRAITS_MEMBER(uv_top_left) - IPC_STRUCT_TRAITS_MEMBER(uv_bottom_right) - IPC_STRUCT_TRAITS_MEMBER(background_color) - IPC_STRUCT_TRAITS_MEMBER(vertex_opacity[0]) - IPC_STRUCT_TRAITS_MEMBER(vertex_opacity[1]) - IPC_STRUCT_TRAITS_MEMBER(vertex_opacity[2]) - IPC_STRUCT_TRAITS_MEMBER(vertex_opacity[3]) - IPC_STRUCT_TRAITS_MEMBER(y_flipped) - IPC_STRUCT_TRAITS_MEMBER(nearest_neighbor) - IPC_STRUCT_TRAITS_MEMBER(secure_output_only) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::TextureDrawQuad::OverlayResources) - IPC_STRUCT_TRAITS_MEMBER(size_in_pixels) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::TileDrawQuad) - IPC_STRUCT_TRAITS_PARENT(viz::DrawQuad) - IPC_STRUCT_TRAITS_MEMBER(tex_coord_rect) - IPC_STRUCT_TRAITS_MEMBER(texture_size) - IPC_STRUCT_TRAITS_MEMBER(swizzle_contents) - IPC_STRUCT_TRAITS_MEMBER(is_premultiplied) - IPC_STRUCT_TRAITS_MEMBER(nearest_neighbor) - IPC_STRUCT_TRAITS_MEMBER(force_anti_aliasing_off) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::SharedQuadState) - IPC_STRUCT_TRAITS_MEMBER(quad_to_target_transform) - IPC_STRUCT_TRAITS_MEMBER(quad_layer_rect) - IPC_STRUCT_TRAITS_MEMBER(visible_quad_layer_rect) - IPC_STRUCT_TRAITS_MEMBER(clip_rect) - IPC_STRUCT_TRAITS_MEMBER(is_clipped) - IPC_STRUCT_TRAITS_MEMBER(opacity) - IPC_STRUCT_TRAITS_MEMBER(blend_mode) - IPC_STRUCT_TRAITS_MEMBER(sorting_context_id) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::TransferableResource) - IPC_STRUCT_TRAITS_MEMBER(id) - IPC_STRUCT_TRAITS_MEMBER(format) - IPC_STRUCT_TRAITS_MEMBER(buffer_format) - IPC_STRUCT_TRAITS_MEMBER(filter) - IPC_STRUCT_TRAITS_MEMBER(size) - IPC_STRUCT_TRAITS_MEMBER(mailbox_holder) - IPC_STRUCT_TRAITS_MEMBER(read_lock_fences_enabled) - IPC_STRUCT_TRAITS_MEMBER(is_software) - IPC_STRUCT_TRAITS_MEMBER(is_overlay_candidate) - IPC_STRUCT_TRAITS_MEMBER(color_space) -#if defined(OS_ANDROID) - IPC_STRUCT_TRAITS_MEMBER(is_backed_by_surface_texture) - IPC_STRUCT_TRAITS_MEMBER(wants_promotion_hint) -#endif -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::ReturnedResource) - IPC_STRUCT_TRAITS_MEMBER(id) - IPC_STRUCT_TRAITS_MEMBER(sync_token) - IPC_STRUCT_TRAITS_MEMBER(count) - IPC_STRUCT_TRAITS_MEMBER(lost) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::Selection<gfx::SelectionBound>) - IPC_STRUCT_TRAITS_MEMBER(start) - IPC_STRUCT_TRAITS_MEMBER(end) -IPC_STRUCT_TRAITS_END() - -IPC_ENUM_TRAITS_MAX_VALUE(viz::BeginFrameArgs::BeginFrameArgsType, - viz::BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX - 1) - -IPC_STRUCT_TRAITS_BEGIN(viz::BeginFrameArgs) - IPC_STRUCT_TRAITS_MEMBER(frame_time) - IPC_STRUCT_TRAITS_MEMBER(deadline) - IPC_STRUCT_TRAITS_MEMBER(interval) - IPC_STRUCT_TRAITS_MEMBER(sequence_number) - IPC_STRUCT_TRAITS_MEMBER(source_id) - IPC_STRUCT_TRAITS_MEMBER(type) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(viz::CompositorFrameMetadata) - IPC_STRUCT_TRAITS_MEMBER(device_scale_factor) - IPC_STRUCT_TRAITS_MEMBER(root_scroll_offset) - IPC_STRUCT_TRAITS_MEMBER(page_scale_factor) - IPC_STRUCT_TRAITS_MEMBER(scrollable_viewport_size) - IPC_STRUCT_TRAITS_MEMBER(root_layer_size) - IPC_STRUCT_TRAITS_MEMBER(min_page_scale_factor) - IPC_STRUCT_TRAITS_MEMBER(max_page_scale_factor) - IPC_STRUCT_TRAITS_MEMBER(root_overflow_y_hidden) - IPC_STRUCT_TRAITS_MEMBER(may_contain_video) - IPC_STRUCT_TRAITS_MEMBER( - is_resourceless_software_draw_with_scroll_or_animation) - IPC_STRUCT_TRAITS_MEMBER(top_controls_height) - IPC_STRUCT_TRAITS_MEMBER(top_controls_shown_ratio) - IPC_STRUCT_TRAITS_MEMBER(bottom_controls_height) - IPC_STRUCT_TRAITS_MEMBER(bottom_controls_shown_ratio) - IPC_STRUCT_TRAITS_MEMBER(root_background_color) - IPC_STRUCT_TRAITS_MEMBER(selection) - IPC_STRUCT_TRAITS_MEMBER(latency_info) - IPC_STRUCT_TRAITS_MEMBER(referenced_surfaces) - IPC_STRUCT_TRAITS_MEMBER(deadline) - IPC_STRUCT_TRAITS_MEMBER(activation_dependencies) - IPC_STRUCT_TRAITS_MEMBER(content_source_id) - IPC_STRUCT_TRAITS_MEMBER(begin_frame_ack) - IPC_STRUCT_TRAITS_MEMBER(frame_token) -IPC_STRUCT_TRAITS_END() - -#endif // CC_IPC_CC_PARAM_TRAITS_MACROS_H_
diff --git a/cc/ipc/cc_param_traits_unittest.cc b/cc/ipc/cc_param_traits_unittest.cc deleted file mode 100644 index 867bb9c..0000000 --- a/cc/ipc/cc_param_traits_unittest.cc +++ /dev/null
@@ -1,665 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <string.h> -#include <algorithm> -#include <utility> - -#include "base/macros.h" -#include "build/build_config.h" -#include "cc/ipc/cc_param_traits.h" -#include "cc/resources/resource_provider.h" -#include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/frame_deadline.h" -#include "components/viz/common/quads/picture_draw_quad.h" -#include "components/viz/common/quads/render_pass_draw_quad.h" -#include "ipc/ipc_message.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_POSIX) -#include "base/file_descriptor_posix.h" -#endif - -using viz::CompositorFrame; -using cc::BlurPaintFilter; -using cc::FilterOperation; -using cc::FilterOperations; -using cc::ResourceProvider; -using gfx::Transform; -using viz::DebugBorderDrawQuad; -using viz::DrawQuad; -using viz::FrameDeadline; -using viz::PictureDrawQuad; -using viz::RenderPass; -using viz::RenderPassDrawQuad; -using viz::ResourceId; -using viz::SharedQuadState; -using viz::SolidColorDrawQuad; -using viz::StreamVideoDrawQuad; -using viz::SurfaceDrawQuad; -using viz::TextureDrawQuad; -using viz::TileDrawQuad; -using viz::TransferableResource; -using viz::YUVVideoDrawQuad; - -namespace content { -namespace { - -static constexpr viz::FrameSinkId kArbitraryFrameSinkId(1, 1); - -class CCParamTraitsTest : public testing::Test { - protected: - void Compare(const RenderPass* a, const RenderPass* b) { - EXPECT_EQ(a->id, b->id); - EXPECT_EQ(a->output_rect.ToString(), b->output_rect.ToString()); - EXPECT_EQ(a->damage_rect.ToString(), b->damage_rect.ToString()); - EXPECT_EQ(a->transform_to_root_target, b->transform_to_root_target); - EXPECT_EQ(a->filters.size(), b->filters.size()); - for (size_t i = 0; i < a->filters.size(); ++i) { - if (a->filters.at(i).type() != cc::FilterOperation::REFERENCE) { - EXPECT_EQ(a->filters.at(i), b->filters.at(i)); - } else { - EXPECT_EQ(b->filters.at(i).type(), cc::FilterOperation::REFERENCE); - EXPECT_EQ(a->filters.at(i).image_filter()->count_inputs(), - b->filters.at(i).image_filter()->count_inputs()); - } - } - EXPECT_EQ(a->background_filters.size(), b->background_filters.size()); - for (size_t i = 0; i < a->background_filters.size(); ++i) { - if (a->background_filters.at(i).type() != - cc::FilterOperation::REFERENCE) { - EXPECT_EQ(a->background_filters.at(i), b->background_filters.at(i)); - } else { - EXPECT_EQ(b->background_filters.at(i).type(), - cc::FilterOperation::REFERENCE); - EXPECT_EQ(a->background_filters.at(i).image_filter()->count_inputs(), - b->background_filters.at(i).image_filter()->count_inputs()); - } - } - EXPECT_EQ(a->color_space, b->color_space); - EXPECT_EQ(a->has_transparent_background, b->has_transparent_background); - } - - void Compare(const SharedQuadState* a, const SharedQuadState* b) { - EXPECT_EQ(a->quad_to_target_transform, b->quad_to_target_transform); - EXPECT_EQ(a->quad_layer_rect, b->quad_layer_rect); - EXPECT_EQ(a->visible_quad_layer_rect, b->visible_quad_layer_rect); - EXPECT_EQ(a->clip_rect, b->clip_rect); - EXPECT_EQ(a->is_clipped, b->is_clipped); - EXPECT_EQ(a->opacity, b->opacity); - EXPECT_EQ(a->blend_mode, b->blend_mode); - EXPECT_EQ(a->sorting_context_id, b->sorting_context_id); - } - - void Compare(const DrawQuad* a, const DrawQuad* b) { - ASSERT_NE(DrawQuad::INVALID, a->material); - ASSERT_EQ(a->material, b->material); - EXPECT_EQ(a->rect.ToString(), b->rect.ToString()); - EXPECT_EQ(a->visible_rect.ToString(), b->visible_rect.ToString()); - EXPECT_EQ(a->needs_blending, b->needs_blending); - - Compare(a->shared_quad_state, b->shared_quad_state); - - switch (a->material) { - case DrawQuad::DEBUG_BORDER: - Compare(DebugBorderDrawQuad::MaterialCast(a), - DebugBorderDrawQuad::MaterialCast(b)); - break; - case DrawQuad::PICTURE_CONTENT: - Compare(PictureDrawQuad::MaterialCast(a), - PictureDrawQuad::MaterialCast(b)); - break; - case DrawQuad::RENDER_PASS: - Compare(RenderPassDrawQuad::MaterialCast(a), - RenderPassDrawQuad::MaterialCast(b)); - break; - case DrawQuad::TEXTURE_CONTENT: - Compare(TextureDrawQuad::MaterialCast(a), - TextureDrawQuad::MaterialCast(b)); - break; - case DrawQuad::TILED_CONTENT: - Compare(TileDrawQuad::MaterialCast(a), TileDrawQuad::MaterialCast(b)); - break; - case DrawQuad::SOLID_COLOR: - Compare(SolidColorDrawQuad::MaterialCast(a), - SolidColorDrawQuad::MaterialCast(b)); - break; - case DrawQuad::STREAM_VIDEO_CONTENT: - Compare(StreamVideoDrawQuad::MaterialCast(a), - StreamVideoDrawQuad::MaterialCast(b)); - break; - case DrawQuad::SURFACE_CONTENT: - Compare(SurfaceDrawQuad::MaterialCast(a), - SurfaceDrawQuad::MaterialCast(b)); - break; - case DrawQuad::YUV_VIDEO_CONTENT: - Compare(YUVVideoDrawQuad::MaterialCast(a), - YUVVideoDrawQuad::MaterialCast(b)); - break; - case DrawQuad::INVALID: - break; - } - } - - void Compare(const DebugBorderDrawQuad* a, const DebugBorderDrawQuad* b) { - EXPECT_EQ(a->color, b->color); - EXPECT_EQ(a->width, b->width); - } - - void Compare(const RenderPassDrawQuad* a, const RenderPassDrawQuad* b) { - EXPECT_EQ(a->render_pass_id, b->render_pass_id); - EXPECT_EQ(a->mask_resource_id(), b->mask_resource_id()); - EXPECT_EQ(a->mask_uv_rect.ToString(), b->mask_uv_rect.ToString()); - EXPECT_EQ(a->mask_texture_size.ToString(), b->mask_texture_size.ToString()); - EXPECT_EQ(a->filters_scale, b->filters_scale); - EXPECT_EQ(a->filters_origin, b->filters_origin); - EXPECT_EQ(a->tex_coord_rect, b->tex_coord_rect); - EXPECT_EQ(a->force_anti_aliasing_off, b->force_anti_aliasing_off); - } - - void Compare(const SolidColorDrawQuad* a, const SolidColorDrawQuad* b) { - EXPECT_EQ(a->color, b->color); - EXPECT_EQ(a->force_anti_aliasing_off, b->force_anti_aliasing_off); - } - - void Compare(const StreamVideoDrawQuad* a, const StreamVideoDrawQuad* b) { - EXPECT_EQ(a->resource_id(), b->resource_id()); - EXPECT_EQ(a->resource_size_in_pixels(), b->resource_size_in_pixels()); - EXPECT_EQ(a->matrix, b->matrix); - } - - void Compare(const SurfaceDrawQuad* a, const SurfaceDrawQuad* b) { - EXPECT_EQ(a->primary_surface_id, b->primary_surface_id); - EXPECT_EQ(a->fallback_surface_id, b->fallback_surface_id); - EXPECT_EQ(a->default_background_color, b->default_background_color); - EXPECT_EQ(a->stretch_content_to_fill_bounds, - b->stretch_content_to_fill_bounds); - } - - void Compare(const TextureDrawQuad* a, const TextureDrawQuad* b) { - EXPECT_EQ(a->resource_id(), b->resource_id()); - EXPECT_EQ(a->resource_size_in_pixels(), b->resource_size_in_pixels()); - EXPECT_EQ(a->premultiplied_alpha, b->premultiplied_alpha); - EXPECT_EQ(a->uv_top_left, b->uv_top_left); - EXPECT_EQ(a->uv_bottom_right, b->uv_bottom_right); - EXPECT_EQ(a->background_color, b->background_color); - EXPECT_EQ(a->vertex_opacity[0], b->vertex_opacity[0]); - EXPECT_EQ(a->vertex_opacity[1], b->vertex_opacity[1]); - EXPECT_EQ(a->vertex_opacity[2], b->vertex_opacity[2]); - EXPECT_EQ(a->vertex_opacity[3], b->vertex_opacity[3]); - EXPECT_EQ(a->y_flipped, b->y_flipped); - EXPECT_EQ(a->nearest_neighbor, b->nearest_neighbor); - EXPECT_EQ(a->secure_output_only, b->secure_output_only); - } - - void Compare(const TileDrawQuad* a, const TileDrawQuad* b) { - EXPECT_EQ(a->resource_id(), b->resource_id()); - EXPECT_EQ(a->tex_coord_rect, b->tex_coord_rect); - EXPECT_EQ(a->texture_size, b->texture_size); - EXPECT_EQ(a->swizzle_contents, b->swizzle_contents); - EXPECT_EQ(a->nearest_neighbor, b->nearest_neighbor); - EXPECT_EQ(a->force_anti_aliasing_off, b->force_anti_aliasing_off); - } - - void Compare(const YUVVideoDrawQuad* a, const YUVVideoDrawQuad* b) { - EXPECT_EQ(a->ya_tex_coord_rect, b->ya_tex_coord_rect); - EXPECT_EQ(a->uv_tex_coord_rect, b->uv_tex_coord_rect); - EXPECT_EQ(a->ya_tex_size, b->ya_tex_size); - EXPECT_EQ(a->uv_tex_size, b->uv_tex_size); - EXPECT_EQ(a->y_plane_resource_id(), b->y_plane_resource_id()); - EXPECT_EQ(a->u_plane_resource_id(), b->u_plane_resource_id()); - EXPECT_EQ(a->v_plane_resource_id(), b->v_plane_resource_id()); - EXPECT_EQ(a->a_plane_resource_id(), b->a_plane_resource_id()); - EXPECT_EQ(a->video_color_space, b->video_color_space); - EXPECT_EQ(a->bits_per_channel, b->bits_per_channel); - EXPECT_EQ(a->require_overlay, b->require_overlay); - } - - void Compare(const TransferableResource& a, const TransferableResource& b) { - EXPECT_EQ(a.id, b.id); - EXPECT_EQ(a.format, b.format); - EXPECT_EQ(a.filter, b.filter); - EXPECT_EQ(a.size.ToString(), b.size.ToString()); - for (size_t i = 0; i < arraysize(a.mailbox_holder.mailbox.name); ++i) { - EXPECT_EQ(a.mailbox_holder.mailbox.name[i], - b.mailbox_holder.mailbox.name[i]); - } - EXPECT_EQ(a.mailbox_holder.texture_target, b.mailbox_holder.texture_target); - EXPECT_EQ(a.mailbox_holder.sync_token, b.mailbox_holder.sync_token); - EXPECT_EQ(a.is_overlay_candidate, b.is_overlay_candidate); -#if defined(OS_ANDROID) - EXPECT_EQ(a.is_backed_by_surface_texture, b.is_backed_by_surface_texture); - EXPECT_EQ(a.wants_promotion_hint, b.wants_promotion_hint); -#endif - } -}; - -TEST_F(CCParamTraitsTest, AllQuads) { - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - - Transform arbitrary_matrix1; - arbitrary_matrix1.Scale(3, 3); - arbitrary_matrix1.Translate(-5, 20); - arbitrary_matrix1.Rotate(15); - Transform arbitrary_matrix2; - arbitrary_matrix2.Scale(10, -1); - arbitrary_matrix2.Translate(20, 3); - arbitrary_matrix2.Rotate(24); - gfx::Rect arbitrary_rect1(-5, 9, 3, 15); - gfx::Rect arbitrary_rect1_inside_rect1(-4, 12, 2, 8); - gfx::Rect arbitrary_rect2(40, 23, 11, 7); - gfx::Rect arbitrary_rect1_inside_rect2(44, 23, 4, 2); - gfx::Rect arbitrary_rect3(7, -53, 22, 19); - gfx::Rect arbitrary_rect2_inside_rect3(12, -51, 5, 12); - gfx::Size arbitrary_size1(15, 19); - gfx::Size arbitrary_size2(3, 99); - gfx::RectF arbitrary_rectf1(4.2f, -922.1f, 15.6f, 29.5f); - gfx::RectF arbitrary_rectf2(2.1f, -411.05f, 7.8f, 14.75f); - gfx::PointF arbitrary_pointf1(31.4f, 15.9f); - gfx::PointF arbitrary_pointf2(26.5f, -35.8f); - gfx::Vector2dF arbitrary_vector2df2(-8.3f, 0.47f); - float arbitrary_float1 = 0.7f; - float arbitrary_float2 = 0.3f; - float arbitrary_float3 = 0.9f; - float arbitrary_float_array[4] = {3.5f, 6.2f, 9.3f, 12.3f}; - bool arbitrary_bool1 = true; - bool arbitrary_bool2 = false; - bool arbitrary_bool3 = true; - bool arbitrary_bool4 = true; - bool arbitrary_bool5 = false; - bool arbitrary_bool6 = true; - int arbitrary_context_id1 = 12; - int arbitrary_context_id2 = 57; - int arbitrary_context_id3 = -503; - int arbitrary_int = 13; - SkColor arbitrary_color = SkColorSetARGB(25, 36, 47, 58); - SkBlendMode arbitrary_blend_mode1 = SkBlendMode::kScreen; - SkBlendMode arbitrary_blend_mode2 = SkBlendMode::kLighten; - SkBlendMode arbitrary_blend_mode3 = SkBlendMode::kOverlay; - ResourceId arbitrary_resourceid1 = 55; - ResourceId arbitrary_resourceid2 = 47; - ResourceId arbitrary_resourceid3 = 23; - ResourceId arbitrary_resourceid4 = 16; - SkScalar arbitrary_sigma = SkFloatToScalar(2.0f); - gfx::ColorSpace arbitrary_color_space = gfx::ColorSpace::CreateREC601(); - - int child_id = 30; - int root_id = 14; - - FilterOperations arbitrary_filters1; - arbitrary_filters1.Append( - FilterOperation::CreateGrayscaleFilter(arbitrary_float1)); - arbitrary_filters1.Append( - FilterOperation::CreateBlurFilter(arbitrary_float2)); - arbitrary_filters1.Append( - cc::FilterOperation::CreateReferenceFilter(sk_make_sp<BlurPaintFilter>( - arbitrary_sigma, arbitrary_sigma, - BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr))); - - FilterOperations arbitrary_filters2; - arbitrary_filters2.Append( - FilterOperation::CreateBrightnessFilter(arbitrary_float2)); - - std::unique_ptr<RenderPass> child_pass_in = RenderPass::Create(); - child_pass_in->SetAll( - child_id, arbitrary_rect2, arbitrary_rect3, arbitrary_matrix2, - arbitrary_filters1, arbitrary_filters2, arbitrary_color_space, - arbitrary_bool2, arbitrary_bool3, arbitrary_bool4, arbitrary_bool5); - - std::unique_ptr<RenderPass> child_pass_cmp = RenderPass::Create(); - child_pass_cmp->SetAll( - child_id, arbitrary_rect2, arbitrary_rect3, arbitrary_matrix2, - arbitrary_filters1, arbitrary_filters2, arbitrary_color_space, - arbitrary_bool2, arbitrary_bool3, arbitrary_bool4, arbitrary_bool5); - - std::unique_ptr<RenderPass> pass_in = RenderPass::Create(); - pass_in->SetAll(root_id, arbitrary_rect1, arbitrary_rect2, arbitrary_matrix1, - arbitrary_filters2, arbitrary_filters1, arbitrary_color_space, - arbitrary_bool1, arbitrary_bool2, arbitrary_bool3, - arbitrary_bool4); - - SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state1_in->SetAll(arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1, - arbitrary_rect2, arbitrary_bool1, arbitrary_bool2, - arbitrary_float1, arbitrary_blend_mode1, - arbitrary_context_id1); - - std::unique_ptr<RenderPass> pass_cmp = RenderPass::Create(); - pass_cmp->SetAll(root_id, arbitrary_rect1, arbitrary_rect2, arbitrary_matrix1, - arbitrary_filters2, arbitrary_filters1, - arbitrary_color_space, arbitrary_bool1, arbitrary_bool2, - arbitrary_bool3, arbitrary_bool4); - - SharedQuadState* shared_state1_cmp = - pass_cmp->CreateAndAppendSharedQuadState(); - *shared_state1_cmp = *shared_state1_in; - - DebugBorderDrawQuad* debugborder_in = - pass_in->CreateAndAppendDrawQuad<DebugBorderDrawQuad>(); - debugborder_in->SetAll(shared_state1_in, arbitrary_rect3, - arbitrary_rect2_inside_rect3, arbitrary_bool1, - arbitrary_color, arbitrary_int); - pass_cmp->CopyFromAndAppendDrawQuad(debugborder_in); - - SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state2_in->SetAll(arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2, - arbitrary_rect3, arbitrary_bool1, arbitrary_bool1, - arbitrary_float2, arbitrary_blend_mode2, - arbitrary_context_id2); - SharedQuadState* shared_state2_cmp = - pass_cmp->CreateAndAppendSharedQuadState(); - *shared_state2_cmp = *shared_state2_in; - - RenderPassDrawQuad* renderpass_in = - pass_in->CreateAndAppendDrawQuad<RenderPassDrawQuad>(); - renderpass_in->SetAll(shared_state2_in, arbitrary_rect1, - arbitrary_rect1_inside_rect1, arbitrary_bool1, child_id, - arbitrary_resourceid2, arbitrary_rectf1, - arbitrary_size1, arbitrary_vector2df2, - arbitrary_pointf2, arbitrary_rectf1, arbitrary_bool2); - pass_cmp->CopyFromAndAppendRenderPassDrawQuad(renderpass_in, - renderpass_in->render_pass_id); - - SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state3_in->SetAll(arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3, - arbitrary_rect1, arbitrary_bool1, arbitrary_bool2, - arbitrary_float3, arbitrary_blend_mode3, - arbitrary_context_id3); - SharedQuadState* shared_state3_cmp = - pass_cmp->CreateAndAppendSharedQuadState(); - *shared_state3_cmp = *shared_state3_in; - - auto* solidcolor_in = pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - solidcolor_in->SetAll(shared_state3_in, arbitrary_rect3, - arbitrary_rect2_inside_rect3, arbitrary_bool1, - arbitrary_color, arbitrary_bool2); - pass_cmp->CopyFromAndAppendDrawQuad(solidcolor_in); - - StreamVideoDrawQuad* streamvideo_in = - pass_in->CreateAndAppendDrawQuad<StreamVideoDrawQuad>(); - streamvideo_in->SetAll(shared_state3_in, arbitrary_rect2, - arbitrary_rect1_inside_rect2, arbitrary_bool1, - arbitrary_resourceid2, arbitrary_size1, - arbitrary_matrix1); - pass_cmp->CopyFromAndAppendDrawQuad(streamvideo_in); - - viz::SurfaceId arbitrary_surface_id( - kArbitraryFrameSinkId, - viz::LocalSurfaceId(3, base::UnguessableToken::Create())); - SurfaceDrawQuad* surface_in = - pass_in->CreateAndAppendDrawQuad<SurfaceDrawQuad>(); - surface_in->SetAll(shared_state3_in, arbitrary_rect2, - arbitrary_rect1_inside_rect2, arbitrary_bool1, - arbitrary_surface_id, base::nullopt, SK_ColorWHITE, true); - pass_cmp->CopyFromAndAppendDrawQuad(surface_in); - - TextureDrawQuad* texture_in = - pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>(); - texture_in->SetAll( - shared_state3_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, - arbitrary_bool1, arbitrary_resourceid1, arbitrary_size1, arbitrary_bool2, - arbitrary_pointf1, arbitrary_pointf2, arbitrary_color, - arbitrary_float_array, arbitrary_bool4, arbitrary_bool5, arbitrary_bool6); - pass_cmp->CopyFromAndAppendDrawQuad(texture_in); - - TileDrawQuad* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>(); - tile_in->SetAll( - shared_state3_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, - arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1, arbitrary_size1, - arbitrary_bool2, arbitrary_bool3, arbitrary_bool4, arbitrary_bool5); - pass_cmp->CopyFromAndAppendDrawQuad(tile_in); - - YUVVideoDrawQuad* yuvvideo_in = - pass_in->CreateAndAppendDrawQuad<YUVVideoDrawQuad>(); - yuvvideo_in->SetAll( - shared_state3_in, arbitrary_rect1, arbitrary_rect1_inside_rect1, - arbitrary_bool1, arbitrary_rectf1, arbitrary_rectf2, arbitrary_size1, - arbitrary_size2, arbitrary_resourceid1, arbitrary_resourceid2, - arbitrary_resourceid3, arbitrary_resourceid4, arbitrary_color_space, - arbitrary_float1, arbitrary_float2, arbitrary_int, arbitrary_bool2); - pass_cmp->CopyFromAndAppendDrawQuad(yuvvideo_in); - - // Make sure the in and cmp RenderPasses match. - Compare(child_pass_cmp.get(), child_pass_in.get()); - ASSERT_EQ(0u, child_pass_in->shared_quad_state_list.size()); - ASSERT_EQ(0u, child_pass_in->quad_list.size()); - Compare(pass_cmp.get(), pass_in.get()); - ASSERT_EQ(3u, pass_in->shared_quad_state_list.size()); - ASSERT_EQ(8u, pass_in->quad_list.size()); - for (auto cmp_iterator = pass_cmp->shared_quad_state_list.cbegin(), - in_iterator = pass_in->shared_quad_state_list.cbegin(); - in_iterator != pass_in->shared_quad_state_list.cend(); - ++cmp_iterator, ++in_iterator) { - Compare(*cmp_iterator, *in_iterator); - } - for (auto in_iter = pass_in->quad_list.cbegin(), - cmp_iter = pass_cmp->quad_list.cbegin(); - in_iter != pass_in->quad_list.cend(); ++in_iter, ++cmp_iter) - Compare(*cmp_iter, *in_iter); - - for (size_t i = 1; i < pass_in->quad_list.size(); ++i) { - bool same_shared_quad_state_cmp = - pass_cmp->quad_list.ElementAt(i)->shared_quad_state == - pass_cmp->quad_list.ElementAt(i - 1)->shared_quad_state; - bool same_shared_quad_state_in = - pass_in->quad_list.ElementAt(i)->shared_quad_state == - pass_in->quad_list.ElementAt(i - 1)->shared_quad_state; - EXPECT_EQ(same_shared_quad_state_cmp, same_shared_quad_state_in); - } - - CompositorFrame frame_in; - frame_in.render_pass_list.push_back(std::move(child_pass_in)); - frame_in.render_pass_list.push_back(std::move(pass_in)); - frame_in.metadata.begin_frame_ack.sequence_number = - viz::BeginFrameArgs::kStartingFrameNumber; - const base::TimeTicks now = base::TimeTicks::Now(); - frame_in.metadata.deadline = - FrameDeadline(now, 4u, base::TimeDelta::FromMilliseconds(16), true); - - IPC::ParamTraits<CompositorFrame>::Write(&msg, frame_in); - - CompositorFrame frame_out; - base::PickleIterator iter(msg); - EXPECT_TRUE(IPC::ParamTraits<CompositorFrame>::Read(&msg, &iter, &frame_out)); - - EXPECT_EQ(FrameDeadline(now, 4u, base::TimeDelta::FromMilliseconds(16), true), - frame_out.metadata.deadline); - - // Make sure the out and cmp RenderPasses match. - std::unique_ptr<RenderPass> child_pass_out = - std::move(frame_out.render_pass_list[0]); - Compare(child_pass_cmp.get(), child_pass_out.get()); - ASSERT_EQ(0u, child_pass_out->shared_quad_state_list.size()); - ASSERT_EQ(0u, child_pass_out->quad_list.size()); - std::unique_ptr<RenderPass> pass_out = - std::move(frame_out.render_pass_list[1]); - Compare(pass_cmp.get(), pass_out.get()); - ASSERT_EQ(3u, pass_out->shared_quad_state_list.size()); - ASSERT_EQ(8u, pass_out->quad_list.size()); - for (auto cmp_iterator = pass_cmp->shared_quad_state_list.cbegin(), - out_iterator = pass_out->shared_quad_state_list.cbegin(); - out_iterator != pass_out->shared_quad_state_list.cend(); - ++cmp_iterator, ++out_iterator) { - Compare(*cmp_iterator, *out_iterator); - } - for (auto out_iter = pass_out->quad_list.cbegin(), - cmp_iter = pass_cmp->quad_list.cbegin(); - out_iter != pass_out->quad_list.cend(); ++out_iter, ++cmp_iter) - Compare(*cmp_iter, *out_iter); - - for (size_t i = 1; i < pass_out->quad_list.size(); ++i) { - bool same_shared_quad_state_cmp = - pass_cmp->quad_list.ElementAt(i)->shared_quad_state == - pass_cmp->quad_list.ElementAt(i - 1)->shared_quad_state; - bool same_shared_quad_state_out = - pass_out->quad_list.ElementAt(i)->shared_quad_state == - pass_out->quad_list.ElementAt(i - 1)->shared_quad_state; - EXPECT_EQ(same_shared_quad_state_cmp, same_shared_quad_state_out); - } -} - -TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) { - std::unique_ptr<RenderPass> pass_in = RenderPass::Create(); - pass_in->SetAll(1, gfx::Rect(100, 100), gfx::Rect(), gfx::Transform(), - FilterOperations(), FilterOperations(), - gfx::ColorSpace::CreateSRGB(), false, false, false, false); - - // The first SharedQuadState is used. - SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state1_in->SetAll(gfx::Transform(), gfx::Rect(1, 1), gfx::Rect(), - gfx::Rect(), false, true, 1.f, SkBlendMode::kSrcOver, - 0); - - auto* quad1 = pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - quad1->SetAll(shared_state1_in, gfx::Rect(10, 10), gfx::Rect(10, 10), false, - SK_ColorRED, false); - - // The second and third SharedQuadStates are not used. - SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state2_in->SetAll(gfx::Transform(), gfx::Rect(2, 2), gfx::Rect(), - gfx::Rect(), false, true, 1.f, SkBlendMode::kSrcOver, - 0); - - SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state3_in->SetAll(gfx::Transform(), gfx::Rect(3, 3), gfx::Rect(), - gfx::Rect(), false, true, 1.f, SkBlendMode::kSrcOver, - 0); - - // The fourth SharedQuadState is used. - SharedQuadState* shared_state4_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state4_in->SetAll(gfx::Transform(), gfx::Rect(4, 4), gfx::Rect(), - gfx::Rect(), false, true, 1.f, SkBlendMode::kSrcOver, - 0); - - auto* quad2 = pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); - quad2->SetAll(shared_state4_in, gfx::Rect(10, 10), gfx::Rect(10, 10), false, - SK_ColorRED, false); - - // The fifth is not used again. - SharedQuadState* shared_state5_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state5_in->SetAll(gfx::Transform(), gfx::Rect(5, 5), gfx::Rect(), - gfx::Rect(), false, true, 1.f, SkBlendMode::kSrcOver, - 0); - - // 5 SharedQuadStates go in. - ASSERT_EQ(5u, pass_in->shared_quad_state_list.size()); - ASSERT_EQ(2u, pass_in->quad_list.size()); - - CompositorFrame frame_in; - frame_in.render_pass_list.push_back(std::move(pass_in)); - frame_in.metadata.begin_frame_ack.sequence_number = - viz::BeginFrameArgs::kStartingFrameNumber; - - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - IPC::ParamTraits<CompositorFrame>::Write(&msg, frame_in); - - CompositorFrame frame_out; - base::PickleIterator iter(msg); - EXPECT_TRUE(IPC::ParamTraits<CompositorFrame>::Read(&msg, &iter, &frame_out)); - - std::unique_ptr<RenderPass> pass_out = - std::move(frame_out.render_pass_list[0]); - - // 2 SharedQuadStates come out. The first and fourth SharedQuadStates were - // used by quads, and so serialized. Others were not. - ASSERT_EQ(2u, pass_out->shared_quad_state_list.size()); - ASSERT_EQ(2u, pass_out->quad_list.size()); - - EXPECT_EQ(gfx::Rect(1, 1).ToString(), - pass_out->shared_quad_state_list.ElementAt(0) - ->quad_layer_rect.ToString()); - EXPECT_EQ(gfx::Rect(4, 4).ToString(), - pass_out->shared_quad_state_list.ElementAt(1) - ->quad_layer_rect.ToString()); -} - -TEST_F(CCParamTraitsTest, Resources) { - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - gpu::SyncToken arbitrary_token1(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), - 71234838); - arbitrary_token1.SetVerifyFlush(); - gpu::SyncToken arbitrary_token2(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), - 53589793); - arbitrary_token2.SetVerifyFlush(); - - GLbyte arbitrary_mailbox1[GL_MAILBOX_SIZE_CHROMIUM] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}; - - GLbyte arbitrary_mailbox2[GL_MAILBOX_SIZE_CHROMIUM] = { - 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 2}; - - TransferableResource arbitrary_resource1; - arbitrary_resource1.id = 2178312; - arbitrary_resource1.format = viz::RGBA_8888; - arbitrary_resource1.filter = 53; - arbitrary_resource1.size = gfx::Size(37189, 123123); - arbitrary_resource1.mailbox_holder.mailbox.SetName(arbitrary_mailbox1); - arbitrary_resource1.mailbox_holder.texture_target = GL_TEXTURE_2D; - arbitrary_resource1.mailbox_holder.sync_token = arbitrary_token1; - arbitrary_resource1.is_overlay_candidate = true; -#if defined(OS_ANDROID) - arbitrary_resource1.is_backed_by_surface_texture = true; - arbitrary_resource1.wants_promotion_hint = true; -#endif - - TransferableResource arbitrary_resource2; - arbitrary_resource2.id = 789132; - arbitrary_resource2.format = viz::RGBA_4444; - arbitrary_resource2.filter = 47; - arbitrary_resource2.size = gfx::Size(89123, 23789); - arbitrary_resource2.mailbox_holder.mailbox.SetName(arbitrary_mailbox2); - arbitrary_resource2.mailbox_holder.texture_target = GL_TEXTURE_EXTERNAL_OES; - arbitrary_resource2.mailbox_holder.sync_token = arbitrary_token2; - arbitrary_resource2.is_overlay_candidate = false; -#if defined(OS_ANDROID) - arbitrary_resource2.is_backed_by_surface_texture = false; - arbitrary_resource2.wants_promotion_hint = false; -#endif - - std::unique_ptr<RenderPass> renderpass_in = RenderPass::Create(); - renderpass_in->SetNew(1u, gfx::Rect(0, 0, 5, 5), gfx::Rect(), - gfx::Transform()); - - CompositorFrame frame_in; - frame_in.resource_list.push_back(arbitrary_resource1); - frame_in.resource_list.push_back(arbitrary_resource2); - frame_in.render_pass_list.push_back(std::move(renderpass_in)); - frame_in.metadata.begin_frame_ack.sequence_number = - viz::BeginFrameArgs::kStartingFrameNumber; - - IPC::ParamTraits<CompositorFrame>::Write(&msg, frame_in); - - CompositorFrame frame_out; - base::PickleIterator iter(msg); - EXPECT_TRUE(IPC::ParamTraits<CompositorFrame>::Read(&msg, &iter, &frame_out)); - - ASSERT_EQ(2u, frame_out.resource_list.size()); - Compare(arbitrary_resource1, frame_out.resource_list[0]); - Compare(arbitrary_resource2, frame_out.resource_list[1]); -} - -TEST_F(CCParamTraitsTest, SurfaceInfo) { - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - const viz::SurfaceId kArbitrarySurfaceId( - kArbitraryFrameSinkId, - viz::LocalSurfaceId(3, base::UnguessableToken::Create())); - constexpr float kArbitraryDeviceScaleFactor = 0.9f; - const gfx::Size kArbitrarySize(65, 321); - const viz::SurfaceInfo surface_info_in( - kArbitrarySurfaceId, kArbitraryDeviceScaleFactor, kArbitrarySize); - IPC::ParamTraits<viz::SurfaceInfo>::Write(&msg, surface_info_in); - - viz::SurfaceInfo surface_info_out; - base::PickleIterator iter(msg); - EXPECT_TRUE( - IPC::ParamTraits<viz::SurfaceInfo>::Read(&msg, &iter, &surface_info_out)); - - ASSERT_EQ(surface_info_in, surface_info_out); -} - -} // namespace -} // namespace content
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index 418736d3..994b1ce 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc
@@ -618,9 +618,12 @@ } void LayerTreeTest::EndTest() { - if (ended_) - return; - ended_ = true; + { + base::AutoLock hold(test_ended_lock_); + if (ended_) + return; + ended_ = true; + } // For the case where we EndTest during BeginTest(), set a flag to indicate // that the test should end the second BeginTest regains control.
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 5f9d252..20094f1 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h
@@ -130,7 +130,10 @@ TaskGraphRunner* task_graph_runner() const { return task_graph_runner_.get(); } - bool TestEnded() const { return ended_; } + bool TestEnded() const { + base::AutoLock hold(test_ended_lock_); + return ended_; + } LayerTreeHost* layer_tree_host(); gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager() { @@ -202,6 +205,8 @@ bool timed_out_ = false; bool scheduled_ = false; bool started_ = false; + + mutable base::Lock test_ended_lock_; bool ended_ = false; int timeout_seconds_ = false;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 17d49f6d..2cdbd7f 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -563,7 +563,8 @@ } }; -SINGLE_AND_MULTI_THREAD_TEST_F( +// Android Webview only runs in multi-threaded compositing mode. +MULTI_THREAD_TEST_F( LayerTreeHostFreesWorkerContextResourcesOnZeroMemoryLimitSynchronous); // Test if the LTH successfully frees main and worker resources when the @@ -5707,19 +5708,22 @@ void SetVisibleFalseAndQueueSwapPromise() { layer_tree_host()->SetVisible(false); - std::unique_ptr<SwapPromise> swap_promise( - new TestSwapPromise(&swap_promise_result_)); + auto swap_promise = + std::make_unique<TestSwapPromise>(&swap_promise_result_); layer_tree_host()->GetSwapPromiseManager()->QueueSwapPromise( std::move(swap_promise)); } void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, const viz::BeginFrameArgs& args) override { - MainThreadTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&LayerTreeHostTestBreakSwapPromiseForVisibility:: - SetVisibleFalseAndQueueSwapPromise, - base::Unretained(this))); + if (!sent_queue_request_) { + sent_queue_request_ = true; + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&LayerTreeHostTestBreakSwapPromiseForVisibility:: + SetVisibleFalseAndQueueSwapPromise, + base::Unretained(this))); + } } void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl, @@ -5739,11 +5743,10 @@ } TestSwapPromiseResult swap_promise_result_; + bool sent_queue_request_ = false; }; -// Flaky: https://crbug.com/657910 -// SINGLE_AND_MULTI_THREAD_TEST_F( -// LayerTreeHostTestBreakSwapPromiseForVisibility); +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromiseForVisibility); class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { public: @@ -5892,41 +5895,43 @@ LayerTreeHostTest::SetupTree(); ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()->GetUIResourceManager()); - client_.set_bounds(layer_tree_host()->root_layer()->bounds()); } void BeginTest() override { PostSetNeedsCommitToMainThread(); } void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + if (TestEnded()) + return; + host_impl->EvictAllUIResources(); // Existence of evicted UI resources will trigger NEW_CONTENT_TAKES_PRIORITY // mode. Active tree should require high-res to draw after entering this // mode to ensure that high-res tiles are also required for a pending tree // to be activated. EXPECT_TRUE(host_impl->RequiresHighResToDraw()); + + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + &LayerTreeHostTestHighResRequiredAfterEvictingUIResources:: + DeleteResourceAndEndTest, + base::Unretained(this))); } - void DidCommit() override { - int frame = layer_tree_host()->SourceFrameNumber(); - switch (frame) { - case 1: - PostSetNeedsCommitToMainThread(); - break; - case 2: - ui_resource_ = nullptr; - EndTest(); - break; - } + void DeleteResourceAndEndTest() { + // This must be destroyed before the test ends and tears down the + // LayerTreeHost. It causes another commit+activation though, which + // may run before the test exits. + ui_resource_ = nullptr; + EndTest(); } void AfterTest() override {} - FakeContentLayerClient client_; std::unique_ptr<FakeScopedUIResource> ui_resource_; }; -// This test is flaky, see http://crbug.com/386199 -// MULTI_THREAD_TEST_F(LayerTreeHostTestHighResRequiredAfterEvictingUIResources) +MULTI_THREAD_TEST_F(LayerTreeHostTestHighResRequiredAfterEvictingUIResources); class LayerTreeHostTestGpuRasterizationDefault : public LayerTreeHostTest { protected:
diff --git a/cc/trees/layer_tree_host_unittest_picture.cc b/cc/trees/layer_tree_host_unittest_picture.cc index 40aeeb1..205d1f2c 100644 --- a/cc/trees/layer_tree_host_unittest_picture.cc +++ b/cc/trees/layer_tree_host_unittest_picture.cc
@@ -41,14 +41,16 @@ } void BeginTest() override { - activates_ = 0; + // Commit and activate to produce a pending (recycled) layer and an active + // layer. PostSetNeedsCommitToMainThread(); } void DidCommit() override { switch (layer_tree_host()->SourceFrameNumber()) { case 1: - // Activate while there are pending and active twins in place. + // Activate reusing an existing recycled pending layer, to an already + // existing active layer. layer_tree_host()->SetNeedsCommit(); break; case 2: @@ -66,12 +68,9 @@ break; } case 4: - // Active while there are pending and active twins again. + // Activate while there are pending and active twins again. layer_tree_host()->SetNeedsCommit(); break; - case 5: - EndTest(); - break; } } @@ -131,11 +130,14 @@ } ++activates_; + if (activates_ == 5) + EndTest(); } - void AfterTest() override { EXPECT_EQ(5, activates_); } + void AfterTest() override {} - int activates_; + int activates_ = 0; + int picture_id1_; int picture_id2_; };
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 541e264e..00bff77 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc
@@ -839,7 +839,9 @@ } void SingleThreadProxy::ScheduledActionInvalidateLayerTreeFrameSink() { - NOTREACHED(); + // This is an Android WebView codepath, which only uses multi-thread + // compositor. So this should not occur in single-thread mode. + NOTREACHED() << "Android Webview use-case, so multi-thread only"; } void SingleThreadProxy::ScheduledActionPerformImplSideInvalidation() {
diff --git a/chrome/DEPS b/chrome/DEPS index cf50aee..44a9f94 100644 --- a/chrome/DEPS +++ b/chrome/DEPS
@@ -35,6 +35,7 @@ "-tools", # Allow inclusion of WebKit API files. + "+third_party/blink/public/common", "+third_party/blink/public/platform", "+third_party/blink/public/public_buildflags.h", "+third_party/blink/public/web",
diff --git a/chrome/android/java/res/drawable/signin_header_animation.xml b/chrome/android/java/res/drawable/signin_header_animation.xml index 61dfa40..badebed 100644 --- a/chrome/android/java/res/drawable/signin_header_animation.xml +++ b/chrome/android/java/res/drawable/signin_header_animation.xml
@@ -238,21 +238,33 @@ android:valueFrom="-120" android:valueTo="120"/> <objectAnimator - android:duration="700" + android:duration="1900" android:propertyName="rotation" - android:startOffset="1900" android:valueFrom="120" - android:valueTo="-120"/> - <objectAnimator - android:duration="700" - android:propertyName="rotation" - android:startOffset="1900" - android:valueFrom="-120" android:valueTo="120"/> <objectAnimator android:duration="700" android:propertyName="rotation" - android:startOffset="1900" + android:valueFrom="120" + android:valueTo="-120"/> + <objectAnimator + android:duration="1900" + android:propertyName="rotation" + android:valueFrom="-120" + android:valueTo="-120"/> + <objectAnimator + android:duration="700" + android:propertyName="rotation" + android:valueFrom="-120" + android:valueTo="120"/> + <objectAnimator + android:duration="1900" + android:propertyName="rotation" + android:valueFrom="120" + android:valueTo="120"/> + <objectAnimator + android:duration="700" + android:propertyName="rotation" android:valueFrom="120" android:valueTo="-120"/> </set>
diff --git a/chrome/android/java/res/layout/contextual_suggestions_footer.xml b/chrome/android/java/res/layout/contextual_suggestions_footer.xml deleted file mode 100644 index aed843d8..0000000 --- a/chrome/android/java/res/layout/contextual_suggestions_footer.xml +++ /dev/null
@@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2018 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<!-- TODO(twellington): Remove this XML after the contextual suggestions feedback link is - shown in the feature's overflow menu. --> -<!-- - The TextView is wrapped in a FrameLayout so that the entire view can be centered and use a width - of wrap_content. A RecyclerView does not support layout_gravity alignment. The width of the - TextView must be wrap_content because otherwise clicks on the padding can activate its clickable - span. - See http://crbug.com/660083 for more details. ---> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="4dp" - android:paddingBottom="@dimen/modern_suggestions_footer_padding_bottom" - android:paddingStart="16dp" - android:paddingEnd="16dp"> - <org.chromium.ui.widget.TextViewWithClickableSpans - android:id="@+id/text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:textAppearance="@style/BlueLink3" /> -</FrameLayout> \ No newline at end of file
diff --git a/chrome/android/java/res/layout/snackbar.xml b/chrome/android/java/res/layout/snackbar.xml index b13bb93..b73a40e 100644 --- a/chrome/android/java/res/layout/snackbar.xml +++ b/chrome/android/java/res/layout/snackbar.xml
@@ -3,24 +3,48 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="bottom|start" - android:orientation="vertical" > + android:layout_gravity="bottom|start" > <View - android:id="@+id/snackbar_shadow" + android:id="@+id/snackbar_shadow_top" android:layout_width="match_parent" android:layout_height="@dimen/snackbar_shadow_height" + android:layout_above="@+id/snackbar" + android:layout_toEndOf="@+id/snackbar_shadow_left" + android:layout_toStartOf="@+id/snackbar_shadow_right" android:background="@drawable/infobar_shadow_top" android:visibility="gone" /> + <View + android:id="@+id/snackbar_shadow_left" + android:layout_width="@dimen/snackbar_shadow_height" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_alignTop="@id/snackbar_shadow_top" + android:background="@drawable/infobar_shadow_left" + android:visibility="gone" /> + + <View + android:id="@+id/snackbar_shadow_right" + android:layout_width="@dimen/snackbar_shadow_height" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_alignTop="@id/snackbar_shadow_top" + android:background="@drawable/infobar_shadow_left" + android:scaleX="-1" + android:visibility="gone" /> + <LinearLayout android:id="@+id/snackbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="@dimen/snackbar_min_height" + android:layout_alignParentBottom="true" + android:layout_toEndOf="@id/snackbar_shadow_left" + android:layout_toStartOf="@id/snackbar_shadow_right" android:orientation="horizontal" > <ImageView android:id="@+id/snackbar_profile_image" @@ -57,4 +81,4 @@ android:textSize="@dimen/text_size_medium" style="@style/ButtonCompatBorderless" /> </LinearLayout> -</LinearLayout> +</RelativeLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java index e6ce9c4..158a0ad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
@@ -22,12 +22,6 @@ for (ContextualSuggestionsCluster cluster : clusters) { addChild(cluster); } - - // TODO(twellington): Remove when feedback link is moved into overflow menu - // (https://crbug.com/831782). - if (clusters.size() > 0) { - addChild(new ContextualSuggestionsFooter()); - } } /** Remove all clusters and detach itself from its parent. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java index 8f767de..f8aa7e0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
@@ -39,9 +39,7 @@ return new ContextualSuggestionCardViewHolder(mRecyclerView, mContextMenuManager, mUiDelegate, mUiConfig, OfflinePageBridge.getForProfile(mProfile)); - case ItemViewType.FOOTER: - return new ContextualSuggestionsFooter.ViewHolder( - mRecyclerView, mUiDelegate.getNavigationDelegate()); + default: assert false; return null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsFooter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsFooter.java deleted file mode 100644 index 3a392e60..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsFooter.java +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.contextual_suggestions; - -import android.text.SpannableString; -import android.text.method.LinkMovementMethod; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ntp.cards.ItemViewType; -import org.chromium.chrome.browser.ntp.cards.Leaf; -import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; -import org.chromium.chrome.browser.ntp.cards.NodeVisitor; -import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate; -import org.chromium.ui.text.NoUnderlineClickableSpan; - -/** - * A footer to show some a "Send Feedback" link. - * TODO(twellington): Remove when feedback link is moved into overflow menu - * (https://crbug.com/831782). - */ -public class ContextualSuggestionsFooter extends Leaf { - @Override - @ItemViewType - protected int getItemViewType() { - return ItemViewType.FOOTER; - } - - @Override - protected void onBindViewHolder(NewTabPageViewHolder holder) { - // Nothing to do (the footer view is static). - } - - @Override - public void visitItems(NodeVisitor visitor) { - visitor.visitFooter(); - } - - /** - * The {@code ViewHolder} for the {@link Footer}. - */ - public static class ViewHolder extends NewTabPageViewHolder { - public ViewHolder(ViewGroup root, final SuggestionsNavigationDelegate navigationDelegate) { - super(LayoutInflater.from(root.getContext()) - .inflate(R.layout.contextual_suggestions_footer, root, false)); - - NoUnderlineClickableSpan link = new NoUnderlineClickableSpan() { - @Override - public void onClick(View view) { - navigationDelegate.showFeedback(); - } - }; - - SpannableString text = new SpannableString( - root.getResources().getString(R.string.sad_tab_send_feedback_label)); - text.setSpan(link, 0, text.length(), 0); - - TextView textView = (TextView) itemView.findViewById(R.id.text); - textView.setText(text); - textView.setMovementMethod(LinkMovementMethod.getInstance()); - } - } -} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java index 6c7e34f4..13b59bd0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
@@ -166,6 +166,13 @@ @Override public void requestSuggestions(String url) { + // Guard against null tabs when requesting suggestions. https://crbug.com/836672. + if (mTabModelSelector.getCurrentTab() == null + || mTabModelSelector.getCurrentTab().getWebContents() == null) { + assert false; + return; + } + reportEvent(ContextualSuggestionsEvent.FETCH_REQUESTED); mCurrentRequestUrl = url; mSuggestionsSource.fetchSuggestions(url, (suggestionsResult) -> { @@ -193,7 +200,8 @@ @Override public void reportFetchDelayed(WebContents webContents) { - if (mTabModelSelector.getCurrentTab().getWebContents() == webContents) { + if (mTabModelSelector.getCurrentTab() != null + && mTabModelSelector.getCurrentTab().getWebContents() == webContents) { reportEvent(ContextualSuggestionsEvent.FETCH_DELAYED); } } @@ -332,6 +340,15 @@ } private void reportEvent(@ContextualSuggestionsEvent int event) { + if (mTabModelSelector.getCurrentTab() == null + || mTabModelSelector.getCurrentTab().getWebContents() == null) { + // This method is not expected to be called if the current tab or webcontents are null. + // If this assert is hit, please alert someone on the Chrome Explore on Content team. + // See https://crbug.com/836672. + assert false; + return; + } + mSuggestionsSource.reportEvent(mTabModelSelector.getCurrentTab().getWebContents(), event); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java index 127ea71a..2dd2698 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java
@@ -185,7 +185,11 @@ @Override public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { - if (tab == null) return; + if (tab == null) { + if (mCurrentTab != null) clearState(); + mCurrentTab = null; + return; + } if (mCurrentTab != null && mCurrentTab != tab) { clearState(); @@ -278,6 +282,11 @@ } private void postDelayedFetch(final String url, final Tab tab, long delayMillis) { + if (tab == null) { + assert false; + return; + } + mDelegate.reportFetchDelayed(tab.getWebContents()); ThreadUtils.postOnUiThreadDelayed(new Runnable() { @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java index bf12a23..5880a7a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -67,6 +67,7 @@ import org.chromium.chrome.browser.metrics.PageLoadMetrics; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.page_info.PageInfoPopup; +import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge; import org.chromium.chrome.browser.rappor.RapporServiceBridge; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; @@ -872,6 +873,14 @@ public final void finishAndClose(boolean reparenting) { if (mIsClosing) return; mIsClosing = true; + + // Notify the window is closing so as to abort invoking payment app early. + if (mIntentDataProvider.isForPaymentRequest() + && getActivityTab().getWebContents() != null) { + ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow( + getActivityTab().getWebContents()); + } + if (!reparenting) { // Closing the activity destroys the renderer as well. Re-create a spare renderer some // time after, so that we have one ready for the next tab open. This does not increase
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java index 0b932114..341c73b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -573,4 +573,11 @@ boolean shouldShowDownloadButton() { return !mDisableDownload; } + + /** + * @return Whether the Custom Tab is opened for payment request. + */ + boolean isForPaymentRequest() { + return mUiType == CUSTOM_TABS_UI_TYPE_PAYMENT_REQUEST; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java index d18f73d..48f4bb3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java
@@ -60,17 +60,17 @@ @CalledByNative private static boolean getPowerConditions() { - return DeviceConditions.isPowerConnected(ContextUtils.getApplicationContext()); + return DeviceConditions.isCurrentlyPowerConnected(ContextUtils.getApplicationContext()); } @CalledByNative private static int getBatteryConditions() { - return DeviceConditions.getBatteryPercentage(ContextUtils.getApplicationContext()); + return DeviceConditions.getCurrentBatteryPercentage(ContextUtils.getApplicationContext()); } @CalledByNative private static int getNetworkConditions() { - return DeviceConditions.getNetConnectionType(ContextUtils.getApplicationContext()); + return DeviceConditions.getCurrentNetConnectionType(ContextUtils.getApplicationContext()); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/DeviceConditions.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/DeviceConditions.java index ab02368..b47236a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/DeviceConditions.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/DeviceConditions.java
@@ -19,65 +19,75 @@ import org.chromium.net.NetworkChangeNotifier; /** - * Device network and power conditions. - * TODO(https://crbug.com/833644): improve API and implementation of DeviceConditions. + * Device network and power conditions that can be either checked individually with the specific + * static methods or gathered all at once using {@link.getCurrent}. */ public class DeviceConditions { - private final boolean mPowerConnected; - private final int mBatteryPercentage; - private final int mNetConnectionType; - private final boolean mPowerSaveOn; + // Battery and power related variables. + private boolean mPowerConnected = false; + private int mBatteryPercentage = 0; + private boolean mPowerSaveOn = false; + + // Network related variables. + private int mNetConnectionType = ConnectionType.CONNECTION_NONE; + private boolean mActiveNetworkMetered = false; /** - * Creates set of device network and power conditions. - * @param powerConnected whether device is connected to power - * @param batteryPercentage percentage (0-100) of remaining battery power - * @param connectionType the org.chromium.net.ConnectionType value for the network connection + * Creates a DeviceConditions instance that stores a snapshot of the current set of device + * network and power conditions. Also used when setting up tests simulating specific conditions. */ + @VisibleForTesting public DeviceConditions(boolean powerConnected, int batteryPercentage, int netConnectionType, - boolean powerSaveOn) { + boolean powerSaveOn, boolean activeNetworkMetered) { mPowerConnected = powerConnected; mBatteryPercentage = batteryPercentage; - mNetConnectionType = netConnectionType; mPowerSaveOn = powerSaveOn; + mNetConnectionType = netConnectionType; + mActiveNetworkMetered = activeNetworkMetered; } @VisibleForTesting DeviceConditions() { - mPowerConnected = false; - mBatteryPercentage = 0; - mNetConnectionType = ConnectionType.CONNECTION_NONE; - mPowerSaveOn = false; } /** * Returns the current device conditions if the device supports obtaining battery status. - * Otherwise it will return null. May be overridden for testing. + * Otherwise it will return null. */ - public static DeviceConditions getCurrentConditions(Context context) { + public static DeviceConditions getCurrent(Context context) { Intent batteryStatus = getBatteryStatus(context); if (batteryStatus == null) return null; - return new DeviceConditions(isPowerConnected(context), getBatteryPercentage(context), - getNetConnectionType(context), getInPowerSaveMode(context)); + return new DeviceConditions(isCurrentlyPowerConnected(context, batteryStatus), + getCurrentBatteryPercentage(context, batteryStatus), + getCurrentNetConnectionType(context), isCurrentlyInPowerSaveMode(context), + isCurrentActiveNetworkMetered(context)); } - /** @return Whether power is connected. */ - public static boolean isPowerConnected(Context context) { + /** @return Whether the device is connected to a power source. */ + public static boolean isCurrentlyPowerConnected(Context context) { Intent batteryStatus = getBatteryStatus(context); if (batteryStatus == null) return false; + return isCurrentlyPowerConnected(context, batteryStatus); + } + + private static boolean isCurrentlyPowerConnected(Context context, Intent batteryStatus) { int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isConnected = (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL); return isConnected; } - /** @return Battery percentage. */ - public static int getBatteryPercentage(Context context) { + /** @return The battery percentage or 0 if the device can't provide that information. */ + public static int getCurrentBatteryPercentage(Context context) { Intent batteryStatus = getBatteryStatus(context); if (batteryStatus == null) return 0; + return getCurrentBatteryPercentage(context, batteryStatus); + } + + private static int getCurrentBatteryPercentage(Context context, Intent batteryStatus) { int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1); if (scale == 0) return 0; @@ -87,18 +97,18 @@ } /** - * @return true if the device is in power save mode. + * @return Whether the device is in power save mode. This feature is only available in Lollipop + * and later versions of Android devices so it will return false for earlier versions. */ - public static boolean getInPowerSaveMode(Context context) { - // The PowerSaver feature is only available in Lollipop+, return false if earlier OS. + public static boolean isCurrentlyInPowerSaveMode(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return false; } - return getInPowerSaveModeLollipop(context); + return isCurrentlyInPowerSaveModeLollipop(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private static boolean getInPowerSaveModeLollipop(Context context) { + private static boolean isCurrentlyInPowerSaveModeLollipop(Context context) { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); return powerManager.isPowerSaveMode(); } @@ -107,7 +117,7 @@ * @return Network connection type, where possible values are defined by * org.chromium.net.ConnectionType. */ - public static int getNetConnectionType(Context context) { + public static int getCurrentNetConnectionType(Context context) { int connectionType = ConnectionType.CONNECTION_NONE; // If we are starting in the background, native portion might not be initialized. @@ -134,37 +144,12 @@ /** * @return true if the active network is a metered network */ - public static boolean isActiveNetworkMetered(Context context) { + public static boolean isCurrentActiveNetworkMetered(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); return cm.isActiveNetworkMetered(); } - /** @return Whether power is connected. */ - public boolean isPowerConnected() { - return mPowerConnected; - } - - /** @return Battery percentage. */ - public int getBatteryPercentage() { - return mBatteryPercentage; - } - - /** - * @return Network connection type, where possible values are defined by - * org.chromium.net.ConnectionType. - */ - public int getNetConnectionType() { - return mNetConnectionType; - } - - /** - * @return true if the device is in power save mode. - */ - public boolean inPowerSaveMode() { - return mPowerSaveOn; - } - private static Intent getBatteryStatus(Context context) { IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); // Note this is a sticky intent, so we aren't really registering a receiver, just getting @@ -188,4 +173,32 @@ // Since NetworkConnectivityManager doesn't understand the other types, call them UNKNOWN. return ConnectionType.CONNECTION_UNKNOWN; } + + /** Returns whether power is connected. */ + public boolean isPowerConnected() { + return mPowerConnected; + } + + /** Returns the remaining battery power percentage (0-100). */ + public int getBatteryPercentage() { + return mBatteryPercentage; + } + + /** Returns whether the device is in power save mode. */ + public boolean isInPowerSaveMode() { + return mPowerSaveOn; + } + + /** + * Returns the network connection type based on the values defined in + * org.chromium.net.ConnectionType. + */ + public int getNetConnectionType() { + return mNetConnectionType; + } + + /** Returns whether network connection is metered. */ + public boolean isActiveNetworkMetered() { + return mActiveNetworkMetered; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java index e3f16a2f..18ffe12ae 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java
@@ -101,7 +101,7 @@ long taskScheduledTimeMillis = TaskExtrasPacker.unpackTimeFromBundle(taskExtras); OfflinePageUtils.recordWakeupUMA(context, taskScheduledTimeMillis); - DeviceConditions deviceConditions = DeviceConditions.getCurrentConditions(context); + DeviceConditions deviceConditions = DeviceConditions.getCurrent(context); return bridge.startScheduledProcessing(deviceConditions, callback); } @@ -111,7 +111,7 @@ TriggerConditions triggerConditions = TaskExtrasPacker.unpackTriggerConditionsFromBundle(taskExtras); - DeviceConditions deviceConditions = DeviceConditions.getCurrentConditions(context); + DeviceConditions deviceConditions = DeviceConditions.getCurrent(context); if (!areBatteryConditionsMet(deviceConditions, triggerConditions)) { Log.d(TAG, "Battery percentage is lower than minimum to start processing"); return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java index 8d7a0797..09829b19 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
@@ -319,7 +319,7 @@ * @param context android context */ public static void recordWakeupUMA(Context context, long taskScheduledTimeMillis) { - DeviceConditions deviceConditions = DeviceConditions.getCurrentConditions(context); + DeviceConditions deviceConditions = DeviceConditions.getCurrent(context); if (deviceConditions == null) return; // Report charging state.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTask.java index caba6e8..89c87522 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTask.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTask.java
@@ -127,7 +127,8 @@ return NativeBackgroundTask.DONE; } - if (DeviceConditions.getNetConnectionType(context) != ConnectionType.CONNECTION_NONE) { + if (DeviceConditions.getCurrentNetConnectionType(context) + != ConnectionType.CONNECTION_NONE) { scheduleTaskWhenOnline(); // We schedule ourselves and return DONE because we want to reschedule using the normal
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java index c939c80..98ba4ab 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java
@@ -63,21 +63,20 @@ // specific Android devices. final DeviceConditions deviceConditions; if (!sSkipConditionCheckingForTesting) { - deviceConditions = DeviceConditions.getCurrentConditions(context); + deviceConditions = DeviceConditions.getCurrent(context); } else { deviceConditions = null; } // Note: when |deviceConditions| is null native is always loaded because with the evidence // we have so far only specific devices that do not run on batteries actually return null. - if (deviceConditions != null - && (!areBatteryConditionsMet(deviceConditions) - || !areNetworkConditionsMet(context, deviceConditions) - || deviceConditions.inPowerSaveMode())) { - return NativeBackgroundTask.RESCHEDULE; + if (deviceConditions == null + || (areBatteryConditionsMet(deviceConditions) + && areNetworkConditionsMet(deviceConditions))) { + return NativeBackgroundTask.LOAD_NATIVE; } - return NativeBackgroundTask.LOAD_NATIVE; + return NativeBackgroundTask.RESCHEDULE; } /** @@ -148,18 +147,19 @@ /** Whether battery conditions (on power or enough battery percentage) are met. */ private boolean areBatteryConditionsMet(DeviceConditions deviceConditions) { - return deviceConditions.isPowerConnected() - || (deviceConditions.getBatteryPercentage() - >= MINIMUM_BATTERY_PERCENTAGE_FOR_PREFETCHING) + return (!deviceConditions.isInPowerSaveMode() + && (deviceConditions.isPowerConnected() + || (deviceConditions.getBatteryPercentage() + >= MINIMUM_BATTERY_PERCENTAGE_FOR_PREFETCHING))) || mLimitlessPrefetchingEnabled; } /** Whether network conditions are met. */ - private boolean areNetworkConditionsMet(Context context, DeviceConditions deviceConditions) { + private boolean areNetworkConditionsMet(DeviceConditions deviceConditions) { if (mLimitlessPrefetchingEnabled) { return deviceConditions.getNetConnectionType() != ConnectionType.CONNECTION_NONE; } - return !DeviceConditions.isActiveNetworkMetered(context) + return !deviceConditions.isActiveNetworkMetered() && deviceConditions.getNetConnectionType() == ConnectionType.CONNECTION_WIFI; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java index ecd239d..654da5c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -14,7 +14,6 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.Rect; @@ -84,7 +83,6 @@ import org.chromium.chrome.browser.widget.FadingBackgroundView; import org.chromium.chrome.browser.widget.TintedImageButton; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; -import org.chromium.components.security_state.ConnectionSecurityLevel; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.common.ContentUrlConstants; @@ -1294,43 +1292,6 @@ } /** - * @param provider The {@link ToolbarDataProvider}. - * @param resources The Resources for the Context. - * @return The {@link ColorStateList} to use to tint the security state icon. - */ - public static ColorStateList getColorStateList( - ToolbarDataProvider provider, Resources resources) { - int securityLevel = provider.getSecurityLevel(); - - ColorStateList list = null; - int color = provider.getPrimaryColor(); - boolean needLightIcon = ColorUtils.shouldUseLightForegroundOnBackground(color); - if (provider.isIncognito() || needLightIcon) { - // For a dark theme color, use light icons. - list = ApiCompatibilityUtils.getColorStateList(resources, R.color.light_mode_tint); - } else if (provider.isUsingBrandColor()) { - // For theme colors which are not dark and are also not - // light enough to warrant an opaque URL bar, use dark - // icons. - list = ApiCompatibilityUtils.getColorStateList(resources, R.color.dark_mode_tint); - } else { - // For the default toolbar color, use a green or red icon. - if (securityLevel == ConnectionSecurityLevel.DANGEROUS) { - assert !provider.shouldDisplaySearchTerms(); - list = ApiCompatibilityUtils.getColorStateList(resources, R.color.google_red_700); - } else if (!provider.shouldDisplaySearchTerms() - && (securityLevel == ConnectionSecurityLevel.SECURE - || securityLevel == ConnectionSecurityLevel.EV_SECURE)) { - list = ApiCompatibilityUtils.getColorStateList(resources, R.color.google_green_700); - } else { - list = ApiCompatibilityUtils.getColorStateList(resources, R.color.dark_mode_tint); - } - } - assert list != null : "Missing ColorStateList for Security Button."; - return list; - } - - /** * Updates the security icon displayed in the LocationBar. */ @Override @@ -1342,7 +1303,7 @@ } else { // ImageView#setImageResource is no-op if given resource is the current one. mSecurityButton.setImageResource(id); - mSecurityButton.setTint(getColorStateList(mToolbarDataProvider, getResources())); + mSecurityButton.setTint(mToolbarDataProvider.getSecurityIconColorStateList()); } updateVerboseStatusVisibility();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java index c2894b6..c39f9fd5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -545,6 +545,9 @@ if (mUrlDirectionListener != null) { mUrlDirectionListener.onUrlDirectionChanged(urlDirection); } + + // Ensure the display text is visible after updating the URL direction. + scrollDisplayText(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java index 70586f8..3e8489d8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
@@ -154,7 +154,6 @@ @CalledByNative private void addResetCertDecisionsButton(String label) { - assert mNativeConnectionInfoPopup != 0; assert mResetCertDecisionsButton == null; mResetCertDecisionsButton = new Button(mContext);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java index 8c33f39..21d0be2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -236,6 +236,10 @@ nativeAbortPaymentApp(webContents, registrationId, callback); } + public static void onClosingPaymentAppWindow(WebContents webContents) { + nativeOnClosingPaymentAppWindow(webContents); + } + @CalledByNative private static String[] getSupportedMethodsFromMethodData(PaymentMethodData data) { return data.supportedMethods; @@ -436,4 +440,6 @@ private static native void nativeCanMakePayment(WebContents webContents, long registrationId, String topLevelOrigin, String paymentRequestOrigin, PaymentMethodData[] methodData, PaymentDetailsModifier[] modifiers, CanMakePaymentCallback callback); + + private static native void nativeOnClosingPaymentAppWindow(WebContents webContents); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java index b550e0d..0b1ee27 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.searchwidget; +import android.content.res.ColorStateList; + import org.chromium.base.library_loader.LibraryLoader; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.profiles.Profile; @@ -111,6 +113,11 @@ } @Override + public ColorStateList getSecurityIconColorStateList() { + return null; + } + + @Override public boolean shouldDisplaySearchTerms() { return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java index 419700c..b2e9b824 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarView.java
@@ -45,7 +45,6 @@ private final TemplatePreservingTextView mMessageView; private final TextView mActionButtonView; private final ImageView mProfileImageView; - private final View mShadowView; private final int mAnimationDuration; private final boolean mIsTablet; private ViewGroup mOriginalParent; @@ -100,7 +99,6 @@ mActionButtonView = (TextView) mContainerView.findViewById(R.id.snackbar_button); mActionButtonView.setOnClickListener(listener); mProfileImageView = (ImageView) mContainerView.findViewById(R.id.snackbar_profile_image); - mShadowView = mContainerView.findViewById(R.id.snackbar_shadow); updateInternal(snackbar, false); } @@ -240,13 +238,16 @@ int backgroundColor = snackbar.getBackgroundColor(); if (backgroundColor == 0) { backgroundColor = ApiCompatibilityUtils.getColor(mContainerView.getResources(), - useModernDesign() ? R.color.modern_primary_color - : R.color.snackbar_background_color); + FeatureUtilities.isChromeModernDesignEnabled() + ? R.color.modern_primary_color + : R.color.snackbar_background_color); } int textAppearanceResId = snackbar.getTextAppearance(); if (textAppearanceResId == 0) { - textAppearanceResId = useModernDesign() ? R.style.BlackBodyDefault : R.style.WhiteBody; + textAppearanceResId = FeatureUtilities.isChromeModernDesignEnabled() + ? R.style.BlackBodyDefault + : R.style.WhiteBody; } ApiCompatibilityUtils.setTextAppearance(mMessageView, textAppearanceResId); @@ -274,10 +275,15 @@ mProfileImageView.setVisibility(View.GONE); } - if (useModernDesign()) { + if (FeatureUtilities.isChromeModernDesignEnabled()) { mActionButtonView.setTextColor(ApiCompatibilityUtils.getColor( mContainerView.getResources(), R.color.blue_when_enabled)); - mShadowView.setVisibility(View.VISIBLE); + + mContainerView.findViewById(R.id.snackbar_shadow_top).setVisibility(View.VISIBLE); + if (mIsTablet) { + mContainerView.findViewById(R.id.snackbar_shadow_left).setVisibility(View.VISIBLE); + mContainerView.findViewById(R.id.snackbar_shadow_right).setVisibility(View.VISIBLE); + } } return true; } @@ -321,8 +327,4 @@ view.setText(text); } } - - private boolean useModernDesign() { - return !mIsTablet && FeatureUtilities.isChromeModernDesignEnabled(); - } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegate.java index 1b01bfd..6699f35 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegate.java
@@ -49,7 +49,4 @@ */ @Nullable Tab openUrl(int windowOpenDisposition, LoadUrlParams loadUrlParams); - - /** Start an activity prompting the user to enter feedback. */ - void showFeedback(); } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java index 85dbde7..1f61d29f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
@@ -16,7 +16,6 @@ import org.chromium.chrome.browser.device.DeviceClassManager; import org.chromium.chrome.browser.download.DownloadMetrics; import org.chromium.chrome.browser.download.DownloadUtils; -import org.chromium.chrome.browser.help.HelpAndFeedback; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.ntp.NewTabPageUma; @@ -205,14 +204,6 @@ return loadingTab; } - @Override - public void showFeedback() { - Tab currentTab = mTabModelSelector.getCurrentTab(); - HelpAndFeedback.getInstance(mActivity).showFeedback(mActivity, Profile.getLastUsedProfile(), - currentTab != null ? currentTab.getUrl() : null, - /* categoryTag = */ null); - } - private boolean openRecentTabSnippet(SnippetArticle article) { TabModel tabModel = mTabModelSelector.getModel(false); int tabId = article.getRecentTabId();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java index 2977b626..2040437 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -550,8 +550,7 @@ } else { // ImageView#setImageResource is no-op if given resource is the current one. mSecurityButton.setImageResource(securityIconResource); - mSecurityButton.setTint( - LocationBarLayout.getColorStateList(getToolbarDataProvider(), getResources())); + mSecurityButton.setTint(getToolbarDataProvider().getSecurityIconColorStateList()); mAnimDelegate.showSecurityButton(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java index e10b814..bd91084 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.toolbar; +import android.content.res.ColorStateList; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -109,6 +110,11 @@ int getSecurityIconResource(boolean isTablet); /** + * @return The {@link ColorStateList} to use to tint the security state icon. + */ + ColorStateList getSecurityIconColorStateList(); + + /** * @return Whether or not we should display search terms instead of a URL for query in omnibox. */ boolean shouldDisplaySearchTerms();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java index d4f7f48a..cfa566b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
@@ -234,6 +234,11 @@ } @Override + public ColorStateList getSecurityIconColorStateList() { + return null; + } + + @Override public boolean shouldDisplaySearchTerms() { return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java index b898050..ba54bf4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -190,7 +190,7 @@ mActivity = activity; mActionBarDelegate = new ViewShiftingActionBarDelegate(activity, controlContainer); - mToolbarModel = new ToolbarModel(activity.getBottomSheet(), + mToolbarModel = new ToolbarModel(activity, activity.getBottomSheet(), activity.supportsModernDesign() && FeatureUtilities.isChromeModernDesignEnabled()); mControlContainer = controlContainer; assert mControlContainer != null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java index 3454805..c10d0963 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
@@ -5,10 +5,13 @@ package org.chromium.chrome.browser.toolbar; import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.text.TextUtils; +import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ContextUtils; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; @@ -39,8 +42,10 @@ * Provides a way of accessing toolbar data and state. */ public class ToolbarModel implements ToolbarDataProvider { + private final Context mContext; private final BottomSheet mBottomSheet; private final boolean mUseModernDesign; + private Tab mTab; private boolean mIsIncognito; private int mPrimaryColor; @@ -57,15 +62,18 @@ /** * Default constructor for this class. + * @param context The Context used for styling the toolbar visuals. * @param bottomSheet The {@link BottomSheet} for the activity displaying this toolbar. * @param useModernDesign Whether the modern design should be used for the toolbar represented * by this model. */ - public ToolbarModel(@Nullable BottomSheet bottomSheet, boolean useModernDesign) { + public ToolbarModel( + Context context, @Nullable BottomSheet bottomSheet, boolean useModernDesign) { + mContext = context; mBottomSheet = bottomSheet; mUseModernDesign = useModernDesign; - mPrimaryColor = ColorUtils.getDefaultThemeColor( - ContextUtils.getApplicationContext().getResources(), useModernDesign, false); + mPrimaryColor = + ColorUtils.getDefaultThemeColor(context.getResources(), useModernDesign, false); } /** @@ -377,6 +385,41 @@ } @Override + public ColorStateList getSecurityIconColorStateList() { + int securityLevel = getSecurityLevel(); + + Resources resources = mContext.getResources(); + ColorStateList list = null; + int color = getPrimaryColor(); + boolean needLightIcon = ColorUtils.shouldUseLightForegroundOnBackground(color); + if (isIncognito() || needLightIcon) { + // For a dark theme color, use light icons. + list = ApiCompatibilityUtils.getColorStateList(resources, R.color.light_mode_tint); + } else if (isUsingBrandColor() + || ChromeFeatureList.isEnabled( + ChromeFeatureList.OMNIBOX_HIDE_SCHEME_DOMAIN_IN_STEADY_STATE)) { + // For theme colors which are not dark and are also not + // light enough to warrant an opaque URL bar, use dark + // icons. + list = ApiCompatibilityUtils.getColorStateList(resources, R.color.dark_mode_tint); + } else { + // For the default toolbar color, use a green or red icon. + if (securityLevel == ConnectionSecurityLevel.DANGEROUS) { + assert !shouldDisplaySearchTerms(); + list = ApiCompatibilityUtils.getColorStateList(resources, R.color.google_red_700); + } else if (!shouldDisplaySearchTerms() + && (securityLevel == ConnectionSecurityLevel.SECURE + || securityLevel == ConnectionSecurityLevel.EV_SECURE)) { + list = ApiCompatibilityUtils.getColorStateList(resources, R.color.google_green_700); + } else { + list = ApiCompatibilityUtils.getColorStateList(resources, R.color.dark_mode_tint); + } + } + assert list != null : "Missing ColorStateList for Security Button."; + return list; + } + + @Override public boolean shouldDisplaySearchTerms() { return (mIgnoreSecurityLevelForSearchTerms || securityLevelSafeForQueryInOmnibox()) && isUrlApplicableToDisplaySearchTerms(getCurrentUrl());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java index 6952599..f407ac6b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -2095,7 +2095,8 @@ // is only needed for RTL calculations, we proceed if the location bar is showing // LTR content. boolean isLocationBarRtl = ApiCompatibilityUtils.isLayoutRtl(mLocationBar); - if (!isLocationBarRtl || mUrlBar.getLayout() != null) { + if (!TextUtils.isEmpty(mUrlBar.getText()) + && (!isLocationBarRtl || mUrlBar.getLayout() != null)) { int urlBarStartScrollX = 0; if (TextUtils.equals(mUrlBar.getText(), mUrlTextPreFocus)) { urlBarStartScrollX = mUrlScrollXPositionPreFocus;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java index 2af564c..5c02419 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
@@ -15,7 +15,8 @@ /** * Performs native VrShell initialization. */ - void initializeNative(boolean forWebVr, boolean webVrAutopresentationExpected, boolean inCct); + void initializeNative(boolean forWebVr, boolean webVrAutopresentationExpected, boolean inCct, + boolean isStandaloneVrDevice); /** * Pauses VrShell. @@ -78,21 +79,6 @@ void navigateBack(); /** - * Asks VrShell to reload the current page. - */ - void reloadTab(); - - /** - * Asks VrShell to open a new tab. - */ - void openNewTab(boolean incognito); - - /** - * Asks VrShell to close all incognito tabs. - */ - void closeAllIncognitoTabs(); - - /** * Simulates a user accepting the currently visible DOFF prompt. */ void acceptDoffPromptForTesting();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java index 4ebcbf0..c4722bf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -1231,13 +1231,6 @@ // UI which is gone. assert !mPaused; if (mInVr) return; - if (!mRequestedWebVr && !tentativeWebVrMode && !mAutopresentWebVr - && getVrClassesWrapper().bootsToVr()) { - // We don't support VR browsing on standalone devices, so if we try to enter browsing - // mode, just drop back to the 2D-in-VR path. - cancelPendingVrEntry(); - return; - } if (mNativeVrShellDelegate == 0) { cancelPendingVrEntry(); return; @@ -1258,8 +1251,8 @@ addVrViews(); boolean webVrMode = mRequestedWebVr || tentativeWebVrMode || mAutopresentWebVr; - mVrShell.initializeNative( - webVrMode, mAutopresentWebVr, mActivity instanceof CustomTabActivity); + mVrShell.initializeNative(webVrMode, mAutopresentWebVr, + mActivity instanceof CustomTabActivity, getVrClassesWrapper().bootsToVr()); mVrShell.setWebVrModeEnabled(webVrMode); // We're entering VR, but not in WebVr mode. @@ -1606,11 +1599,6 @@ return; } - if (getVrClassesWrapper().bootsToVr()) { - shutdownVr(true /* disableVrMode */, true /* stayingInChrome */); - return; - } - if (!isVrBrowsingEnabled()) { if (isDaydreamCurrentViewerInternal()) { getVrDaydreamApi().launchVrHomescreen();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java index 52a9287..d16655aa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -314,8 +314,8 @@ @Override @TargetApi(Build.VERSION_CODES.N) - public void initializeNative( - boolean forWebVr, boolean webVrAutopresentationExpected, boolean inCct) { + public void initializeNative(boolean forWebVr, boolean webVrAutopresentationExpected, + boolean inCct, boolean isStandaloneVrDevice) { Tab tab = mActivity.getActivityTab(); if (mActivity.isInOverviewMode() || tab == null) { launchNTP(); @@ -360,7 +360,8 @@ mNativeVrShell = nativeInit(mDelegate, forWebVr, webVrAutopresentationExpected, inCct, !mVrBrowsingEnabled, hasOrCanRequestAudioPermission, getGvrApi().getNativeGvrContext(), mReprojectedRendering, displayWidthMeters, - displayHeightMeters, dm.widthPixels, dm.heightPixels, pauseContent, lowDensity); + displayHeightMeters, dm.widthPixels, dm.heightPixels, pauseContent, lowDensity, + isStandaloneVrDevice); swapToTab(tab); createTabList(); @@ -981,24 +982,56 @@ updateHistoryButtonsVisibility(); } - @Override @CalledByNative public void reloadTab() { mTab.reload(); } - @Override @CalledByNative public void openNewTab(boolean incognito) { mActivity.getTabCreator(incognito).launchNTP(); } - @Override + @CalledByNative + public void openBookmarks() { + mActivity.onMenuOrKeyboardAction(R.id.all_bookmarks_menu_id, true); + } + + @CalledByNative + public void openRecentTabs() { + mActivity.onMenuOrKeyboardAction(R.id.recent_tabs_menu_id, true); + } + + @CalledByNative + public void openHistory() { + mActivity.onMenuOrKeyboardAction(R.id.open_history_menu_id, true); + } + + @CalledByNative + public void openDownloads() { + mActivity.onMenuOrKeyboardAction(R.id.downloads_menu_id, true); + } + + @CalledByNative + public void openSettings() { + mActivity.onMenuOrKeyboardAction(R.id.preferences_id, true); + } + + @CalledByNative + public void closeAllTabs() { + mTabModelSelector.closeAllTabs(); + } + @CalledByNative public void closeAllIncognitoTabs() { mTabModelSelector.getModel(true).closeAllTabs(); } + @CalledByNative + public void openFeedback() { + mActivity.onMenuOrKeyboardAction(R.id.help_id, true); + } + private void updateHistoryButtonsVisibility() { if (mNativeVrShell == 0) return; if (mTab == null) { @@ -1141,7 +1174,8 @@ boolean webVrAutopresentationExpected, boolean inCct, boolean browsingDisabled, boolean hasOrCanRequestAudioPermission, long gvrApi, boolean reprojectedRendering, float displayWidthMeters, float displayHeightMeters, int displayWidthPixels, - int displayHeightPixels, boolean pauseContent, boolean lowDensity); + int displayHeightPixels, boolean pauseContent, boolean lowDensity, + boolean isStandaloneVrDevice); private native void nativeSetSurface(long nativeVrShell, Surface surface); private native void nativeSwapContents(long nativeVrShell, Tab tab); private native void nativeSetAndroidGestureTarget(
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 46fddff..2ccadde 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -307,7 +307,6 @@ "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsDependencyFactory.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsEventReporter.java", - "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsFooter.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSource.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/FakeContextualSuggestionsSource.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/FakeContextualSuggestionsSource.java index 7b51697..476c968 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/FakeContextualSuggestionsSource.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/FakeContextualSuggestionsSource.java
@@ -21,9 +21,8 @@ final static String TEST_TOOLBAR_TITLE = "Test toolbar title"; final static Integer NUMBER_OF_CLUSTERS = 3; final static Integer ARTICLES_PER_CLUSTER = 3; - // There should be 13 items in the cluster list - 3 articles and a title per cluster, - // and a footer. TODO(twellington): Drop down to 12 after footer is removed. - final static Integer TOTAL_ITEM_COUNT = 13; + // There should be 12 items in the cluster list - 3 articles and a title per cluster. + final static Integer TOTAL_ITEM_COUNT = 12; FakeContextualSuggestionsSource() { super(null);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java index ce7e7efb..a0691a5b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -14,6 +14,7 @@ import org.junit.rules.TestRule; import org.junit.runner.RunWith; +import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; @@ -69,7 +70,8 @@ private Integer mSecurityLevel; public TestToolbarModel() { - super(null /* bottomSheet */, false /* useModernDesign */); + super(ContextUtils.getApplicationContext(), null /* bottomSheet */, + false /* useModernDesign */); initializeWithNative(); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java index 7812d545..07bf49c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
@@ -8,6 +8,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.ColorStateList; import android.os.Bundle; import android.support.test.filters.SmallTest; import android.view.View; @@ -215,6 +216,11 @@ } @Override + public ColorStateList getSecurityIconColorStateList() { + return null; + } + + @Override public boolean shouldDisplaySearchTerms() { return false; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageUnitTest.java index d54c0b9..77cd0098 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageUnitTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageUnitTest.java
@@ -104,7 +104,7 @@ @Test @SmallTest @Feature({"Homepage"}) - @DisabledTest + @DisabledTest(message = "crbug.com/836700") public void testProviderNotFromSystemPackage() throws InterruptedException { mHomepageManager.setPrefHomepageEnabled(true); mHomepageManager.setPrefHomepageUseDefaultUri(true); @@ -142,7 +142,7 @@ @Test @SmallTest @Feature({"Homepage"}) - @DisabledTest + @DisabledTest(message = "crbug.com/836110") public void testNoProvider() throws InterruptedException { mHomepageManager.setPrefHomepageEnabled(true); mHomepageManager.setPrefHomepageUseDefaultUri(true); @@ -247,6 +247,7 @@ */ @Test @SmallTest + @DisabledTest(message = "crbug.com/837311") @Feature({"Homepage"}) public void testHomepageProviderTimeout() throws InterruptedException { mHomepageManager.setPrefHomepageEnabled(true); @@ -293,7 +294,7 @@ @Test @SmallTest @Feature({"Homepage"}) - @DisabledTest + @DisabledTest(message = "crbug.com/837130") public void testHomepageProviderDelayed() throws InterruptedException { mHomepageManager.setPrefHomepageEnabled(true); mHomepageManager.setPrefHomepageUseDefaultUri(true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarModelTest.java index 5983c1d..c91dae5 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarModelTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarModelTest.java
@@ -15,6 +15,7 @@ import org.junit.rules.TestRule; import org.junit.runner.RunWith; +import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Feature; @@ -143,7 +144,8 @@ private String mUrl; public TestToolbarModel() { - super(null /* bottomSheet */, false /* useModernDesign */); + super(ContextUtils.getApplicationContext(), null /* bottomSheet */, + false /* useModernDesign */); initializeWithNative(); Tab tab = new Tab(0, false, null) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java index fe76a5e..ae74f01 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java
@@ -59,6 +59,7 @@ private static final boolean REQUIRE_UNMETERED = true; private static final boolean POWER_CONNECTED = true; private static final boolean POWER_SAVE_MODE_ON = true; + private static final boolean METERED = true; private static final int MINIMUM_BATTERY_LEVEL = 33; private static final String IS_LOW_END_DEVICE_SWITCH = "--" + BaseSwitches.ENABLE_LOW_END_DEVICE_MODE; @@ -71,7 +72,7 @@ private TriggerConditions mTriggerConditions = new TriggerConditions(!REQUIRE_POWER, MINIMUM_BATTERY_LEVEL, REQUIRE_UNMETERED); private DeviceConditions mDeviceConditions = new DeviceConditions(!POWER_CONNECTED, - MINIMUM_BATTERY_LEVEL + 5, ConnectionType.CONNECTION_3G, !POWER_SAVE_MODE_ON); + MINIMUM_BATTERY_LEVEL + 5, ConnectionType.CONNECTION_3G, !POWER_SAVE_MODE_ON, !METERED); private Activity mTestActivity; @Mock @@ -94,7 +95,7 @@ .when(mTaskScheduler) .schedule(eq(RuntimeEnvironment.application), mTaskInfo.capture()); - ShadowDeviceConditions.setCurrentConditions(mDeviceConditions, false /* unmetered */); + ShadowDeviceConditions.setCurrentConditions(mDeviceConditions); // Set up background scheduler processor mock. BackgroundSchedulerProcessor.setInstanceForTesting(mBackgroundSchedulerProcessor); @@ -133,10 +134,10 @@ @Feature({"OfflinePages"}) public void testCheckConditions_BatteryConditions_LowBattery_NoPower() { // Setup low battery conditions with no power connected. - DeviceConditions deviceConditionsLowBattery = new DeviceConditions(!POWER_CONNECTED, - MINIMUM_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions( - deviceConditionsLowBattery, false /* unmetered */); + DeviceConditions deviceConditionsLowBattery = + new DeviceConditions(!POWER_CONNECTED, MINIMUM_BATTERY_LEVEL - 1, + ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsLowBattery); // Verify that conditions for processing are not met. assertFalse( @@ -158,10 +159,10 @@ @Feature({"OfflinePages"}) public void testCheckConditions_BatteryConditions_LowBattery_WithPower() { // Set battery percentage below minimum level, but connect power. - DeviceConditions deviceConditionsPowerConnected = new DeviceConditions(POWER_CONNECTED, - MINIMUM_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions( - deviceConditionsPowerConnected, false /* unmetered */); + DeviceConditions deviceConditionsPowerConnected = + new DeviceConditions(POWER_CONNECTED, MINIMUM_BATTERY_LEVEL - 1, + ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsPowerConnected); // Now verify that same battery level, with power connected, will pass the conditions. assertTrue(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/ShadowDeviceConditions.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/ShadowDeviceConditions.java index 57bd411..bf4b817 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/ShadowDeviceConditions.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/ShadowDeviceConditions.java
@@ -15,42 +15,38 @@ /** Device conditions for testing. */ private static DeviceConditions sDeviceConditions = new DeviceConditions(); - /** Whether to count the network as metered or not */ - private static boolean sMetered = true; - /** Sets device conditions that will be used in test. */ - public static void setCurrentConditions(DeviceConditions deviceConditions, boolean metered) { + public static void setCurrentConditions(DeviceConditions deviceConditions) { sDeviceConditions = deviceConditions; - sMetered = metered; } @Implementation - public static DeviceConditions getCurrentConditions(Context context) { + public static DeviceConditions getCurrent(Context context) { return sDeviceConditions; } @Implementation - public static boolean isPowerConnected(Context context) { + public static boolean isCurrentlyPowerConnected(Context context) { return sDeviceConditions.isPowerConnected(); } @Implementation - public static int getBatteryPercentage(Context context) { + public static int getCurrentBatteryPercentage(Context context) { return sDeviceConditions.getBatteryPercentage(); } @Implementation - public static int getNetConnectionType(Context context) { + public static boolean isCurrentlyInPowerSaveMode(Context context) { + return sDeviceConditions.isInPowerSaveMode(); + } + + @Implementation + public static int getCurrentNetConnectionType(Context context) { return sDeviceConditions.getNetConnectionType(); } @Implementation - public static boolean isActiveNetworkMetered(Context context) { - return sMetered; - } - - @Implementation - public static boolean getInPowerSaveMode(Context context) { - return sDeviceConditions.inPowerSaveMode(); + public static boolean isCurrentActiveNetworkMetered(Context context) { + return sDeviceConditions.isActiveNetworkMetered(); } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java index 4df73bf..6143f02 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTaskUnitTest.java
@@ -202,8 +202,8 @@ DeviceConditions deviceConditions = new DeviceConditions(false /* POWER_CONNECTED */, 75 /* BATTERY_LEVEL */, online ? ConnectionType.CONNECTION_WIFI : ConnectionType.CONNECTION_NONE, - false /* POWER_SAVE */); - ShadowDeviceConditions.setCurrentConditions(deviceConditions, false /* metered */); + false /* POWER_SAVE */, false /* metered */); + ShadowDeviceConditions.setCurrentConditions(deviceConditions); } public void assertTaskScheduledForOfflineDelay(String message) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java index 8e05ab3..4b90c5a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
@@ -90,6 +90,7 @@ public static final boolean POWER_SAVE_MODE_ON = true; public static final int HIGH_BATTERY_LEVEL = 75; public static final int LOW_BATTERY_LEVEL = 25; + public static final boolean METERED = true; @Spy private PrefetchBackgroundTask mPrefetchBackgroundTask = new PrefetchBackgroundTask(); @@ -194,9 +195,10 @@ TaskParameters.create(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID).build(); // Setup battery conditions with no power connected. - DeviceConditions deviceConditions = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditions, false /* metered */); + DeviceConditions deviceConditions = + new DeviceConditions(!POWER_CONNECTED, HIGH_BATTERY_LEVEL - 1, + ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditions); mPrefetchBackgroundTask.onStartTask(null, params, new TaskFinishedCallback() { @Override @@ -227,8 +229,8 @@ // Setup battery conditions with no power connected. DeviceConditions deviceConditions = new DeviceConditions(!POWER_CONNECTED, - 0 /* battery level */, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditions, true /* metered */); + 0 /* battery level */, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON, METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditions); mPrefetchBackgroundTask.onStartTask(null, params, new TaskFinishedCallback() { @Override @@ -246,8 +248,8 @@ public void testBatteryLow() throws Exception { // Setup low battery conditions with no power connected. DeviceConditions deviceConditionsLowBattery = new DeviceConditions(!POWER_CONNECTED, - LOW_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditionsLowBattery, true /* metered */); + LOW_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsLowBattery); // Check impact on starting before native loaded. TaskParameters params = @@ -267,9 +269,8 @@ public void testBatteryHigh() throws Exception { // Setup high battery conditions with no power connected. DeviceConditions deviceConditionsHighBattery = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions( - deviceConditionsHighBattery, false /* metered */); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsHighBattery); // Check impact on starting before native loaded. TaskParameters params = @@ -289,8 +290,8 @@ public void testNoNetwork() throws Exception { // Setup no network conditions. DeviceConditions deviceConditionsNoNetwork = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditionsNoNetwork, false /* metered */); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsNoNetwork); // Check impact on starting before native loaded. TaskParameters params = @@ -315,9 +316,10 @@ @Test public void testNoNetworkLimitless() throws Exception { // Setup no network conditions. - DeviceConditions deviceConditionsNoNetwork = new DeviceConditions(!POWER_CONNECTED, - 0 /* battery level */, ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditionsNoNetwork, false /* metered */); + DeviceConditions deviceConditionsNoNetwork = + new DeviceConditions(!POWER_CONNECTED, 0 /* battery level */, + ConnectionType.CONNECTION_NONE, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsNoNetwork); // Check impact on starting before native loaded. Bundle extrasBundle = new Bundle(); @@ -340,9 +342,8 @@ public void testUnmeteredWifiNetwork() throws Exception { // Setup unmetered wifi conditions. DeviceConditions deviceConditionsUnmeteredWifi = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions( - deviceConditionsUnmeteredWifi, false /* metered */); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsUnmeteredWifi); // Check impact on starting before native loaded. TaskParameters params = @@ -362,9 +363,8 @@ public void testMeteredWifiNetwork() throws Exception { // Setup metered wifi conditions. DeviceConditions deviceConditionsMeteredWifi = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions( - deviceConditionsMeteredWifi, true /* metered */); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsMeteredWifi); // Check impact on starting before native loaded. TaskParameters params = @@ -384,9 +384,9 @@ public void test2GNetwork() throws Exception { // Setup metered 2g connection conditions. DeviceConditions deviceConditions2G = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_2G, !POWER_SAVE_MODE_ON, METERED); // TODO(petewil): this test name and the condition below do not match. - ShadowDeviceConditions.setCurrentConditions(deviceConditions2G, true /* metered */); + ShadowDeviceConditions.setCurrentConditions(deviceConditions2G); // Check impact on starting before native loaded. TaskParameters params = @@ -405,9 +405,10 @@ @Test public void testBluetoothNetwork() throws Exception { // Setup bluetooth connection conditions. - DeviceConditions deviceConditionsBluetooth = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_BLUETOOTH, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditionsBluetooth, false /* metered */); + DeviceConditions deviceConditionsBluetooth = + new DeviceConditions(!POWER_CONNECTED, HIGH_BATTERY_LEVEL, + ConnectionType.CONNECTION_BLUETOOTH, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsBluetooth); // Check impact on starting before native loaded. TaskParameters params = @@ -430,9 +431,10 @@ TaskParameters.create(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID).build(); // Conditions should be appropriate for running the task. - DeviceConditions deviceConditions = new DeviceConditions(POWER_CONNECTED, - HIGH_BATTERY_LEVEL - 1, ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditions, false /* metered */); + DeviceConditions deviceConditions = + new DeviceConditions(POWER_CONNECTED, HIGH_BATTERY_LEVEL - 1, + ConnectionType.CONNECTION_WIFI, !POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditions); mPrefetchBackgroundTask.onStartTask(null, params, new TaskFinishedCallback() { @Override @@ -451,8 +453,8 @@ public void testPowerSaverOn() throws Exception { // Setup power save mode, battery is high, wifi, not plugged in. DeviceConditions deviceConditionsPowerSave = new DeviceConditions(!POWER_CONNECTED, - HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, POWER_SAVE_MODE_ON); - ShadowDeviceConditions.setCurrentConditions(deviceConditionsPowerSave, false /* metered */); + HIGH_BATTERY_LEVEL, ConnectionType.CONNECTION_WIFI, POWER_SAVE_MODE_ON, !METERED); + ShadowDeviceConditions.setCurrentConditions(deviceConditionsPowerSave); // Check impact on starting before native loaded. TaskParameters params =
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn index 2db768a0f..4293d420a 100644 --- a/chrome/app/BUILD.gn +++ b/chrome/app/BUILD.gn
@@ -11,6 +11,7 @@ import("//services/catalog/public/tools/catalog.gni") import("//services/service_manager/public/service_manifest.gni") import("//tools/grit/grit_rule.gni") +import("//tools/ipc_fuzzer/ipc_fuzzer.gni") # This target is for dependency tracking for the command ID header. source_set("command_ids") { @@ -495,7 +496,7 @@ ] } -if (use_aura) { +if (use_aura || enable_ipc_fuzzer) { # NOTE: These rules generate compiled versions of the content service # manifests with Chrome's overlays applied. These are only used at run-time, # and only when running Chrome inside the Mash environment. In production
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index f788cf7..6ed97ed6 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -132,12 +132,30 @@ <message name="IDS_MULTIDEVICE_SETUP_DIALOG_TITLE" desc="Title for the MultiDevice setup dialog, which allows users to enable features which involve communication between multiple devices (e.g., a Chromebook and a phone)." translateable="false"> MultiDevice Setup </message> - <message name="IDS_MULTIDEVICE_SETUP_ACCEPT_LABEL" desc="Label for button to accept conditions and begin MultiDevice setup workflow."> + <message name="IDS_MULTIDEVICE_SETUP_ACCEPT_LABEL" desc="Label for button to accept conditions and begin MultiDevice setup workflow." translateable="false"> Accept </message> - <message name="IDS_MULTIDEVICE_SETUP_TRY_AGAIN_LABEL" desc="Label for button to retry setup upon failure."> + <message name="IDS_MULTIDEVICE_SETUP_TRY_AGAIN_LABEL" desc="Label for button to retry setup upon failure." translateable="false"> Try again </message> + <message name="IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_HEADER" desc="Header for failed setup page." translateable="false"> + Could not connect to phone (PlAcEhOlDeR tExT!!) + </message> + <message name="IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_MESSAGE" desc="Message for failed setup page." translateable="false"> + PlAcEhOlDeR tExT!! This is a paragraph about an error. Blah blah blah. Something went wrong. Whomp whomp whomp. + </message> + <message name="IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_HEADER" desc="Header for successful setup page." translateable="false"> + You're all set! (PlAcEhOlDeR tExT!!) + </message> + <message name="IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_MESSAGE" desc="Message for successful setup page." translateable="false"> + PlAcEhOlDeR tExT!! This String includes a link with the text <a id="settings-link">Settings</a> that opens the settings page. Blah blah blah. + </message> + <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_HEADER" desc="Header for welcome page." translateable="false"> + Better together (PlAcEhOlDeR tExT!!) + </message> + <message name="IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE" desc="Message for welcome page." translateable="false"> + PlAcEhOlDeR tExT!! This String includes a link with the text <a id="terms-and-conditions-link" href="chrome://foo-bar">Terms and Conditions</a> that opens a browser window to that info. Blah blah blah. + </message> <!-- File Manager --> <message name="IDS_FILE_SYSTEM_PROVIDER_UNRESPONSIVE_WARNING" desc="A warning shown in a notification that an operation is taking longer than expected.">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 59227ea4..a3a3194 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -3784,15 +3784,6 @@ </if> <!-- Extension/App error messages --> - <message name="IDS_EXTENSION_PACK_WARNING_TITLE" desc="Warning title message for pack extension"> - Pack Extension Warning - </message> - <message name="IDS_EXTENSION_PACK_ERROR_TITLE" desc="Error title message for pack extension"> - Pack Extension Error - </message> - <message name="IDS_EXTENSION_PROCEED_ANYWAY" desc="Button to continue with operation in extension packing"> - Proceed anyway - </message> <message name="IDS_EXTENSION_CANT_DOWNGRADE_VERSION" desc="Error message when a user tries to install an extension with a lower version than a version that it already installed."> Attempted to downgrade extension. </message> @@ -3932,34 +3923,12 @@ </message> <!-- chrome://settings/extensions page --> + <!-- TODO(devlin): Move these strings to --> + <!-- chrome/app/md_extensions_strings.grdp. --> <if expr="enable_extensions"> - <message name="IDS_EXTENSIONS_DEVELOPER_MODE_LINK" desc="Text of the link for developer mode."> - Developer mode - </message> - <message name="IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON" desc="Text of the 'Load Extension' button."> - Load unpacked extension... - </message> - <message name="IDS_EXTENSIONS_PACK_BUTTON" desc="Text for the 'Pack Extension' button."> - Pack extension... - </message> - <message name="IDS_EXTENSIONS_UPDATE_BUTTON" desc="Text for the 'Update extensions' button."> - Update extensions now - </message> - <message name="IDS_EXTENSIONS_NONE_INSTALLED" desc="Text that lets the user know that no extensions are installed."> - Boo... You have no extensions :-( - </message> - <message name="IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY" desc="Text on next line after IDS_EXTENSIONS_NONE_INSTALLED that suggests the user look in the Chrome Web Store for extensions to install."> - Want to <ph name="BEGIN_LINK"><a target="_blank" href="$1"></ph>browse the Chrome Web Store<ph name="END_LINK"></a><ex></a></ex></ph> instead? - </message> <message name="IDS_EXTENSIONS_INSTALL_DROP_TARGET" desc="Text on drop target for installing extensions."> Drop to install </message> - <message name="IDS_EXTENSIONS_CRASHED_EXTENSION" desc="Text that signifies that the extension process was terminated, usually because it has crashed."> - (Crashed) - </message> - <message name="IDS_EXTENSIONS_IN_DEVELOPMENT" desc="Text that signifies that the extension is currently in development."> - (Unpacked) - </message> <message name="IDS_EXTENSIONS_VIEW_INCOGNITO" desc="Text that signifies that the extension view is in an incognito process."> (Incognito) </message> @@ -3972,15 +3941,9 @@ <message name="IDS_EXTENSIONS_BACKGROUND_PAGE" desc="Display name for an autogenerated background page."> background page </message> - <message name="IDS_EXTENSIONS_ID" desc="The ID label in front of the extension ID."> - ID: - </message> <message name="IDS_EXTENSIONS_PATH" desc="The label in front of the extension load path."> Loaded from: </message> - <message name="IDS_EXTENSIONS_INSPECT_VIEWS" desc="The text for the label in front of the links to inspect views (some of which may not be loaded yet)."> - Inspect views: - </message> <message name="IDS_EXTENSIONS_INSTALL_WARNINGS" desc="The text which says that an extension has warnings when it was installed."> There were warnings when trying to install this extension: </message> @@ -3990,60 +3953,9 @@ <message name="IDS_EXTENSIONS_LOAD_ERROR_MESSAGE" desc="The message which tells the user that an extension failed to load."> Failed to load extension from: </message> - <message name="IDS_EXTENSIONS_LOAD_ERROR_RETRY" desc="The text of the button to retry loading an extension."> - Retry - </message> - <message name="IDS_EXTENSIONS_LOAD_ERROR_GIVE_UP" desc="The text of the button to give up on trying to load an extension."> - I give up - </message> - <message name="IDS_EXTENSIONS_LOAD_COULD_NOT_LOAD_MANIFEST" desc="The text to indicate that the source of the extension's manifest failed to load."> - Could not load manifest. - </message> - <message name="IDS_EXTENSIONS_LOAD_ADDITIONAL_FAILURES" desc="The text displayed above additional extension load failures."> - Other extensions that failed to load: - </message> - <message name="IDS_EXTENSIONS_ERROR_HEADING" desc="The heading of the list of errors for an extension."> - Errors - </message> - <message name="IDS_EXTENSIONS_ERROR_CLEAR_ALL" desc="The text for the button to delete all extension errors."> - Clear all - </message> - <message name="IDS_EXTENSIONS_ERROR_NO_ERRORS" desc="The text displayed when an extension has no errors."> - Awesome, no errors! - </message> <message name="IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE" desc="The text displayed in the 'code' section when an extension has no errors."> Nothing to see here, move along. </message> - <message name="IDS_EXTENSIONS_ERROR_CONTEXT" desc="The label for the context of an extension's error."> - Context: - </message> - <message name="IDS_EXTENSIONS_ERROR_STACK_TRACE" desc="The label for the stack trace of an extension's error."> - Stack Trace - </message> - <message name="IDS_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION" desc="The label indicating that an error was caused within an anonymous function in the code."> - anonymous function - </message> - <message name="IDS_EXTENSIONS_ERROR_LAUNCH_DEVTOOLS" desc="The text for the button to view an extension error in the developer tools."> - View in Developer Tools - </message> - <message name="IDS_EXTENSIONS_ERROR_CONTEXT_UNKNOWN" desc="Text indicating that the context URL for an error is unknown."> - Unknown - </message> - <message name="IDS_EXTENSIONS_ERROR_NO_CODE_TO_DISPLAY" desc="Text indicating that there is no code to be displayed for a given error."> - Sorry, we can't show the code for this error. - </message> - <message name="IDS_EXTENSIONS_ENABLE" desc="The link for enabling extensions."> - Enable - </message> - <message name="IDS_EXTENSIONS_ENABLED" desc="The label for an enabled extension."> - Enabled - </message> - <message name="IDS_EXTENSIONS_REMOVE" desc="The label for uninstalling an extension."> - Remove - </message> - <message name="IDS_EXTENSIONS_ENABLE_INCOGNITO" desc="The checkbox for enabling an extension in incognito."> - Allow in incognito - </message> <message name="IDS_EXTENSIONS_ENABLE_ERROR_COLLECTION" desc="The checkbox for enabling error collection for an extension."> Collect errors </message> @@ -4053,40 +3965,15 @@ <message name="IDS_EXTENSIONS_ALLOW_FILE_ACCESS" desc="The checkbox for allowing an extension access to run scripts on file URLs."> Allow access to file URLs </message> - <message name="IDS_EXTENSIONS_VISIT_WEBSITE" desc="The link for visiting the extension's homepage."> - Developer website - </message> - <message name="IDS_EXTENSIONS_VISIT_WEBSTORE" desc="The link for visiting the extension's gallery page."> - Details - </message> <message name="IDS_EXTENSIONS_RELOAD_TERMINATED" desc="The link for reloading extensions."> Reload </message> <message name="IDS_EXTENSIONS_REPAIR_CORRUPTED" desc="The link for repairing corrupted extensions."> Repair </message> - <message name="IDS_EXTENSIONS_LAUNCH" desc="The link for launching apps."> - Launch - </message> - <if expr="is_macosx"> - <message name="IDS_EXTENSIONS_RELOAD_UNPACKED" desc="The link for reloading unpacked extensions."> - Reload (⌘R) - </message> - </if> - <if expr="not is_macosx"> - <message name="IDS_EXTENSIONS_RELOAD_UNPACKED" desc="The link for reloading unpacked extensions."> - Reload (Ctrl+R) - </message> - </if> <message name="IDS_EXTENSIONS_OPTIONS_LINK" desc="The link text for the Options link."> Options </message> - <message name="IDS_EXTENSIONS_PERMISSIONS_LINK" desc="The link text for the Permissions link."> - Permissions - </message> - <message name="IDS_EXTENSIONS_INFO_LINK" desc="The text for the link that opens the App Info dialog for the extension, displaying permissions and other useful information about the app."> - Details - </message> <message name="IDS_EXTENSIONS_LOG_LEVEL_INFO" desc="Alt-text indicating a low severity level for the error icon in the chrome://extensions page."> Log </message> @@ -4174,79 +4061,25 @@ </if> </if> - <message name="IDS_EXTENSIONS_POLICY_CONTROLLED" desc="The text in the extensions UI informing the user that an extension is policy controlled"> - (This extension is managed and cannot be removed or disabled.) - </message> - <message name="IDS_EXTENSIONS_POLICY_RECOMMENDED" desc="The text in the extensions UI informing the user that an extension is recommended installed by policy"> - (This extension is managed and cannot be removed.) - </message> - <message name="IDS_EXTENSIONS_DEPENDENT_EXTENSIONS" desc="The text in the extensions UI informing the user that an extension is depended on by on other installed extensions"> - The following extensions depend on this extension: - </message> <message name="IDS_EXTENSIONS_LOCKED_SUPERVISED_USER" desc="The error message (either shown in the extensions UI or logged) informing a supervised user that extensions cannot be changed."> Apps and extensions can only be modified by the manager (<ph name="CUSTODIAN_NAME">$1<ex>Jane Doe</ex></ph>). </message> - <message name="IDS_EXTENSIONS_LOADING" desc="Placeholder text shown when loading the list of extensions."> - Loading... - </message> <message name="IDS_EXTENSIONS_INSTALLED_BY_CHILD_CUSTODIAN" desc="The text in the extensions UI informing a child user that an extension was installed by their parent and cannot be changed."> Installed by your parent. </message> <message name="IDS_EXTENSIONS_INSTALLED_BY_SUPERVISED_USER_CUSTODIAN" desc="The text in the extensions UI informing a supervised user that an extension was installed by their custodian and cannot be changed"> Installed by your custodian. </message> - <message name="IDS_GET_MORE_EXTENSIONS" desc="The text for getting more extensions. Displayed at bottom of extension management page when there is at least one extension installed."> - Get more extensions - </message> <message name="IDS_EXTENSION_LOAD_FROM_DIRECTORY" desc="Title of directory browse dialog when user wants to load an extension from a directory."> Select the extension directory. </message> - <message name="IDS_EXTENSIONS_COMMANDS_CONFIGURE" desc="Text for the 'Keyboard shortcuts' link to configure extension keyboard shortcuts."> - Keyboard shortcuts - </message> - <message name="IDS_EXTENSION_COMMANDS_DIALOG_TITLE" desc="Title of extension commands dialog"> - Keyboard Shortcuts for Extensions and Apps - </message> - <message name="IDS_EXTENSION_COMMANDS_EMPTY" desc="The heading of the extension commands dialog when there are no commands to show."> - No extensions have keyboard shortcuts assigned. - </message> - <message name="IDS_EXTENSION_COMMANDS_INACTIVE" desc="The text shown instead of the shortcut text (Ctrl+F) if the shortcut is inactive."> - Not set - </message> - <message name="IDS_EXTENSION_TYPE_SHORTCUT" desc="The text shown in a text box to instruct the user to start typing (to capture which shortcut to use for the extension command)."> - Type a shortcut - </message> - <message name="IDS_EXTENSION_DELETE_SHORTCUT" desc="The text shown when you hover over the X inside the input text field."> - Delete shortcut - </message> <message name="IDS_EXTENSION_COMMANDS_GENERIC_ACTIVATE" desc="The generic 'activate extension' text used as description for some commands."> Activate the extension </message> - <message name="IDS_EXTENSION_COMMANDS_GLOBAL" desc="The text in the select box for commands that are global in nature (the shortcut works when Chrome does not have focus)."> - Global - </message> - <message name="IDS_EXTENSION_COMMANDS_NOT_GLOBAL" desc="The text in the select box for commands that are not global in nature (the shortcut works only when Chrome has focus)."> - In Chrome - </message> - <message name="IDS_EXTENSION_PACK_DIALOG_TITLE" desc="Title of pack extension dialog"> - Pack Extension - </message> - <message name="IDS_EXTENSION_PACK_BUTTON" desc="Text of the pack extension button"> - Pack Extension - </message> <message name="IDS_EXTENSION_PACK_DIALOG_HEADING" desc="The heading of the pack extension dialog."> Select the root directory of the extension to pack. To update an extension, also select the private key file to reuse. </message> - <message name="IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL" desc="Label in the pack extension dialog for the control that lets the user select the extension to pack."> - Extension root directory: - </message> - <message name="IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL" desc="Label in the pack extension dialog for the control that lets the user select the private key to use to pack the extension. The label should indicate that the input is not required to be filled in."> - Private key file (optional): - </message> - <message name="IDS_EXTENSION_PACK_DIALOG_BROWSE" desc="Text on buttons in the pack extension dialog that open up file browsing UI to pick the extension to pack."> - Browse... - </message> <message name="IDS_EXTENSION_PACK_DIALOG_SELECT_KEY" desc="Heading in file browse dialog instructing user to select a private key."> Select private key file. </message> @@ -4361,9 +4194,6 @@ <message name="IDS_EXTENSION_WARNINGS_WRENCH_MENU_ITEM" desc="The wrench menu item indicating that extensions caused problems."> Extension error </message> - <message name="IDS_EXTENSION_WARNINGS_TITLE" desc="The title of a section in chrome://extensions which contains the warning(s) that relates to one particular extension"> - Warning: - </message> <!-- External extension install alerts --> <!-- TODO(mpcomplete): We may need to change Chrome to @@ -4412,9 +4242,6 @@ <message name="IDS_EXTENSIONS_CORRUPTED_EXTENSION" desc="The warning for the user that an extension may have been tampered with on disk."> This extension may have been corrupted. </message> - <message name="IDS_EXTENSIONS_DISABLED_UPDATE_REQUIRED_BY_POLICY" desc="Text shown in the extensions settings for extensions disabled due to minimum version requirement from enterprise policy"> - This extension is outdated and disabled by enterprise policy. It might become enabled automatically when a newer version is available. - </message> <!-- Settings API bubble --> <message name="IDS_EXTENSIONS_SETTINGS_API_TITLE_HOME_PAGE_BUBBLE" desc="Title of a bubble warning users that an extension has overridden their home page setting"> @@ -11008,12 +10835,36 @@ <message name="IDS_VR_BUTTON_APP_REPOSITION" desc="This is the label for the app button while in reposition mode"> Finish </message> + <message name="IDS_VR_MENU_NEW_TAB" desc="Menu item for opening a new tab. [CHAR-LIMIT=27]"> + New tab + </message> <message name="IDS_VR_MENU_NEW_INCOGNITO_TAB" desc="Menu item for opening a new incognito tab that facilitates pseudononymous browsing. [CHAR-LIMIT=27]"> New incognito tab </message> + <message name="IDS_VR_MENU_BOOKMARKS" desc="Menu item for opening the bookmarks page that contains all the user's bookmarks. [CHAR-LIMIT=27]"> + Bookmarks + </message> + <message name="IDS_VR_MENU_RECENT_TABS" desc="Menu item for opening the 'Recent tabs' page that shows recently closed tabs and pages the user has opened on his/her other devices. [CHAR-LIMIT=27]"> + Recent tabs + </message> + <message name="IDS_VR_MENU_HISTORY" desc="Menu item for opening the history page. [CHAR-LIMIT=27]"> + History + </message> + <message name="IDS_VR_MENU_DOWNLOADS" desc="Menu item for opening the downloads page. [CHAR-LIMIT=27]"> + Downloads + </message> + <message name="IDS_VR_MENU_PREFERENCES" desc="Menu item for opening browser preferences. [CHAR-LIMIT=27]"> + Settings + </message> + <message name="IDS_VR_MENU_CLOSE_ALL_TABS" desc="Menu item for closing all open tabs. [CHAR-LIMIT=27]"> + Close all tabs + </message> <message name="IDS_VR_MENU_CLOSE_INCOGNITO_TABS" desc="Menu item for closing all open incognito tabs. [CHAR-LIMIT=27]"> Close incognito tabs - </message> + </message> + <message name="IDS_VR_MENU_SEND_FEEDBACK" desc="Menu item for sending feedback. [CHAR-LIMIT=27]"> + Send feedback + </message> </if> <message name="IDS_CONFIRM_FILE_UPLOAD_TITLE" desc="Title of dialog for confirming that many files are about to be uploaded / given to the site. [ICU Syntax]">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index c9769c2..e242b37 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -2540,6 +2540,9 @@ "metrics/tab_stats_data_store.h", "metrics/tab_stats_tracker.cc", "metrics/tab_stats_tracker.h", + "metrics/tab_stats_tracker_delegate.h", + "metrics/tab_stats_tracker_delegate_win.cc", + "metrics/tab_stats_tracker_win.cc", "metrics/tab_usage_recorder.cc", "metrics/tab_usage_recorder.h", "notifications/message_center_notification_manager.cc", @@ -2935,6 +2938,7 @@ "//third_party/iaccessible2", "//third_party/isimpledom", "//third_party/wtl", + "//ui/aura_extra", "//ui/base:fullscreen_win", ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 5a8090f..55331884 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1172,6 +1172,12 @@ arraysize(kWebXrRenderPathChoiceSharedBuffer), nullptr}}; #endif // defined(OS_ANDROID) && BUILDFLAG(ENABLE_VR) +const FeatureEntry::FeatureParam kUnifiedConsentShowBump[] = { + {signin::kUnifiedConsentShowBumpParameter, "true"}}; +const FeatureEntry::FeatureVariation kUnifiedConsentVariations[] = { + {"(with consent bump)", kUnifiedConsentShowBump, + arraysize(kUnifiedConsentShowBump), nullptr}}; + const FeatureEntry::FeatureParam kSimplifyHttpsIndicatorEvToSecure[] = { {toolbar::features::kSimplifyHttpsIndicatorParameterName, toolbar::features::kSimplifyHttpsIndicatorParameterEvToSecure}}; @@ -3707,7 +3713,9 @@ {"unified-consent", flag_descriptions::kUnifiedConsentName, flag_descriptions::kUnifiedConsentDescription, kOsAll, - FEATURE_VALUE_TYPE(signin::kUnifiedConsent)}, + FEATURE_WITH_PARAMS_VALUE_TYPE(signin::kUnifiedConsent, + kUnifiedConsentVariations, + "UnifiedConsentVariations")}, {"simplify-https-indicator", flag_descriptions::kSimplifyHttpsIndicatorName, flag_descriptions::kSimplifyHttpsIndicatorDescription, kOsDesktop,
diff --git a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc index 3b2a3fc..74473827 100644 --- a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc +++ b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
@@ -505,3 +505,14 @@ ScopedJavaGlobalRef<jobject>(env, jweb_contents), ScopedJavaGlobalRef<jobject>(env, jcallback))); } + +static void JNI_ServiceWorkerPaymentAppBridge_OnClosingPaymentAppWindow( + JNIEnv* env, + const JavaParamRef<jclass>& jcaller, + const JavaParamRef<jobject>& jweb_contents) { + content::WebContents* web_contents = + content::WebContents::FromJavaWebContents(jweb_contents); + + content::PaymentAppProvider::GetInstance()->OnClosingOpenedWindow( + web_contents->GetBrowserContext()); +}
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc index 7fa31b0..83aeb777 100644 --- a/chrome/browser/android/tab_android.cc +++ b/chrome/browser/android/tab_android.cc
@@ -297,8 +297,8 @@ void TabAndroid::HandlePopupNavigation(NavigateParams* params) { DCHECK(params->source_contents == web_contents()); - DCHECK(params->target_contents == NULL || - params->target_contents == web_contents()); + DCHECK(!params->contents_to_insert); + DCHECK(!params->switch_to_singleton_tab); WindowOpenDisposition disposition = params->disposition; const GURL& url = params->url; @@ -591,7 +591,9 @@ if (prerender_manager) { bool prefetched_page_loaded = HasPrerenderedUrl(gurl); // Getting the load status before MaybeUsePrerenderedPage() b/c it resets. - NavigateParams params(web_contents()); + prerender::PrerenderManager::Params params( + /*uses_post=*/false, /*extra_headers=*/std::string(), + /*should_replace_current_entry=*/false, web_contents()); if (prerender_manager->MaybeUsePrerenderedPage(gurl, ¶ms)) { return prefetched_page_loaded ? FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc index 6e28ff0b..378ef958f 100644 --- a/chrome/browser/android/tab_web_contents_delegate_android.cc +++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -366,7 +366,7 @@ params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB || params.disposition == WindowOpenDisposition::NEW_WINDOW) && PopupBlockerTabHelper::MaybeBlockPopup(source, base::Optional<GURL>(), - nav_params, ¶ms, + &nav_params, ¶ms, blink::mojom::WindowFeatures())) { return nullptr; } @@ -374,12 +374,12 @@ if (disposition == WindowOpenDisposition::CURRENT_TAB) { // Only prerender for a current-tab navigation to avoid session storage // namespace issues. - nav_params.target_contents = source; + prerender::PrerenderManager::Params prerender_params(&nav_params, source); prerender::PrerenderManager* prerender_manager = prerender::PrerenderManagerFactory::GetForBrowserContext(profile); - if (prerender_manager && - prerender_manager->MaybeUsePrerenderedPage(params.url, &nav_params)) { - return nav_params.target_contents; + if (prerender_manager && prerender_manager->MaybeUsePrerenderedPage( + params.url, &prerender_params)) { + return prerender_params.replaced_contents; } }
diff --git a/chrome/browser/android/vr/vr_gl_thread.cc b/chrome/browser/android/vr/vr_gl_thread.cc index 0acdf49..d40f5be 100644 --- a/chrome/browser/android/vr/vr_gl_thread.cc +++ b/chrome/browser/android/vr/vr_gl_thread.cc
@@ -249,6 +249,42 @@ base::BindOnce(&VrShell::OpenNewTab, weak_vr_shell_, incognito)); } +void VrGLThread::OpenBookmarks() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::OpenBookmarks, weak_vr_shell_)); +} + +void VrGLThread::OpenRecentTabs() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::OpenRecentTabs, weak_vr_shell_)); +} + +void VrGLThread::OpenHistory() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::OpenHistory, weak_vr_shell_)); +} + +void VrGLThread::OpenDownloads() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::OpenDownloads, weak_vr_shell_)); +} + +void VrGLThread::OpenSettings() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::OpenSettings, weak_vr_shell_)); +} + +void VrGLThread::CloseAllTabs() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::CloseAllTabs, weak_vr_shell_)); +} + void VrGLThread::CloseAllIncognitoTabs() { DCHECK(OnGlThread()); main_thread_task_runner_->PostTask( @@ -256,6 +292,12 @@ base::BindOnce(&VrShell::CloseAllIncognitoTabs, weak_vr_shell_)); } +void VrGLThread::OpenFeedback() { + DCHECK(OnGlThread()); + main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VrShell::OpenFeedback, weak_vr_shell_)); +} + void VrGLThread::ExitCct() { DCHECK(OnGlThread()); main_thread_task_runner_->PostTask( @@ -443,6 +485,13 @@ weak_browser_ui_)); } +void VrGLThread::SetRegularTabsOpen(bool open) { + DCHECK(OnMainThread()); + task_runner()->PostTask( + FROM_HERE, base::BindOnce(&BrowserUiInterface::SetRegularTabsOpen, + weak_browser_ui_, open)); +} + void VrGLThread::SetIncognitoTabsOpen(bool open) { DCHECK(OnMainThread()); task_runner()->PostTask(
diff --git a/chrome/browser/android/vr/vr_gl_thread.h b/chrome/browser/android/vr/vr_gl_thread.h index 71681cf..ee76f9c 100644 --- a/chrome/browser/android/vr/vr_gl_thread.h +++ b/chrome/browser/android/vr/vr_gl_thread.h
@@ -86,7 +86,14 @@ void NavigateForward() override; void ReloadTab() override; void OpenNewTab(bool incognito) override; + void OpenBookmarks() override; + void OpenRecentTabs() override; + void OpenHistory() override; + void OpenDownloads() override; + void OpenSettings() override; + void CloseAllTabs() override; void CloseAllIncognitoTabs() override; + void OpenFeedback() override; void ExitCct() override; void CloseHostedDialog() override; void OnUnsupportedMode(UiUnsupportedMode mode) override; @@ -118,6 +125,7 @@ std::unique_ptr<Assets> assets, const base::Version& component_version) override; void OnAssetsUnavailable() override; + void SetRegularTabsOpen(bool open) override; void SetIncognitoTabsOpen(bool open) override; void SetOverlayTextureEmpty(bool empty) override; void ShowSoftInput(bool show) override;
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc index 26b8d414..c014319b 100644 --- a/chrome/browser/android/vr/vr_shell.cc +++ b/chrome/browser/android/vr/vr_shell.cc
@@ -373,11 +373,46 @@ Java_VrShellImpl_openNewTab(env, j_vr_shell_, incognito); } +void VrShell::OpenBookmarks() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_openBookmarks(env, j_vr_shell_); +} + +void VrShell::OpenRecentTabs() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_openRecentTabs(env, j_vr_shell_); +} + +void VrShell::OpenHistory() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_openHistory(env, j_vr_shell_); +} + +void VrShell::OpenDownloads() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_openDownloads(env, j_vr_shell_); +} + +void VrShell::OpenSettings() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_openSettings(env, j_vr_shell_); +} + +void VrShell::CloseAllTabs() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_closeAllTabs(env, j_vr_shell_); +} + void VrShell::CloseAllIncognitoTabs() { JNIEnv* env = base::android::AttachCurrentThread(); Java_VrShellImpl_closeAllIncognitoTabs(env, j_vr_shell_); } +void VrShell::OpenFeedback() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_VrShellImpl_openFeedback(env, j_vr_shell_); +} + void VrShell::ExitCct() { JNIEnv* env = base::android::AttachCurrentThread(); Java_VrShellImpl_exitCct(env, j_vr_shell_); @@ -552,6 +587,7 @@ jobjectArray tabs, jobjectArray incognito_tabs) { incognito_tab_ids_.clear(); + regular_tab_ids_.clear(); size_t len = env->GetArrayLength(incognito_tabs); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef<jobject> j_tab( @@ -559,7 +595,15 @@ TabAndroid* tab = TabAndroid::GetNativeTab(env, j_tab); incognito_tab_ids_.insert(tab->GetAndroidId()); } + + len = env->GetArrayLength(tabs); + for (size_t i = 0; i < len; ++i) { + ScopedJavaLocalRef<jobject> j_tab(env, env->GetObjectArrayElement(tabs, i)); + TabAndroid* tab = TabAndroid::GetNativeTab(env, j_tab); + regular_tab_ids_.insert(tab->GetAndroidId()); + } ui_->SetIncognitoTabsOpen(!incognito_tab_ids_.empty()); + ui_->SetRegularTabsOpen(!regular_tab_ids_.empty()); } void VrShell::OnTabUpdated(JNIEnv* env, @@ -570,6 +614,9 @@ if (incognito) { incognito_tab_ids_.insert(id); ui_->SetIncognitoTabsOpen(!incognito_tab_ids_.empty()); + } else { + regular_tab_ids_.insert(id); + ui_->SetRegularTabsOpen(!regular_tab_ids_.empty()); } } @@ -580,6 +627,9 @@ if (incognito) { incognito_tab_ids_.erase(id); ui_->SetIncognitoTabsOpen(!incognito_tab_ids_.empty()); + } else { + regular_tab_ids_.erase(id); + ui_->SetRegularTabsOpen(!regular_tab_ids_.empty()); } } @@ -1265,7 +1315,8 @@ jint display_width_pixels, jint display_pixel_height, jboolean pause_content, - jboolean low_density) { + jboolean low_density, + jboolean is_standalone_vr_device) { UiInitialState ui_initial_state; ui_initial_state.browsing_disabled = browsing_disabled; ui_initial_state.in_cct = in_cct; @@ -1277,6 +1328,7 @@ ui_initial_state.skips_redraw_when_not_dirty = base::FeatureList::IsEnabled(features::kVrBrowsingExperimentalRendering); ui_initial_state.assets_supported = AssetsLoader::AssetsSupported(); + ui_initial_state.is_standalone_vr_device = is_standalone_vr_device; return reinterpret_cast<intptr_t>(new VrShell( env, obj, ui_initial_state,
diff --git a/chrome/browser/android/vr/vr_shell.h b/chrome/browser/android/vr/vr_shell.h index 89e0f6d6..8e7abc9 100644 --- a/chrome/browser/android/vr/vr_shell.h +++ b/chrome/browser/android/vr/vr_shell.h
@@ -135,7 +135,14 @@ void NavigateForward(); void ReloadTab(); void OpenNewTab(bool incognito); + void OpenBookmarks(); + void OpenRecentTabs(); + void OpenHistory(); + void OpenDownloads(); + void OpenSettings(); + void CloseAllTabs(); void CloseAllIncognitoTabs(); + void OpenFeedback(); void ExitCct(); void CloseHostedDialog(); void ToggleCardboardGamepad(bool enabled); @@ -359,6 +366,7 @@ base::Timer waiting_for_assets_component_timer_; + std::set<int> regular_tab_ids_; std::set<int> incognito_tab_ids_; base::WeakPtrFactory<VrShell> weak_ptr_factory_;
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm index 7602fe5..ee86dac 100644 --- a/chrome/browser/app_controller_mac.mm +++ b/chrome/browser/app_controller_mac.mm
@@ -725,7 +725,10 @@ // If there's only 1 tab and the tab is NTP, close this NTP tab and open all // startup urls in new tabs, because the omnibox will stay focused if we // load url in NTP tab. - Browser* browser = chrome::GetLastActiveBrowser(); + Profile* profile = + g_browser_process->profile_manager()->GetLastUsedProfileAllowedByPolicy(); + Browser* browser = chrome::FindLastActiveWithProfile(profile); + int startupIndex = TabStripModel::kNoTab; content::WebContents* startupContent = NULL; @@ -1393,8 +1396,10 @@ startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end()); return; } - - Browser* browser = chrome::GetLastActiveBrowser(); + // Pick the last used browser from a regular profile to open the urls. + Profile* profile = + g_browser_process->profile_manager()->GetLastUsedProfileAllowedByPolicy(); + Browser* browser = chrome::FindLastActiveWithProfile(profile); // if no browser window exists then create one with no tabs to be filled in if (!browser) { browser = new Browser(
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm index fa82e0e..0a9ef919 100644 --- a/chrome/browser/app_controller_mac_browsertest.mm +++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -36,6 +36,7 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h" #include "chrome/browser/ui/cocoa/history_menu_bridge.h" +#include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h" #include "chrome/browser/ui/cocoa/test/run_loop_testing.h" #include "chrome/browser/ui/search/local_ntp_test_utils.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -582,6 +583,29 @@ ->GetLastCommittedURL()); } +// Tests that when a GURL is opened, it is not opened in incognito mode. +IN_PROC_BROWSER_TEST_F(AppControllerBrowserTest, OpenInRegularBrowser) { + ASSERT_TRUE(embedded_test_server()->Start()); + // Create an incognito browser. + Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile()); + EXPECT_EQ(incognito_browser, chrome::GetLastActiveBrowser()); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); + EXPECT_EQ(1, incognito_browser->tab_strip_model()->count()); + // Open a url. + GURL simple(embedded_test_server()->GetURL("/simple.html")); + SendAppleEventToOpenUrlToAppController(simple); + // It should be opened in the regular browser. + content::TestNavigationObserver event_navigation_observer( + browser()->tab_strip_model()->GetActiveWebContents()); + event_navigation_observer.Wait(); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); + EXPECT_EQ(1, incognito_browser->tab_strip_model()->count()); + EXPECT_EQ(simple, browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetLastCommittedURL()); +} + class AppControllerMainMenuBrowserTest : public InProcessBrowserTest { protected: AppControllerMainMenuBrowserTest() {
diff --git a/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm b/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm index d57bea5..eb69ee5 100644 --- a/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm +++ b/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm
@@ -8,6 +8,7 @@ #include "apps/app_lifetime_monitor_factory.h" #include "apps/switches.h" #include "base/auto_reset.h" +#include "base/bind.h" #include "base/mac/foundation_util.h" #import "base/mac/launch_services_util.h" #include "base/mac/mac_util.h" @@ -17,6 +18,7 @@ #include "base/process/launch.h" #include "base/run_loop.h" #include "base/strings/sys_string_conversions.h" +#include "base/task_scheduler/post_task.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_timeouts.h" #include "base/threading/thread_restrictions.h" @@ -417,10 +419,23 @@ EXPECT_TRUE(HasAppShimHost(profile(), app->id())); // If the window is closed, the shim should quit. + // Closing the window in views requires this thread to process tasks as the + // final request to close the window is posted to this thread's queue. GetFirstAppWindow()->GetBaseWindow()->Close(); - int exit_code; - ASSERT_TRUE(shim_process.WaitForExitWithTimeout( - TestTimeouts::action_timeout(), &exit_code)); + base::RunLoop run_loop; + base::PostTaskWithTraitsAndReply( + FROM_HERE, {base::MayBlock()}, + base::BindOnce( + [](base::Process* shim_process) { + base::ScopedAllowBaseSyncPrimitivesForTesting + allow_base_sync_primitives; + int exit_code; + ASSERT_TRUE(shim_process->WaitForExitWithTimeout( + TestTimeouts::action_timeout(), &exit_code)); + }, + base::Unretained(&shim_process)), + run_loop.QuitClosure()); + run_loop.Run(); EXPECT_FALSE(GetFirstAppWindow()); EXPECT_FALSE(HasAppShimHost(profile(), app->id()));
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index bda101d1..1e8286e 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1237,9 +1237,7 @@ "platform_apps/web_view/common", "execute_script")) << message_; } -// http://crbug.com/326332 -IN_PROC_BROWSER_TEST_F(WebViewSizeTest, - DISABLED_Shim_TestAutosizeAfterNavigation) { +IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeAfterNavigation) { TestHelper("testAutosizeAfterNavigation", "web_view/shim", NO_TEST_SERVER); } @@ -1285,9 +1283,8 @@ TestHelper("testAutosizeRemoveAttributes", "web_view/shim", NO_TEST_SERVER); } -// This test is disabled due to being flaky. http://crbug.com/282116 IN_PROC_BROWSER_TEST_F(WebViewSizeTest, - DISABLED_Shim_TestAutosizeWithPartialAttributes) { + Shim_TestAutosizeWithPartialAttributes) { TestHelper("testAutosizeWithPartialAttributes", "web_view/shim", NO_TEST_SERVER); @@ -1315,13 +1312,7 @@ #if defined(USE_AURA) // Test validates that select tag can be shown and hidden in webview safely // using quick touch. -// Very high timeout rate under Linux MSAN; see https://crbug.com/818161. -#if defined(OS_LINUX) && defined(MEMORY_SANITIZER) -#define MAYBE_SelectShowHide DISABLED_SelectShowHide -#else -#define MAYBE_SelectShowHide SelectShowHide -#endif -IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_SelectShowHide) { +IN_PROC_BROWSER_TEST_F(WebViewTest, SelectShowHide) { LoadAppWithGuest("web_view/select"); content::WebContents* embedder_contents = GetFirstAppWindowWebContents(); @@ -1353,26 +1344,11 @@ } #endif -// http://crbug.com/315920 -#if defined(GOOGLE_CHROME_BUILD) && (defined(OS_WIN) || defined(OS_LINUX)) -#define MAYBE_Shim_TestChromeExtensionURL DISABLED_Shim_TestChromeExtensionURL -#else -#define MAYBE_Shim_TestChromeExtensionURL Shim_TestChromeExtensionURL -#endif -IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestChromeExtensionURL) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionURL) { TestHelper("testChromeExtensionURL", "web_view/shim", NO_TEST_SERVER); } -// http://crbug.com/315920 -#if defined(GOOGLE_CHROME_BUILD) && (defined(OS_WIN) || defined(OS_LINUX)) -#define MAYBE_Shim_TestChromeExtensionRelativePath \ - DISABLED_Shim_TestChromeExtensionRelativePath -#else -#define MAYBE_Shim_TestChromeExtensionRelativePath \ - Shim_TestChromeExtensionRelativePath -#endif -IN_PROC_BROWSER_TEST_F(WebViewTest, - MAYBE_Shim_TestChromeExtensionRelativePath) { +IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionRelativePath) { TestHelper("testChromeExtensionRelativePath", "web_view/shim", NO_TEST_SERVER);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 177662b3..7e8ca069 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -110,8 +110,10 @@ #include "chrome/browser/ui/blocked_content/blocked_window_params.h" #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" #include "chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h" +#include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/browser/ui/login/login_handler.h" #include "chrome/browser/ui/sync/sync_promo_ui.h" @@ -2687,7 +2689,7 @@ opener_suppressed); NavigateParams nav_params = blocked_params.CreateNavigateParams(web_contents); if (PopupBlockerTabHelper::MaybeBlockPopup( - web_contents, opener_top_level_frame_url, nav_params, + web_contents, opener_top_level_frame_url, &nav_params, nullptr /*=open_url_params*/, blocked_params.features())) { return false; } @@ -3478,7 +3480,7 @@ nav_params.user_gesture = params.user_gesture; Navigate(&nav_params); - callback.Run(nav_params.target_contents); + callback.Run(nav_params.navigated_or_inserted_contents); #endif } @@ -4273,10 +4275,6 @@ return IsWebauthnRPIDListedInEnterprisePolicy(browser_context, rp_id); } -bool ChromeContentBrowserClient::ShouldEnforceFocusChecksForWebauthn() { - return true; -} - void ChromeContentBrowserClient::ShouldReturnAttestationForWebauthnRPID( content::RenderFrameHost* rfh, const std::string& rp_id, @@ -4305,11 +4303,9 @@ return; } - // The created AttestationPermissionRequest deletes itself once complete. - // // |callback| is called via the |MessageLoop| because otherwise the - // permissions bubble will have focus and |AuthenticatorImpl| checks that the - // frame still has focus before returning any results. + // permissions bubble will have focus on ChromeOS and |AuthenticatorImpl| + // checks that the |WebContents| still has focus before returning any results. permission_request_manager->AddRequest(NewAttestationPermissionRequest( origin, base::BindOnce( [](base::OnceCallback<void(bool)> callback, bool result) { @@ -4320,6 +4316,24 @@ #endif } +bool ChromeContentBrowserClient::IsFocused(content::WebContents* web_contents) { +#if defined(OS_ANDROID) + // Android is expected to use platform APIs for webauthn. + return true; +#else + for (const auto* browser : *BrowserList::GetInstance()) { + const int tab_index = + browser->tab_strip_model()->GetIndexOfWebContents(web_contents); + if (tab_index != TabStripModel::kNoTab && + browser->tab_strip_model()->active_index() == tab_index) { + return browser->window()->IsActive(); + } + } + + return false; +#endif +} + // Static; reverse URL handler for Web UI. Maps "chrome://chrome/foo/" to // "chrome://foo/". bool ChromeContentBrowserClient::HandleWebUIReverse(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index edb3833..6be76751a 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -413,7 +413,7 @@ const std::string& rp_id, const url::Origin& origin, base::OnceCallback<void(bool)> callback) override; - bool ShouldEnforceFocusChecksForWebauthn() override; + bool IsFocused(content::WebContents* web_contents) override; std::unique_ptr<net::ClientCertStore> CreateClientCertStore( content::ResourceContext* resource_context) override;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 96a69c8..52d610b 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -962,6 +962,10 @@ "login/quick_unlock/auth_token.h", "login/quick_unlock/fingerprint_storage.cc", "login/quick_unlock/fingerprint_storage.h", + "login/quick_unlock/pin_backend.cc", + "login/quick_unlock/pin_backend.h", + "login/quick_unlock/pin_storage_cryptohome.cc", + "login/quick_unlock/pin_storage_cryptohome.h", "login/quick_unlock/pin_storage_prefs.cc", "login/quick_unlock/pin_storage_prefs.h", "login/quick_unlock/quick_unlock_factory.cc", @@ -1216,8 +1220,6 @@ "net/network_portal_detector_impl.h", "net/network_portal_detector_test_impl.cc", "net/network_portal_detector_test_impl.h", - "net/network_portal_notification_controller.cc", - "net/network_portal_notification_controller.h", "net/network_portal_web_dialog.cc", "net/network_portal_web_dialog.h", "net/network_pref_state_observer.cc", @@ -1997,7 +1999,6 @@ "net/cert_verify_proc_chromeos_unittest.cc", "net/client_cert_store_chromeos_unittest.cc", "net/network_portal_detector_impl_unittest.cc", - "net/network_portal_notification_controller_unittest.cc", "net/network_pref_state_observer_unittest.cc", "net/network_throttling_observer_unittest.cc", "net/wake_on_wifi_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/README.md b/chrome/browser/chromeos/README.md new file mode 100644 index 0000000..a16b56e --- /dev/null +++ b/chrome/browser/chromeos/README.md
@@ -0,0 +1,21 @@ +chrome/browser/chromeos +======================= + +This directory should contain non UI Chrome OS specific code that has +`src/chrome` dependencies. + +Code here should not contain any `ash/` dependencies or `chrome/browser/ui` +dependencies. Any such UI code should be moved to +[`chrome/browser/ui/ash`](/chrome/browser/ui/ash/README.md) +(which may depend on code in this directory). + +Example: + +* The Chrome OS network portal detection model lives in + `chrome/browser/chromeos/net/network_portal_detector_impl.cc`. + +* The notification controller for network portal detection lives in: + `chrome/browser/ui/ash/network/network_portal_notification_controller.cc` + (which depends on *chrome/browser/ui*, + *ash/public/cpp/vector_icons/vector_icons.h*, and + *chrome/browser/chromeos/net/network_portal_detector_impl.h*.
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc index 6d17728..5d9a07f2 100644 --- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc +++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -121,6 +121,29 @@ using RemoveSdpRecordCallback = base::OnceCallback<void(arc::mojom::BluetoothStatus)>; +arc::mojom::BluetoothGattStatus ConvertGattErrorCodeToStatus( + const device::BluetoothGattService::GattErrorCode& error_code, + bool is_read_operation) { + switch (error_code) { + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_INVALID_LENGTH: + return arc::mojom::BluetoothGattStatus::GATT_INVALID_ATTRIBUTE_LENGTH; + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED: + return is_read_operation + ? arc::mojom::BluetoothGattStatus::GATT_READ_NOT_PERMITTED + : arc::mojom::BluetoothGattStatus::GATT_WRITE_NOT_PERMITTED; + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_AUTHORIZED: + return arc::mojom::BluetoothGattStatus::GATT_INSUFFICIENT_AUTHENTICATION; + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_SUPPORTED: + return arc::mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED; + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_UNKNOWN: + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED: + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_IN_PROGRESS: + case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PAIRED: + default: + return arc::mojom::BluetoothGattStatus::GATT_FAILURE; + } +} + // Example of identifier: /org/bluez/hci0/dev_E0_CF_65_8C_86_1A/service001a // Convert the last 4 characters of |identifier| to an // int, by interpreting them as hexadecimal digits. @@ -165,8 +188,8 @@ // GattStatus back to Android. void OnGattOperationError(arc::ArcBluetoothBridge::GattStatusCallback callback, BluetoothGattService::GattErrorCode error_code) { - std::move(callback).Run( - mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code)); + std::move(callback).Run(ConvertGattErrorCodeToStatus( + error_code, /* is_read_operation = */ false)); } // Common success callback for ReadGattCharacteristic and ReadGattDescriptor @@ -185,7 +208,7 @@ arc::mojom::BluetoothGattValuePtr gattValue = arc::mojom::BluetoothGattValue::New(); gattValue->status = - mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code); + ConvertGattErrorCodeToStatus(error_code, /* is_read_operation = */ true); std::move(callback).Run(std::move(gattValue)); }
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc b/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc index 025af2b..4d26e175 100644 --- a/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc +++ b/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc
@@ -233,7 +233,7 @@ // // Mark as not "safe" (aka return false) on the contrary, most likely those // cases will require the user to pass thru the intent picker UI. -bool IsSafeToRedirectToArcWithoutUserConfirmation(content::WebContents* tab) { +bool IsSafeToRedirectToArcWithoutUserConfirmation(WebContents* tab) { const char* key = arc::ArcWebContentsData::ArcWebContentsData::kArcTransitionFlag; arc::ArcWebContentsData* arc_data = @@ -253,11 +253,9 @@ size_t selected_app_index, GetActionResult* out_result) { GurlAndActivityInfo url_and_activity_name( - GURL(), - ArcIntentHelperBridge::ActivityName{"" /* package */, "" /* activity */}); + GURL(), ArcIntentHelperBridge::ActivityName{/*package=*/std::string(), + /*activity=*/std::string()}); - // TODO(djacobo): Evaluate if passing around WebContents instead of - // |render_process_host_id| and |rounting_id| would be equivalent. WebContents* tab = tab_util::GetWebContentsByID(render_process_host_id, routing_id); DCHECK(tab); @@ -374,7 +372,7 @@ // Launch the selected app. HandleUrl(render_process_host_id, routing_id, url, handlers, - selected_app_index, nullptr); + selected_app_index, /*out_result=*/nullptr); break; case chromeos::IntentPickerCloseReason::PREFERRED_APP_FOUND: // We shouldn't be here if a preferred app was found. @@ -425,8 +423,8 @@ auto show_bubble_cb = base::Bind(ShowIntentPickerBubble()); WebContents* web_contents = tab_util::GetWebContentsByID(render_process_host_id, routing_id); - show_bubble_cb.Run(nullptr /* anchor_view */, web_contents, - std::move(app_info), !IsChromeAnAppCandidate(handlers), + show_bubble_cb.Run(/*anchor_view=*/nullptr, web_contents, std::move(app_info), + !IsChromeAnAppCandidate(handlers), base::Bind(OnIntentPickerClosed, render_process_host_id, routing_id, url, base::Passed(&handlers))); } @@ -468,7 +466,7 @@ chromeos::AppsNavigationThrottle::RecordUma( std::string(), chromeos::AppType::ARC, chromeos::IntentPickerCloseReason::PREFERRED_APP_FOUND, - false /* should_persist */); + /*should_persist=*/false); } return; // the |url| has been handled. } @@ -500,8 +498,8 @@ MaskOutPageTransition(page_transition, ui::PAGE_TRANSITION_FROM_API); if (ShouldIgnoreNavigation(masked_page_transition, - true /* allow_form_submit */, - true /* allow_client_redirect */)) { + /*allow_form_submit=*/true, + /*allow_client_redirect=*/true)) { LOG(WARNING) << "RunArcExternalProtocolDialog: ignoring " << url << " with PageTransition=" << masked_page_transition; return false; @@ -547,8 +545,7 @@ return GetUrlToNavigateOnDeactivate(handlers); } -bool IsSafeToRedirectToArcWithoutUserConfirmationForTesting( - content::WebContents* tab) { +bool IsSafeToRedirectToArcWithoutUserConfirmationForTesting(WebContents* tab) { return IsSafeToRedirectToArcWithoutUserConfirmation(tab); }
diff --git a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc index 6e625ed..eb62983 100644 --- a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc +++ b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc
@@ -4,15 +4,37 @@ #include "chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/search/search.h" #include "chrome/browser/ui/ash/tablet_mode_client.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/web_preferences.h" +namespace { + +// Returns true if |contents| is of an internal pages (such as +// chrome://settings, chrome://extensions, ... etc) or the New Tab Page. +bool ShouldExcludePage(content::WebContents* contents) { + DCHECK(contents); + + content::NavigationEntry* entry = contents->GetController().GetVisibleEntry(); + const GURL& url = entry->GetURL(); + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + if (profile && search::IsNTPURL(url, profile)) + return true; + + return url.SchemeIs("chrome"); +} + +} // namespace + ChromeContentBrowserClientChromeOsPart :: ChromeContentBrowserClientChromeOsPart() = default; @@ -22,16 +44,24 @@ void ChromeContentBrowserClientChromeOsPart::OverrideWebkitPrefs( content::RenderViewHost* rvh, content::WebPreferences* web_prefs) { - content::WebContents* contents = - content::WebContents::FromRenderViewHost(rvh); + // Enable some mobile-like behaviors when in tablet mode on Chrome OS. if (!TabletModeClient::Get() || - !TabletModeClient::Get()->tablet_mode_enabled() || - !chrome::FindBrowserWithWebContents(contents)) { + !TabletModeClient::Get()->tablet_mode_enabled()) { return; } - // Enable some mobile-like behaviors when in tablet mode on Chrome OS. Do - // this only for webcontents displayed in browsers. + // Do this only for webcontents displayed in browsers and are not of hosted + // apps. + content::WebContents* contents = + content::WebContents::FromRenderViewHost(rvh); + auto* browser = chrome::FindBrowserWithWebContents(contents); + if (!browser || browser->is_app()) + return; + + // Also exclude internal pages and NTPs. + if (ShouldExcludePage(contents)) + return; + web_prefs->viewport_enabled = true; web_prefs->viewport_meta_enabled = true; web_prefs->shrinks_viewport_contents_to_fit = true;
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc index c57657d..6a45fffc 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.cc +++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -494,6 +494,29 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } +void CrostiniManager::GetContainerAppIcons( + std::string vm_name, + std::string container_name, + std::vector<std::string> desktop_file_ids, + int icon_size, + int scale, + GetContainerAppIconsCallback callback) { + vm_tools::concierge::ContainerAppIconRequest request; + request.set_vm_name(std::move(vm_name)); + request.set_container_name(std::move(container_name)); + google::protobuf::RepeatedPtrField<std::string> ids( + std::make_move_iterator(desktop_file_ids.begin()), + std::make_move_iterator(desktop_file_ids.end())); + request.mutable_desktop_file_ids()->Swap(&ids); + request.set_size(icon_size); + request.set_scale(scale); + + GetConciergeClient()->GetContainerAppIcons( + std::move(request), + base::BindOnce(&CrostiniManager::OnGetContainerAppIcons, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + void CrostiniManager::LaunchContainerTerminal( Profile* profile, const std::string& vm_name, @@ -708,4 +731,22 @@ std::move(callback).Run(ConciergeClientResult::SUCCESS); } +void CrostiniManager::OnGetContainerAppIcons( + GetContainerAppIconsCallback callback, + base::Optional<vm_tools::concierge::ContainerAppIconResponse> reply) { + std::vector<Icon> icons; + if (!reply.has_value()) { + LOG(ERROR) << "Failed to get container application icons. Empty response."; + std::move(callback).Run(ConciergeClientResult::DBUS_ERROR, icons); + return; + } + vm_tools::concierge::ContainerAppIconResponse response = reply.value(); + for (auto& icon : *response.mutable_icons()) { + icons.emplace_back( + Icon{.desktop_file_id = std::move(*icon.mutable_desktop_file_id()), + .content = std::move(*icon.mutable_icon())}); + } + std::move(callback).Run(ConciergeClientResult::SUCCESS, icons); +} + } // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h index 175d6a8..76c078f 100644 --- a/chrome/browser/chromeos/crostini/crostini_manager.h +++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -35,6 +35,14 @@ UNKNOWN_ERROR, }; +// Return type when getting app icons from within a container. +struct Icon { + std::string desktop_file_id; + + // Icon file content in PNG format. + std::string content; +}; + // CrostiniManager is a singleton which is used to check arguments for the // ConciergeClient. ConciergeClient is dedicated to communication with the // Concierge service and should remain as thin as possible. @@ -61,6 +69,10 @@ // The type of the callback for CrostiniManager::LaunchContainerApplication. using LaunchContainerApplicationCallback = base::OnceCallback<void(ConciergeClientResult result)>; + // The type of the callback for CrostiniManager::GetContainerAppIcons. + using GetContainerAppIconsCallback = + base::OnceCallback<void(ConciergeClientResult result, + std::vector<Icon>& icons)>; // The type of the callback for CrostiniManager::RestartCrostini. using RestartCrostiniCallback = base::OnceCallback<void(ConciergeClientResult result)>; @@ -141,6 +153,15 @@ std::string desktop_file_id, LaunchContainerApplicationCallback callback); + // Asynchronously gets app icons as specified by their desktop file ids. + // |callback| is called after the method call finishes. + void GetContainerAppIcons(std::string vm_name, + std::string container_name, + std::vector<std::string> desktop_file_ids, + int icon_size, + int scale, + GetContainerAppIconsCallback callback); + // Launches the crosh-in-a-window that displays a shell in an already running // container on a VM. void LaunchContainerTerminal(Profile* profile, @@ -216,6 +237,12 @@ base::Optional<vm_tools::concierge::LaunchContainerApplicationResponse> response); + // Callback for CrostiniManager::GetContainerAppIcons. Called after the + // Concierge service finishes. + void OnGetContainerAppIcons( + GetContainerAppIconsCallback callback, + base::Optional<vm_tools::concierge::ContainerAppIconResponse> response); + // Helper for CrostiniManager::CreateDiskImage. Separated so it can be run // off the main thread. void CreateDiskImageAfterSizeCheck(
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc index a5110655..99ad8ba6 100644 --- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc +++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -6,6 +6,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/task_scheduler/post_task.h" #include "base/time/clock.h" #include "base/time/default_clock.h" #include "base/time/time.h" @@ -18,6 +19,7 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" +#include "ui/app_list/app_list_constants.h" #include "ui/base/l10n/l10n_util.h" using vm_tools::apps::App; @@ -37,6 +39,7 @@ constexpr char kCrostiniAppIdPrefix[] = "crostini:"; constexpr char kCrostiniRegistryPref[] = "crostini.registry"; +constexpr char kCrostiniIconFolder[] = "crostini.icons"; // Keys for the Dictionary stored in prefs for each app. constexpr char kAppDesktopFileIdKey[] = "desktop_file_id"; @@ -160,6 +163,33 @@ return left_iter == left_items.end() && right_iter == right_items.end(); } +bool InstallIconFromFileThread(const base::FilePath& icon_path, + const std::string& content_png) { + DCHECK(!content_png.empty()); + + base::CreateDirectory(icon_path.DirName()); + + int wrote = + base::WriteFile(icon_path, content_png.c_str(), content_png.size()); + if (wrote != static_cast<int>(content_png.size())) { + VLOG(2) << "Failed to write Crostini icon file: " + << icon_path.MaybeAsASCII(); + if (!base::DeleteFile(icon_path, false)) { + VLOG(2) << "Couldn't delete broken icon file" << icon_path.MaybeAsASCII(); + } + return false; + } + + return true; +} + +void DeleteIconFolderFromFileThread(const base::FilePath& path) { + DCHECK(path.DirName().BaseName().MaybeAsASCII() == kCrostiniIconFolder && + (!base::PathExists(path) || base::DirectoryExists(path))); + const bool deleted = base::DeleteFile(path, true); + DCHECK(deleted); +} + } // namespace CrostiniRegistryService::Registration::Registration( @@ -207,7 +237,10 @@ } CrostiniRegistryService::CrostiniRegistryService(Profile* profile) - : prefs_(profile->GetPrefs()), clock_(base::DefaultClock::GetInstance()) {} + : prefs_(profile->GetPrefs()), + base_icon_path_(profile->GetPath().AppendASCII(kCrostiniIconFolder)), + clock_(base::DefaultClock::GetInstance()), + weak_ptr_factory_(this) {} CrostiniRegistryService::~CrostiniRegistryService() = default; @@ -352,6 +385,60 @@ GetTime(*pref_registration, kAppLastLaunchTimeKey)); } +base::FilePath CrostiniRegistryService::GetAppPath( + const std::string& app_id) const { + return base_icon_path_.AppendASCII(app_id); +} + +base::FilePath CrostiniRegistryService::GetIconPath( + const std::string& app_id, + ui::ScaleFactor scale_factor) const { + const base::FilePath app_path = GetAppPath(app_id); + switch (scale_factor) { + case ui::SCALE_FACTOR_100P: + return app_path.AppendASCII("icon_100p.png"); + case ui::SCALE_FACTOR_125P: + return app_path.AppendASCII("icon_125p.png"); + case ui::SCALE_FACTOR_133P: + return app_path.AppendASCII("icon_133p.png"); + case ui::SCALE_FACTOR_140P: + return app_path.AppendASCII("icon_140p.png"); + case ui::SCALE_FACTOR_150P: + return app_path.AppendASCII("icon_150p.png"); + case ui::SCALE_FACTOR_180P: + return app_path.AppendASCII("icon_180p.png"); + case ui::SCALE_FACTOR_200P: + return app_path.AppendASCII("icon_200p.png"); + case ui::SCALE_FACTOR_250P: + return app_path.AppendASCII("icon_250p.png"); + case ui::SCALE_FACTOR_300P: + return app_path.AppendASCII("icon_300p.png"); + default: + NOTREACHED(); + return base::FilePath(); + } +} + +void CrostiniRegistryService::MaybeRequestIcon(const std::string& app_id, + ui::ScaleFactor scale_factor) { + // First check to see if this request is already in process or not. + const auto active_iter = active_icon_requests_.find(app_id); + if (active_iter != active_icon_requests_.end()) { + if (active_iter->second & (1 << scale_factor)) { + // Icon request already in progress. + return; + } + } + const auto retry_iter = retry_icon_requests_.find(app_id); + if (retry_iter != active_icon_requests_.end()) { + if (retry_iter->second & (1 << scale_factor)) { + // Icon request already setup to be retried when we are active. + return; + } + } + RequestIcon(app_id, scale_factor); +} + void CrostiniRegistryService::UpdateApplicationList( const vm_tools::apps::ApplicationList& app_list) { if (app_list.vm_name().empty()) { @@ -447,10 +534,25 @@ } } - for (const std::string& removed_app : removed_apps) + for (const std::string& removed_app : removed_apps) { + RemoveAppData(removed_app); apps->RemoveKey(removed_app); + } } + // When we receive notification of the application list then the container + // *should* be online and we can retry all of our icon requests that failed + // due to the container being offline. + for (auto retry_iter = retry_icon_requests_.begin(); + retry_iter != retry_icon_requests_.end(); ++retry_iter) { + for (ui::ScaleFactor scale_factor : ui::GetSupportedScaleFactors()) { + if (retry_iter->second & (1 << scale_factor)) { + RequestIcon(retry_iter->first, scale_factor); + } + } + } + retry_icon_requests_.clear(); + if (!updated_apps.empty() || !removed_apps.empty() || !inserted_apps.empty()) { for (Observer& obs : observers_) @@ -458,6 +560,17 @@ } } +void CrostiniRegistryService::RemoveAppData(const std::string& app_id) { + // Remove any pending requests we have for this icon. + active_icon_requests_.erase(app_id); + retry_icon_requests_.erase(app_id); + + // Remove local data on filesystem for the icons. + base::PostTaskWithTraits( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + base::BindOnce(&DeleteIconFolderFromFileThread, GetAppPath(app_id))); +} + void CrostiniRegistryService::AddObserver(Observer* observer) { observers_.AddObserver(observer); } @@ -506,4 +619,77 @@ registry->RegisterDictionaryPref(kCrostiniRegistryPref); } +void CrostiniRegistryService::RequestIcon(const std::string& app_id, + ui::ScaleFactor scale_factor) { + // Ignore requests for app_id that isn't registered. + std::unique_ptr<CrostiniRegistryService::Registration> registration = + GetRegistration(app_id); + if (!registration) { + VLOG(2) << "Request to load icon for non-registered app: " << app_id; + return; + } + + // Mark that we're doing a request for this icon so we don't duplicate + // requests. + active_icon_requests_[app_id] |= (1 << scale_factor); + + // Now make the call to request the actual icon. + std::vector<std::string> desktop_file_ids{registration->desktop_file_id}; + // We can only send integer scale factors to Crostini, so if we have a + // non-integral scale factor we need round the scale factor. We do not expect + // Crostini to give us back exactly what we ask for and we deal with that in + // the CrostiniAppIcon class and may rescale the result in there to match our + // needs. + uint32_t icon_scale = 1; + switch (scale_factor) { + case ui::SCALE_FACTOR_180P: // Close enough to 2, so use 2. + case ui::SCALE_FACTOR_200P: + case ui::SCALE_FACTOR_250P: // Rounding scale factor down is better. + icon_scale = 2; + break; + case ui::SCALE_FACTOR_300P: + icon_scale = 3; + break; + default: + break; + } + + crostini::CrostiniManager::GetInstance()->GetContainerAppIcons( + registration->vm_name, registration->container_name, desktop_file_ids, + app_list::kTileIconSize, icon_scale, + base::BindOnce(&CrostiniRegistryService::OnContainerAppIcon, + weak_ptr_factory_.GetWeakPtr(), app_id, scale_factor)); +} + +void CrostiniRegistryService::OnContainerAppIcon(const std::string& app_id, + ui::ScaleFactor scale_factor, + ConciergeClientResult result, + std::vector<Icon>& icons) { + if (result != ConciergeClientResult::SUCCESS) { + // Add this to the list of retryable icon requests so we redo this when + // we get feedback from the container that it's available. + retry_icon_requests_[app_id] |= (1 << scale_factor); + return; + } + if (icons.empty()) + return; + // Now install the icon that we received. + const base::FilePath icon_path = GetIconPath(app_id, scale_factor); + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + base::BindOnce(&InstallIconFromFileThread, icon_path, icons[0].content), + base::BindOnce(&CrostiniRegistryService::OnIconInstalled, + weak_ptr_factory_.GetWeakPtr(), app_id, scale_factor)); +} + +void CrostiniRegistryService::OnIconInstalled(const std::string& app_id, + ui::ScaleFactor scale_factor, + bool success) { + if (!success) + return; + + for (Observer& obs : observers_) + obs.OnAppIconUpdated(app_id, scale_factor); +} + } // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.h b/chrome/browser/chromeos/crostini/crostini_registry_service.h index 2a95d8916..9d0ad63f 100644 --- a/chrome/browser/chromeos/crostini/crostini_registry_service.h +++ b/chrome/browser/chromeos/crostini/crostini_registry_service.h
@@ -10,9 +10,13 @@ #include <string> #include <vector> +#include "base/files/file_path.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "chrome/browser/chromeos/crostini/crostini_manager.h" #include "components/keyed_service/core/keyed_service.h" +#include "ui/base/resource/scale_factor.h" class Profile; class PrefRegistrySimple; @@ -87,7 +91,6 @@ std::string vm_name; std::string container_name; - // TODO(timloh): Support icons. LocaleString name; LocaleString comment; std::vector<std::string> mime_types; @@ -110,7 +113,12 @@ CrostiniRegistryService* registry_service, const std::vector<std::string>& updated_apps, const std::vector<std::string>& removed_apps, - const std::vector<std::string>& inserted_apps) = 0; + const std::vector<std::string>& inserted_apps) {} + + // Called when an icon has been installed for the specified app so loading + // of that icon should be requested again. + virtual void OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) {} protected: virtual ~Observer() = default; @@ -140,6 +148,14 @@ std::unique_ptr<CrostiniRegistryService::Registration> GetRegistration( const std::string& app_id) const; + // Constructs path to app icon for specific scale factor. + base::FilePath GetIconPath(const std::string& app_id, + ui::ScaleFactor scale_factor) const; + + // Calls RequestIcon if no request is recorded. + void MaybeRequestIcon(const std::string& app_id, + ui::ScaleFactor scale_factor); + // The existing list of apps is replaced by |application_list|. void UpdateApplicationList(const vm_tools::apps::ApplicationList& app_list); @@ -158,13 +174,46 @@ static void RegisterProfilePrefs(PrefRegistrySimple* registry); private: + // Construct path to app local data. + base::FilePath GetAppPath(const std::string& app_id) const; + // Called to request an icon from the container. + void RequestIcon(const std::string& app_id, ui::ScaleFactor scale_factor); + // Callback for when we request an icon from the container. + void OnContainerAppIcon(const std::string& app_id, + ui::ScaleFactor scale_factor, + ConciergeClientResult result, + std::vector<Icon>& icons); + // Callback for our internal call for saving out icon data. + void OnIconInstalled(const std::string& app_id, + ui::ScaleFactor scale_factor, + bool success); + // Removes all the icons installed for an application. + void RemoveAppData(const std::string& app_id); + // Owned by the BrowserContext. PrefService* const prefs_; + // Keeps root folder where Crostini app icons for different scale factors are + // stored. + base::FilePath base_icon_path_; + base::ObserverList<Observer> observers_; const base::Clock* clock_; + // Keeps record for icon request to avoid duplication. Each app may contain + // several requests for different scale factor. Scale factor is defined by + // specific bit position. The |active_icon_requests_| holds icon request that + // are either in flight or have been completed successfully so they should not + // be requested again. |retry_icon_requests| holds failed requests which we + // should attempt again when we get an app list refresh from the container + // which means there's a good chance the container is online and the request + // will then succeed. + std::map<std::string, uint32_t> active_icon_requests_; + std::map<std::string, uint32_t> retry_icon_requests_; + + base::WeakPtrFactory<CrostiniRegistryService> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(CrostiniRegistryService); };
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc index 35fb088..88280311 100644 --- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
@@ -12,6 +12,7 @@ #include "base/stl_util.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h" #include "chrome/browser/chromeos/login/quick_unlock/auth_token.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" @@ -47,6 +48,8 @@ using QuickUnlockModeList = std::vector<QuickUnlockMode>; using QuickUnlockStorage = chromeos::quick_unlock::QuickUnlockStorage; +using ActiveModeCallback = base::OnceCallback<void(const QuickUnlockModeList&)>; + namespace { const char kModesAndCredentialsLengthMismatch[] = @@ -74,16 +77,19 @@ "1122", "1313", "2001", "1010"}; // Returns the active set of quick unlock modes. -QuickUnlockModeList ComputeActiveModes(Profile* profile) { - QuickUnlockModeList modes; - - QuickUnlockStorage* quick_unlock_storage = - chromeos::quick_unlock::QuickUnlockFactory::GetForProfile(profile); - if (quick_unlock_storage && - quick_unlock_storage->pin_storage_prefs()->IsPinSet()) - modes.push_back(quick_unlock_private::QUICK_UNLOCK_MODE_PIN); - - return modes; +void ComputeActiveModes(Profile* profile, ActiveModeCallback result) { + user_manager::User* user = + chromeos::ProfileHelper::Get()->GetUserByProfile(profile); + chromeos::quick_unlock::PinBackend::IsSet( + user->GetAccountId(), + base::BindOnce( + [](ActiveModeCallback result, bool is_set) { + QuickUnlockModeList modes; + if (is_set) + modes.push_back(quick_unlock_private::QUICK_UNLOCK_MODE_PIN); + std::move(result).Run(modes); + }, + std::move(result))); } // Returns true if |a| and |b| contain the same elements. The elements do not @@ -263,6 +269,11 @@ result->token = quick_unlock_storage->CreateAuthToken(user_context); result->lifetime_seconds = AuthToken::kTokenExpirationSeconds; + // The user has successfully authenticated so we should reset pin/fingerprint + // attempt counts. + quick_unlock_storage->pin_storage_prefs()->ResetUnlockAttemptCount(); + quick_unlock_storage->fingerprint_storage()->ResetUnlockAttemptCount(); + Respond(ArgumentList( quick_unlock_private::GetAuthToken::Results::Create(*result))); Release(); // Balanced in Run(). @@ -323,13 +334,20 @@ : chrome_details_(this) {} QuickUnlockPrivateGetActiveModesFunction:: - ~QuickUnlockPrivateGetActiveModesFunction() {} + ~QuickUnlockPrivateGetActiveModesFunction() = default; ExtensionFunction::ResponseAction QuickUnlockPrivateGetActiveModesFunction::Run() { - const QuickUnlockModeList modes = - ComputeActiveModes(chrome_details_.GetProfile()); - return RespondNow(ArgumentList(GetActiveModes::Results::Create(modes))); + ComputeActiveModes( + chrome_details_.GetProfile(), + base::BindOnce( + &QuickUnlockPrivateGetActiveModesFunction::OnGetActiveModes, this)); + return RespondLater(); +} + +void QuickUnlockPrivateGetActiveModesFunction::OnGetActiveModes( + const std::vector<api::quick_unlock_private::QuickUnlockMode>& modes) { + Respond(ArgumentList(GetActiveModes::Results::Create(modes))); } // quickUnlockPrivate.checkCredential @@ -463,26 +481,18 @@ return RespondNow(Error(kWeakCredential)); } - const QuickUnlockModeList initial_modes = - ComputeActiveModes(chrome_details_.GetProfile()); - ApplyModeChange(); - const QuickUnlockModeList updated_modes = - ComputeActiveModes(chrome_details_.GetProfile()); + ComputeActiveModes( + chrome_details_.GetProfile(), + base::BindOnce(&QuickUnlockPrivateSetModesFunction::OnGetActiveModes, + this)); - if (!AreModesEqual(initial_modes, updated_modes)) - FireEvent(updated_modes); - - const user_manager::User* const user = - chromeos::ProfileHelper::Get()->GetUserByProfile( - chrome_details_.GetProfile()); - chromeos::UserContext user_context(user->GetAccountId()); - chromeos::EasyUnlockService::Get(chrome_details_.GetProfile()) - ->HandleUserReauth(user_context); - - return RespondNow(ArgumentList(SetModes::Results::Create())); + return RespondLater(); } -void QuickUnlockPrivateSetModesFunction::ApplyModeChange() { +void QuickUnlockPrivateSetModesFunction::OnGetActiveModes( + const std::vector<QuickUnlockMode>& initial_modes) { + initial_modes_ = initial_modes; + // This function is setup so it is easy to add another quick unlock mode while // following all of the invariants, which are: // @@ -508,18 +518,49 @@ // Apply changes. if (update_pin) { Profile* profile = chrome_details_.GetProfile(); - QuickUnlockStorage* quick_unlock_storage = - chromeos::quick_unlock::QuickUnlockFactory::GetForProfile(profile); - + user_manager::User* user = + chromeos::ProfileHelper::Get()->GetUserByProfile(profile); if (pin_credential.empty()) { - quick_unlock_storage->pin_storage_prefs()->RemovePin(); + chromeos::quick_unlock::PinBackend::Remove( + user->GetAccountId(), params_->token, + base::BindOnce( + &QuickUnlockPrivateSetModesFunction::PinBackendCallComplete, + this)); } else { - quick_unlock_storage->pin_storage_prefs()->SetPin(pin_credential); - quick_unlock_storage->MarkStrongAuth(); + chromeos::quick_unlock::PinBackend::Set( + user->GetAccountId(), params_->token, pin_credential, + base::BindOnce( + &QuickUnlockPrivateSetModesFunction::PinBackendCallComplete, + this)); } + } else { + // No changes to apply. Call result directly. + ModeChangeComplete(initial_modes_); } } +void QuickUnlockPrivateSetModesFunction::PinBackendCallComplete(bool result) { + ComputeActiveModes( + chrome_details_.GetProfile(), + base::BindOnce(&QuickUnlockPrivateSetModesFunction::ModeChangeComplete, + this)); +} + +void QuickUnlockPrivateSetModesFunction::ModeChangeComplete( + const std::vector<QuickUnlockMode>& updated_modes) { + if (!AreModesEqual(initial_modes_, updated_modes)) + FireEvent(updated_modes); + + const user_manager::User* const user = + chromeos::ProfileHelper::Get()->GetUserByProfile( + chrome_details_.GetProfile()); + const chromeos::UserContext user_context(user->GetAccountId()); + chromeos::EasyUnlockService::Get(chrome_details_.GetProfile()) + ->HandleUserReauth(user_context); + + Respond(ArgumentList(SetModes::Results::Create())); +} + // Triggers a quickUnlockPrivate.onActiveModesChanged change event. void QuickUnlockPrivateSetModesFunction::FireEvent( const QuickUnlockModeList& modes) {
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h index 764ed0b1..1d8b90bc 100644 --- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
@@ -109,6 +109,9 @@ ResponseAction Run() override; private: + void OnGetActiveModes( + const std::vector<api::quick_unlock_private::QuickUnlockMode>& modes); + ChromeExtensionFunctionDetails chrome_details_; DISALLOW_COPY_AND_ASSIGN(QuickUnlockPrivateGetActiveModesFunction); @@ -172,7 +175,13 @@ // ExtensionFunction overrides. ResponseAction Run() override; - void ApplyModeChange(); + // Continuation of OnAuthSuccess after active modes have been fetched. + void OnGetActiveModes(const std::vector<QuickUnlockMode>& modes); + + void PinBackendCallComplete(bool result); + + // Apply any changes specified in |params_|. Returns the new active modes. + void ModeChangeComplete(const std::vector<QuickUnlockMode>& updated_modes); private: void FireEvent(const std::vector<QuickUnlockMode>& modes); @@ -180,6 +189,8 @@ ChromeExtensionFunctionDetails chrome_details_; std::unique_ptr<api::quick_unlock_private::SetModes::Params> params_; + std::vector<QuickUnlockMode> initial_modes_; + ModesChangedEventHandler modes_changed_handler_; DISALLOW_COPY_AND_ASSIGN(QuickUnlockPrivateSetModesFunction);
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc index fbe53fd..8aa5254 100644 --- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -11,13 +11,16 @@ #include "ash/public/cpp/ash_pref_names.h" #include "base/bind.h" #include "base/memory/ptr_util.h" +#include "base/run_loop.h" #include "base/stl_util.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_factory.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/extensions/extension_api_unittest.h" #include "chrome/common/pref_names.h" #include "chromeos/login/auth/fake_extended_authenticator.h" @@ -112,12 +115,16 @@ quick_unlock::EnableForTesting(quick_unlock::PinStorageType::kPrefs); + run_loop_ = std::make_unique<base::RunLoop>(); + // Setup a primary user. auto test_account = AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId); fake_user_manager_->AddUser(test_account); fake_user_manager_->UserLoggedIn(test_account, kTestUserEmailHash, false, false); + ProfileHelper::Get()->SetUserToProfileMappingForTesting( + fake_user_manager_->GetPrimaryUser(), GetProfile()); // Generate an auth token. token_ = quick_unlock::QuickUnlockFactory::GetForProfile(profile()) @@ -132,9 +139,20 @@ void TearDown() override { quick_unlock::DisablePinByPolicyForTesting(false); + PumpRunLoop(); + run_loop_.reset(); + ExtensionApiUnittest::TearDown(); } + // Executes all pending tasks. + void PumpRunLoop() { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, run_loop_->QuitWhenIdleClosure()); + run_loop_->Run(); + run_loop_ = std::make_unique<base::RunLoop>(); + } + TestingProfile::TestingFactories GetTestingFactories() override { return {{EasyUnlockServiceFactory::GetInstance(), &CreateEasyUnlockServiceForTest}}; @@ -366,26 +384,68 @@ std::string token() { return token_; } + // Returns if the pin is set in the backend. + bool IsPinSetInBackend() { + const AccountId account_id = AccountId::FromUserEmail(kTestUserEmail); + + bool called = false; + bool is_set = false; + quick_unlock::PinBackend::IsSet( + account_id, base::BindOnce( + [](bool* out_called, bool* out_is_set, bool is_set) { + *out_called = true; + *out_is_set = is_set; + }, + &called, &is_set)); + PumpRunLoop(); + CHECK(called); + return is_set; + } + + // Run an authentication attempt with the plain-text |password|. + bool TryAuthenticate(const std::string& password) { + const AccountId account_id = AccountId::FromUserEmail(kTestUserEmail); + bool called = false; + bool success = false; + quick_unlock::PinBackend::TryAuthenticate( + account_id, password, Key::KEY_TYPE_PASSWORD_PLAIN, + base::BindOnce( + [](bool* out_called, bool* out_success, bool success) { + *out_called = true; + *out_success = success; + }, + &called, &success)); + PumpRunLoop(); + CHECK(called); + return success; + } + private: // Runs the given |func| with the given |params|. std::unique_ptr<base::Value> RunFunction( scoped_refptr<UIThreadExtensionFunction> func, std::unique_ptr<base::ListValue> params) { - return api_test_utils::RunFunctionWithDelegateAndReturnSingleResult( - func, std::move(params), profile(), - std::make_unique<ExtensionFunctionDispatcher>(profile()), - api_test_utils::NONE); + PumpRunLoop(); + std::unique_ptr<base::Value> result = + api_test_utils::RunFunctionWithDelegateAndReturnSingleResult( + func, std::move(params), profile(), + std::make_unique<ExtensionFunctionDispatcher>(profile()), + api_test_utils::NONE); + PumpRunLoop(); + return result; } // Runs |func| with |params|. Expects and returns an error result. std::string RunFunctionAndReturnError( scoped_refptr<UIThreadExtensionFunction> func, std::unique_ptr<base::ListValue> params) { + PumpRunLoop(); std::unique_ptr<ExtensionFunctionDispatcher> dispatcher( new ExtensionFunctionDispatcher(profile())); api_test_utils::RunFunction(func.get(), std::move(params), profile(), std::move(dispatcher), api_test_utils::NONE); EXPECT_TRUE(func->GetResultList()->empty()); + PumpRunLoop(); return func->GetError(); } @@ -397,6 +457,8 @@ expect_modes_changed_ = false; } + // Run loop that collects all pending tasks. Use |PumpRunLoop| to run them. + std::unique_ptr<base::RunLoop> run_loop_; FakeChromeUserManager* fake_user_manager_; user_manager::ScopedUserManager scoped_user_manager_; QuickUnlockPrivateSetModesFunction::ModesChangedEventHandler @@ -531,9 +593,6 @@ // Ensures that quick unlock can be enabled and disabled by checking the result // of quickUnlockPrivate.GetActiveModes and PinStoragePrefs::IsPinSet. TEST_F(QuickUnlockPrivateUnitTest, SetModesAndGetActiveModes) { - quick_unlock::QuickUnlockStorage* quick_unlock_storage = - quick_unlock::QuickUnlockFactory::GetForProfile(profile()); - // Update mode to PIN raises an event and updates GetActiveModes. ExpectModesChanged( QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); @@ -541,33 +600,27 @@ {"111111"}); EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); - EXPECT_TRUE(quick_unlock_storage->pin_storage_prefs()->IsPinSet()); + EXPECT_TRUE(IsPinSetInBackend()); // SetModes can be used to turn off a quick unlock mode. ExpectModesChanged(QuickUnlockModeList{}); RunSetModes(QuickUnlockModeList{}, CredentialList{}); EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{}); - EXPECT_FALSE(quick_unlock_storage->pin_storage_prefs()->IsPinSet()); + EXPECT_FALSE(IsPinSetInBackend()); } // Verifies that enabling PIN quick unlock actually talks to the PIN subsystem. TEST_F(QuickUnlockPrivateUnitTest, VerifyAuthenticationAgainstPIN) { - quick_unlock::QuickUnlockStorage* quick_unlock_storage = - quick_unlock::QuickUnlockFactory::GetForProfile(profile()); - RunSetModes(QuickUnlockModeList{}, CredentialList{}); - EXPECT_FALSE(quick_unlock_storage->pin_storage_prefs()->IsPinSet()); + EXPECT_FALSE(IsPinSetInBackend()); RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"111111"}); - EXPECT_TRUE(quick_unlock_storage->pin_storage_prefs()->IsPinSet()); + EXPECT_TRUE(IsPinSetInBackend()); - quick_unlock_storage->MarkStrongAuth(); - quick_unlock_storage->pin_storage_prefs()->ResetUnlockAttemptCount(); - EXPECT_TRUE(quick_unlock_storage->TryAuthenticatePin( - "111111", Key::KEY_TYPE_PASSWORD_PLAIN)); - EXPECT_FALSE(quick_unlock_storage->TryAuthenticatePin( - "000000", Key::KEY_TYPE_PASSWORD_PLAIN)); + EXPECT_FALSE(TryAuthenticate("000000")); + EXPECT_TRUE(TryAuthenticate("111111")); + EXPECT_FALSE(TryAuthenticate("000000")); } // Verifies that the number of modes and the number of passwords given must be
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index 42315189..aba4e88 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -87,15 +87,8 @@ TestParameter(NOT_IN_GUEST_MODE, "searchCaseInsensitive"), TestParameter(NOT_IN_GUEST_MODE, "searchNotFound"))); -// Fails on official build. http://crbug.com/429294 -// Disabled due to flakiness. https://crbug.com/701451 -#if defined(DISABLE_SLOW_FILESAPP_TESTS) || defined(OFFICIAL_BUILD) -#define MAYBE_OpenVideoFiles DISABLED_OpenVideoFiles -#else -#define MAYBE_OpenVideoFiles OpenVideoFiles -#endif WRAPPED_INSTANTIATE_TEST_CASE_P( - DISABLED_OpenVideoFiles, + OpenVideoFiles, FileManagerBrowserTest, ::testing::Values(TestParameter(IN_GUEST_MODE, "videoOpenDownloads"), TestParameter(NOT_IN_GUEST_MODE, "videoOpenDownloads"), @@ -401,7 +394,7 @@ ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "searchBoxFocus"))); WRAPPED_INSTANTIATE_TEST_CASE_P( - DISABLED_TabindexFocus, + TabindexFocus, FileManagerBrowserTestWithLegacyEventDispatch, ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "tabindexFocus")));
diff --git a/chrome/browser/chromeos/login/app_launch_signin_screen.cc b/chrome/browser/chromeos/login/app_launch_signin_screen.cc index 0c959a4..9f192f0 100644 --- a/chrome/browser/chromeos/login/app_launch_signin_screen.cc +++ b/chrome/browser/chromeos/login/app_launch_signin_screen.cc
@@ -108,10 +108,6 @@ NOTREACHED(); } -void AppLaunchSigninScreen::ShowDemoModeSetupScreen() { - NOTREACHED(); -} - void AppLaunchSigninScreen::ShowKioskEnableScreen() { NOTREACHED(); }
diff --git a/chrome/browser/chromeos/login/app_launch_signin_screen.h b/chrome/browser/chromeos/login/app_launch_signin_screen.h index e9cd94c..9403bfca 100644 --- a/chrome/browser/chromeos/login/app_launch_signin_screen.h +++ b/chrome/browser/chromeos/login/app_launch_signin_screen.h
@@ -64,7 +64,6 @@ void ResyncUserData() override; void ShowEnterpriseEnrollmentScreen() override; void ShowEnableDebuggingScreen() override; - void ShowDemoModeSetupScreen() override; void ShowKioskEnableScreen() override; void ShowKioskAutolaunchScreen() override; void ShowUpdateRequiredScreen() override;
diff --git a/chrome/browser/chromeos/login/demo_setup_browsertest.cc b/chrome/browser/chromeos/login/demo_setup_browsertest.cc index 6904053..8ed2fc21 100644 --- a/chrome/browser/chromeos/login/demo_setup_browsertest.cc +++ b/chrome/browser/chromeos/login/demo_setup_browsertest.cc
@@ -26,8 +26,7 @@ } void ShowDemoSetupScreen() { - LoginDisplayHost::default_host()->StartWizard( - OobeScreen::SCREEN_OOBE_DEMO_SETUP); + ASSERT_TRUE(JSExecute("cr.ui.Oobe.handleAccelerator('demo_mode');")); OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_SETUP).Wait(); ASSERT_NE(nullptr, GetDemoSetupScreen()); }
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 981c64f..633c022 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -192,11 +192,6 @@ !session_manager::SessionManager::Get()->IsSessionStarted(); } -bool DemoModeEnabled() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - chromeos::switches::kEnableDemoMode); -} - void RecordPasswordChangeFlow(LoginPasswordChangeFlow flow) { UMA_HISTOGRAM_ENUMERATION("Login.PasswordChangeFlow", flow, LOGIN_PASSWORD_CHANGE_FLOW_COUNT); @@ -693,11 +688,6 @@ ShowEnableDebuggingScreen(); } -void ExistingUserController::OnStartDemoModeSetupScreen() { - if (DemoModeEnabled()) - ShowDemoModeSetupScreen(); -} - void ExistingUserController::OnStartKioskEnableScreen() { KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(base::Bind( &ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted, @@ -783,10 +773,6 @@ host_->StartWizard(OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING); } -void ExistingUserController::ShowDemoModeSetupScreen() { - host_->StartWizard(OobeScreen::SCREEN_OOBE_DEMO_SETUP); -} - void ExistingUserController::ShowKioskEnableScreen() { host_->StartWizard(OobeScreen::SCREEN_KIOSK_ENABLE); }
diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index 0f80374..74557bb4 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h
@@ -99,7 +99,6 @@ void OnSigninScreenReady() override; void OnStartEnterpriseEnrollment() override; void OnStartEnableDebuggingScreen() override; - void OnStartDemoModeSetupScreen() override; void OnStartKioskEnableScreen() override; void OnStartKioskAutolaunchScreen() override; void ResetAutoLoginTimer() override;
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.cc b/chrome/browser/chromeos/login/lock/screen_locker.cc index c893eac1..a3190a19 100644 --- a/chrome/browser/chromeos/login/lock/screen_locker.cc +++ b/chrome/browser/chromeos/login/lock/screen_locker.cc
@@ -29,6 +29,7 @@ #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/lock/views_screen_locker.h" #include "chrome/browser/chromeos/login/lock/webui_screen_locker.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/chromeos/login/session/user_session_manager.h" @@ -377,15 +378,32 @@ Key::KeyType key_type = user_context.GetKey()->GetKeyType(); if (unlock_attempt_type_ == AUTH_PIN) { - quick_unlock::QuickUnlockStorage* quick_unlock_storage = - quick_unlock::QuickUnlockFactory::GetForUser(user); - if (quick_unlock_storage && - quick_unlock_storage->TryAuthenticatePin(pin, key_type)) { - OnAuthSuccess(user_context); - return; - } + quick_unlock::PinBackend::TryAuthenticate( + user_context.GetAccountId(), pin, key_type, + base::BindOnce(&ScreenLocker::OnPinAttemptDone, + weak_factory_.GetWeakPtr(), user_context)); + // OnPinAttemptDone will call ContinueAuthenticate. + return; } + } + ContinueAuthenticate(user_context); +} + +void ScreenLocker::OnPinAttemptDone(const UserContext& user_context, + bool success) { + if (success) { + OnAuthSuccess(user_context); + } else { + // PIN authentication has failed; try submitting as a normal password. + ContinueAuthenticate(user_context); + } +} + +void ScreenLocker::ContinueAuthenticate( + const chromeos::UserContext& user_context) { + const user_manager::User* user = FindUnlockUser(user_context.GetAccountId()); + if (user) { // Special case: supervised users. Use special authenticator. if (user->GetType() == user_manager::USER_TYPE_SUPERVISED) { UserContext updated_context = ChromeUserManager::Get()
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.h b/chrome/browser/chromeos/login/lock/screen_locker.h index 27ab0a4..8bc125f 100644 --- a/chrome/browser/chromeos/login/lock/screen_locker.h +++ b/chrome/browser/chromeos/login/lock/screen_locker.h
@@ -236,6 +236,12 @@ // lock request is failed. void OnStartLockCallback(bool locked); + void OnPinAttemptDone(const UserContext& user_context, bool success); + + // Called to continue authentication against cryptohome after the pin login + // check has completed. + void ContinueAuthenticate(const UserContext& user_context); + // WebUIScreenLocker instance in use. std::unique_ptr<WebUIScreenLocker> web_ui_;
diff --git a/chrome/browser/chromeos/login/lock/webui_screen_locker.cc b/chrome/browser/chromeos/login/lock/webui_screen_locker.cc index 09065a7..08a48fb 100644 --- a/chrome/browser/chromeos/login/lock/webui_screen_locker.cc +++ b/chrome/browser/chromeos/login/lock/webui_screen_locker.cc
@@ -323,10 +323,6 @@ NOTREACHED(); } -void WebUIScreenLocker::OnStartDemoModeSetupScreen() { - NOTREACHED(); -} - void WebUIScreenLocker::OnStartKioskEnableScreen() { NOTREACHED(); }
diff --git a/chrome/browser/chromeos/login/lock/webui_screen_locker.h b/chrome/browser/chromeos/login/lock/webui_screen_locker.h index b92b62c..065dcc56 100644 --- a/chrome/browser/chromeos/login/lock/webui_screen_locker.h +++ b/chrome/browser/chromeos/login/lock/webui_screen_locker.h
@@ -94,7 +94,6 @@ void OnSigninScreenReady() override; void OnStartEnterpriseEnrollment() override; void OnStartEnableDebuggingScreen() override; - void OnStartDemoModeSetupScreen() override; void OnStartKioskEnableScreen() override; void OnStartKioskAutolaunchScreen() override; void ShowWrongHWIDScreen() override;
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_backend.cc b/chrome/browser/chromeos/login/quick_unlock/pin_backend.cc new file mode 100644 index 0000000..f591d74 --- /dev/null +++ b/chrome/browser/chromeos/login/quick_unlock/pin_backend.cc
@@ -0,0 +1,151 @@ +// Copyright 2018 The Chromium 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/login/quick_unlock/pin_backend.h" + +#include "base/no_destructor.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h" +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" + +namespace chromeos { +namespace quick_unlock { + +namespace { + +PinStorageCryptohome* GetCryptohomeStorage() { + static base::NoDestructor<PinStorageCryptohome> instance; + return instance.get(); +} + +QuickUnlockStorage* GetPrefsBackend(const AccountId& account_id) { + return QuickUnlockFactory::GetForAccountId(account_id); +} + +void PostResponse(PinBackend::BoolCallback result, bool value) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(result), value)); +} + +} // namespace + +// static +void PinBackend::IsSet(const AccountId& account_id, BoolCallback result) { + if (GetPinStorageType() == PinStorageType::kCryptohome) { + GetCryptohomeStorage()->IsPinSetInCryptohome(account_id, std::move(result)); + } else { + QuickUnlockStorage* storage = GetPrefsBackend(account_id); + PostResponse(std::move(result), + storage && storage->pin_storage_prefs()->IsPinSet()); + } +} + +// static +void PinBackend::Set(const AccountId& account_id, + const std::string& token, + const std::string& pin, + BoolCallback did_set) { + QuickUnlockStorage* storage = GetPrefsBackend(account_id); + DCHECK(storage); + + if (GetPinStorageType() == PinStorageType::kCryptohome) { + // If |user_context| is null, then the token timed out. + UserContext* user_context = storage->GetUserContext(token); + if (!user_context) { + PostResponse(std::move(did_set), false); + return; + } + // There may be a pref value if resetting PIN and the device now supports + // cryptohome-based PIN. + storage->pin_storage_prefs()->RemovePin(); + GetCryptohomeStorage()->SetPin(*user_context, pin, std::move(did_set)); + } else { + storage->pin_storage_prefs()->SetPin(pin); + storage->MarkStrongAuth(); + PostResponse(std::move(did_set), true); + } +} + +// static +void PinBackend::Remove(const AccountId& account_id, + const std::string& token, + BoolCallback did_remove) { + QuickUnlockStorage* storage = GetPrefsBackend(account_id); + DCHECK(storage); + + if (GetPinStorageType() == PinStorageType::kCryptohome) { + // If |user_context| is null, then the token timed out. + UserContext* user_context = storage->GetUserContext(token); + if (!user_context) { + PostResponse(std::move(did_remove), false); + return; + } + GetCryptohomeStorage()->RemovePin(*user_context, std::move(did_remove)); + } else { + const bool had_pin = storage->pin_storage_prefs()->IsPinSet(); + storage->pin_storage_prefs()->RemovePin(); + PostResponse(std::move(did_remove), had_pin); + } +} + +// static +void PinBackend::CanAuthenticate(const AccountId& account_id, + BoolCallback result) { + if (GetPinStorageType() == PinStorageType::kCryptohome) { + GetCryptohomeStorage()->IsPinSetInCryptohome(account_id, std::move(result)); + } else { + QuickUnlockStorage* storage = GetPrefsBackend(account_id); + PostResponse( + std::move(result), + storage && storage->HasStrongAuth() && + storage->pin_storage_prefs()->IsPinAuthenticationAvailable()); + } +} + +// static +void PinBackend::TryAuthenticate(const AccountId& account_id, + const std::string& key, + const Key::KeyType& key_type, + BoolCallback result) { + if (GetPinStorageType() == PinStorageType::kCryptohome) { + GetCryptohomeStorage()->TryAuthenticate(account_id, key, key_type, + std::move(result)); + } else { + QuickUnlockStorage* storage = GetPrefsBackend(account_id); + DCHECK(storage); + + if (!storage->HasStrongAuth()) { + PostResponse(std::move(result), false); + } else { + PostResponse( + std::move(result), + storage->pin_storage_prefs()->TryAuthenticatePin(key, key_type)); + } + } +} + +// static +std::string PinBackend::ComputeSecret(const std::string& pin, + const std::string& salt, + Key::KeyType key_type) { + DCHECK(!pin.empty()); + DCHECK(!salt.empty()); + if (key_type != Key::KEY_TYPE_PASSWORD_PLAIN) + return pin; + + Key key(pin); + key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, salt); + return key.GetSecret(); +} + +// static +void PinBackend::ResetForTesting() { + new (GetCryptohomeStorage()) PinStorageCryptohome(); +} + +} // namespace quick_unlock +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_backend.h b/chrome/browser/chromeos/login/quick_unlock/pin_backend.h new file mode 100644 index 0000000..418d621 --- /dev/null +++ b/chrome/browser/chromeos/login/quick_unlock/pin_backend.h
@@ -0,0 +1,65 @@ +// Copyright 2018 The Chromium 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_LOGIN_QUICK_UNLOCK_PIN_BACKEND_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_QUICK_UNLOCK_PIN_BACKEND_H_ + +#include <string> + +#include "base/callback.h" +#include "chromeos/login/auth/key.h" + +class AccountId; + +namespace chromeos { + +namespace quick_unlock { + +// Provides high-level access to the user's PIN. The underlying storage can be +// either cryptohome or prefs. +class PinBackend { + public: + using BoolCallback = base::OnceCallback<void(bool)>; + + // Check if the given account_id has a PIN registered. + static void IsSet(const AccountId& account_id, BoolCallback result); + + // Set the PIN for the given user. + static void Set(const AccountId& account_id, + const std::string& auth_token, + const std::string& pin, + BoolCallback did_set); + + // Remove the given user's PIN. + static void Remove(const AccountId& account_id, + const std::string& auth_token, + BoolCallback did_remove); + + // Is PIN authentication available for the given account? Even if PIN is set, + // it may not be available for authentication due to some additional + // restrictions. + static void CanAuthenticate(const AccountId& account_id, BoolCallback result); + + // Try to authenticate. + static void TryAuthenticate(const AccountId& account_id, + const std::string& key, + const Key::KeyType& key_type, + BoolCallback result); + + // Computes the secret for a given |pin| and |salt|. + static std::string ComputeSecret(const std::string& pin, + const std::string& salt, + Key::KeyType key_type); + + // Resets any cached state for testing purposes. + static void ResetForTesting(); + + private: + DISALLOW_COPY_AND_ASSIGN(PinBackend); +}; + +} // namespace quick_unlock +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_QUICK_UNLOCK_PIN_BACKEND_H_
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.cc b/chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.cc new file mode 100644 index 0000000..147094b1 --- /dev/null +++ b/chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.cc
@@ -0,0 +1,38 @@ +// Copyright 2018 The Chromium 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/login/quick_unlock/pin_storage_cryptohome.h" + +namespace chromeos { +namespace quick_unlock { + +PinStorageCryptohome::PinStorageCryptohome() = default; + +PinStorageCryptohome::~PinStorageCryptohome() = default; + +void PinStorageCryptohome::IsPinSetInCryptohome(const AccountId& account_id, + BoolCallback callback) const { + NOTREACHED(); +} + +void PinStorageCryptohome::SetPin(const UserContext& user_context, + const std::string& pin, + BoolCallback did_set) { + NOTREACHED(); +} + +void PinStorageCryptohome::RemovePin(const UserContext& user_context, + BoolCallback did_remove) { + NOTREACHED(); +} + +void PinStorageCryptohome::TryAuthenticate(const AccountId& account_id, + const std::string& key, + const Key::KeyType& key_type, + BoolCallback result) { + NOTREACHED(); +} + +} // namespace quick_unlock +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.h b/chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.h new file mode 100644 index 0000000..c907012 --- /dev/null +++ b/chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.h
@@ -0,0 +1,46 @@ +// Copyright 2018 The Chromium 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_LOGIN_QUICK_UNLOCK_PIN_STORAGE_CRYPTOHOME_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_QUICK_UNLOCK_PIN_STORAGE_CRYPTOHOME_H_ + +#include <string> + +#include "base/callback.h" +#include "chromeos/login/auth/user_context.h" + +class AccountId; + +namespace chromeos { + +class UserContext; + +namespace quick_unlock { + +class PinStorageCryptohome { + public: + using BoolCallback = base::OnceCallback<void(bool)>; + + PinStorageCryptohome(); + ~PinStorageCryptohome(); + + void IsPinSetInCryptohome(const AccountId& account_id, + BoolCallback result) const; + void SetPin(const UserContext& user_context, + const std::string& pin, + BoolCallback did_set); + void RemovePin(const UserContext& user_context, BoolCallback did_remove); + void TryAuthenticate(const AccountId& account_id, + const std::string& key, + const Key::KeyType& key_type, + BoolCallback result); + + private: + DISALLOW_COPY_AND_ASSIGN(PinStorageCryptohome); +}; + +} // namespace quick_unlock +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_QUICK_UNLOCK_PIN_STORAGE_CRYPTOHOME_H_
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h b/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h index 39b212e..c9142cc 100644 --- a/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h +++ b/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h
@@ -8,6 +8,7 @@ #include <string> #include "base/gtest_prod_util.h" +#include "base/macros.h" #include "base/time/time.h" #include "chromeos/login/auth/key.h" @@ -36,7 +37,7 @@ // Add a PIN unlock attempt count. void AddUnlockAttempt(); - // Reset the number of unlock attempts to 0. + // Reset the unlock attempt count to 0. Not applicable to all implementations. void ResetUnlockAttemptCount(); // Returns the number of unlock attempts. int unlock_attempt_count() const { return unlock_attempt_count_; } @@ -48,10 +49,6 @@ // Removes the pin; IsPinSet will return false. void RemovePin(); - private: - friend class chromeos::PinStoragePrefsTestApi; - friend class QuickUnlockStorage; - // Is PIN entry currently available? bool IsPinAuthenticationAvailable() const; @@ -59,6 +56,10 @@ // This always returns false if IsPinAuthenticationAvailable returns false. bool TryAuthenticatePin(const std::string& pin, Key::KeyType key_type); + private: + friend class chromeos::PinStoragePrefsTestApi; + friend class QuickUnlockStorage; + // Return the stored salt/secret. This is fetched directly from pref_service_. std::string PinSalt() const; std::string PinSecret() const;
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc index 45478a4..e2e54c5 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc
@@ -92,14 +92,9 @@ return false; } - // Do not show the notification if the pin is already set. - PinStoragePrefs* pin_storage = - QuickUnlockFactory::GetForProfile(profile)->pin_storage_prefs(); - if (pin_storage->IsPinSet()) - return false; - - // TODO(jdufault): Enable after PIN sign-in is supported. See - // https://crbug.com/826773. + // TODO(jdufault): Enable once quick unlock settings land. See + // https://crbug.com/826773. Do not show the notification if the PIN is + // already set. return false; }
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.cc index 0378b2a3..807ae3c 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.cc
@@ -79,6 +79,12 @@ return *auth_token_->Identifier(); } +UserContext* QuickUnlockStorage::GetUserContext(const std::string& auth_token) { + if (GetAuthToken() != auth_token) + return nullptr; + return auth_token_->user_context(); +} + void QuickUnlockStorage::Shutdown() { fingerprint_storage_.reset(); pin_storage_prefs_.reset();
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h index 80b2e17..bba912d 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h
@@ -66,9 +66,15 @@ // token if valid, or an empty string if it has expired. std::string GetAuthToken(); + // Fetch the user context if |auth_token| is valid. May return null. + UserContext* GetUserContext(const std::string& auth_token); + FingerprintStorage* fingerprint_storage() { return fingerprint_storage_.get(); } + + // Fetch the underlying pref pin storage. If iteracting with pin generally, + // use the PinBackend APIs. PinStoragePrefs* pin_storage_prefs() { return pin_storage_prefs_.get(); } private:
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc index be336c2..e033bb52 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc
@@ -102,6 +102,8 @@ return base::FeatureList::IsEnabled(features::kQuickUnlockPin); } +// TODO(jdufault): Remove PinStorageType and make the backend transparent to +// callers. PinStorageType GetPinStorageType() { if (enable_for_testing_) return testing_pin_storage_type_;
diff --git a/chrome/browser/chromeos/login/ui/login_display.h b/chrome/browser/chromeos/login/ui/login_display.h index 376b24d..f6370a0 100644 --- a/chrome/browser/chromeos/login/ui/login_display.h +++ b/chrome/browser/chromeos/login/ui/login_display.h
@@ -68,9 +68,6 @@ // Called when the user requests enable developer features screen. virtual void OnStartEnableDebuggingScreen() = 0; - // Called when the user requests demo mode setup screen. - virtual void OnStartDemoModeSetupScreen() = 0; - // Called when the user requests kiosk enable screen. virtual void OnStartKioskEnableScreen() = 0;
diff --git a/chrome/browser/chromeos/login/ui/login_display_webui.cc b/chrome/browser/chromeos/login/ui/login_display_webui.cc index 9a52633..6c06492 100644 --- a/chrome/browser/chromeos/login/ui/login_display_webui.cc +++ b/chrome/browser/chromeos/login/ui/login_display_webui.cc
@@ -241,11 +241,6 @@ delegate_->OnStartEnableDebuggingScreen(); } -void LoginDisplayWebUI::ShowDemoModeSetupScreen() { - if (delegate_) - delegate_->OnStartDemoModeSetupScreen(); -} - void LoginDisplayWebUI::ShowKioskEnableScreen() { if (delegate_) delegate_->OnStartKioskEnableScreen();
diff --git a/chrome/browser/chromeos/login/ui/login_display_webui.h b/chrome/browser/chromeos/login/ui/login_display_webui.h index 3e6bb17..db0a2ce 100644 --- a/chrome/browser/chromeos/login/ui/login_display_webui.h +++ b/chrome/browser/chromeos/login/ui/login_display_webui.h
@@ -67,7 +67,6 @@ void CancelUserAdding() override; void ShowEnterpriseEnrollmentScreen() override; void ShowEnableDebuggingScreen() override; - void ShowDemoModeSetupScreen() override; void ShowKioskEnableScreen() override; void ShowKioskAutolaunchScreen() override; void ShowUpdateRequiredScreen() override;
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc index 97b1483..0ab8d9ba 100644 --- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc +++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
@@ -68,8 +68,9 @@ #include "google_apis/gaia/gaia_oauth_client.h" #include "google_apis/gaia/oauth2_token_service.h" #include "net/test/embedded_test_server/embedded_test_server.h" -#include "net/url_request/test_url_fetcher_factory.h" -#include "net/url_request/url_fetcher_delegate.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "net/test/embedded_test_server/simple_connection_listener.h" #include "net/url_request/url_request_status.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -90,6 +91,8 @@ constexpr char kTestUser2[] = "test-user2@gmail.com"; constexpr char kTestUser2GaiaId[] = "2222222222"; +constexpr char kRandomTokenStrForTesting[] = "random-token-str-for-testing"; + policy::CloudPolicyStore* GetStoreForUser(const user_manager::User* user) { Profile* profile = ProfileHelper::Get()->GetProfileByUserUnsafe(user); if (!profile) { @@ -134,6 +137,33 @@ class UserImageManagerTest : public LoginManagerTest, public user_manager::UserManager::Observer { + public: + std::unique_ptr<net::test_server::HttpResponse> HandleRequest( + const net::test_server::HttpRequest& request) { + // Check whether the token string is the same. + EXPECT_TRUE(request.headers.find(net::HttpRequestHeaders::kAuthorization) != + request.headers.end()); + const std::string authorization_header = + request.headers.at(net::HttpRequestHeaders::kAuthorization); + const size_t pos = authorization_header.find(" "); + EXPECT_TRUE(pos != std::string::npos); + const std::string token = authorization_header.substr(pos + 1); + EXPECT_TRUE(token == kRandomTokenStrForTesting); + + std::string profile_image_data; + base::FilePath test_data_dir; + EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)); + EXPECT_TRUE( + ReadFileToString(test_data_dir.Append("chromeos").Append("avatar1.jpg"), + &profile_image_data)); + std::unique_ptr<net::test_server::BasicHttpResponse> response = + std::make_unique<net::test_server::BasicHttpResponse>(); + response->set_content_type("image/jpeg"); + response->set_code(net::HTTP_OK); + response->set_content(profile_image_data); + return std::move(response); + } + protected: UserImageManagerTest() : LoginManagerTest(true) {} @@ -156,6 +186,16 @@ } void SetUpOnMainThread() override { + // Set up the test server. + embedded_test_server()->RegisterRequestHandler(base::BindRepeating( + &UserImageManagerTest::HandleRequest, base::Unretained(this))); + connection_listener_ = + std::make_unique<net::test_server::SimpleConnectionListener>( + 1, net::test_server::SimpleConnectionListener:: + ALLOW_ADDITIONAL_CONNECTIONS); + embedded_test_server()->SetConnectionListener(connection_listener_.get()); + ASSERT_TRUE(embedded_test_server()->Started()); + LoginManagerTest::SetUpOnMainThread(); local_state_ = g_browser_process->local_state(); user_manager::UserManager::Get()->AddObserver(this); @@ -231,17 +271,17 @@ info.given_name = account_id.GetUserEmail(); info.hosted_domain = AccountTrackerService::kNoHostedDomainFound; info.locale = account_id.GetUserEmail(); - info.picture_url = "http://localhost/avatar.jpg"; + info.picture_url = embedded_test_server()->GetURL("/avatar.jpg").spec(); info.is_child_account = false; AccountTrackerServiceFactory::GetForProfile(profile)->SeedAccountInfo(info); } - // Completes the download of all non-image profile data for the user - // |account_id|. This method must only be called after a profile data + // Triggers the download of all non-image profile data for the user + // |account_id|. This method must only be called after a profile data // download has been started. - // The net::TestURLFetcherFactory instance installed on the caller - // side will capture the net::TestURLFetcher created by the ProfileDownloader + // The |test_server::EmbeddedTestServer| instance installed on the caller + // side will capture the net::SimpleURLLoader created by the ProfileDownloader // to download the profile image. void CompleteProfileMetadataDownload(const AccountId& account_id) { ProfileDownloader* profile_downloader = @@ -251,7 +291,7 @@ ASSERT_TRUE(profile_downloader); static_cast<OAuth2TokenService::Consumer*>(profile_downloader) - ->OnGetTokenSuccess(NULL, std::string(), + ->OnGetTokenSuccess(NULL, kRandomTokenStrForTesting, base::Time::Now() + base::TimeDelta::FromDays(1)); } @@ -259,28 +299,13 @@ // This method must only be called after a profile data download including // the profile image has been started, the download of all non-image data has // been completed by calling CompleteProfileMetadataDownload() and the - // net::TestURLFetcher created by the ProfileDownloader to download the - // profile image has been captured by |url_fetcher_factory|. - void CompleteProfileImageDownload( - net::TestURLFetcherFactory* url_fetcher_factory) { - std::string profile_image_data; - base::FilePath test_data_dir; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)); - EXPECT_TRUE( - ReadFileToString(test_data_dir.Append("chromeos").Append("avatar1.jpg"), - &profile_image_data)); - + // net::SimpleURLLoader created by the ProfileDownloader to download the + // profile image has been captured by |embedded_test_server()|. + void CompleteProfileImageDownload() { base::RunLoop run_loop; PrefChangeRegistrar pref_change_registrar; pref_change_registrar.Init(local_state_); pref_change_registrar.Add("UserDisplayName", run_loop.QuitClosure()); - net::TestURLFetcher* fetcher = url_fetcher_factory->GetFetcherByID(0); - ASSERT_TRUE(fetcher); - fetcher->SetResponseString(profile_image_data); - fetcher->set_status( - net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); - fetcher->set_response_code(200); - fetcher->delegate()->OnURLFetchComplete(fetcher); run_loop.Run(); const user_manager::User* user = @@ -312,6 +337,9 @@ const cryptohome::Identification cryptohome_id_ = cryptohome::Identification(enterprise_account_id_); + std::unique_ptr<net::test_server::SimpleConnectionListener> + connection_listener_; + private: DISALLOW_COPY_AND_ASSIGN(UserImageManagerTest); }; @@ -509,9 +537,9 @@ user_image_manager->SaveUserImageFromProfileImage(); run_loop_->Run(); - net::TestURLFetcherFactory url_fetcher_factory; CompleteProfileMetadataDownload(test_account_id1_); - CompleteProfileImageDownload(&url_fetcher_factory); + connection_listener_->WaitForConnections(); + CompleteProfileImageDownload(); const gfx::ImageSkia& profile_image = user_image_manager->DownloadedProfileImage(); @@ -561,13 +589,14 @@ user_image_manager->SaveUserImageFromProfileImage(); run_loop_->Run(); - net::TestURLFetcherFactory url_fetcher_factory; CompleteProfileMetadataDownload(test_account_id1_); user_image_manager->SaveUserDefaultImageIndex( default_user_image::kFirstDefaultImageIndex); - CompleteProfileImageDownload(&url_fetcher_factory); + connection_listener_->WaitForConnections(); + + CompleteProfileImageDownload(); EXPECT_TRUE(user->HasDefaultImage()); EXPECT_EQ(default_user_image::kFirstDefaultImageIndex, user->image_index());
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 32a61c10..06a0a31 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -967,7 +967,8 @@ } void WizardController::OnDemoSetupClosed() { - ShowLoginScreen(LoginScreenContext()); + DCHECK(previous_screen_); + SetCurrentScreen(previous_screen_); } void WizardController::OnOobeFlowFinished() {
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc index 1bf0242..800602b 100644 --- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -1264,14 +1264,23 @@ IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, CloseDemoSetupShouldShowSignIn) { - LoginDisplayHost::default_host()->StartSignInScreen(LoginScreenContext()); - EXPECT_NE(nullptr, ExistingUserController::current_controller()); + CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK); + WaitUntilJSIsReady(); - ExistingUserController::current_controller()->OnStartDemoModeSetupScreen(); + EXPECT_CALL(*mock_network_screen_, Hide()).Times(1); + EXPECT_CALL(*mock_demo_setup_screen_, Show()).Times(1); + + WizardController::default_controller()->AdvanceToScreen( + OobeScreen::SCREEN_OOBE_DEMO_SETUP); + CheckCurrentScreen(OobeScreen::SCREEN_OOBE_DEMO_SETUP); + EXPECT_CALL(*mock_demo_setup_screen_, Hide()).Times(1); + EXPECT_CALL(*mock_network_screen_, Show()).Times(1); + OnExit(*mock_demo_setup_screen_, ScreenExitCode::DEMO_MODE_SETUP_CLOSED); - EXPECT_NE(nullptr, ExistingUserController::current_controller()); + + CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK); } class WizardControllerOobeResumeTest : public WizardControllerTest {
diff --git a/chrome/browser/chromeos/net/DEPS b/chrome/browser/chromeos/net/DEPS index b8bd215..f7dec82b 100644 --- a/chrome/browser/chromeos/net/DEPS +++ b/chrome/browser/chromeos/net/DEPS
@@ -4,14 +4,3 @@ "+ash/public", "+components/captive_portal", ] - -specific_include_rules = { - # TODO(mash): Eliminate these. - "auto_connect_notifier\.cc": [ - "+ash/system/system_notifier.h", - ], - "network_portal_notification_controller\.cc": [ - "+ash/system/tray/system_tray_notifier.h", - "+ash/shell.h", - ], -}
diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc b/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc index 6b0df851..07412d1a 100644 --- a/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc +++ b/chrome/browser/chromeos/net/network_portal_detector_impl_browsertest.cc
@@ -16,11 +16,11 @@ #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/net/network_portal_detector_impl.h" #include "chrome/browser/chromeos/net/network_portal_detector_test_utils.h" -#include "chrome/browser/chromeos/net/network_portal_notification_controller.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/notifications/notification_display_service_tester.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/ash/network/network_portal_notification_controller.h" #include "chrome/common/pref_names.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/chromeos/net/network_portal_web_dialog.h b/chrome/browser/chromeos/net/network_portal_web_dialog.h index d483353..fd137431 100644 --- a/chrome/browser/chromeos/net/network_portal_web_dialog.h +++ b/chrome/browser/chromeos/net/network_portal_web_dialog.h
@@ -7,7 +7,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/chromeos/net/network_portal_notification_controller.h" +#include "chrome/browser/ui/ash/network/network_portal_notification_controller.h" #include "ui/web_dialogs/web_dialog_delegate.h" namespace views {
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc index 194dadd..63e4df3 100644 --- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc +++ b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
@@ -1620,17 +1620,19 @@ for (const FakeDeviceData& dev : kFakeDevices) { device_client->AddDevice(dev.device_path, dev.type, dev.object_path); if (*dev.mac_address) { - device_client->SetDeviceProperty(dev.device_path, - shill::kAddressProperty, - base::Value(dev.mac_address)); + device_client->SetDeviceProperty( + dev.device_path, shill::kAddressProperty, + base::Value(dev.mac_address), /*notify_changed=*/true); } if (*dev.meid) { device_client->SetDeviceProperty(dev.device_path, shill::kMeidProperty, - base::Value(dev.meid)); + base::Value(dev.meid), + /*notify_changed=*/true); } if (*dev.imei) { device_client->SetDeviceProperty(dev.device_path, shill::kImeiProperty, - base::Value(dev.imei)); + base::Value(dev.imei), + /*notify_changed=*/true); } }
diff --git a/chrome/browser/chromeos/profiles/profile_helper.h b/chrome/browser/chromeos/profiles/profile_helper.h index 39e4652..de7a525a 100644 --- a/chrome/browser/chromeos/profiles/profile_helper.h +++ b/chrome/browser/chromeos/profiles/profile_helper.h
@@ -27,6 +27,10 @@ class FilePath; } +namespace chromeos { +class QuickUnlockPrivateUnitTest; +} + namespace extensions { class ExtensionGarbageCollectorChromeOSUnitTest; } @@ -189,6 +193,7 @@ friend class arc::ArcSessionManagerTest; friend class arc::ArcAuthServiceTest; friend class arc::ArcCertStoreBridgeTest; + friend class chromeos::QuickUnlockPrivateUnitTest; friend class ::ArcAppTest; friend class ::SessionControllerClientTest; friend class ::test::BrowserFinderChromeOSTest;
diff --git a/chrome/browser/chromeos/smb_client/smb_service.cc b/chrome/browser/chromeos/smb_client/smb_service.cc index 6e567b38b..0ad1446 100644 --- a/chrome/browser/chromeos/smb_client/smb_service.cc +++ b/chrome/browser/chromeos/smb_client/smb_service.cc
@@ -11,6 +11,7 @@ #include "chrome/browser/chromeos/smb_client/smb_file_system_id.h" #include "chrome/browser/chromeos/smb_client/smb_provider.h" #include "chrome/browser/chromeos/smb_client/smb_service_factory.h" +#include "chrome/browser/chromeos/smb_client/temp_file_manager.h" #include "chrome/common/chrome_features.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/smb_provider_client.h" @@ -39,9 +40,13 @@ void SmbService::Mount(const file_system_provider::MountOptions& options, const base::FilePath& share_path, MountResponse callback) { + // TODO(allenvic): Implement passing of credentials. This currently passes + // empty credentials to SmbProvider. GetSmbProviderClient()->Mount( - share_path, base::BindOnce(&SmbService::OnMountResponse, AsWeakPtr(), - base::Passed(&callback), options, share_path)); + share_path, "" /* workgroup */, "" /* username */, + temp_file_manager_.WritePasswordToFile("" /* password */), + base::BindOnce(&SmbService::OnMountResponse, AsWeakPtr(), + base::Passed(&callback), options, share_path)); } void SmbService::OnMountResponse(
diff --git a/chrome/browser/chromeos/smb_client/smb_service.h b/chrome/browser/chromeos/smb_client/smb_service.h index ad7a617..17f979cc 100644 --- a/chrome/browser/chromeos/smb_client/smb_service.h +++ b/chrome/browser/chromeos/smb_client/smb_service.h
@@ -14,6 +14,7 @@ #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" #include "chrome/browser/chromeos/file_system_provider/provider_interface.h" #include "chrome/browser/chromeos/file_system_provider/service.h" +#include "chrome/browser/chromeos/smb_client/temp_file_manager.h" #include "chrome/browser/profiles/profile.h" #include "chromeos/dbus/smb_provider_client.h" #include "components/keyed_service/core/keyed_service.h" @@ -84,6 +85,7 @@ const ProviderId provider_id_; Profile* profile_; + TempFileManager temp_file_manager_; DISALLOW_COPY_AND_ASSIGN(SmbService); };
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc index ac2fee6..2f156c3 100644 --- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc +++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -141,9 +141,10 @@ ui::PAGE_TRANSITION_AUTO_TOPLEVEL); params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms); - if (!params.target_contents) + if (!params.navigated_or_inserted_contents) return nullptr; - return DevToolsAgentHost::GetOrCreateFor(params.target_contents); + return DevToolsAgentHost::GetOrCreateFor( + params.navigated_or_inserted_contents); } std::string ChromeDevToolsManagerDelegate::GetDiscoveryPageHTML() {
diff --git a/chrome/browser/devtools/protocol/target_handler.cc b/chrome/browser/devtools/protocol/target_handler.cc index c9b57fd..cb544fe 100644 --- a/chrome/browser/devtools/protocol/target_handler.cc +++ b/chrome/browser/devtools/protocol/target_handler.cc
@@ -81,12 +81,12 @@ } Navigate(¶ms); - if (!params.target_contents) + if (!params.navigated_or_inserted_contents) return protocol::Response::Error("Failed to open a new tab"); - *out_target_id = - content::DevToolsAgentHost::GetOrCreateFor(params.target_contents) - ->GetId(); + *out_target_id = content::DevToolsAgentHost::GetOrCreateFor( + params.navigated_or_inserted_contents) + ->GetId(); return protocol::Response::OK(); }
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index 65a21bee6..6278f43 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -46,7 +46,6 @@ #include "chrome/browser/ui/extensions/app_launch_params.h" #include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/webui/extensions/extension_loader_handler.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/pref_names.h" @@ -81,6 +80,7 @@ #include "extensions/common/feature_switch.h" #include "extensions/common/install_warning.h" #include "extensions/common/manifest.h" +#include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_handlers/options_page_info.h" #include "extensions/common/manifest_url_handlers.h" #include "extensions/common/permissions/permissions_data.h" @@ -91,6 +91,7 @@ #include "storage/browser/fileapi/file_system_operation.h" #include "storage/browser/fileapi/file_system_operation_runner.h" #include "storage/browser/fileapi/isolated_context.h" +#include "third_party/re2/src/re2/re2.h" #include "ui/base/l10n/l10n_util.h" namespace extensions { @@ -134,10 +135,43 @@ std::string ReadFileToString(const base::FilePath& path) { std::string data; + // This call can fail, but it doesn't matter for our purposes. If it fails, + // we simply return an empty string for the manifest, and ignore it. ignore_result(base::ReadFileToString(path, &data)); return data; } +using GetManifestErrorCallback = + base::OnceCallback<void(const base::FilePath& file_path, + const std::string& error, + size_t line_number, + const std::string& manifest)>; +// Takes in an |error| string and tries to parse it as a manifest error (with +// line number), asynchronously calling |callback| with the results. +void GetManifestError(const std::string& error, + const base::FilePath& extension_path, + GetManifestErrorCallback callback) { + size_t line = 0u; + size_t column = 0u; + std::string regex = base::StringPrintf("%s Line: (\\d+), column: (\\d+), .*", + manifest_errors::kManifestParseError); + // If this was a JSON parse error, we can highlight the exact line with the + // error. Otherwise, we should still display the manifest (for consistency, + // reference, and so that if we ever make this really fancy and add an editor, + // it's ready). + // + // This regex call can fail, but if it does, we just don't highlight anything. + re2::RE2::FullMatch(error, regex, &line, &column); + + // This will read the manifest and call AddFailure with the read manifest + // contents. + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, + base::BindOnce(&ReadFileToString, + extension_path.Append(kManifestFilename)), + base::BindOnce(std::move(callback), extension_path, error, line)); +} + bool UserCanModifyExtensionConfiguration( const Extension* extension, content::BrowserContext* browser_context, @@ -909,11 +943,10 @@ const std::string& error) { if (file_path == reloading_extension_path_) { // Reload failed - create an error to pass back to the extension. - ExtensionLoaderHandler::GetManifestError( + GetManifestError( error, file_path, - // TODO(devlin): Update GetManifestError to take a OnceCallback. - base::BindRepeating(&DeveloperPrivateReloadFunction::OnGotManifestError, - this)); // Creates a reference. + base::BindOnce(&DeveloperPrivateReloadFunction::OnGotManifestError, + this)); // Creates a reference. ClearObservers(); } } @@ -1075,7 +1108,7 @@ return; } - ExtensionLoaderHandler::GetManifestError( + GetManifestError( error, file_path, base::Bind(&DeveloperPrivateLoadUnpackedFunction::OnGotManifestError, this));
diff --git a/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc b/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc index 5cdb6dbd6..49a11dfa 100644 --- a/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc +++ b/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc
@@ -13,10 +13,10 @@ #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/net/network_portal_detector_impl.h" -#include "chrome/browser/chromeos/net/network_portal_notification_controller.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/notifications/notification_display_service_tester.h" +#include "chrome/browser/ui/ash/network/network_portal_notification_controller.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_device_client.h" #include "chromeos/dbus/shill_profile_client.h"
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc index 4abe0b89..991d90c 100644 --- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc +++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -52,6 +52,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/test/test_utils.h" +#include "dbus/object_path.h" #include "extensions/browser/api/networking_private/networking_private_chromeos.h" #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h" #include "extensions/browser/notification_types.h" @@ -252,33 +253,30 @@ // Add a Cellular GSM Device. device_test_->AddDevice(kCellularDevicePath, shill::kTypeCellular, "stub_cellular_device1"); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kCarrierProperty, - base::Value("Cellular1_Carrier")); + SetDeviceProperty(kCellularDevicePath, shill::kCarrierProperty, + base::Value("Cellular1_Carrier")); base::DictionaryValue home_provider; home_provider.SetString("name", "Cellular1_Provider"); home_provider.SetString("code", "000000"); home_provider.SetString("country", "us"); - device_test_->SetDeviceProperty( - kCellularDevicePath, shill::kHomeProviderProperty, home_provider); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kTechnologyFamilyProperty, - base::Value(shill::kNetworkTechnologyGsm)); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMeidProperty, - base::Value("test_meid")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kImeiProperty, - base::Value("test_imei")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kIccidProperty, - base::Value("test_iccid")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kEsnProperty, - base::Value("test_esn")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMdnProperty, - base::Value("test_mdn")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMinProperty, - base::Value("test_min")); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kModelIdProperty, - base::Value("test_model_id")); + SetDeviceProperty(kCellularDevicePath, shill::kHomeProviderProperty, + home_provider); + SetDeviceProperty(kCellularDevicePath, shill::kTechnologyFamilyProperty, + base::Value(shill::kNetworkTechnologyGsm)); + SetDeviceProperty(kCellularDevicePath, shill::kMeidProperty, + base::Value("test_meid")); + SetDeviceProperty(kCellularDevicePath, shill::kImeiProperty, + base::Value("test_imei")); + SetDeviceProperty(kCellularDevicePath, shill::kIccidProperty, + base::Value("test_iccid")); + SetDeviceProperty(kCellularDevicePath, shill::kEsnProperty, + base::Value("test_esn")); + SetDeviceProperty(kCellularDevicePath, shill::kMdnProperty, + base::Value("test_mdn")); + SetDeviceProperty(kCellularDevicePath, shill::kMinProperty, + base::Value("test_min")); + SetDeviceProperty(kCellularDevicePath, shill::kModelIdProperty, + base::Value("test_model_id")); device_test_->SetSimLocked(kCellularDevicePath, false); // Add the Cellular Service. @@ -323,6 +321,13 @@ state, true /* add_to_visible */); } + void SetDeviceProperty(const std::string& device_path, + const std::string& name, + const base::Value& value) { + device_test_->SetDeviceProperty(device_path, name, value, + /*notify_changed=*/true); + } + static std::unique_ptr<KeyedService> CreateNetworkingPrivateDelegate( content::BrowserContext* context) { std::unique_ptr<NetworkingPrivateDelegate> result( @@ -389,10 +394,10 @@ "stub_wifi_device1"); base::ListValue wifi_ip_configs; wifi_ip_configs.AppendString(kIPConfigPath); - device_test_->SetDeviceProperty(kWifiDevicePath, shill::kIPConfigsProperty, - wifi_ip_configs); - device_test_->SetDeviceProperty(kWifiDevicePath, shill::kAddressProperty, - base::Value("001122aabbcc")); + SetDeviceProperty(kWifiDevicePath, shill::kIPConfigsProperty, + wifi_ip_configs); + SetDeviceProperty(kWifiDevicePath, shill::kAddressProperty, + base::Value("001122aabbcc")); // Add Services AddService("stub_ethernet", "eth0", shill::kTypeEthernet, @@ -543,8 +548,10 @@ IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest, StartActivateSprint) { SetupCellular(); // Set the carrier to Sprint. - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kCarrierProperty, - base::Value(shill::kCarrierSprint)); + DBusThreadManager::Get()->GetShillDeviceClient()->SetCarrier( + dbus::ObjectPath(kCellularDevicePath), shill::kCarrierSprint, + base::DoNothing(), + base::BindRepeating([](const std::string&, const std::string&) {})); EXPECT_TRUE(RunNetworkingSubtest("startActivateSprint")) << message_; EXPECT_EQ(0, UIDelegateStub::s_show_account_details_called_); } @@ -908,8 +915,8 @@ .Set(shill::kStatusProperty, "available") .Build()) .Build(); - device_test_->SetDeviceProperty( - kCellularDevicePath, shill::kFoundNetworksProperty, *found_networks); + SetDeviceProperty(kCellularDevicePath, shill::kFoundNetworksProperty, + *found_networks); EXPECT_TRUE(RunNetworkingSubtest("selectCellularMobileNetwork")) << message_; }
diff --git a/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc b/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc index ca89718..a8f77fda 100644 --- a/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc +++ b/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc
@@ -1021,8 +1021,9 @@ // Tests that clicking a target=_blank link from a URL out of the Web App's // scope but with the same origin to an in-scope URL results in a new App // window. +// TODO(crbug.com/837277): Deflake and reenable. IN_PROC_BROWSER_TEST_P(BookmarkAppNavigationThrottleExperimentalLinkBrowserTest, - FromOutOfScopeUrlToInScopeUrlBlank) { + DISABLED_FromOutOfScopeUrlToInScopeUrlBlank) { InstallTestBookmarkApp(); // Navigate to out-of-scope URL. Shouldn't open a new window.
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.cc b/chrome/browser/extensions/chrome_extension_host_delegate.cc index 5f524f5..c8c11db 100644 --- a/chrome/browser/extensions/chrome_extension_host_delegate.cc +++ b/chrome/browser/extensions/chrome_extension_host_delegate.cc
@@ -63,19 +63,20 @@ return app_modal::JavaScriptDialogManager::GetInstance(); } -void ChromeExtensionHostDelegate::CreateTab(content::WebContents* web_contents, - const std::string& extension_id, - WindowOpenDisposition disposition, - const gfx::Rect& initial_rect, - bool user_gesture) { +void ChromeExtensionHostDelegate::CreateTab( + std::unique_ptr<content::WebContents> web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) { // Verify that the browser is not shutting down. It can be the case if the // call is propagated through a posted task that was already in the queue when // shutdown started. See crbug.com/625646 if (g_browser_process->IsShuttingDown()) return; - ExtensionTabUtil::CreateTab( - web_contents, extension_id, disposition, initial_rect, user_gesture); + ExtensionTabUtil::CreateTab(std::move(web_contents), extension_id, + disposition, initial_rect, user_gesture); } void ChromeExtensionHostDelegate::ProcessMediaAccessRequest(
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.h b/chrome/browser/extensions/chrome_extension_host_delegate.h index f08006a..d525324 100644 --- a/chrome/browser/extensions/chrome_extension_host_delegate.h +++ b/chrome/browser/extensions/chrome_extension_host_delegate.h
@@ -19,7 +19,7 @@ void OnExtensionHostCreated(content::WebContents* web_contents) override; void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) override; content::JavaScriptDialogManager* GetJavaScriptDialogManager() override; - void CreateTab(content::WebContents* web_contents, + void CreateTab(std::unique_ptr<content::WebContents> web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect,
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc index fae948e..fbfb3d7b 100644 --- a/chrome/browser/extensions/extension_tab_util.cc +++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -251,8 +251,8 @@ // The tab may have been created in a different window, so make sure we look // at the right tab strip. tab_strip = navigate_params.browser->tab_strip_model(); - int new_index = - tab_strip->GetIndexOfWebContents(navigate_params.target_contents); + int new_index = tab_strip->GetIndexOfWebContents( + navigate_params.navigated_or_inserted_contents); if (opener) { // Only set the opener if the opener tab is in the same tab strip as the // new tab. @@ -261,12 +261,12 @@ } if (active) - navigate_params.target_contents->SetInitialFocus(); + navigate_params.navigated_or_inserted_contents->SetInitialFocus(); // Return data about the newly created tab. - return ExtensionTabUtil::CreateTabObject(navigate_params.target_contents, - kScrubTab, function->extension(), - tab_strip, new_index) + return ExtensionTabUtil::CreateTabObject( + navigate_params.navigated_or_inserted_contents, kScrubTab, + function->extension(), tab_strip, new_index) ->ToValue() .release(); } @@ -620,7 +620,7 @@ return false; } -void ExtensionTabUtil::CreateTab(WebContents* web_contents, +void ExtensionTabUtil::CreateTab(std::unique_ptr<WebContents> web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, @@ -633,7 +633,7 @@ Browser::CreateParams params = Browser::CreateParams(profile, user_gesture); browser = new Browser(params); } - NavigateParams params(browser, web_contents); + NavigateParams params(browser, std::move(web_contents)); // The extension_app_id parameter ends up as app_name in the Browser // which causes the Browser to return true for is_app(). This affects @@ -731,7 +731,7 @@ params.path_behavior = open_in_tab ? NavigateParams::RESPECT : NavigateParams::IGNORE_AND_NAVIGATE; params.url = url_to_navigate; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); return true; }
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h index a813302..0be4ae2 100644 --- a/chrome/browser/extensions/extension_tab_util.h +++ b/chrome/browser/extensions/extension_tab_util.h
@@ -179,7 +179,7 @@ static bool IsKillURL(const GURL& url); // Opens a tab for the specified |web_contents|. - static void CreateTab(content::WebContents* web_contents, + static void CreateTab(std::unique_ptr<content::WebContents> web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect,
diff --git a/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc b/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc index d58df4d..22ab8cc4 100644 --- a/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc +++ b/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc
@@ -197,6 +197,8 @@ bool is_primary = id == primary.id() || (info.is_primary && *info.is_primary); if (info.is_unified) { + if (!ash::Shell::Get()->display_manager()->unified_desktop_enabled()) + return "Unified desktop mode is not enabled."; if (!is_primary) return "Unified desktop mode can only be set for the primary display."; if (info.mirroring_source_id)
diff --git a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc index 81810207..a9c53f9 100644 --- a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc +++ b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
@@ -93,7 +93,7 @@ } protected: - std::string CallSetDisplayUnitInfo( + bool CallSetDisplayUnitInfo( const std::string& display_id, const api::system_display::DisplayProperties& info) { std::string result; @@ -102,7 +102,7 @@ display_id, info, base::BindOnce(&ErrorCallback, &result, run_loop.QuitClosure())); run_loop.Run(); - return result; + return result.empty(); } bool DisplayExists(int64_t display_id) const { @@ -203,7 +203,7 @@ int64_t display_id; ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -216,7 +216,7 @@ EXPECT_TRUE(result[0].is_enabled); ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ(GetDisplayManager()->GetDisplayNameForId(display_id), @@ -244,7 +244,7 @@ int64_t display_id; ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -258,13 +258,13 @@ EXPECT_TRUE(result[0].is_enabled); ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ(GetDisplayManager()->GetDisplayNameForId(display_id), result[1].name); - // Initial multipple display configuration. + // Initial multiple display configuration. EXPECT_EQ("500,0 400x520", SystemInfoDisplayBoundsToString(result[1].bounds)); EXPECT_EQ("0,0,0,0", SystemInfoDisplayInsetsToString(result[1].overscan)); EXPECT_EQ(0, result[1].rotation); @@ -284,7 +284,7 @@ ASSERT_EQ(2u, result.size()); ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds)); EXPECT_EQ("0,0,0,0", SystemInfoDisplayInsetsToString(result[0].overscan)); @@ -297,7 +297,7 @@ EXPECT_TRUE(result[0].is_enabled); ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; EXPECT_EQ(GetDisplayManager()->GetDisplayNameForId(display_id), result[1].name); @@ -322,7 +322,7 @@ ASSERT_EQ(2u, result.size()); ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -336,7 +336,7 @@ EXPECT_TRUE(result[0].is_enabled); ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ(GetDisplayManager()->GetDisplayNameForId(display_id), @@ -363,7 +363,7 @@ int64_t display_id; ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -377,13 +377,13 @@ EXPECT_TRUE(result[0].is_enabled); ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ(GetDisplayManager()->GetDisplayNameForId(display_id), result[1].name); - // Initial multipple display configuration. + // Initial multiple display configuration. EXPECT_EQ("500,0 400x520", SystemInfoDisplayBoundsToString(result[1].bounds)); EXPECT_EQ("0,0,0,0", SystemInfoDisplayInsetsToString(result[1].overscan)); EXPECT_EQ(0, result[1].rotation); @@ -406,7 +406,7 @@ ASSERT_EQ(1u, result.size()); ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; EXPECT_EQ("0,0 961x600", SystemInfoDisplayBoundsToString(result[0].bounds)); EXPECT_EQ("0,0,0,0", SystemInfoDisplayInsetsToString(result[0].overscan)); @@ -426,7 +426,7 @@ ASSERT_EQ(2u, result.size()); ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -440,7 +440,7 @@ EXPECT_TRUE(result[0].is_enabled); ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ(GetDisplayManager()->GetDisplayNameForId(display_id), @@ -464,7 +464,7 @@ int64_t display_id; ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 600x500", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -549,7 +549,7 @@ int64_t display_id; ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id)) - << "Display id must be convertable to integer: " << result[1].id; + << "Display id must be convertible to integer: " << result[1].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; // Default overscan is 5%. @@ -568,7 +568,7 @@ // Set insets for the primary screen. Note that it has 2x scale. ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id)) << display_id << " not found"; EXPECT_EQ("0,0 304x342", SystemInfoDisplayBoundsToString(result[0].bounds)); @@ -594,13 +594,13 @@ int64_t display_id_primary; ASSERT_TRUE(base::StringToInt64(result[0].id, &display_id_primary)) - << "Display id must be convertable to integer: " << result[0].id; + << "Display id must be convertible to integer: " << result[0].id; ASSERT_TRUE(DisplayExists(display_id_primary)) << display_id_primary << " not found"; int64_t display_id_secondary; ASSERT_TRUE(base::StringToInt64(result[1].id, &display_id_secondary)) - << "Display id must be convertable to integer: " << result[1].id; + << "Display id must be convertible to integer: " << result[1].id; ASSERT_TRUE(DisplayExists(display_id_secondary)) << display_id_secondary << " not found"; @@ -791,6 +791,100 @@ EXPECT_EQ(0, new_layout[2].offset); } +TEST_F(DisplayInfoProviderChromeosTest, SetUnified) { + UpdateDisplay("500x400,500x400"); + EXPECT_FALSE(GetDisplayManager()->unified_desktop_enabled()); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + api::system_display::DisplayProperties info; + + // Test that setting is_unified to true fails unless EnableUnifiedDesktop is + // called first. + info.is_unified = std::make_unique<bool>(true); + EXPECT_FALSE(CallSetDisplayUnitInfo( + base::Int64ToString( + display::Screen::GetScreen()->GetPrimaryDisplay().id()), + info)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + // Test that enabling unified desktop succeeds and sets the desktop mode to + // unified. + DisplayInfoProvider::Get()->EnableUnifiedDesktop(true); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(GetDisplayManager()->unified_desktop_enabled()); + EXPECT_TRUE(GetDisplayManager()->IsInUnifiedMode()); + + // Test that setting is_unified to false turns off unified mode but leaves it + // enabled. + info.is_unified = std::make_unique<bool>(false); + EXPECT_TRUE(CallSetDisplayUnitInfo( + base::Int64ToString( + display::Screen::GetScreen()->GetPrimaryDisplay().id()), + info)); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + // Test that setting is_unified to true succeeds without an additional call to + // EnableUnifiedDesktop. + info.is_unified = std::make_unique<bool>(true); + EXPECT_TRUE(CallSetDisplayUnitInfo( + base::Int64ToString( + display::Screen::GetScreen()->GetPrimaryDisplay().id()), + info)); + EXPECT_TRUE(GetDisplayManager()->IsInUnifiedMode()); + + // Restore extended mode. + DisplayInfoProvider::Get()->EnableUnifiedDesktop(false); +} + +TEST_F(DisplayInfoProviderChromeosTest, SetUnifiedOneDisplay) { + UpdateDisplay("500x400"); + EXPECT_FALSE(GetDisplayManager()->unified_desktop_enabled()); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + api::system_display::DisplayProperties info; + + // Enabling unified desktop with one display should succeed, but desktop mode + // will not be unified. + DisplayInfoProvider::Get()->EnableUnifiedDesktop(true); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(GetDisplayManager()->unified_desktop_enabled()); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + // Adding another display should set unified mode. + UpdateDisplay("500x400,500x400"); + EXPECT_TRUE(GetDisplayManager()->IsInUnifiedMode()); + + // Restore extended mode. + DisplayInfoProvider::Get()->EnableUnifiedDesktop(false); +} + +TEST_F(DisplayInfoProviderChromeosTest, SetUnifiedMirrored) { + UpdateDisplay("500x400,500x400"); + + GetDisplayManager()->SetMirrorMode(display::MirrorMode::kNormal, + base::nullopt); + EXPECT_TRUE(GetDisplayManager()->IsInMirrorMode()); + + EXPECT_FALSE(GetDisplayManager()->unified_desktop_enabled()); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + api::system_display::DisplayProperties info; + + // Enabling unified desktop while mirroring should succeed, but desktop mode + // will not be unified. + DisplayInfoProvider::Get()->EnableUnifiedDesktop(true); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode()); + + // Turning off mirroring should set unified mode. + GetDisplayManager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt); + EXPECT_TRUE(GetDisplayManager()->IsInUnifiedMode()); + + // Restore extended mode. + DisplayInfoProvider::Get()->EnableUnifiedDesktop(false); +} + TEST_F(DisplayInfoProviderChromeosTest, SetBoundsOriginLeftExact) { UpdateDisplay("1200x600,520x400"); @@ -799,8 +893,8 @@ info.bounds_origin_x.reset(new int(-520)); info.bounds_origin_y.reset(new int(50)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("-520,50 520x400", secondary.bounds().ToString()); } @@ -813,8 +907,8 @@ info.bounds_origin_x.reset(new int(1200)); info.bounds_origin_y.reset(new int(100)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,100 520x400", secondary.bounds().ToString()); } @@ -827,8 +921,8 @@ info.bounds_origin_x.reset(new int(1100)); info.bounds_origin_y.reset(new int(-400)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1100,-400 520x400", secondary.bounds().ToString()); } @@ -841,8 +935,8 @@ info.bounds_origin_x.reset(new int(-350)); info.bounds_origin_y.reset(new int(600)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("-350,600 520x400", secondary.bounds().ToString()); } @@ -855,8 +949,8 @@ info.bounds_origin_x.reset(new int(340)); info.bounds_origin_y.reset(new int(100)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,100 520x400", secondary.bounds().ToString()); } @@ -869,8 +963,8 @@ info.bounds_origin_x.reset(new int(-1040)); info.bounds_origin_y.reset(new int(100)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("-520,100 520x400", secondary.bounds().ToString()); } @@ -883,8 +977,8 @@ info.bounds_origin_x.reset(new int(-360)); info.bounds_origin_y.reset(new int(-301)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("-360,-400 520x400", secondary.bounds().ToString()); } @@ -898,8 +992,8 @@ info.bounds_origin_x.reset(new int(-650)); info.bounds_origin_y.reset(new int(700)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("-650,600 1000x100", secondary.bounds().ToString()); } @@ -912,8 +1006,8 @@ info.bounds_origin_x.reset(new int(850)); info.bounds_origin_y.reset(new int(-150)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("850,-100 1000x100", secondary.bounds().ToString()); } @@ -926,8 +1020,8 @@ info.bounds_origin_x.reset(new int(-150)); info.bounds_origin_y.reset(new int(-650)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("-100,-650 100x1000", secondary.bounds().ToString()); } @@ -941,8 +1035,8 @@ info.bounds_origin_x.reset(new int(1350)); info.bounds_origin_y.reset(new int(450)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,450 100x1000", secondary.bounds().ToString()); } @@ -955,8 +1049,8 @@ info.bounds_origin_x.reset(new int(250)); info.bounds_origin_y.reset(new int(-100)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("600,-100 500x500", secondary.bounds().ToString()); } @@ -969,8 +1063,8 @@ info.bounds_origin_x.reset(new int(450)); info.bounds_origin_y.reset(new int(-100)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("450,-500 300x500", secondary.bounds().ToString()); } @@ -983,9 +1077,8 @@ info.bounds_origin_x.reset(new int(0x200001)); info.bounds_origin_y.reset(new int(-100)); - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_EQ("Bounds origin x out of bounds.", error); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); } @@ -998,9 +1091,8 @@ info.bounds_origin_x.reset(new int(300)); info.bounds_origin_y.reset(new int(-0x200001)); - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_EQ("Bounds origin y out of bounds.", error); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); } @@ -1013,8 +1105,8 @@ info.bounds_origin_x.reset(new int(200000)); info.bounds_origin_y.reset(new int(10)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,10 300x500", secondary.bounds().ToString()); } @@ -1027,9 +1119,8 @@ info.bounds_origin_x.reset(new int(300)); info.is_primary.reset(new bool(true)); - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_EQ("Bounds origin not allowed for the primary display.", error); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); // The operation failed because the primary property would be set before @@ -1050,10 +1141,8 @@ info.mirroring_source_id.reset( new std::string(base::Int64ToString(primary.id()))); - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_EQ("No other parameter should be set alongside mirroringSourceId.", - error); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); } TEST_F(DisplayInfoProviderChromeosTest, SetRotation) { @@ -1063,15 +1152,15 @@ api::system_display::DisplayProperties info; info.rotation.reset(new int(90)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 500x300", secondary.bounds().ToString()); EXPECT_EQ(display::Display::ROTATE_90, secondary.rotation()); info.rotation.reset(new int(270)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 500x300", secondary.bounds().ToString()); EXPECT_EQ(display::Display::ROTATE_270, secondary.rotation()); @@ -1079,8 +1168,8 @@ info.rotation.reset(new int(180)); // Switch primary display. info.is_primary.reset(new bool(true)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("0,0 300x500", secondary.bounds().ToString()); EXPECT_EQ(display::Display::ROTATE_180, secondary.rotation()); @@ -1088,8 +1177,8 @@ secondary.id()); info.rotation.reset(new int(0)); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("0,0 300x500", secondary.bounds().ToString()); EXPECT_EQ(display::Display::ROTATE_0, secondary.rotation()); @@ -1105,10 +1194,8 @@ api::system_display::DisplayProperties info; info.rotation.reset(new int(90)); - EXPECT_EQ( - std::string(), - CallSetDisplayUnitInfo( - base::Int64ToString(display::Display::InternalDisplayId()), info)); + EXPECT_TRUE(CallSetDisplayUnitInfo( + base::Int64ToString(display::Display::InternalDisplayId()), info)); EXPECT_FALSE(screen_orientation_controller->rotation_locked()); // Entering tablet mode enables accelerometer screen rotations. @@ -1144,10 +1231,8 @@ api::system_display::DisplayProperties info; info.rotation.reset(new int(90)); - EXPECT_EQ( - std::string(), - CallSetDisplayUnitInfo( - base::Int64ToString(display::Display::InternalDisplayId()), info)); + EXPECT_TRUE(CallSetDisplayUnitInfo( + base::Int64ToString(display::Display::InternalDisplayId()), info)); EXPECT_TRUE( ash::Shell::Get()->screen_orientation_controller()->rotation_locked()); EXPECT_TRUE(ash::Shell::Get() @@ -1162,9 +1247,8 @@ api::system_display::DisplayProperties info; info.rotation.reset(new int(91)); - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_EQ("Invalid rotation.", error); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); } TEST_F(DisplayInfoProviderChromeosTest, SetNegativeOverscan) { @@ -1175,41 +1259,40 @@ info.overscan.reset(new api::system_display::Insets); info.overscan->left = -10; - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); info.overscan->left = 0; info.overscan->right = -200; - error = CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); info.overscan->right = 0; info.overscan->top = -300; - error = CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); info.overscan->right = 0; info.overscan->top = -1000; - error = CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); info.overscan->right = 0; info.overscan->top = 0; - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString()); } @@ -1226,9 +1309,8 @@ info.overscan->right = 101; info.overscan->bottom = 20; - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); } TEST_F(DisplayInfoProviderChromeosTest, SetOverscanLargerThanVerticalBounds) { @@ -1243,9 +1325,8 @@ info.overscan->right = 101; info.overscan->bottom = 251; - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); } TEST_F(DisplayInfoProviderChromeosTest, SetOverscan) { @@ -1259,8 +1340,8 @@ info.overscan->right = 130; info.overscan->bottom = 51; - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info)); EXPECT_EQ("1200,0 150x250", secondary.bounds().ToString()); const gfx::Insets overscan = @@ -1286,9 +1367,8 @@ info.overscan->right = 20; info.overscan->bottom = 20; - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(internal_display_id), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(internal_display_id), info)); } TEST_F(DisplayInfoProviderChromeosTest, DisplayMode) { @@ -1298,7 +1378,7 @@ ASSERT_GE(result.size(), 1u); const api::system_display::DisplayUnitInfo& primary_info = result[0]; // Ensure that we have two modes for the primary display so that we can - // test chaning modes. + // test changing modes. ASSERT_GE(primary_info.modes.size(), 2u); // Get the currently active mode and one other mode to switch to. @@ -1334,8 +1414,7 @@ info.display_mode = api::system_display::DisplayMode::FromValue(*other_mode->ToValue()); - EXPECT_EQ(std::string(), - CallSetDisplayUnitInfo(base::Int64ToString(id), info)); + EXPECT_TRUE(CallSetDisplayUnitInfo(base::Int64ToString(id), info)); // Verify that other_mode now matches the active mode. EXPECT_TRUE(GetDisplayManager()->GetActiveModeForDisplayId(id, &active_mode)); @@ -1524,9 +1603,8 @@ api::system_display::DisplayProperties info; info.display_zoom_factor = std::make_unique<double>(zoom_factor_1); - std::string error = - CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info)); EXPECT_EQ(GetDisplayZoom(display_id_list[0]), final_zoom_factor_1); // Display 2 has not been updated yet, so it will still have the old zoom @@ -1534,8 +1612,8 @@ EXPECT_EQ(GetDisplayZoom(display_id_list[1]), zoom_factor_1); info.display_zoom_factor = std::make_unique<double>(zoom_factor_2); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info)); // Both displays should now have the correct zoom factor set. EXPECT_EQ(GetDisplayZoom(display_id_list[0]), final_zoom_factor_1); @@ -1545,17 +1623,17 @@ // in an effective width greater than 4096, which is out of range. float invalid_zoom_factor_1 = 0.285f; info.display_zoom_factor = std::make_unique<double>(invalid_zoom_factor_1); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info)); // This zoom factor when applied to the display with width 1200, will result // in an effective width greater less than 640, which is out of range. float invalid_zoom_factor_2 = 1.88f; info.display_zoom_factor = std::make_unique<double>(invalid_zoom_factor_2); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info)); - // Initalize displays that have bounds outside the valid width range of 640px + // Initialize displays that have bounds outside the valid width range of 640px // to 4096px. UpdateDisplay("400x400, 4500x1000#4500x1000"); @@ -1566,26 +1644,26 @@ // allows a minimum width of 400px. float valid_zoom_factor_1 = 0.8f; info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_1); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info)); // Results in a logical width of 4200px. This is above the 4096px threshold // but is valid because the initial width was 4500px, so logical width of up // to 4500px is allowed in this case. float valid_zoom_factor_2 = 1.07f; info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_2); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info)); float valid_zoom_factor_3 = 0.5f; info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_3); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info)); float valid_zoom_factor_4 = 2.f; info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_4); - error = CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE( + CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info)); } class DisplayInfoProviderChromeosTouchviewTest
diff --git a/chrome/browser/media/media_engagement_browsertest.cc b/chrome/browser/media/media_engagement_browsertest.cc index 09f88d1..41eb353 100644 --- a/chrome/browser/media/media_engagement_browsertest.cc +++ b/chrome/browser/media/media_engagement_browsertest.cc
@@ -154,8 +154,8 @@ Navigate(¶ms); InjectTimerTaskRunner(); - params.target_contents->SetAudioMuted(false); - content::WaitForLoadStop(params.target_contents); + params.navigated_or_inserted_contents->SetAudioMuted(false); + content::WaitForLoadStop(params.navigated_or_inserted_contents); } void OpenTabAsLink(const GURL& url) {
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service.cc b/chrome/browser/media/router/discovery/dial/dial_media_sink_service.cc index 8b699e7..c814c47 100644 --- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service.cc +++ b/chrome/browser/media/router/discovery/dial/dial_media_sink_service.cc
@@ -34,16 +34,7 @@ base::BindRepeating(&DialMediaSinkService::RunSinksDiscoveredCallback, weak_ptr_factory_.GetWeakPtr(), sink_discovery_cb)); - OnAvailableSinksUpdatedCallback available_sinks_updated_cb_impl = - base::BindRepeating( - &RunAvailableSinksUpdatedCallbackOnSequence, - base::SequencedTaskRunnerHandle::Get(), - base::BindRepeating( - &DialMediaSinkService::RunAvailableSinksUpdatedCallback, - weak_ptr_factory_.GetWeakPtr())); - - impl_ = CreateImpl(sink_discovery_cb_impl, dial_sink_added_cb, - available_sinks_updated_cb_impl); + impl_ = CreateImpl(sink_discovery_cb_impl, dial_sink_added_cb); impl_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&DialMediaSinkServiceImpl::Start, @@ -57,46 +48,10 @@ base::Unretained(impl_.get()))); } -DialMediaSinkService::SinkQueryByAppSubscription -DialMediaSinkService::StartMonitoringAvailableSinksForApp( - const std::string& app_name, - const SinkQueryByAppCallback& callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - auto& sink_list = sinks_by_app_name_[app_name]; - if (!sink_list) { - // Register first observer for |app_name|. - sink_list = std::make_unique<SinkListByAppName>(); - impl_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce( - &DialMediaSinkServiceImpl::StartMonitoringAvailableSinksForApp, - base::Unretained(impl_.get()), app_name)); - sink_list->callbacks.set_removal_callback(base::BindRepeating( - &DialMediaSinkService::OnAvailableSinksUpdatedCallbackRemoved, - base::Unretained(this), app_name)); - } - - return sink_list->callbacks.Add(callback); -} - -std::vector<MediaSinkInternal> DialMediaSinkService::GetCachedAvailableSinks( - const std::string& app_name) { - const auto& sinks_it = sinks_by_app_name_.find(app_name); - if (sinks_it == sinks_by_app_name_.end()) - return std::vector<MediaSinkInternal>(); - - return sinks_it->second->cached_sinks; -} - -DialMediaSinkService::SinkListByAppName::SinkListByAppName() = default; -DialMediaSinkService::SinkListByAppName::~SinkListByAppName() = default; - std::unique_ptr<DialMediaSinkServiceImpl, base::OnTaskRunnerDeleter> DialMediaSinkService::CreateImpl( const OnSinksDiscoveredCallback& sink_discovery_cb, - const OnDialSinkAddedCallback& dial_sink_added_cb, - const OnAvailableSinksUpdatedCallback& available_sinks_updated_cb) { + const OnDialSinkAddedCallback& dial_sink_added_cb) { // Clone the connector so it can be used on the IO thread. std::unique_ptr<service_manager::Connector> connector = content::ServiceManagerConnection::GetForProcess() @@ -110,8 +65,7 @@ content::BrowserThread::IO); return std::unique_ptr<DialMediaSinkServiceImpl, base::OnTaskRunnerDeleter>( new DialMediaSinkServiceImpl(std::move(connector), sink_discovery_cb, - dial_sink_added_cb, - available_sinks_updated_cb, task_runner), + dial_sink_added_cb, task_runner), base::OnTaskRunnerDeleter(task_runner)); } @@ -121,36 +75,4 @@ sinks_discovered_cb.Run(std::move(sinks)); } -void DialMediaSinkService::RunAvailableSinksUpdatedCallback( - const std::string& app_name, - std::vector<MediaSinkInternal> available_sinks) { - const auto& sinks_it = sinks_by_app_name_.find(app_name); - if (sinks_it == sinks_by_app_name_.end()) - return; - - sinks_it->second->callbacks.Notify(app_name, available_sinks); - sinks_it->second->cached_sinks = std::move(available_sinks); -} - -void DialMediaSinkService::OnAvailableSinksUpdatedCallbackRemoved( - const std::string& app_name) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - const auto& sinks_it = sinks_by_app_name_.find(app_name); - if (sinks_it == sinks_by_app_name_.end()) - return; - - // Other profile is still monitoring |app_name|. - if (!sinks_it->second->callbacks.empty()) - return; - - impl_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce( - &DialMediaSinkServiceImpl::StopMonitoringAvailableSinksForApp, - base::Unretained(impl_.get()), app_name)); - - sinks_by_app_name_.erase(app_name); -} - } // namespace media_router
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h b/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h index 5a4f0ef..cf23ec82 100644 --- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h +++ b/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h
@@ -31,17 +31,6 @@ // SequencedTaskRunner. class DialMediaSinkService { public: - // Callback is invoked when available sinks for |app_name| changes. - // |app_name|: app name, e.g. YouTube. - // |available_sinks|: media sinks on which app with |app_name| is available. - using SinkQueryByAppFunc = - void(const std::string& app_name, - const std::vector<MediaSinkInternal>& available_sinks); - using SinkQueryByAppCallback = base::RepeatingCallback<SinkQueryByAppFunc>; - using SinkQueryByAppCallbackList = base::CallbackList<SinkQueryByAppFunc>; - using SinkQueryByAppSubscription = - std::unique_ptr<SinkQueryByAppCallbackList::Subscription>; - DialMediaSinkService(); virtual ~DialMediaSinkService(); @@ -62,58 +51,26 @@ // Marked virtual for tests. virtual void OnUserGesture(); - // Registers |callback| to callback list entry in |sink_queries_|, with the - // key |app_name|. Returns a unique_ptr of callback list subscription. Caller - // owns the returned subscription and is responsible for destroying when it - // wants to unregister |callback|. - virtual SinkQueryByAppSubscription StartMonitoringAvailableSinksForApp( - const std::string& app_name, - const SinkQueryByAppCallback& callback); - - // Returns cached available sinks for |app_name|. - virtual std::vector<MediaSinkInternal> GetCachedAvailableSinks( - const std::string& app_name); + // Returns a raw pointer to |impl_|. This method is only valid to call after + // |Start()| has been called. Always returns non-null. + DialMediaSinkServiceImpl* impl() { + DCHECK(impl_); + return impl_.get(); + } private: - friend class DialMediaSinkServiceTest; - - struct SinkListByAppName { - SinkListByAppName(); - ~SinkListByAppName(); - - // Keeps track of callbacks, which could come from different profiles. - SinkQueryByAppCallbackList callbacks; - - std::vector<MediaSinkInternal> cached_sinks; - - DISALLOW_COPY_AND_ASSIGN(SinkListByAppName); - }; - // Marked virtual for tests. virtual std::unique_ptr<DialMediaSinkServiceImpl, base::OnTaskRunnerDeleter> CreateImpl(const OnSinksDiscoveredCallback& sink_discovery_cb, - const OnDialSinkAddedCallback& dial_sink_added_cb, - const OnAvailableSinksUpdatedCallback& available_sinks_updated_cb); + const OnDialSinkAddedCallback& dial_sink_added_cb); void RunSinksDiscoveredCallback( const OnSinksDiscoveredCallback& sinks_discovered_cb, std::vector<MediaSinkInternal> sinks); - void RunAvailableSinksUpdatedCallback( - const std::string& app_name, - std::vector<MediaSinkInternal> available_sinks); - - // Invoked when callback subscription returned by - // |StartMonitoringAvailableSinksForApp| is destroyed by the caller. - void OnAvailableSinksUpdatedCallbackRemoved(const std::string& app_name); - // Created on the UI thread, used and destroyed on its SequencedTaskRunner. std::unique_ptr<DialMediaSinkServiceImpl, base::OnTaskRunnerDeleter> impl_; - // Map of available sinks and sink query callbacks, keyed by app name. - base::flat_map<std::string, std::unique_ptr<SinkListByAppName>> - sinks_by_app_name_; - SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<DialMediaSinkService> weak_ptr_factory_;
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.cc b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.cc index d80eead8..71ac495 100644 --- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.cc +++ b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.cc
@@ -53,12 +53,10 @@ std::unique_ptr<service_manager::Connector> connector, const OnSinksDiscoveredCallback& on_sinks_discovered_cb, const OnDialSinkAddedCallback& dial_sink_added_cb, - const OnAvailableSinksUpdatedCallback& available_sinks_updated_callback, const scoped_refptr<base::SequencedTaskRunner>& task_runner) : MediaSinkServiceBase(on_sinks_discovered_cb), connector_(std::move(connector)), dial_sink_added_cb_(dial_sink_added_cb), - available_sinks_updated_callback_(available_sinks_updated_callback), task_runner_(task_runner) { DETACH_FROM_SEQUENCE(sequence_checker_); } @@ -113,24 +111,25 @@ RescanAppInfo(); } -void DialMediaSinkServiceImpl::StartMonitoringAvailableSinksForApp( - const std::string& app_name) { +DialMediaSinkServiceImpl::SinkQueryByAppSubscription +DialMediaSinkServiceImpl::StartMonitoringAvailableSinksForApp( + const std::string& app_name, + const SinkQueryByAppCallback& callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!registered_apps_.insert(app_name).second) - return; + auto& callback_list = sink_queries_[app_name]; + if (!callback_list) { + callback_list = std::make_unique<SinkQueryByAppCallbackList>(); + callback_list->set_removal_callback(base::BindRepeating( + &DialMediaSinkServiceImpl::MaybeRemoveSinkQueryCallbackList, + base::Unretained(this), app_name, callback_list.get())); - // Start checking if |app_name| is available on existing sinks. - for (const auto& dial_sink_it : current_sinks_) - FetchAppInfoForSink(dial_sink_it.second, app_name); + // Start checking if |app_name| is available on existing sinks. + for (const auto& dial_sink_it : current_sinks_) + FetchAppInfoForSink(dial_sink_it.second, app_name); + } - NotifySinkObservers(app_name); -} - -void DialMediaSinkServiceImpl::StopMonitoringAvailableSinksForApp( - const std::string& app_name) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - registered_apps_.erase(app_name); + return callback_list->Add(callback); } void DialMediaSinkServiceImpl::SetDialRegistryForTest( @@ -151,8 +150,8 @@ void DialMediaSinkServiceImpl::OnDiscoveryComplete() { MediaSinkServiceBase::OnDiscoveryComplete(); - for (const auto& app_name : registered_apps_) - NotifySinkObservers(app_name); + for (const auto& query : sink_queries_) + query.second->Notify(query.first); } void DialMediaSinkServiceImpl::OnDialDeviceEvent( @@ -205,8 +204,8 @@ if (!IsDiscoveryOnly(description_data.model_name)) { // Start checking if all registered apps are available on |dial_sink|. - for (const auto& app_name : registered_apps_) - FetchAppInfoForSink(dial_sink, app_name); + for (const auto& query : sink_queries_) + FetchAppInfoForSink(dial_sink, query.first); } // Start fetch timer again if device description comes back after @@ -226,10 +225,6 @@ const std::string& app_name, DialAppInfoResult result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!base::ContainsKey(registered_apps_, app_name)) { - DVLOG(2) << "App name not registered: " << app_name; - return; - } SinkAppStatus app_status = GetSinkAppStatusFromResponse(result); if (app_status == SinkAppStatus::kUnknown) { @@ -243,18 +238,18 @@ SinkAppStatus old_status = GetAppStatus(sink_id, app_name); SetAppStatus(sink_id, app_name, app_status); - if (old_status != app_status) - NotifySinkObservers(app_name); -} + if (old_status == app_status) + return; -void DialMediaSinkServiceImpl::NotifySinkObservers( - const std::string& app_name) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - base::flat_set<MediaSinkInternal> sinks = GetAvailableSinks(app_name); - DVLOG(2) << "NotifySinkObservers " << app_name << " has [" << sinks.size() - << "] sinks"; - available_sinks_updated_callback_.Run( - app_name, std::vector<MediaSinkInternal>(sinks.begin(), sinks.end())); + // The sink might've been removed before the parse was complete. In that case + // the callbacks won't be notified, but the app status will be saved for later + // use. + if (!base::ContainsKey(current_sinks_, sink_id)) + return; + + auto query_it = sink_queries_.find(app_name); + if (query_it != sink_queries_.end()) + query_it->second->Notify(app_name); } void DialMediaSinkServiceImpl::FetchAppInfoForSink( @@ -287,8 +282,8 @@ continue; } - for (const auto& app_name : registered_apps_) { - FetchAppInfoForSink(dial_sink_it.second, app_name); + for (const auto& query : sink_queries_) { + FetchAppInfoForSink(dial_sink_it.second, query.first); } } } @@ -315,14 +310,24 @@ current_devices_.size()); } -base::flat_set<MediaSinkInternal> DialMediaSinkServiceImpl::GetAvailableSinks( +void DialMediaSinkServiceImpl::MaybeRemoveSinkQueryCallbackList( + const std::string& app_name, + SinkQueryByAppCallbackList* callback_list) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // There are no more profiles monitoring |app_name|. + if (callback_list->empty()) + sink_queries_.erase(app_name); +} + +std::vector<MediaSinkInternal> DialMediaSinkServiceImpl::GetAvailableSinks( const std::string& app_name) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - base::flat_set<MediaSinkInternal> sinks; + std::vector<MediaSinkInternal> sinks; for (const auto& sink_it : current_sinks_) { std::string sink_id = sink_it.second.sink().id(); if (GetAppStatus(sink_id, app_name) == SinkAppStatus::kAvailable) - sinks.insert(sink_it.second); + sinks.push_back(sink_it.second); } return sinks; }
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h index a4e253d..2095dd0 100644 --- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h +++ b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h
@@ -35,6 +35,15 @@ class DialMediaSinkServiceImpl : public MediaSinkServiceBase, public DialRegistry::Observer { public: + // Callbacks invoked when the list of available sinks for |app_name| changes. + // The client can call |GetAvailableSinks()| to obtain the latest sink list. + // |app_name|: app name, e.g. YouTube. + using SinkQueryByAppFunc = void(const std::string& app_name); + using SinkQueryByAppCallback = base::RepeatingCallback<SinkQueryByAppFunc>; + using SinkQueryByAppCallbackList = base::CallbackList<SinkQueryByAppFunc>; + using SinkQueryByAppSubscription = + std::unique_ptr<SinkQueryByAppCallbackList::Subscription>; + // Represents DIAL app status on receiver device. enum SinkAppStatus { kUnknown = 0, kAvailable, kUnavailable }; @@ -49,7 +58,6 @@ std::unique_ptr<service_manager::Connector> connector, const OnSinksDiscoveredCallback& on_sinks_discovered_cb, const OnDialSinkAddedCallback& dial_sink_added_cb, - const OnAvailableSinksUpdatedCallback& available_sinks_updated_callback, const scoped_refptr<base::SequencedTaskRunner>& task_runner); ~DialMediaSinkServiceImpl() override; @@ -63,14 +71,22 @@ return task_runner_; } - // Starts monitoring available sinks for |app_name|. If available sinks - // change, invokes |available_sinks_updated_callback_|. + // Registers |callback| to callback list entry in |sink_queries_|, with the + // key |app_name|. Returns a unique_ptr of callback list subscription. Caller + // owns the returned subscription and is responsible for destroying when it + // wants to unregister |callback|. // Marked virtual for tests. - virtual void StartMonitoringAvailableSinksForApp(const std::string& app_name); + virtual SinkQueryByAppSubscription StartMonitoringAvailableSinksForApp( + const std::string& app_name, + const SinkQueryByAppCallback& callback); - // Stops monitoring available sinks for |app_name|. + // Returns the current list of sinks compatible with |app_name|. The caller + // can call this method after calling |StartMonitoringAvailableSinksForApp()| + // to obtain the initial list, or when the callback fires to get the updated + // list. // Marked virtual for tests. - virtual void StopMonitoringAvailableSinksForApp(const std::string& app_name); + virtual std::vector<MediaSinkInternal> GetAvailableSinks( + const std::string& app_name) const; protected: // Does not take ownership of |dial_registry|. @@ -133,10 +149,6 @@ const std::string& app_name, DialAppInfoResult result); - // Invokes |available_sinks_updated_callback_| with |app_name| and current - // available sinks for |app_name|. - void NotifySinkObservers(const std::string& app_name); - // Queries app status of |app_name| on |dial_sink|. void FetchAppInfoForSink(const MediaSinkInternal& dial_sink, const std::string& app_name); @@ -153,12 +165,13 @@ const std::string& app_name, SinkAppStatus app_status); + void MaybeRemoveSinkQueryCallbackList( + const std::string& app_name, + SinkQueryByAppCallbackList* callback_list); + // MediaSinkServiceBase implementation. void RecordDeviceCounts() override; - base::flat_set<MediaSinkInternal> GetAvailableSinks( - const std::string& app_name) const; - // Connector to ServiceManager for safe XML parsing requests. std::unique_ptr<service_manager::Connector> connector_; @@ -170,8 +183,6 @@ OnDialSinkAddedCallback dial_sink_added_cb_; - OnAvailableSinksUpdatedCallback available_sinks_updated_callback_; - // Raw pointer to DialRegistry singleton. DialRegistry* dial_registry_ = nullptr; @@ -184,8 +195,9 @@ // Map of app status, keyed by <sink id:app name>. base::flat_map<std::string, SinkAppStatus> app_statuses_; - // Set of registered app names. - base::flat_set<std::string> registered_apps_; + // Set of sink queries keyed by app name. + base::flat_map<std::string, std::unique_ptr<SinkQueryByAppCallbackList>> + sink_queries_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl_unittest.cc b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl_unittest.cc index b61fbf7..83c86d1f 100644 --- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl_unittest.cc +++ b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl_unittest.cc
@@ -50,7 +50,6 @@ std::unique_ptr<service_manager::Connector>(), mock_sink_discovered_cb_.Get(), dial_sink_added_cb_.Get(), - mock_available_sinks_updated_cb_.Get(), base::SequencedTaskRunnerHandle::Get())) {} void SetUp() override { @@ -74,6 +73,23 @@ std::move(mock_app_discovery_service)); } + DialMediaSinkServiceImpl::SinkQueryByAppSubscription + StartMonitoringAvailableSinksForApp(const std::string& app_name) { + return media_sink_service_->StartMonitoringAvailableSinksForApp( + app_name, base::BindRepeating( + &DialMediaSinkServiceImplTest::GetAvailableSinksForApp, + base::Unretained(this))); + } + + void GetAvailableSinksForApp(const std::string& app_name) { + OnSinksAvailableForApp(app_name, + media_sink_service_->GetAvailableSinks(app_name)); + } + + MOCK_METHOD2(OnSinksAvailableForApp, + void(const std::string& app_name, + const std::vector<MediaSinkInternal>& available_sinks)); + DialAppInfoResult CreateDialAppInfoResult(const std::string& app_name) { return DialAppInfoResult( CreateParsedDialAppInfoPtr(app_name, DialAppState::kRunning), @@ -85,8 +101,6 @@ base::MockCallback<OnSinksDiscoveredCallback> mock_sink_discovered_cb_; base::MockCallback<OnDialSinkAddedCallback> dial_sink_added_cb_; - base::MockCallback<OnAvailableSinksUpdatedCallback> - mock_available_sinks_updated_cb_; base::MockCallback< MockDeviceDescriptionService::DeviceDescriptionParseSuccessCallback> mock_success_cb_; @@ -101,6 +115,9 @@ std::unique_ptr<DialMediaSinkServiceImpl> media_sink_service_; + MediaSinkInternal dial_sink_1_ = CreateDialSink(1); + MediaSinkInternal dial_sink_2_ = CreateDialSink(2); + DISALLOW_COPY_AND_ASSIGN(DialMediaSinkServiceImplTest); }; @@ -190,9 +207,8 @@ } TEST_F(DialMediaSinkServiceImplTest, OnDialDeviceEventRestartsTimer) { - MediaSinkInternal dial_sink = CreateDialSink(1); - media_sink_service_->current_sinks_.insert_or_assign(dial_sink.sink().id(), - dial_sink); + media_sink_service_->current_sinks_.insert_or_assign(dial_sink_1_.sink().id(), + dial_sink_1_); EXPECT_CALL(mock_sink_discovered_cb_, Run(_)); media_sink_service_->OnDiscoveryComplete(); @@ -251,49 +267,45 @@ } TEST_F(DialMediaSinkServiceImplTest, StartStopMonitoringAvailableSinksForApp) { - MediaSinkInternal dial_sink = CreateDialSink(1); - const MediaSink::Id& sink_id = dial_sink.sink().id(); + const MediaSink::Id& sink_id = dial_sink_1_.sink().id(); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(sink_id, "YouTube")) .Times(1); - media_sink_service_->current_sinks_.emplace(sink_id, dial_sink); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); - EXPECT_EQ(1u, media_sink_service_->registered_apps_.size()); + media_sink_service_->current_sinks_.emplace(sink_id, dial_sink_1_); + auto sub1 = StartMonitoringAvailableSinksForApp("YouTube"); + auto sub2 = StartMonitoringAvailableSinksForApp("YouTube"); + EXPECT_EQ(1u, media_sink_service_->sink_queries_.size()); - media_sink_service_->StopMonitoringAvailableSinksForApp("YouTube"); - EXPECT_TRUE(media_sink_service_->registered_apps_.empty()); + sub1.reset(); + EXPECT_EQ(1u, media_sink_service_->sink_queries_.size()); + sub2.reset(); + EXPECT_TRUE(media_sink_service_->sink_queries_.empty()); } TEST_F(DialMediaSinkServiceImplTest, OnDialAppInfoAvailableNoStartMonitoring) { - MediaSinkInternal dial_sink = CreateDialSink(1); - const MediaSink::Id& sink_id = dial_sink.sink().id(); + const MediaSink::Id& sink_id = dial_sink_1_.sink().id(); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run(_, _)).Times(0); - media_sink_service_->current_sinks_.emplace(sink_id, dial_sink); + EXPECT_CALL(*this, OnSinksAvailableForApp(_, _)).Times(0); + media_sink_service_->current_sinks_.emplace(sink_id, dial_sink_1_); media_sink_service_->OnAppInfoParseCompleted( sink_id, "YouTube", CreateDialAppInfoResult("YouTube")); } TEST_F(DialMediaSinkServiceImplTest, OnDialAppInfoAvailableNoSink) { - MediaSinkInternal dial_sink = CreateDialSink(1); - std::string sink_id = dial_sink.sink().id(); + std::string sink_id = dial_sink_1_.sink().id(); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run("YouTube", IsEmpty())); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run("YouTube", IsEmpty())); + EXPECT_CALL(*this, OnSinksAvailableForApp("YouTube", _)).Times(0); + auto sub = StartMonitoringAvailableSinksForApp("YouTube"); media_sink_service_->OnAppInfoParseCompleted( sink_id, "YouTube", CreateDialAppInfoResult("YouTube")); } TEST_F(DialMediaSinkServiceImplTest, OnDialAppInfoAvailableSinksAdded) { - MediaSinkInternal dial_sink1 = CreateDialSink(1); - MediaSinkInternal dial_sink2 = CreateDialSink(2); - const MediaSink::Id& sink_id1 = dial_sink1.sink().id(); - const MediaSink::Id& sink_id2 = dial_sink2.sink().id(); + const MediaSink::Id& sink_id1 = dial_sink_1_.sink().id(); + const MediaSink::Id& sink_id2 = dial_sink_2_.sink().id(); - media_sink_service_->current_sinks_.emplace(sink_id1, dial_sink1); - media_sink_service_->current_sinks_.emplace(sink_id2, dial_sink2); + media_sink_service_->current_sinks_.emplace(sink_id1, dial_sink_1_); + media_sink_service_->current_sinks_.emplace(sink_id2, dial_sink_2_); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(sink_id1, "YouTube")); @@ -303,57 +315,68 @@ DoFetchDialAppInfo(sink_id1, "Netflix")); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(sink_id2, "Netflix")); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); - media_sink_service_->StartMonitoringAvailableSinksForApp("Netflix"); + EXPECT_CALL(*this, OnSinksAvailableForApp(_, _)).Times(0); + auto sub1 = StartMonitoringAvailableSinksForApp("YouTube"); + auto sub2 = StartMonitoringAvailableSinksForApp("Netflix"); // Either kStopped or kRunning means the app is available on the sink. - EXPECT_CALL(mock_available_sinks_updated_cb_, - Run("YouTube", std::vector<MediaSinkInternal>({dial_sink1}))); + EXPECT_CALL(*this, + OnSinksAvailableForApp( + "YouTube", std::vector<MediaSinkInternal>({dial_sink_1_}))); media_sink_service_->OnAppInfoParseCompleted( sink_id1, "YouTube", CreateDialAppInfoResult("YouTube")); - EXPECT_CALL( - mock_available_sinks_updated_cb_, - Run("YouTube", std::vector<MediaSinkInternal>({dial_sink1, dial_sink2}))); + EXPECT_CALL(*this, OnSinksAvailableForApp("YouTube", + std::vector<MediaSinkInternal>( + {dial_sink_1_, dial_sink_2_}))); media_sink_service_->OnAppInfoParseCompleted( sink_id2, "YouTube", CreateDialAppInfoResult("YouTube")); - EXPECT_CALL(mock_available_sinks_updated_cb_, - Run("Netflix", std::vector<MediaSinkInternal>({dial_sink2}))); + EXPECT_CALL(*this, + OnSinksAvailableForApp( + "Netflix", std::vector<MediaSinkInternal>({dial_sink_2_}))); media_sink_service_->OnAppInfoParseCompleted( sink_id2, "Netflix", CreateDialAppInfoResult("Netflix")); + + // Stop listening for Netflix. + sub2.reset(); + EXPECT_CALL(*this, OnSinksAvailableForApp("Netflix", _)).Times(0); + media_sink_service_->OnAppInfoParseCompleted( + sink_id1, "Netflix", CreateDialAppInfoResult("Netflix")); + + std::vector<MediaSinkInternal> expected_sinks = {dial_sink_1_, dial_sink_2_}; + EXPECT_EQ(expected_sinks, media_sink_service_->GetAvailableSinks("YouTube")); + EXPECT_EQ(expected_sinks, media_sink_service_->GetAvailableSinks("Netflix")); } TEST_F(DialMediaSinkServiceImplTest, OnDialAppInfoAvailableSinksRemoved) { - MediaSinkInternal dial_sink = CreateDialSink(1); - const MediaSink::Id& sink_id = dial_sink.sink().id(); + const MediaSink::Id& sink_id = dial_sink_1_.sink().id(); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(_, _)); - media_sink_service_->current_sinks_.emplace(sink_id, dial_sink); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); + media_sink_service_->current_sinks_.emplace(sink_id, dial_sink_1_); + auto sub1 = StartMonitoringAvailableSinksForApp("YouTube"); - EXPECT_CALL(mock_available_sinks_updated_cb_, - Run("YouTube", std::vector<MediaSinkInternal>({dial_sink}))); + EXPECT_CALL(*this, + OnSinksAvailableForApp( + "YouTube", std::vector<MediaSinkInternal>({dial_sink_1_}))); media_sink_service_->OnAppInfoParseCompleted( sink_id, "YouTube", CreateDialAppInfoResult("YouTube")); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run("YouTube", IsEmpty())); + EXPECT_CALL(*this, OnSinksAvailableForApp("YouTube", IsEmpty())); media_sink_service_->current_sinks_.clear(); media_sink_service_->OnDiscoveryComplete(); } - TEST_F(DialMediaSinkServiceImplTest, OnDialAppInfoAvailableWithAlreadyAvailableSinks) { - MediaSinkInternal dial_sink = CreateDialSink(1); - const MediaSink::Id& sink_id = dial_sink.sink().id(); + const MediaSink::Id& sink_id = dial_sink_1_.sink().id(); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(_, _)); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run(_, _)); - media_sink_service_->current_sinks_.emplace(sink_id, dial_sink); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); + media_sink_service_->current_sinks_.emplace(sink_id, dial_sink_1_); + auto sub1 = StartMonitoringAvailableSinksForApp("YouTube"); - EXPECT_CALL(mock_available_sinks_updated_cb_, - Run("YouTube", std::vector<MediaSinkInternal>({dial_sink}))) + EXPECT_CALL(*this, + OnSinksAvailableForApp( + "YouTube", std::vector<MediaSinkInternal>({dial_sink_1_}))) .Times(1); media_sink_service_->OnAppInfoParseCompleted( sink_id, "YouTube", CreateDialAppInfoResult("YouTube")); @@ -362,36 +385,33 @@ } TEST_F(DialMediaSinkServiceImplTest, StartAfterStopMonitoringForApp) { - MediaSinkInternal dial_sink = CreateDialSink(1); - const MediaSink::Id& sink_id = dial_sink.sink().id(); - + const MediaSink::Id& sink_id = dial_sink_1_.sink().id(); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(_, _)); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run(_, _)); - media_sink_service_->current_sinks_.emplace(sink_id, dial_sink); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); + media_sink_service_->current_sinks_.emplace(sink_id, dial_sink_1_); + auto sub1 = StartMonitoringAvailableSinksForApp("YouTube"); + std::vector<MediaSinkInternal> expected_sinks = {dial_sink_1_}; - EXPECT_CALL(mock_available_sinks_updated_cb_, - Run("YouTube", std::vector<MediaSinkInternal>({dial_sink}))); + EXPECT_CALL(*this, OnSinksAvailableForApp("YouTube", expected_sinks)) + .Times(1); media_sink_service_->OnAppInfoParseCompleted( - dial_sink.sink().id(), "YouTube", CreateDialAppInfoResult("YouTube")); + dial_sink_1_.sink().id(), "YouTube", CreateDialAppInfoResult("YouTube")); - media_sink_service_->StopMonitoringAvailableSinksForApp("YouTube"); + sub1.reset(); - EXPECT_CALL(mock_available_sinks_updated_cb_, - Run("YouTube", std::vector<MediaSinkInternal>({dial_sink}))); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); + EXPECT_EQ(expected_sinks, media_sink_service_->GetAvailableSinks("YouTube")); + auto sub2 = StartMonitoringAvailableSinksForApp("YouTube"); + EXPECT_EQ(expected_sinks, media_sink_service_->GetAvailableSinks("YouTube")); } TEST_F(DialMediaSinkServiceImplTest, FetchDialAppInfoWithDiscoveryOnlySink) { - MediaSinkInternal dial_sink = CreateDialSink(1); - media_router::DialSinkExtraData extra_data = dial_sink.dial_data(); + media_router::DialSinkExtraData extra_data = dial_sink_1_.dial_data(); extra_data.model_name = "Eureka Dongle"; - dial_sink.set_dial_data(extra_data); + dial_sink_1_.set_dial_data(extra_data); EXPECT_CALL(*mock_app_discovery_service_, DoFetchDialAppInfo(_, _)).Times(0); - EXPECT_CALL(mock_available_sinks_updated_cb_, Run(_, _)); - media_sink_service_->current_sinks_.emplace(dial_sink.sink().id(), dial_sink); - media_sink_service_->StartMonitoringAvailableSinksForApp("YouTube"); + media_sink_service_->current_sinks_.emplace(dial_sink_1_.sink().id(), + dial_sink_1_); + auto sub1 = StartMonitoringAvailableSinksForApp("YouTube"); } } // namespace media_router
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_unittest.cc b/chrome/browser/media/router/discovery/dial/dial_media_sink_service_unittest.cc deleted file mode 100644 index 295e8ca..0000000 --- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service_unittest.cc +++ /dev/null
@@ -1,258 +0,0 @@ -// 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/media/router/discovery/dial/dial_media_sink_service.h" -#include "base/run_loop.h" -#include "base/test/mock_callback.h" -#include "base/test/test_simple_task_runner.h" -#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h" -#include "chrome/browser/media/router/test/mock_media_router.h" -#include "chrome/browser/media/router/test/test_helper.h" -#include "chrome/common/media_router/discovery/media_sink_service_util.h" -#include "chrome/test/base/testing_profile.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "services/service_manager/public/cpp/connector.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::InvokeWithoutArgs; -using ::testing::Return; - -namespace media_router { - -class MockDialMediaSinkServiceImpl : public DialMediaSinkServiceImpl { - public: - MockDialMediaSinkServiceImpl( - const OnSinksDiscoveredCallback& sinks_discovered_cb, - const OnDialSinkAddedCallback& dial_sink_added_cb, - const OnAvailableSinksUpdatedCallback& available_sinks_updated_cb, - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) - : DialMediaSinkServiceImpl(std::unique_ptr<service_manager::Connector>(), - sinks_discovered_cb, - dial_sink_added_cb, - available_sinks_updated_cb, - task_runner), - sinks_discovered_cb_(sinks_discovered_cb), - dial_sink_added_cb_(dial_sink_added_cb), - available_sinks_updated_cb_(available_sinks_updated_cb) {} - ~MockDialMediaSinkServiceImpl() override = default; - - MOCK_METHOD0(Start, void()); - MOCK_METHOD1(StartMonitoringAvailableSinksForApp, void(const std::string&)); - MOCK_METHOD1(StopMonitoringAvailableSinksForApp, void(const std::string&)); - - OnSinksDiscoveredCallback sinks_discovered_cb() { - return sinks_discovered_cb_; - } - - OnDialSinkAddedCallback dial_sink_added_cb() { return dial_sink_added_cb_; } - - OnAvailableSinksUpdatedCallback available_sinks_updated_cb() { - return available_sinks_updated_cb_; - } - - private: - OnSinksDiscoveredCallback sinks_discovered_cb_; - OnDialSinkAddedCallback dial_sink_added_cb_; - OnAvailableSinksUpdatedCallback available_sinks_updated_cb_; -}; - -class TestDialMediaSinkService : public DialMediaSinkService { - public: - explicit TestDialMediaSinkService( - const scoped_refptr<base::TestSimpleTaskRunner>& task_runner) - : DialMediaSinkService(), task_runner_(task_runner) {} - ~TestDialMediaSinkService() override = default; - - std::unique_ptr<DialMediaSinkServiceImpl, base::OnTaskRunnerDeleter> - CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb, - const OnDialSinkAddedCallback& dial_sink_added_cb, - const OnAvailableSinksUpdatedCallback& available_sinks_updated_cb) - override { - auto mock_impl = std::unique_ptr<MockDialMediaSinkServiceImpl, - base::OnTaskRunnerDeleter>( - new MockDialMediaSinkServiceImpl( - sinks_discovered_cb, dial_sink_added_cb, available_sinks_updated_cb, - task_runner_), - base::OnTaskRunnerDeleter(task_runner_)); - mock_impl_ = mock_impl.get(); - return mock_impl; - } - - MockDialMediaSinkServiceImpl* mock_impl() { return mock_impl_; } - - private: - scoped_refptr<base::TestSimpleTaskRunner> task_runner_; - MockDialMediaSinkServiceImpl* mock_impl_ = nullptr; -}; - -class DialMediaSinkServiceTest : public ::testing::Test { - public: - using SinkQueryByAppCallback = DialMediaSinkService::SinkQueryByAppCallback; - - DialMediaSinkServiceTest() - : task_runner_(new base::TestSimpleTaskRunner()), - service_(new TestDialMediaSinkService(task_runner_)) {} - - void SetUp() override { - service_->Start( - base::BindRepeating(&DialMediaSinkServiceTest::OnSinksDiscovered, - base::Unretained(this)), - base::BindRepeating(&DialMediaSinkServiceTest::OnDialSinkAdded, - base::Unretained(this))); - mock_impl_ = service_->mock_impl(); - ASSERT_TRUE(mock_impl_); - EXPECT_CALL(*mock_impl_, Start()).WillOnce(InvokeWithoutArgs([this]() { - EXPECT_TRUE(this->task_runner_->RunsTasksInCurrentSequence()); - })); - task_runner_->RunUntilIdle(); - } - - void TearDown() override { - service_.reset(); - task_runner_->RunUntilIdle(); - } - - MOCK_METHOD1(OnSinksDiscovered, void(std::vector<MediaSinkInternal> sinks)); - MOCK_METHOD1(OnDialSinkAdded, void(const MediaSinkInternal& sink)); - - protected: - content::TestBrowserThreadBundle thread_bundle_; - TestingProfile profile_; - - scoped_refptr<base::TestSimpleTaskRunner> task_runner_; - std::unique_ptr<TestDialMediaSinkService> service_; - MockDialMediaSinkServiceImpl* mock_impl_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(DialMediaSinkServiceTest); -}; - -TEST_F(DialMediaSinkServiceTest, OnDialSinkAddedCallback) { - // Runs the callback on |task_runner_| and expects it to post task back to - // UI thread. - MediaSinkInternal sink = CreateDialSink(1); - - EXPECT_CALL(*this, OnDialSinkAdded(sink)); - mock_impl_->dial_sink_added_cb().Run(sink); -} - -TEST_F(DialMediaSinkServiceTest, TestAddRemoveSinkQuery) { - base::MockCallback<SinkQueryByAppCallback> mock_callback; - std::string app_name("YouTube"); - - EXPECT_CALL(*mock_impl_, StartMonitoringAvailableSinksForApp(app_name)); - auto subscription = service_->StartMonitoringAvailableSinksForApp( - app_name, mock_callback.Get()); - task_runner_->RunUntilIdle(); - - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; - - EXPECT_CALL(mock_callback, Run(app_name, sinks)); - mock_impl_->available_sinks_updated_cb().Run(app_name, sinks); - base::RunLoop().RunUntilIdle(); - - subscription.reset(); - EXPECT_CALL(*mock_impl_, StopMonitoringAvailableSinksForApp(app_name)); - base::RunLoop().RunUntilIdle(); - - EXPECT_CALL(mock_callback, Run(_, _)).Times(0); - mock_impl_->available_sinks_updated_cb().Run(app_name, sinks); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(DialMediaSinkServiceTest, TestAddSinkQuerySameAppSameObserver) { - base::MockCallback<SinkQueryByAppCallback> mock_callback; - std::string app_name("YouTube"); - - EXPECT_CALL(*mock_impl_, StartMonitoringAvailableSinksForApp(app_name)) - .Times(1); - auto subscription1 = service_->StartMonitoringAvailableSinksForApp( - app_name, mock_callback.Get()); - auto subscription2 = service_->StartMonitoringAvailableSinksForApp( - app_name, mock_callback.Get()); - task_runner_->RunUntilIdle(); - - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; - - EXPECT_CALL(mock_callback, Run(app_name, sinks)).Times(2); - mock_impl_->available_sinks_updated_cb().Run("YouTube", sinks); - base::RunLoop().RunUntilIdle(); - - subscription1.reset(); - subscription2.reset(); - EXPECT_CALL(*mock_impl_, StopMonitoringAvailableSinksForApp(app_name)); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(DialMediaSinkServiceTest, TestAddSinkQuerySameAppDifferentObservers) { - base::MockCallback<SinkQueryByAppCallback> mock_callback1; - base::MockCallback<SinkQueryByAppCallback> mock_callback2; - std::string app_name("YouTube"); - - EXPECT_CALL(*mock_impl_, StartMonitoringAvailableSinksForApp(app_name)) - .Times(1); - auto subscription1 = service_->StartMonitoringAvailableSinksForApp( - app_name, mock_callback1.Get()); - auto subscription2 = service_->StartMonitoringAvailableSinksForApp( - app_name, mock_callback2.Get()); - task_runner_->RunUntilIdle(); - - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; - - EXPECT_CALL(mock_callback1, Run(app_name, sinks)); - EXPECT_CALL(mock_callback2, Run(app_name, sinks)); - mock_impl_->available_sinks_updated_cb().Run(app_name, sinks); - base::RunLoop().RunUntilIdle(); - - subscription1.reset(); - EXPECT_CALL(*mock_impl_, StopMonitoringAvailableSinksForApp(app_name)) - .Times(0); - base::RunLoop().RunUntilIdle(); - - subscription2.reset(); - EXPECT_CALL(*mock_impl_, StopMonitoringAvailableSinksForApp(app_name)); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(DialMediaSinkServiceTest, TestAddSinkQueryDifferentApps) { - base::MockCallback<SinkQueryByAppCallback> mock_callback1; - base::MockCallback<SinkQueryByAppCallback> mock_callback2; - std::string youtube_app_name("YouTube"); - std::string netflix_app_name("Netflix"); - - EXPECT_CALL(*mock_impl_, - StartMonitoringAvailableSinksForApp(youtube_app_name)); - EXPECT_CALL(*mock_impl_, - StartMonitoringAvailableSinksForApp(netflix_app_name)); - auto subscription1 = service_->StartMonitoringAvailableSinksForApp( - youtube_app_name, mock_callback1.Get()); - auto subscription2 = service_->StartMonitoringAvailableSinksForApp( - netflix_app_name, mock_callback2.Get()); - task_runner_->RunUntilIdle(); - - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; - - EXPECT_CALL(mock_callback1, Run(youtube_app_name, sinks)); - mock_impl_->available_sinks_updated_cb().Run(youtube_app_name, sinks); - base::RunLoop().RunUntilIdle(); - - EXPECT_CALL(mock_callback2, Run(netflix_app_name, sinks)); - mock_impl_->available_sinks_updated_cb().Run(netflix_app_name, sinks); - base::RunLoop().RunUntilIdle(); - - subscription1.reset(); - subscription2.reset(); - EXPECT_CALL(*mock_impl_, - StopMonitoringAvailableSinksForApp(youtube_app_name)); - EXPECT_CALL(*mock_impl_, - StopMonitoringAvailableSinksForApp(netflix_app_name)); - base::RunLoop().RunUntilIdle(); -} - -} // namespace media_router
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc index ced5c11..81e141de 100644 --- a/chrome/browser/media/router/media_router_feature.cc +++ b/chrome/browser/media/router/media_router_feature.cc
@@ -24,9 +24,9 @@ namespace media_router { #if !defined(OS_ANDROID) -// Controls if browser side DIAL sink query is enabled. -const base::Feature kEnableDialSinkQuery{"EnableDialSinkQuery", - base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls if browser side DialMediaRouteProvider is enabled. +const base::Feature kDialMediaRouteProvider{"DialMediaRouteProvider", + base::FEATURE_DISABLED_BY_DEFAULT}; // Controls if browser side Cast device discovery is enabled. const base::Feature kEnableCastDiscovery{"EnableCastDiscovery", @@ -91,12 +91,10 @@ return allow_all_ips; } -// Returns true if browser side DIAL sink query is enabled. -bool DialSinkQueryEnabled() { - return base::FeatureList::IsEnabled(kEnableDialSinkQuery); +bool DialMediaRouteProviderEnabled() { + return base::FeatureList::IsEnabled(kDialMediaRouteProvider); } -// Returns true if browser side Cast discovery is enabled. bool CastDiscoveryEnabled() { return base::FeatureList::IsEnabled(kEnableCastDiscovery); } @@ -105,13 +103,10 @@ return base::FeatureList::IsEnabled(kCastMediaRouteProvider); } -// Returns true if local media casting is enabled. bool CastLocalMediaEnabled() { return base::FeatureList::IsEnabled(kEnableCastLocalMedia); } -// Returns true if the presentation receiver window for local media casting is -// available on the current platform. bool PresentationReceiverWindowEnabled() { #if defined(OS_MACOSX) && !BUILDFLAG(MAC_VIEWS_BROWSER) return false;
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h index a4d1d20..f8ce05a 100644 --- a/chrome/browser/media/router/media_router_feature.h +++ b/chrome/browser/media/router/media_router_feature.h
@@ -35,7 +35,7 @@ // https://crbug.com/813974. extern const base::Feature kCastAllowAllIPsFeature; -// Returns |true| if CastMediaSinkService can connect to Cast devices on +// Returns true if CastMediaSinkService can connect to Cast devices on // all IPs, as determined by local state |pref_service| / feature flag. bool GetCastAllowAllIPsPref(PrefService* pref_service); @@ -44,8 +44,8 @@ extern const base::Feature kCastMediaRouteProvider; extern const base::Feature kEnableCastLocalMedia; -// Returns true if browser side DIAL sink query is enabled. -bool DialSinkQueryEnabled(); +// Returns true if browser side DIAL Media Route Provider is enabled. +bool DialMediaRouteProviderEnabled(); // Returns true if browser side Cast discovery is enabled. bool CastDiscoveryEnabled();
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc index 2089fa1..dca903dc 100644 --- a/chrome/browser/media/router/mojo/media_router_desktop.cc +++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -71,10 +71,7 @@ } MediaRouterDesktop::MediaRouterDesktop(content::BrowserContext* context) - : MediaRouterMojoImpl(context), - cast_provider_(nullptr, base::OnTaskRunnerDeleter(nullptr)), - media_sink_service_(DualMediaSinkService::GetInstance()), - weak_factory_(this) { + : MediaRouterDesktop(context, DualMediaSinkService::GetInstance()) { InitializeMediaRouteProviders(); #if defined(OS_WIN) CanFirewallUseLocalPorts( @@ -87,6 +84,7 @@ DualMediaSinkService* media_sink_service) : MediaRouterMojoImpl(context), cast_provider_(nullptr, base::OnTaskRunnerDeleter(nullptr)), + dial_provider_(nullptr, base::OnTaskRunnerDeleter(nullptr)), media_sink_service_(media_sink_service), weak_factory_(this) { InitializeMediaRouteProviders(); @@ -102,7 +100,8 @@ // Route Provider to the Media Router (https://crbug.com/687383), so we need // to disable it in the provider. config->enable_cast_discovery = !media_router::CastDiscoveryEnabled(); - config->enable_dial_sink_query = !media_router::DialSinkQueryEnabled(); + config->enable_dial_sink_query = + !media_router::DialMediaRouteProviderEnabled(); config->enable_cast_sink_query = !media_router::CastMediaRouteProviderEnabled(); std::move(callback).Run(instance_id(), std::move(config)); @@ -208,7 +207,7 @@ InitializeWiredDisplayMediaRouteProvider(); if (CastMediaRouteProviderEnabled()) InitializeCastMediaRouteProvider(); - if (DialSinkQueryEnabled()) + if (DialMediaRouteProviderEnabled()) InitializeDialMediaRouteProvider(); } @@ -254,11 +253,16 @@ mojom::MediaRouterPtr media_router_ptr; MediaRouterMojoImpl::BindToMojoRequest(mojo::MakeRequest(&media_router_ptr)); mojom::MediaRouteProviderPtr dial_provider_ptr; - DCHECK(media_sink_service_); - dial_provider_ = std::make_unique<DialMediaRouteProvider>( - mojo::MakeRequest(&dial_provider_ptr), std::move(media_router_ptr), - media_sink_service_->dial_media_sink_service()); + auto* dial_media_sink_service = + media_sink_service_->GetDialMediaSinkServiceImpl(); + auto task_runner = dial_media_sink_service->task_runner(); + dial_provider_ = + std::unique_ptr<DialMediaRouteProvider, base::OnTaskRunnerDeleter>( + new DialMediaRouteProvider(mojo::MakeRequest(&dial_provider_ptr), + media_router_ptr.PassInterface(), + dial_media_sink_service, task_runner), + base::OnTaskRunnerDeleter(task_runner)); RegisterMediaRouteProvider(MediaRouteProviderId::DIAL, std::move(dial_provider_ptr), base::DoNothing()); }
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.h b/chrome/browser/media/router/mojo/media_router_desktop.h index 1fccb567..c0aa802b 100644 --- a/chrome/browser/media/router/mojo/media_router_desktop.h +++ b/chrome/browser/media/router/mojo/media_router_desktop.h
@@ -138,7 +138,8 @@ cast_provider_; // MediaRouteProvider for DIAL. - std::unique_ptr<DialMediaRouteProvider> dial_provider_; + std::unique_ptr<DialMediaRouteProvider, base::OnTaskRunnerDeleter> + dial_provider_; DualMediaSinkService* media_sink_service_; DualMediaSinkService::Subscription media_sink_service_subscription_;
diff --git a/chrome/browser/media/router/presentation/independent_otr_profile_manager_browsertest.cc b/chrome/browser/media/router/presentation/independent_otr_profile_manager_browsertest.cc index b7a2205..92e81ce 100644 --- a/chrome/browser/media/router/presentation/independent_otr_profile_manager_browsertest.cc +++ b/chrome/browser/media/router/presentation/independent_otr_profile_manager_browsertest.cc
@@ -272,8 +272,16 @@ EXPECT_TRUE(destroyed2); } +// Flaky on mac, crbug.com/837236 +#if defined(OS_MACOSX) +#define MAYBE_BrowserClosingDoesntRemoveProfileObserver \ + DISABLED_BrowserClosingDoesntRemoveProfileObserver +#else +#define MAYBE_BrowserClosingDoesntRemoveProfileObserver \ + BrowserClosingDoesntRemoveProfileObserver +#endif IN_PROC_BROWSER_TEST_F(IndependentOTRProfileManagerTest, - BrowserClosingDoesntRemoveProfileObserver) { + MAYBE_BrowserClosingDoesntRemoveProfileObserver) { ProfileDestructionWatcher watcher1; ProfileDestructionWatcher watcher2; bool destroyed1 = false; @@ -309,6 +317,7 @@ EXPECT_TRUE(destroyed1); } +// Flaky on mac, crbug.com/835834 #if defined(OS_MACOSX) #define MAYBE_CallbackNotCalledAfterUnregister \ DISABLED_CallbackNotCalledAfterUnregister
diff --git a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc index d8ce89a7..8773648 100644 --- a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc +++ b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h" +using cast_channel::GetAppAvailabilityResult; + namespace media_router { CastAppAvailabilityTracker::CastAppAvailabilityTracker() {} @@ -45,15 +47,19 @@ std::vector<CastMediaSource> CastAppAvailabilityTracker::UpdateAppAvailability( const MediaSink::Id& sink_id, const std::string& app_id, - AppAvailability availability) { + CastAppAvailabilityTracker::AppAvailability availability) { auto& availabilities = app_availabilities_[sink_id]; auto it = availabilities.find(app_id); - // Updated if value changes from unknown / unavailable to available, or from - // available to unavailble. - AppAvailability old_availability = - it != availabilities.end() ? it->second : AppAvailability::kUnavailable; - bool updated = old_availability != availability; + GetAppAvailabilityResult old_availability = + it != availabilities.end() ? it->second.first + : GetAppAvailabilityResult::kUnknown; + GetAppAvailabilityResult new_availability = availability.first; + + // Updated if status changes from/to kAvailable. + bool updated = (old_availability == GetAppAvailabilityResult::kAvailable || + new_availability == GetAppAvailabilityResult::kAvailable) && + old_availability != new_availability; availabilities[app_id] = availability; if (!updated) @@ -76,7 +82,7 @@ // Find all app IDs that were available on the sink. std::vector<std::string> affected_app_ids; for (const auto& availability : it->second) { - if (availability.second == AppAvailability::kAvailable) + if (availability.second.first == GetAppAvailabilityResult::kAvailable) affected_app_ids.push_back(availability.first); } @@ -91,12 +97,19 @@ return affected_sources; } -bool CastAppAvailabilityTracker::IsAvailabilityKnown( - const MediaSink::Id& sink_id, - const std::string& app_id) const { +CastAppAvailabilityTracker::AppAvailability +CastAppAvailabilityTracker::GetAvailability(const MediaSink::Id& sink_id, + const std::string& app_id) const { auto availabilities_it = app_availabilities_.find(sink_id); - return availabilities_it != app_availabilities_.end() && - base::ContainsKey(availabilities_it->second, app_id); + if (availabilities_it == app_availabilities_.end()) + return {GetAppAvailabilityResult::kUnknown, base::TimeTicks()}; + + const auto& availability_map = availabilities_it->second; + auto availability_it = availability_map.find(app_id); + if (availability_it == availability_map.end()) + return {GetAppAvailabilityResult::kUnknown, base::TimeTicks()}; + + return availability_it->second; } std::vector<std::string> CastAppAvailabilityTracker::GetRegisteredApps() const { @@ -116,7 +129,8 @@ const auto& availabilities_map = availabilities.second; auto availability_it = availabilities_map.find(app_info.app_id); if (availability_it != availabilities_map.end() && - availability_it->second == AppAvailability::kAvailable) { + availability_it->second.first == + GetAppAvailabilityResult::kAvailable) { sink_ids.insert(availabilities.first); break; }
diff --git a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h index 0c66ee2..a035768 100644 --- a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h +++ b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h
@@ -11,17 +11,14 @@ #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/macros.h" +#include "base/time/time.h" #include "chrome/common/media_router/discovery/media_sink_internal.h" #include "chrome/common/media_router/media_source.h" #include "chrome/common/media_router/providers/cast/cast_media_source.h" +#include "components/cast_channel/cast_message_util.h" namespace media_router { -// Possible app availability values. -// TODO(crbug.com/809249): Determine whether it's necessary to implement RESCAN -// statuses, or just do rescan triggered on user gesture. -enum class AppAvailability { kUnavailable, kAvailable }; - // Tracks sink queries and their extracted Cast app IDs and their availabilities // on discovered sinks. // Example usage: @@ -38,7 +35,7 @@ // (3) Once the caller knows the availability value for a (sink, app) pair, it // may inform the tracker to update its results: // auto affected_sources = -// tracker.UpdateAppAvailability(sink_id, app_id, availability); +// tracker.UpdateAppAvailability(sink_id, app_id, {availability, now}); // // (4) The tracker returns a subset of discovered sources that were affected by // the update. The caller can then call |GetAvailableSinks()| to get the updated @@ -52,6 +49,10 @@ // source is registered) to determine if there are cached results available. class CastAppAvailabilityTracker { public: + // The result of an app availability request and the time when it is obtained. + using AppAvailability = + std::pair<cast_channel::GetAppAvailabilityResult, base::TimeTicks>; + CastAppAvailabilityTracker(); ~CastAppAvailabilityTracker(); @@ -62,7 +63,7 @@ // Unregisters the source given by |source| with the tracker. void UnregisterSource(const CastMediaSource& source); - // Updates the availability of |app_id| on |sink_id| with |availability|. + // Updates the availability of |app_id| on |sink_id| to |availability|. // Returns a list of registered CastMediaSources for which the set of // available sinks might have been updated by this call. The caller should // call |GetAvailableSinks| with the returned CastMediaSources to get the @@ -81,9 +82,11 @@ std::vector<CastMediaSource> RemoveResultsForSink( const MediaSink::Id& sink_id); - // Returns whether availability status is known for |app_id| on |sink_id|. - bool IsAvailabilityKnown(const MediaSink::Id& sink_id, - const std::string& app_id) const; + // Returns the availability for |app_id| on |sink_id| and the time at + // which the availability was determined. If availability is kUnknown, then + // the time may be null (e.g. if an availability request was never sent). + AppAvailability GetAvailability(const MediaSink::Id& sink_id, + const std::string& app_id) const; // Returns a list of registered app IDs. std::vector<std::string> GetRegisteredApps() const; @@ -94,7 +97,7 @@ const CastMediaSource& source) const; private: - // App ID to availability value. + // App ID to availability. using AppAvailabilityMap = base::flat_map<std::string, AppAvailability>; // Registered sources and corresponding CastMediaSource's.
diff --git a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc index 8ea4023..591ef24 100644 --- a/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc +++ b/chrome/browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc
@@ -4,10 +4,14 @@ #include "chrome/browser/media/router/providers/cast/cast_app_availability_tracker.h" +#include "base/test/simple_test_tick_clock.h" #include "chrome/common/media_router/providers/cast/cast_media_source.h" +#include "components/cast_channel/cast_message_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using cast_channel::GetAppAvailabilityResult; + namespace media_router { namespace { @@ -29,7 +33,10 @@ CastAppAvailabilityTrackerTest() {} ~CastAppAvailabilityTrackerTest() override = default; + base::TimeTicks Now() const { return clock_.NowTicks(); } + protected: + base::SimpleTestTickClock clock_; CastAppAvailabilityTracker tracker_; private: @@ -98,9 +105,10 @@ tracker_.RegisterSource(*source3); // |source3| not affected. - EXPECT_THAT(tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA", - AppAvailability::kAvailable), - CastMediaSourcesEqual(std::vector<CastMediaSource>())); + EXPECT_THAT( + tracker_.UpdateAppAvailability( + "sinkId1", "AAAAAAAA", {GetAppAvailabilityResult::kAvailable, Now()}), + CastMediaSourcesEqual(std::vector<CastMediaSource>())); base::flat_set<MediaSink::Id> sinks_1 = {"sinkId1"}; base::flat_set<MediaSink::Id> sinks_1_2 = {"sinkId1", "sinkId2"}; @@ -114,63 +122,46 @@ tracker_.RegisterSource(*source1); // Only |source1| is registered for this app. - EXPECT_THAT(tracker_.UpdateAppAvailability("sinkId2", "AAAAAAAA", - AppAvailability::kAvailable), - CastMediaSourcesEqual(sources_1)); + EXPECT_THAT( + tracker_.UpdateAppAvailability( + "sinkId2", "AAAAAAAA", {GetAppAvailabilityResult::kAvailable, Now()}), + CastMediaSourcesEqual(sources_1)); EXPECT_EQ(sinks_1_2, tracker_.GetAvailableSinks(*source1)); EXPECT_EQ(sinks_1_2, tracker_.GetAvailableSinks(*source2)); EXPECT_TRUE(tracker_.GetAvailableSinks(*source3).empty()); tracker_.RegisterSource(*source2); - EXPECT_THAT(tracker_.UpdateAppAvailability("sinkId2", "AAAAAAAA", - AppAvailability::kUnavailable), + EXPECT_THAT(tracker_.UpdateAppAvailability( + "sinkId2", "AAAAAAAA", + {GetAppAvailabilityResult::kUnavailable, Now()}), CastMediaSourcesEqual(sources_1_2)); EXPECT_EQ(sinks_1, tracker_.GetAvailableSinks(*source1)); EXPECT_EQ(sinks_1, tracker_.GetAvailableSinks(*source2)); EXPECT_TRUE(tracker_.GetAvailableSinks(*source3).empty()); } -TEST_F(CastAppAvailabilityTrackerTest, IsAvailabilityKnown) { - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); - - tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA", - AppAvailability::kAvailable); - EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "BBBBBBBB")); - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId2", "AAAAAAAA")); - - tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA", - AppAvailability::kUnavailable); - EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "BBBBBBBB")); - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId2", "AAAAAAAA")); - - tracker_.UpdateAppAvailability("sinkId2", "AAAAAAAA", - AppAvailability::kUnavailable); - EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "BBBBBBBB")); - EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId2", "AAAAAAAA")); -} - TEST_F(CastAppAvailabilityTrackerTest, RemoveResultsForSink) { auto source1 = CastMediaSource::From("cast:AAAAAAAA?clientId=1"); ASSERT_TRUE(source1); tracker_.UpdateAppAvailability("sinkId1", "AAAAAAAA", - AppAvailability::kAvailable); - EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); + {GetAppAvailabilityResult::kAvailable, Now()}); + EXPECT_EQ(GetAppAvailabilityResult::kAvailable, + tracker_.GetAvailability("sinkId1", "AAAAAAAA").first); base::flat_set<MediaSink::Id> expected_sink_ids = {"sinkId1"}; EXPECT_EQ(expected_sink_ids, tracker_.GetAvailableSinks(*source1)); // Unrelated sink ID. tracker_.RemoveResultsForSink("sinkId2"); - EXPECT_TRUE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); + EXPECT_EQ(GetAppAvailabilityResult::kAvailable, + tracker_.GetAvailability("sinkId1", "AAAAAAAA").first); EXPECT_EQ(expected_sink_ids, tracker_.GetAvailableSinks(*source1)); expected_sink_ids.clear(); tracker_.RemoveResultsForSink("sinkId1"); - EXPECT_FALSE(tracker_.IsAvailabilityKnown("sinkId1", "AAAAAAAA")); + EXPECT_EQ(GetAppAvailabilityResult::kUnknown, + tracker_.GetAvailability("sinkId1", "AAAAAAAA").first); EXPECT_EQ(expected_sink_ids, tracker_.GetAvailableSinks(*source1)); }
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc index b9493f4..22ea5d5 100644 --- a/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc +++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/media/router/providers/cast/cast_app_discovery_service.h" +#include "base/time/tick_clock.h" #include "chrome/browser/media/router/providers/cast/cast_media_route_provider_metrics.h" #include "components/cast_channel/cast_message_handler.h" #include "components/cast_channel/cast_socket.h" @@ -11,13 +12,43 @@ namespace media_router { +namespace { + +// The minimum time that must elapse before an app availability result can be +// force refreshed. +static constexpr base::TimeDelta kRefreshThreshold = + base::TimeDelta::FromMinutes(1); + +bool ShouldRefreshAppAvailability( + const CastAppAvailabilityTracker::AppAvailability& availability, + base::TimeTicks now) { + switch (availability.first) { + case cast_channel::GetAppAvailabilityResult::kAvailable: + return false; + case cast_channel::GetAppAvailabilityResult::kUnavailable: + return now - availability.second > kRefreshThreshold; + case cast_channel::GetAppAvailabilityResult::kUnknown: + return true; + } + + NOTREACHED(); + return false; +} + +} // namespace + CastAppDiscoveryService::CastAppDiscoveryService( cast_channel::CastMessageHandler* message_handler, - cast_channel::CastSocketService* socket_service) + cast_channel::CastSocketService* socket_service, + const base::TickClock* clock) : message_handler_(message_handler), socket_service_(socket_service), + clock_(clock), weak_ptr_factory_(this) { DETACH_FROM_SEQUENCE(sequence_checker_); + DCHECK(message_handler_); + DCHECK(socket_service_); + DCHECK(clock_); } CastAppDiscoveryService::~CastAppDiscoveryService() { @@ -70,6 +101,29 @@ return callback_list->Add(callback); } +void CastAppDiscoveryService::Refresh() { + const auto app_ids = availability_tracker_.GetRegisteredApps(); + base::TimeTicks now = clock_->NowTicks(); + // Note: The following logic assumes |sinks_| will not change as it is + // being iterated. + for (const auto& sink : sinks_) { + for (const auto& app_id : app_ids) { + if (ShouldRefreshAppAvailability( + availability_tracker_.GetAvailability(sink.first, app_id), now)) { + int channel_id = sink.second.cast_data().cast_channel_id; + cast_channel::CastSocket* socket = + socket_service_->GetSocket(channel_id); + if (!socket) { + DVLOG(1) << "Socket not found for id " << channel_id; + continue; + } + + RequestAppAvailability(socket, app_id, sink.first); + } + } + } +} + void CastAppDiscoveryService::MaybeRemoveSinkQueryEntry( const CastMediaSource& source) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -89,7 +143,8 @@ const MediaSink::Id& sink_id = sink.sink().id(); sinks_.insert_or_assign(sink_id, sink); for (const std::string& app_id : availability_tracker_.GetRegisteredApps()) { - if (availability_tracker_.IsAvailabilityKnown(sink_id, app_id)) + auto availability = availability_tracker_.GetAvailability(sink_id, app_id); + if (availability.first != cast_channel::GetAppAvailabilityResult::kUnknown) continue; RequestAppAvailability(socket, app_id, sink_id); @@ -110,7 +165,7 @@ message_handler_->RequestAppAvailability( socket, app_id, base::BindOnce(&CastAppDiscoveryService::UpdateAppAvailability, - weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(), + weak_ptr_factory_.GetWeakPtr(), clock_->NowTicks(), sink_id)); } @@ -118,28 +173,17 @@ base::TimeTicks start_time, const MediaSink::Id& sink_id, const std::string& app_id, - cast_channel::GetAppAvailabilityResult result) { + cast_channel::GetAppAvailabilityResult availability) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - RecordAppAvailabilityResult(result, base::TimeTicks::Now() - start_time); + RecordAppAvailabilityResult(availability, clock_->NowTicks() - start_time); if (!base::ContainsKey(sinks_, sink_id)) return; - if (result == cast_channel::GetAppAvailabilityResult::kUnknown) { - // TODO(crbug.com/779892): Implement retry on user gesture. - DVLOG(2) << "App availability unknown for sink: " << sink_id - << ", app: " << app_id; - return; - } - - AppAvailability availability = - result == cast_channel::GetAppAvailabilityResult::kAvailable - ? AppAvailability::kAvailable - : AppAvailability::kUnavailable; - DVLOG(1) << "App " << app_id << " on sink " << sink_id << " is " - << (availability == AppAvailability::kAvailable); - UpdateSinkQueries(availability_tracker_.UpdateAppAvailability(sink_id, app_id, - availability)); + << cast_channel::GetAppAvailabilityResultToString(availability); + + UpdateSinkQueries(availability_tracker_.UpdateAppAvailability( + sink_id, app_id, {availability, clock_->NowTicks()})); } void CastAppDiscoveryService::UpdateSinkQueries(
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h index 4348e8f..639b91e7 100644 --- a/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h +++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service.h
@@ -23,6 +23,10 @@ #include "chrome/common/media_router/providers/cast/cast_media_source.h" #include "components/cast_channel/cast_message_util.h" +namespace base { +class TickClock; +} + namespace cast_channel { class CastMessageHandler; class CastSocket; @@ -43,7 +47,8 @@ using Subscription = std::unique_ptr<SinkQueryCallbackList::Subscription>; CastAppDiscoveryService(cast_channel::CastMessageHandler* message_handler, - cast_channel::CastSocketService* socket_service); + cast_channel::CastSocketService* socket_service, + const base::TickClock* clock); ~CastAppDiscoveryService() override; // Adds a sink query for |source|. Results will be continuously returned via @@ -53,6 +58,12 @@ Subscription StartObservingMediaSinks(const CastMediaSource& source, const SinkQueryCallback& callback); + // Reissues app availability requests for currently registered (sink, app_id) + // pairs whose status is kUnavailable or kUnknown. It is suitable to call + // this method when the user initiates a user gesture (such as opening the + // Media Router dialog). + void Refresh(); + private: friend class CastAppDiscoveryServiceTest; @@ -97,6 +108,8 @@ CastAppAvailabilityTracker availability_tracker_; base::flat_map<MediaSink::Id, MediaSinkInternal> sinks_; + const base::TickClock* const clock_; + SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<CastAppDiscoveryService> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(CastAppDiscoveryService);
diff --git a/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc b/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc index 57250c80..af2f5dc7 100644 --- a/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc +++ b/chrome/browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/media/router/providers/cast/cast_app_discovery_service.h" #include "base/test/scoped_task_environment.h" +#include "base/test/simple_test_tick_clock.h" #include "base/test/test_simple_task_runner.h" #include "chrome/browser/media/router/test/test_helper.h" #include "chrome/common/media_router/providers/cast/cast_media_source.h" @@ -12,6 +13,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using cast_channel::GetAppAvailabilityResult; using testing::_; using testing::Invoke; @@ -25,10 +27,11 @@ message_handler_(&socket_service_), app_discovery_service_( std::make_unique<CastAppDiscoveryService>(&message_handler_, - &socket_service_)), - source_a_1_(CastMediaSource::From("cast:AAAAAAAA?clientId=1")), - source_a_2_(CastMediaSource::From("cast:AAAAAAAA?clientId=2")), - source_b_1_(CastMediaSource::From("cast:BBBBBBBB?clientId=1")) { + &socket_service_, + &clock_)), + source_a_1_(*CastMediaSource::From("cast:AAAAAAAA?clientId=1")), + source_a_2_(*CastMediaSource::From("cast:AAAAAAAA?clientId=2")), + source_b_1_(*CastMediaSource::From("cast:BBBBBBBB?clientId=1")) { ON_CALL(socket_service_, GetSocket(_)) .WillByDefault(testing::Return(&socket_)); } @@ -47,9 +50,10 @@ app_discovery_service_->OnSinkRemoved(sink); } - CastAppDiscoveryService::Subscription StartObservingMediaSinksInitially() { + CastAppDiscoveryService::Subscription StartObservingMediaSinksInitially( + const CastMediaSource& source) { auto subscription = app_discovery_service_->StartObservingMediaSinks( - *source_a_1_, + source, base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated, base::Unretained(this))); task_runner_->RunPendingTasks(); @@ -58,20 +62,21 @@ protected: scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + base::SimpleTestTickClock clock_; cast_channel::MockCastSocketService socket_service_; cast_channel::MockCastSocket socket_; cast_channel::MockCastMessageHandler message_handler_; std::unique_ptr<CastAppDiscoveryService> app_discovery_service_; - std::unique_ptr<CastMediaSource> source_a_1_; - std::unique_ptr<CastMediaSource> source_a_2_; - std::unique_ptr<CastMediaSource> source_b_1_; + CastMediaSource source_a_1_; + CastMediaSource source_a_2_; + CastMediaSource source_b_1_; private: DISALLOW_COPY_AND_ASSIGN(CastAppDiscoveryServiceTest); }; TEST_F(CastAppDiscoveryServiceTest, StartObservingMediaSinks) { - auto subscription1 = StartObservingMediaSinksInitially(); + auto subscription1 = StartObservingMediaSinksInitially(source_a_1_); // Adding a sink after app registered causes app availability request to be // sent. @@ -89,24 +94,73 @@ // Same app ID should not trigger another request. EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0); auto subscription2 = app_discovery_service_->StartObservingMediaSinks( - *source_a_2_, + source_a_2_, base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated, base::Unretained(this))); std::vector<MediaSinkInternal> sinks_1 = {sink1}; - EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), sinks_1)); - EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_2_->source_id(), sinks_1)); - std::move(cb).Run("AAAAAAAA", - cast_channel::GetAppAvailabilityResult::kAvailable); + EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_.source_id(), sinks_1)); + EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_2_.source_id(), sinks_1)); + std::move(cb).Run("AAAAAAAA", GetAppAvailabilityResult::kAvailable); // No more updates for |source_a_1_|. subscription1.reset(); - EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), _)).Times(0); + EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_.source_id(), _)).Times(0); EXPECT_CALL(*this, - OnSinkQueryUpdated(source_a_2_->source_id(), testing::IsEmpty())); + OnSinkQueryUpdated(source_a_2_.source_id(), testing::IsEmpty())); RemoveSink(sink1); } +TEST_F(CastAppDiscoveryServiceTest, Refresh) { + auto subscription1 = StartObservingMediaSinksInitially(source_a_1_); + auto subscription2 = StartObservingMediaSinksInitially(source_b_1_); + + MediaSinkInternal sink1 = CreateCastSink(1); + EXPECT_CALL(*this, OnSinkQueryUpdated(_, _)); + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)) + .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string& app_id, + cast_channel::GetAppAvailabilityCallback& callback) { + std::move(callback).Run(app_id, GetAppAvailabilityResult::kAvailable); + })); + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "BBBBBBBB", _)) + .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string& app_id, + cast_channel::GetAppAvailabilityCallback& callback) { + std::move(callback).Run(app_id, GetAppAvailabilityResult::kUnknown); + })); + AddOrUpdateSink(sink1); + + MediaSinkInternal sink2 = CreateCastSink(2); + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)) + .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string& app_id, + cast_channel::GetAppAvailabilityCallback& callback) { + std::move(callback).Run(app_id, GetAppAvailabilityResult::kUnavailable); + })); + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "BBBBBBBB", _)); + AddOrUpdateSink(sink2); + + clock_.Advance(base::TimeDelta::FromSeconds(30)); + + // Request app availability for app B for both sinks. + // App A on |sink2| is not requested due to timing threshold. + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)) + .Times(0); + EXPECT_CALL(*this, OnSinkQueryUpdated(_, _)).Times(2); + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "BBBBBBBB", _)) + .Times(2) + .WillRepeatedly( + Invoke([](cast_channel::CastSocket*, const std::string& app_id, + cast_channel::GetAppAvailabilityCallback& callback) { + std::move(callback).Run(app_id, + GetAppAvailabilityResult::kAvailable); + })); + app_discovery_service_->Refresh(); + + clock_.Advance(base::TimeDelta::FromSeconds(31)); + + EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)); + app_discovery_service_->Refresh(); +} + TEST_F(CastAppDiscoveryServiceTest, StartObservingMediaSinksAfterSinkAdded) { // No registered apps. MediaSinkInternal sink1 = CreateCastSink(1); @@ -115,13 +169,13 @@ EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)); auto subscription1 = app_discovery_service_->StartObservingMediaSinks( - *source_a_1_, + source_a_1_, base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated, base::Unretained(this))); EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "BBBBBBBB", _)); auto subscription2 = app_discovery_service_->StartObservingMediaSinks( - *source_b_1_, + source_b_1_, base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated, base::Unretained(this))); @@ -134,7 +188,7 @@ } TEST_F(CastAppDiscoveryServiceTest, StartObservingMediaSinksCachedValue) { - auto subscription1 = StartObservingMediaSinksInitially(); + auto subscription1 = StartObservingMediaSinksInitially(source_a_1_); // Adding a sink after app registered causes app availability request to be // sent. @@ -149,16 +203,15 @@ AddOrUpdateSink(sink1); std::vector<MediaSinkInternal> sinks_1 = {sink1}; - EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), sinks_1)); - std::move(cb).Run("AAAAAAAA", - cast_channel::GetAppAvailabilityResult::kAvailable); + EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_.source_id(), sinks_1)); + std::move(cb).Run("AAAAAAAA", GetAppAvailabilityResult::kAvailable); // Same app ID should not trigger another request, but it should return // cached value. EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0); - EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_2_->source_id(), sinks_1)); + EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_2_.source_id(), sinks_1)); auto subscription2 = app_discovery_service_->StartObservingMediaSinks( - *source_a_2_, + source_a_2_, base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated, base::Unretained(this))); @@ -166,7 +219,7 @@ auto source3 = CastMediaSource::From("cast:AAAAAAAA?clientId=1"); ASSERT_TRUE(source3); EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, _, _)).Times(0); - EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_->source_id(), sinks_1)); + EXPECT_CALL(*this, OnSinkQueryUpdated(source_a_1_.source_id(), sinks_1)); auto subscription3 = app_discovery_service_->StartObservingMediaSinks( *source3, base::BindRepeating(&CastAppDiscoveryServiceTest::OnSinkQueryUpdated, @@ -174,7 +227,7 @@ } TEST_F(CastAppDiscoveryServiceTest, AvailabilityUnknownOrUnavailable) { - auto subscription1 = StartObservingMediaSinksInitially(); + auto subscription1 = StartObservingMediaSinksInitially(source_a_1_); // Adding a sink after app registered causes app availability request to be // sent. @@ -183,8 +236,7 @@ EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)) .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string&, cast_channel::GetAppAvailabilityCallback& callback) { - std::move(callback).Run( - "AAAAAAAA", cast_channel::GetAppAvailabilityResult::kUnknown); + std::move(callback).Run("AAAAAAAA", GetAppAvailabilityResult::kUnknown); })); AddOrUpdateSink(sink1); @@ -194,8 +246,8 @@ EXPECT_CALL(message_handler_, DoRequestAppAvailability(_, "AAAAAAAA", _)) .WillOnce(Invoke([](cast_channel::CastSocket*, const std::string&, cast_channel::GetAppAvailabilityCallback& callback) { - std::move(callback).Run( - "AAAAAAAA", cast_channel::GetAppAvailabilityResult::kUnavailable); + std::move(callback).Run("AAAAAAAA", + GetAppAvailabilityResult::kUnavailable); })); AddOrUpdateSink(sink1);
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc index 7c097c3..26c400d 100644 --- a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc +++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
@@ -192,7 +192,7 @@ } void CastMediaRouteProvider::UpdateMediaSinks(const std::string& media_source) { - NOTIMPLEMENTED(); + app_discovery_service_->Refresh(); } void CastMediaRouteProvider::SearchSinks(
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc index 9e2616a7..e8f6eb2 100644 --- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc +++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
@@ -4,7 +4,9 @@ #include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h" +#include "base/time/default_tick_clock.h" #include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h" +#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h" #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h" #include "chrome/browser/media/router/media_router_feature.h" #include "chrome/browser/media/router/providers/cast/cast_app_discovery_service.h" @@ -32,6 +34,10 @@ instance_for_test_ = instance_for_test; } +DialMediaSinkServiceImpl* DualMediaSinkService::GetDialMediaSinkServiceImpl() { + return dial_media_sink_service_->impl(); +} + DualMediaSinkService::Subscription DualMediaSinkService::AddSinksDiscoveredCallback( const OnSinksDiscoveredProviderCallback& callback) { @@ -61,7 +67,8 @@ cast_channel::CastSocketService* cast_socket_service = cast_channel::CastSocketService::GetInstance(); cast_app_discovery_service_ = std::make_unique<CastAppDiscoveryService>( - GetCastMessageHandler(), cast_socket_service); + GetCastMessageHandler(), cast_socket_service, + base::DefaultTickClock::GetInstance()); } cast_media_sink_service_ = std::make_unique<CastMediaSinkService>();
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h index c7f7d79..fd27e65 100644 --- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h +++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
@@ -25,6 +25,7 @@ class CastAppDiscoveryService; class CastMediaSinkService; class DialMediaSinkService; +class DialMediaSinkServiceImpl; // This class uses DialMediaSinkService and CastMediaSinkService to discover // sinks used by the Cast MediaRouteProvider. It also encapsulates the setup @@ -57,9 +58,8 @@ return current_sinks_; } - DialMediaSinkService* dial_media_sink_service() { - return dial_media_sink_service_.get(); - } + // Used by DialMediaRouteProvider only. + DialMediaSinkServiceImpl* GetDialMediaSinkServiceImpl(); // Adds |callback| to be notified when the list of discovered sinks changes. // The caller is responsible for destroying the returned Subscription when it
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc index acbe4cd..f024a77 100644 --- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc +++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
@@ -4,6 +4,9 @@ #include "chrome/browser/media/router/providers/dial/dial_media_route_provider.h" +#include <utility> + +#include "base/containers/flat_map.h" #include "base/no_destructor.h" #include "base/stl_util.h" #include "chrome/common/media_router/media_source_helper.h" @@ -21,14 +24,27 @@ DialMediaRouteProvider::DialMediaRouteProvider( mojom::MediaRouteProviderRequest request, - mojom::MediaRouterPtr media_router, - DialMediaSinkService* dial_media_sink_service) - : binding_(this, std::move(request)), - media_router_(std::move(media_router)), - dial_media_sink_service_(dial_media_sink_service) { + mojom::MediaRouterPtrInfo media_router, + DialMediaSinkServiceImpl* media_sink_service, + const scoped_refptr<base::SequencedTaskRunner>& task_runner) + : binding_(this), media_sink_service_(media_sink_service) { + DETACH_FROM_SEQUENCE(sequence_checker_); + DCHECK(media_sink_service_); + + task_runner->PostTask( + FROM_HERE, + base::BindOnce(&DialMediaRouteProvider::Init, base::Unretained(this), + std::move(request), std::move(media_router))); +} + +void DialMediaRouteProvider::Init(mojom::MediaRouteProviderRequest request, + mojom::MediaRouterPtrInfo media_router) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(dial_media_sink_service_); - // TODO(crbug.com/808720): This needs to be set properly according to sinks + + binding_.Bind(std::move(request)); + media_router_.Bind(std::move(media_router)); + + // TODO(crbug.com/816702): This needs to be set properly according to sinks // discovered. media_router_->OnSinkAvailabilityUpdated( MediaRouteProviderId::DIAL, @@ -109,25 +125,49 @@ void DialMediaRouteProvider::StartObservingMediaSinks( const std::string& media_source) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(dial_media_sink_service_); MediaSource dial_source(media_source); if (!IsDialMediaSource(dial_source)) return; - RegisterDialMediaSource(dial_source); + std::string app_name = AppNameFromDialMediaSource(dial_source); + if (app_name.empty()) + return; + + auto& sink_query = media_sink_queries_[app_name]; + if (!sink_query) { + sink_query = std::make_unique<MediaSinkQuery>(); + sink_query->subscription = + media_sink_service_->StartMonitoringAvailableSinksForApp( + app_name, base::BindRepeating( + &DialMediaRouteProvider::OnAvailableSinksUpdated, + base::Unretained(this))); + } + + // Return cached results immediately. + if (sink_query->media_sources.insert(dial_source).second) { + auto sinks = media_sink_service_->GetAvailableSinks(app_name); + NotifyOnSinksReceived(dial_source.id(), sinks, GetOrigins(app_name)); + } } void DialMediaRouteProvider::StopObservingMediaSinks( const std::string& media_source) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(dial_media_sink_service_); MediaSource dial_source(media_source); - if (!IsDialMediaSource(dial_source)) + std::string app_name = AppNameFromDialMediaSource(dial_source); + if (app_name.empty()) return; - UnregisterDialMediaSource(dial_source); + const auto& sink_query_it = media_sink_queries_.find(app_name); + if (sink_query_it == media_sink_queries_.end()) + return; + + auto& media_sources = sink_query_it->second->media_sources; + media_sources.erase(dial_source); + if (media_sources.empty()) + media_sink_queries_.erase(sink_query_it); } void DialMediaRouteProvider::StartObservingMediaRoutes( @@ -186,8 +226,7 @@ } void DialMediaRouteProvider::OnAvailableSinksUpdated( - const std::string& app_name, - const std::vector<MediaSinkInternal>& sinks) { + const std::string& app_name) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); const auto& sink_query_it = media_sink_queries_.find(app_name); if (sink_query_it == media_sink_queries_.end()) { @@ -198,54 +237,17 @@ const auto& media_sources = sink_query_it->second->media_sources; std::vector<url::Origin> origins = GetOrigins(app_name); - for (const auto& media_source : media_sources) { - media_router_->OnSinksReceived(MediaRouteProviderId::DIAL, - media_source.id(), sinks, origins); - } + auto sinks = media_sink_service_->GetAvailableSinks(app_name); + for (const auto& media_source : media_sources) + NotifyOnSinksReceived(media_source.id(), sinks, origins); } -void DialMediaRouteProvider::RegisterDialMediaSource( - const MediaSource& dial_source) { - std::string app_name = AppNameFromDialMediaSource(dial_source); - auto& sink_query = media_sink_queries_[app_name]; - if (!sink_query) { - sink_query = std::make_unique<MediaSinkQuery>(); - sink_query->subscription = - dial_media_sink_service_->StartMonitoringAvailableSinksForApp( - app_name, base::BindRepeating( - &DialMediaRouteProvider::OnAvailableSinksUpdated, - base::Unretained(this))); - } - - if (sink_query->media_sources.insert(dial_source).second) - MayNotifyMediaSinksObservers(dial_source.id(), app_name); -} - -void DialMediaRouteProvider::MayNotifyMediaSinksObservers( - const MediaSource::Id& media_source_id, - const std::string& app_name) { - const auto& cached_sinks = - dial_media_sink_service_->GetCachedAvailableSinks(app_name); - if (cached_sinks.empty()) - return; - - media_router_->OnSinksReceived(MediaRouteProviderId::DIAL, media_source_id, - cached_sinks, GetOrigins(app_name)); -} - -void DialMediaRouteProvider::UnregisterDialMediaSource( - const MediaSource& dial_source) { - std::string app_name = AppNameFromDialMediaSource(dial_source); - const auto& sink_query_it = media_sink_queries_.find(app_name); - if (sink_query_it == media_sink_queries_.end()) - return; - - auto& media_sources = sink_query_it->second->media_sources; - media_sources.erase(dial_source); - if (!media_sources.empty()) - return; - - media_sink_queries_.erase(app_name); +void DialMediaRouteProvider::NotifyOnSinksReceived( + const MediaSource::Id& source_id, + const std::vector<MediaSinkInternal>& sinks, + const std::vector<url::Origin>& origins) { + media_router_->OnSinksReceived(MediaRouteProviderId::DIAL, source_id, sinks, + origins); } std::vector<url::Origin> DialMediaRouteProvider::GetOrigins(
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.h b/chrome/browser/media/router/providers/dial/dial_media_route_provider.h index 86b2413..9904b67 100644 --- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.h +++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
@@ -6,13 +6,15 @@ #define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_DIAL_DIAL_MEDIA_ROUTE_PROVIDER_H_ #include <memory> +#include <string> +#include <vector> #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/sequence_checker.h" -#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h" +#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h" #include "chrome/common/media_router/mojo/media_router.mojom.h" #include "mojo/public/cpp/bindings/binding.h" @@ -23,11 +25,15 @@ namespace media_router { // MediaRouteProvider for DIAL sinks. +// This class is created on the UI thread. All other methods must be invoked +// on the |task_runner| provided to the constructor. class DialMediaRouteProvider : public mojom::MediaRouteProvider { public: - DialMediaRouteProvider(mojom::MediaRouteProviderRequest request, - mojom::MediaRouterPtr media_router, - DialMediaSinkService* dial_media_sink_service); + DialMediaRouteProvider( + mojom::MediaRouteProviderRequest request, + mojom::MediaRouterPtrInfo media_router, + DialMediaSinkServiceImpl* media_sink_service, + const scoped_refptr<base::SequencedTaskRunner>& task_runner); ~DialMediaRouteProvider() override; // mojom::MediaRouteProvider: @@ -85,13 +91,13 @@ CreateMediaRouteControllerCallback callback) override; private: - FRIEND_TEST_ALL_PREFIXES(DialMediaRouteProviderTest, TestAddRemoveSinkQuery); + FRIEND_TEST_ALL_PREFIXES(DialMediaRouteProviderTest, AddRemoveSinkQuery); FRIEND_TEST_ALL_PREFIXES(DialMediaRouteProviderTest, - TestAddSinkQuerySameMediaSource); + AddSinkQuerySameMediaSource); FRIEND_TEST_ALL_PREFIXES(DialMediaRouteProviderTest, - TestAddSinkQuerySameAppDifferentMediaSources); + AddSinkQuerySameAppDifferentMediaSources); FRIEND_TEST_ALL_PREFIXES(DialMediaRouteProviderTest, - TestAddSinkQueryDifferentApps); + AddSinkQueryDifferentApps); struct MediaSinkQuery { MediaSinkQuery(); @@ -99,21 +105,20 @@ // Set of registered media sources for current sink query. base::flat_set<MediaSource> media_sources; - DialMediaSinkService::SinkQueryByAppSubscription subscription; + DialMediaSinkServiceImpl::SinkQueryByAppSubscription subscription; DISALLOW_COPY_AND_ASSIGN(MediaSinkQuery); }; - void OnAvailableSinksUpdated( - const std::string& app_name, - const std::vector<MediaSinkInternal>& available_sinks); + // Binds the message pipes |request| and |media_router| to |this|. + void Init(mojom::MediaRouteProviderRequest request, + mojom::MediaRouterPtrInfo media_router); - void RegisterDialMediaSource(const MediaSource& dial_source); + void OnAvailableSinksUpdated(const std::string& app_name); - void MayNotifyMediaSinksObservers(const MediaSource::Id& media_source_id, - const std::string& app_name); - - void UnregisterDialMediaSource(const MediaSource& dial_source); + void NotifyOnSinksReceived(const MediaSource::Id& source_id, + const std::vector<MediaSinkInternal>& sinks, + const std::vector<url::Origin>& origins); // Returns a list of valid origins for |app_name|. Returns an empty list if // all origins are valid. @@ -125,15 +130,14 @@ // Mojo pointer to the Media Router. mojom::MediaRouterPtr media_router_; - // Non-owned pointer to the DialMediaSinkService instance. - DialMediaSinkService* const dial_media_sink_service_; + // Non-owned pointer to the DialMediaSinkServiceImpl instance. + DialMediaSinkServiceImpl* const media_sink_service_; // Map of media sink queries, keyed by app name. base::flat_map<std::string, std::unique_ptr<MediaSinkQuery>> media_sink_queries_; SEQUENCE_CHECKER(sequence_checker_); - DISALLOW_COPY_AND_ASSIGN(DialMediaRouteProvider); };
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc b/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc index 90b0fae..99e228a 100644 --- a/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc +++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider_unittest.cc
@@ -7,22 +7,74 @@ #include "chrome/browser/media/router/test/mock_mojo_media_router.h" #include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h" #include "chrome/browser/media/router/test/test_helper.h" -#include "chrome/test/base/testing_profile.h" -#include "content/public/test/test_browser_thread_bundle.h" +#include "services/service_manager/public/cpp/connector.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; -using ::testing::InvokeWithoutArgs; -using ::testing::Return; -using ::testing::StrictMock; +using ::testing::IsEmpty; +using ::testing::SaveArg; namespace media_router { +class TestDialMediaSinkServiceImpl : public DialMediaSinkServiceImpl { + public: + TestDialMediaSinkServiceImpl() + : DialMediaSinkServiceImpl(/* connector */ nullptr, + base::DoNothing(), + base::DoNothing(), + /* task_runner */ nullptr) {} + + ~TestDialMediaSinkServiceImpl() override = default; + + MOCK_METHOD0(Start, void()); + + SinkQueryByAppSubscription StartMonitoringAvailableSinksForApp( + const std::string& app_name, + const SinkQueryByAppCallback& callback) override { + DoStartMonitoringAvailableSinksForApp(app_name); + auto& cb_list = sink_query_cbs_[app_name]; + if (!cb_list) + cb_list = std::make_unique<SinkQueryByAppCallbackList>(); + return cb_list->Add(callback); + } + MOCK_METHOD1(DoStartMonitoringAvailableSinksForApp, + void(const std::string& app_name)); + + void SetAvailableSinks(const std::string& app_name, + const std::vector<MediaSinkInternal>& sinks) { + available_sinks_[app_name] = sinks; + for (const auto& sink : sinks) + all_sinks_[sink.sink().id()] = sink; + } + + void NotifyAvailableSinks(const std::string& app_name) { + auto& cb_list = sink_query_cbs_[app_name]; + if (cb_list) + cb_list->Notify(app_name); + } + + std::vector<MediaSinkInternal> GetAvailableSinks( + const std::string& app_name) const override { + auto it = available_sinks_.find(app_name); + return it != available_sinks_.end() ? it->second + : std::vector<MediaSinkInternal>(); + } + + private: + base::flat_map<std::string, std::unique_ptr<SinkQueryByAppCallbackList>> + sink_query_cbs_; + base::flat_map<std::string, std::vector<MediaSinkInternal>> available_sinks_; + base::flat_map<MediaSink::Id, MediaSinkInternal> all_sinks_; +}; + class DialMediaRouteProviderTest : public ::testing::Test { public: - DialMediaRouteProviderTest() : mock_service_() {} + DialMediaRouteProviderTest() {} void SetUp() override { mojom::MediaRouterPtr router_ptr; @@ -31,40 +83,30 @@ EXPECT_CALL(mock_router_, OnSinkAvailabilityUpdated(_, _)); provider_ = std::make_unique<DialMediaRouteProvider>( - mojo::MakeRequest(&provider_ptr_), std::move(router_ptr), - &mock_service_); + mojo::MakeRequest(&provider_ptr_), router_ptr.PassInterface(), + &mock_sink_service_, base::SequencedTaskRunnerHandle::Get()); + base::RunLoop().RunUntilIdle(); } void TearDown() override { provider_.reset(); } - MOCK_METHOD1(OnSinksDiscovered, void(std::vector<MediaSinkInternal> sinks)); - MOCK_METHOD1(OnDialSinkAdded, void(const MediaSinkInternal& sink)); - protected: - content::TestBrowserThreadBundle thread_bundle_; - TestingProfile profile_; + base::test::ScopedTaskEnvironment environment_; mojom::MediaRouteProviderPtr provider_ptr_; MockMojoMediaRouter mock_router_; std::unique_ptr<mojo::Binding<mojom::MediaRouter>> router_binding_; - StrictMock<MockDialMediaSinkService> mock_service_; + TestDialMediaSinkServiceImpl mock_sink_service_; std::unique_ptr<DialMediaRouteProvider> provider_; - std::vector<MediaSinkInternal> cached_sinks_; + + MediaSinkInternal sink_ = CreateDialSink(1); DISALLOW_COPY_AND_ASSIGN(DialMediaRouteProviderTest); }; -TEST_F(DialMediaRouteProviderTest, TestAddRemoveSinkQuery) { - std::string youtube_source("cast-dial:YouTube"); - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp("YouTube", _)); - EXPECT_CALL(mock_service_, GetCachedAvailableSinks("YouTube")) - .WillOnce(Return(cached_sinks_)); - provider_->StartObservingMediaSinks(youtube_source); - - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; +TEST_F(DialMediaRouteProviderTest, AddRemoveSinkQuery) { std::vector<url::Origin> youtube_origins = { url::Origin::Create(GURL("https://tv.youtube.com")), url::Origin::Create(GURL("https://tv-green-qa.youtube.com")), @@ -72,34 +114,52 @@ url::Origin::Create(GURL("https://web-green-qa.youtube.com")), url::Origin::Create(GURL("https://web-release-qa.youtube.com")), url::Origin::Create(GURL("https://www.youtube.com"))}; + std::string youtube_source("cast-dial:YouTube"); + EXPECT_CALL(mock_sink_service_, + DoStartMonitoringAvailableSinksForApp("YouTube")); + EXPECT_CALL(mock_router_, + OnSinksReceived(MediaRouteProviderId::DIAL, youtube_source, + IsEmpty(), youtube_origins)); + provider_->StartObservingMediaSinks(youtube_source); + base::RunLoop().RunUntilIdle(); + + MediaSinkInternal sink = CreateDialSink(1); + std::vector<MediaSinkInternal> sinks = {sink}; + mock_sink_service_.SetAvailableSinks("YouTube", sinks); + EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, youtube_source, sinks, youtube_origins)); - provider_->OnAvailableSinksUpdated("YouTube", sinks); + mock_sink_service_.NotifyAvailableSinks("YouTube"); base::RunLoop().RunUntilIdle(); EXPECT_CALL(mock_router_, OnSinksReceived(_, _, _, _)).Times(0); provider_->StopObservingMediaSinks(youtube_source); - provider_->OnAvailableSinksUpdated("YouTube", sinks); + mock_sink_service_.NotifyAvailableSinks("YouTube"); base::RunLoop().RunUntilIdle(); } -TEST_F(DialMediaRouteProviderTest, TestAddSinkQuerySameMediaSource) { +TEST_F(DialMediaRouteProviderTest, AddSinkQuerySameMediaSource) { std::string youtube_source("cast-dial:YouTube"); - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp("YouTube", _)); - EXPECT_CALL(mock_service_, GetCachedAvailableSinks("YouTube")) - .WillOnce(Return(cached_sinks_)); + EXPECT_CALL(mock_sink_service_, + DoStartMonitoringAvailableSinksForApp("YouTube")); + EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, + youtube_source, IsEmpty(), _)); provider_->StartObservingMediaSinks(youtube_source); + base::RunLoop().RunUntilIdle(); - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp(_, _)) + EXPECT_CALL(mock_sink_service_, DoStartMonitoringAvailableSinksForApp(_)) + .Times(0); + EXPECT_CALL(mock_router_, + OnSinksReceived(MediaRouteProviderId::DIAL, _, _, _)) .Times(0); provider_->StartObservingMediaSinks(youtube_source); + base::RunLoop().RunUntilIdle(); EXPECT_CALL(mock_router_, OnSinksReceived(_, _, _, _)).Times(0); provider_->StopObservingMediaSinks(youtube_source); provider_->StopObservingMediaSinks(youtube_source); - provider_->OnAvailableSinksUpdated("YouTube", - std::vector<MediaSinkInternal>()); + mock_sink_service_.NotifyAvailableSinks("YouTube"); base::RunLoop().RunUntilIdle(); } @@ -107,16 +167,21 @@ TestAddSinkQuerySameAppDifferentMediaSources) { std::string youtube_source1("cast-dial:YouTube"); std::string youtube_source2("cast-dial:YouTube?clientId=15178573373126446"); - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp("YouTube", _)); - EXPECT_CALL(mock_service_, GetCachedAvailableSinks("YouTube")) - .WillOnce(Return(cached_sinks_)); + EXPECT_CALL(mock_sink_service_, + DoStartMonitoringAvailableSinksForApp("YouTube")); + EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, + youtube_source1, IsEmpty(), _)); provider_->StartObservingMediaSinks(youtube_source1); + base::RunLoop().RunUntilIdle(); - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; - EXPECT_CALL(mock_service_, GetCachedAvailableSinks("YouTube")) - .WillOnce(Return(sinks)); - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp(_, _)) + MediaSinkInternal sink = CreateDialSink(1); + std::vector<MediaSinkInternal> sinks = {sink}; + mock_sink_service_.SetAvailableSinks("YouTube", sinks); + EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, + youtube_source1, sinks, _)); + mock_sink_service_.NotifyAvailableSinks("YouTube"); + + EXPECT_CALL(mock_sink_service_, DoStartMonitoringAvailableSinksForApp(_)) .Times(0); EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, youtube_source2, sinks, _)); @@ -127,52 +192,58 @@ youtube_source1, sinks, _)); EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, youtube_source2, sinks, _)); - provider_->OnAvailableSinksUpdated("YouTube", sinks); + mock_sink_service_.NotifyAvailableSinks("YouTube"); base::RunLoop().RunUntilIdle(); + provider_->StopObservingMediaSinks(youtube_source1); EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, youtube_source2, sinks, _)); - provider_->StopObservingMediaSinks(youtube_source1); - provider_->OnAvailableSinksUpdated("YouTube", sinks); + mock_sink_service_.NotifyAvailableSinks("YouTube"); base::RunLoop().RunUntilIdle(); - EXPECT_CALL(mock_router_, OnSinksReceived(_, _, _, _)).Times(0); provider_->StopObservingMediaSinks(youtube_source2); - provider_->OnAvailableSinksUpdated("YouTube", sinks); + EXPECT_CALL(mock_router_, OnSinksReceived(_, _, _, _)).Times(0); + mock_sink_service_.NotifyAvailableSinks("YouTube"); base::RunLoop().RunUntilIdle(); } -TEST_F(DialMediaRouteProviderTest, TestAddSinkQueryDifferentApps) { +TEST_F(DialMediaRouteProviderTest, AddSinkQueryDifferentApps) { std::string youtube_source("cast-dial:YouTube"); std::string netflix_source("cast-dial:Netflix"); - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp("YouTube", _)); - EXPECT_CALL(mock_service_, GetCachedAvailableSinks("YouTube")) - .WillOnce(Return(cached_sinks_)); - provider_->StartObservingMediaSinks(youtube_source); - - EXPECT_CALL(mock_service_, StartMonitoringAvailableSinksForApp("Netflix", _)); - EXPECT_CALL(mock_service_, GetCachedAvailableSinks("Netflix")) - .WillOnce(Return(cached_sinks_)); - provider_->StartObservingMediaSinks(netflix_source); - - MediaSinkInternal sink_internal = CreateDialSink(1); - std::vector<MediaSinkInternal> sinks{sink_internal}; + EXPECT_CALL(mock_sink_service_, + DoStartMonitoringAvailableSinksForApp("YouTube")); EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, - youtube_source, sinks, _)); - provider_->OnAvailableSinksUpdated("YouTube", sinks); + youtube_source, IsEmpty(), _)); + provider_->StartObservingMediaSinks(youtube_source); base::RunLoop().RunUntilIdle(); + EXPECT_CALL(mock_sink_service_, + DoStartMonitoringAvailableSinksForApp("Netflix")); + EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, + netflix_source, IsEmpty(), _)); + provider_->StartObservingMediaSinks(netflix_source); + base::RunLoop().RunUntilIdle(); + + MediaSinkInternal sink = CreateDialSink(1); + std::vector<MediaSinkInternal> sinks = {sink}; + mock_sink_service_.SetAvailableSinks("YouTube", sinks); + EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, + youtube_source, sinks, _)); + mock_sink_service_.NotifyAvailableSinks("YouTube"); + base::RunLoop().RunUntilIdle(); + + mock_sink_service_.SetAvailableSinks("Netflix", sinks); EXPECT_CALL(mock_router_, OnSinksReceived(MediaRouteProviderId::DIAL, netflix_source, sinks, _)); - provider_->OnAvailableSinksUpdated("Netflix", sinks); + mock_sink_service_.NotifyAvailableSinks("Netflix"); base::RunLoop().RunUntilIdle(); provider_->StopObservingMediaSinks(youtube_source); provider_->StopObservingMediaSinks(netflix_source); EXPECT_CALL(mock_router_, OnSinksReceived(_, _, _, _)).Times(0); - provider_->OnAvailableSinksUpdated("YouTube", sinks); - provider_->OnAvailableSinksUpdated("Netflix", sinks); + mock_sink_service_.NotifyAvailableSinks("YouTube"); + mock_sink_service_.NotifyAvailableSinks("Netflix"); base::RunLoop().RunUntilIdle(); }
diff --git a/chrome/browser/media/router/test/test_helper.h b/chrome/browser/media/router/test/test_helper.h index f343cee..506af46 100644 --- a/chrome/browser/media/router/test/test_helper.h +++ b/chrome/browser/media/router/test/test_helper.h
@@ -126,12 +126,6 @@ void(const OnSinksDiscoveredCallback&, const OnDialSinkAddedCallback&)); MOCK_METHOD0(OnUserGesture, void()); - MOCK_METHOD2(StartMonitoringAvailableSinksForApp, - DialMediaSinkService::SinkQueryByAppSubscription( - const std::string&, - const SinkQueryByAppCallback&)); - MOCK_METHOD1(GetCachedAvailableSinks, - std::vector<MediaSinkInternal>(const std::string& app_name)); }; class MockCastMediaSinkService : public CastMediaSinkService {
diff --git a/chrome/browser/metrics/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats_tracker.cc index c3e1fffb..4b58f23 100644 --- a/chrome/browser/metrics/tab_stats_tracker.cc +++ b/chrome/browser/metrics/tab_stats_tracker.cc
@@ -45,6 +45,11 @@ base::TimeDelta::FromMinutes(10), base::TimeDelta::FromHours(1), base::TimeDelta::FromHours(5), base::TimeDelta::FromHours(12)}; +#if defined(OS_WIN) +const base::TimeDelta kNativeWindowOcclusionCalculationInterval = + base::TimeDelta::FromMinutes(10); +#endif + // The global TabStatsTracker instance. TabStatsTracker* g_tab_stats_tracker_instance = nullptr; @@ -87,6 +92,7 @@ TabStatsTracker::TabStatsTracker(PrefService* pref_service) : reporting_delegate_(std::make_unique<UmaStatsReportingDelegate>()), + delegate_(std::make_unique<TabStatsTrackerDelegate>()), tab_stats_data_store_(std::make_unique<TabStatsDataStore>(pref_service)), daily_event_( std::make_unique<DailyEvent>(pref_service, @@ -130,6 +136,14 @@ interval, interval_map)); usage_interval_timers_.push_back(std::move(timer)); } + +// The native window occlusion calculation is specific to Windows. +#if defined(OS_WIN) + native_window_occlusion_timer_.Start( + FROM_HERE, kNativeWindowOcclusionCalculationInterval, + Bind(&TabStatsTracker::CalculateAndRecordNativeWindowVisibilities, + base::Unretained(this))); +#endif } TabStatsTracker::~TabStatsTracker() { @@ -162,6 +176,11 @@ DailyEvent::RegisterPref(registry, ::prefs::kTabStatsDailySample); } +void TabStatsTracker::SetDelegateForTesting( + std::unique_ptr<TabStatsTrackerDelegate> new_delegate) { + delegate_ = std::move(new_delegate); +} + void TabStatsTracker::TabStatsDailyObserver::OnDailyEvent( DailyEvent::IntervalType type) { reporting_delegate_->ReportDailyMetrics(data_store_->tab_stats());
diff --git a/chrome/browser/metrics/tab_stats_tracker.h b/chrome/browser/metrics/tab_stats_tracker.h index 370b350..e012981 100644 --- a/chrome/browser/metrics/tab_stats_tracker.h +++ b/chrome/browser/metrics/tab_stats_tracker.h
@@ -10,13 +10,16 @@ #include <string> #include <vector> +#include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/power_monitor/power_observer.h" #include "base/sequence_checker.h" #include "base/timer/timer.h" +#include "build/build_config.h" #include "chrome/browser/metrics/tab_stats_data_store.h" +#include "chrome/browser/metrics/tab_stats_tracker_delegate.h" #include "chrome/browser/ui/browser_list_observer.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "components/metrics/daily_event.h" @@ -26,7 +29,6 @@ class PrefService; namespace metrics { - FORWARD_DECLARE_TEST(TabStatsTrackerBrowserTest, TabDeletionGetsHandledProperly); @@ -53,12 +55,19 @@ // Registers prefs used to track tab stats. static void RegisterPrefs(PrefRegistrySimple* registry); + void SetDelegateForTesting( + std::unique_ptr<TabStatsTrackerDelegate> new_delegate); + // Accessors. const TabStatsDataStore::TabsStats& tab_stats() const; protected: FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest, TabDeletionGetsHandledProperly); +#if defined(OS_WIN) + FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest, + TestCalculateAndRecordNativeWindowVisibilities); +#endif // The UmaStatsReportingDelegate is responsible for delivering statistics // reported by the TabStatsTracker via UMA. @@ -146,6 +155,12 @@ // Functions to call when a WebContents get destroyed. void OnWebContentsDestroyed(content::WebContents* web_contents); +#if defined(OS_WIN) + // Function to call aura_extra::ComputeNativeWindowOcclusionStatus() and + // record the Visibility of all Chrome browser windows on Windows. + void CalculateAndRecordNativeWindowVisibilities(); +#endif + // The name of the histogram used to report that the daily event happened. static const char kTabStatsDailyEventHistogramName[]; @@ -157,6 +172,9 @@ // The delegate that reports the events. std::unique_ptr<UmaStatsReportingDelegate> reporting_delegate_; + // Delegate to collect data; + std::unique_ptr<TabStatsTrackerDelegate> delegate_; + // The tab stats. std::unique_ptr<TabStatsDataStore> tab_stats_data_store_; @@ -167,6 +185,12 @@ // triggered. base::RepeatingTimer timer_; +#if defined(OS_WIN) + // The timer used to periodically calculate the occlusion status of native + // windows on Windows. + base::RepeatingTimer native_window_occlusion_timer_; +#endif + // The timers used to analyze how tabs are used during a given interval of // time. std::vector<std::unique_ptr<base::RepeatingTimer>> usage_interval_timers_; @@ -222,6 +246,12 @@ const TabStatsDataStore::TabsStateDuringIntervalMap& interval_map, base::TimeDelta interval); +#if defined(OS_WIN) + void RecordNativeWindowVisibilities(size_t num_occluded, + size_t num_visible, + size_t num_hidden); +#endif + protected: // Generates the name of the histograms that will track tab usage during a // given period of time.
diff --git a/chrome/browser/metrics/tab_stats_tracker_browsertest.cc b/chrome/browser/metrics/tab_stats_tracker_browsertest.cc index 4b08d9f5..e65b8b1d 100644 --- a/chrome/browser/metrics/tab_stats_tracker_browsertest.cc +++ b/chrome/browser/metrics/tab_stats_tracker_browsertest.cc
@@ -4,8 +4,12 @@ #include "chrome/browser/metrics/tab_stats_tracker.h" +#include "base/containers/flat_map.h" +#include "base/test/histogram_tester.h" +#include "build/build_config.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/test/base/in_process_browser_test.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,6 +30,26 @@ } // namespace +class MockTabStatsTrackerDelegate : public TabStatsTrackerDelegate { + public: + MockTabStatsTrackerDelegate() = default; + ~MockTabStatsTrackerDelegate() override = default; + +#if defined(OS_WIN) + OcclusionStatusMap CallComputeNativeWindowOcclusionStatus( + std::vector<aura::WindowTreeHost*> hosts) override { + return mock_occlusion_results_; + } + + void SetMockOcclusionResults(OcclusionStatusMap mock_occlusion_results) { + mock_occlusion_results_ = mock_occlusion_results; + } + + private: + OcclusionStatusMap mock_occlusion_results_; +#endif +}; + class TabStatsTrackerBrowserTest : public InProcessBrowserTest { public: TabStatsTrackerBrowserTest() : tab_stats_tracker_(nullptr) {} @@ -36,6 +60,9 @@ } protected: + // Used to make sure that the metrics are reported properly. + base::HistogramTester histogram_tester_; + TabStatsTracker* tab_stats_tracker_; DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerBrowserTest); @@ -134,4 +161,85 @@ EXPECT_EQ(0U, interval_map->size()); } +#if defined(OS_WIN) +IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest, + TestCalculateAndRecordNativeWindowVisibilities) { + std::unique_ptr<MockTabStatsTrackerDelegate> mock_delegate = + std::make_unique<MockTabStatsTrackerDelegate>(); + + // Maintaining this reference to |mock_delegate| is safe because the + // TabStatsTracker will outlive this test class. + MockTabStatsTrackerDelegate* mock_delegate_raw = mock_delegate.get(); + tab_stats_tracker_->SetDelegateForTesting(std::move(mock_delegate)); + + TabStatsTrackerDelegate::OcclusionStatusMap mock_occlusion_results; + + mock_delegate_raw->SetMockOcclusionResults(mock_occlusion_results); + + tab_stats_tracker_->CalculateAndRecordNativeWindowVisibilities(); + + // There should be 1 entry for each zero window bucket. + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded", + 0, 1); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Visible", + 0, 1); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Hidden", + 0, 1); + + // There should be no entries in the 1 window bucket. + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded", + 1, 0); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Visible", + 1, 0); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Hidden", + 1, 0); + + // Create a browser for each aura::Window::OcclusionState. + mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile()) + ->window() + ->GetNativeWindow() + ->GetHost()] = + aura::Window::OcclusionState::HIDDEN; + mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile()) + ->window() + ->GetNativeWindow() + ->GetHost()] = + aura::Window::OcclusionState::VISIBLE; + mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile()) + ->window() + ->GetNativeWindow() + ->GetHost()] = + aura::Window::OcclusionState::OCCLUDED; + + mock_delegate_raw->SetMockOcclusionResults(mock_occlusion_results); + + // There should now be 1 entry for each 1 window bucket. + tab_stats_tracker_->CalculateAndRecordNativeWindowVisibilities(); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded", + 1, 1); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Visible", + 1, 1); + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Hidden", + 1, 1); + + mock_occlusion_results.clear(); + + // Create 5 aura::Window::OcclusionState browsers. + for (int count = 0; count < 5; count++) { + mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile()) + ->window() + ->GetNativeWindow() + ->GetHost()] = + aura::Window::OcclusionState::OCCLUDED; + } + + mock_delegate_raw->SetMockOcclusionResults(mock_occlusion_results); + tab_stats_tracker_->CalculateAndRecordNativeWindowVisibilities(); + + // There should be 1 entry in the 5 window occluded bucket. + histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded", + 5, 1); +} + +#endif // defined(OS_WIN) } // namespace metrics
diff --git a/chrome/browser/metrics/tab_stats_tracker_delegate.h b/chrome/browser/metrics/tab_stats_tracker_delegate.h new file mode 100644 index 0000000..b93719d5 --- /dev/null +++ b/chrome/browser/metrics/tab_stats_tracker_delegate.h
@@ -0,0 +1,29 @@ +// Copyright 2018 The Chromium 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_METRICS_TAB_STATS_TRACKER_DELEGATE_H_ +#define CHROME_BROWSER_METRICS_TAB_STATS_TRACKER_DELEGATE_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#endif + +class TabStatsTrackerDelegate { + public: + TabStatsTrackerDelegate() {} + virtual ~TabStatsTrackerDelegate() {} + +#if defined(OS_WIN) + using OcclusionStatusMap = + base::flat_map<aura::WindowTreeHost*, aura::Window::OcclusionState>; + + virtual OcclusionStatusMap CallComputeNativeWindowOcclusionStatus( + std::vector<aura::WindowTreeHost*> hosts); +#endif // OS_WIN +}; + +#endif // CHROME_BROWSER_METRICS_TAB_STATS_TRACKER_DELEGATE_H_
diff --git a/chrome/browser/metrics/tab_stats_tracker_delegate_win.cc b/chrome/browser/metrics/tab_stats_tracker_delegate_win.cc new file mode 100644 index 0000000..37c35d1 --- /dev/null +++ b/chrome/browser/metrics/tab_stats_tracker_delegate_win.cc
@@ -0,0 +1,13 @@ +// Copyright 2018 The Chromium 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/metrics/tab_stats_tracker_delegate.h" + +#include "ui/aura_extra/window_occlusion_win.h" + +TabStatsTrackerDelegate::OcclusionStatusMap +TabStatsTrackerDelegate::CallComputeNativeWindowOcclusionStatus( + std::vector<aura::WindowTreeHost*> hosts) { + return aura_extra::ComputeNativeWindowOcclusionStatus(hosts); +}
diff --git a/chrome/browser/metrics/tab_stats_tracker_win.cc b/chrome/browser/metrics/tab_stats_tracker_win.cc new file mode 100644 index 0000000..0d2b94b --- /dev/null +++ b/chrome/browser/metrics/tab_stats_tracker_win.cc
@@ -0,0 +1,70 @@ +// Copyright 2018 The Chromium 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/metrics/tab_stats_tracker.h" + +#include "base/metrics/histogram_macros.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" + +namespace metrics { + +void TabStatsTracker::CalculateAndRecordNativeWindowVisibilities() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + BrowserList* browser_list = BrowserList::GetInstance(); + std::vector<aura::WindowTreeHost*> hosts(browser_list->size()); + + // Get the aura::WindowTreeHost for each Chrome browser. + for (Browser* browser : *browser_list) { + aura::WindowTreeHost* host = + browser->window()->GetNativeWindow()->GetHost(); + hosts.push_back(host); + } + + // Compute native window occlusion if not using mock occlusion results. + TabStatsTrackerDelegate::OcclusionStatusMap native_window_visibilities = + delegate_->CallComputeNativeWindowOcclusionStatus(hosts); + + size_t num_occluded = 0; + size_t num_visible = 0; + size_t num_hidden = 0; + + // Determine the number of Chrome browser windows in each visibility state. + for (auto& window_visibility_pair : native_window_visibilities) { + aura::Window::OcclusionState visibility = window_visibility_pair.second; + + switch (visibility) { + case aura::Window::OcclusionState::OCCLUDED: + num_occluded++; + break; + case aura::Window::OcclusionState::VISIBLE: + num_visible++; + break; + case aura::Window::OcclusionState::HIDDEN: + num_hidden++; + break; + case aura::Window::OcclusionState::UNKNOWN: + break; + } + } + + reporting_delegate_->RecordNativeWindowVisibilities(num_occluded, num_visible, + num_hidden); +} + +void TabStatsTracker::UmaStatsReportingDelegate::RecordNativeWindowVisibilities( + size_t num_occluded, + size_t num_visible, + size_t num_hidden) { + UMA_HISTOGRAM_COUNTS_10000("Windows.NativeWindowVisibility.Occluded", + num_occluded); + UMA_HISTOGRAM_COUNTS_10000("Windows.NativeWindowVisibility.Visible", + num_visible); + UMA_HISTOGRAM_COUNTS_10000("Windows.NativeWindowVisibility.Hidden", + num_hidden); +} + +} // namespace metrics
diff --git a/chrome/browser/notifications/DEPS b/chrome/browser/notifications/DEPS index 795e3dd..b4e1513 100644 --- a/chrome/browser/notifications/DEPS +++ b/chrome/browser/notifications/DEPS
@@ -2,3 +2,9 @@ "+dbus", "+ui/message_center", ] + +specific_include_rules ={ + ".*_unittest\.cc": [ + "+chrome/installer/setup", + ], +}
diff --git a/chrome/browser/notifications/OWNERS b/chrome/browser/notifications/OWNERS index 65f2cd47..340f068 100644 --- a/chrome/browser/notifications/OWNERS +++ b/chrome/browser/notifications/OWNERS
@@ -15,9 +15,7 @@ per-file *_win*=finnur@chromium.org per-file mock_i*=finnur@chromium.org per-file notification_template_builder*=finnur@chromium.org -per-file notification_helper*=chengx@chromium.org -per-file notification_helper*=grt@chromium.org -per-file notification_helper*=robliao@chromium.org +per-file notification_helper*=file://chrome/notification_helper/OWNERS per-file *permission_context*=file://chrome/browser/permissions/PERMISSIONS_OWNERS
diff --git a/chrome/browser/notifications/notification_helper_launches_chrome_unittest.cc b/chrome/browser/notifications/notification_helper_launches_chrome_unittest.cc index 2315ab5..d793754 100644 --- a/chrome/browser/notifications/notification_helper_launches_chrome_unittest.cc +++ b/chrome/browser/notifications/notification_helper_launches_chrome_unittest.cc
@@ -3,11 +3,11 @@ // found in the LICENSE file. // This test file is an evolution of -// notification_helper/notification_helper_process_unittest.cc. In addition to -// testing launching notification_helper.exe by the OS via registry which is -// what notification_helper_process_unittest is all about, this test also tests -// if chrome.exe can be successfully launched by notification_helper.exe via the -// NotificationActivator::Activate function. +// chrome/notification_helper/notification_helper_process_unittest.cc. In +// addition to testing launching notification_helper.exe by the OS via registry +// which is what notification_helper_process_unittest is all about, this test +// also tests if chrome.exe can be successfully launched by +// notification_helper.exe via the NotificationActivator::Activate function. // // This test is compiled into unit_tests.exe under chrome rather than // notification_helper_unittests.exe. This is because unit_tests.exe has data @@ -22,6 +22,7 @@ #include "base/base_paths.h" #include "base/containers/flat_map.h" #include "base/files/file_path.h" +#include "base/memory/ptr_util.h" #include "base/path_service.h" #include "base/process/kill.h" #include "base/process/process.h" @@ -29,11 +30,11 @@ #include "base/stl_util.h" #include "base/strings/string16.h" #include "base/test/test_timeouts.h" -#include "base/win/scoped_winrt_initializer.h" +#include "base/win/scoped_com_initializer.h" #include "base/win/windows_version.h" #include "chrome/install_static/install_util.h" +#include "chrome/installer/setup/install_worker.h" #include "chrome/installer/util/install_util.h" -#include "chrome/installer/util/registry_key_backup.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/work_item.h" #include "chrome/installer/util/work_item_list.h" @@ -189,84 +190,52 @@ class NotificationHelperLaunchesChrome : public testing::Test { protected: - NotificationHelperLaunchesChrome() - : toast_activator_reg_path_(InstallUtil::GetToastActivatorRegistryPath()), - root_(HKEY_CURRENT_USER) {} + NotificationHelperLaunchesChrome() : root_(HKEY_CURRENT_USER) {} ~NotificationHelperLaunchesChrome() override = default; void SetUp() override { - // Back up the existing registration. - ASSERT_TRUE(backup_.Initialize(root_, toast_activator_reg_path_.c_str(), - WorkItem::kWow64Default)); - ASSERT_NO_FATAL_FAILURE(RegisterServer()); } void TearDown() override { ASSERT_NO_FATAL_FAILURE(UnregisterServer()); - - // Restore the registration. - ASSERT_TRUE(backup_.WriteTo(root_, toast_activator_reg_path_.c_str(), - WorkItem::kWow64Default)); } private: // Registers notification_helper.exe as the server. - // TODO(chengx): Remove duplicated code by reusing the code in - // chrome/installer/setup/install_worker.cc void RegisterServer() { - std::unique_ptr<WorkItemList> list(WorkItem::CreateWorkItemList()); - - // Delete the old registration to ensure a clean environment for server - // registration. This is okay because we have already backed up the existing - // registration in SetUp(), and will restore it in TearDown(). - list->AddDeleteRegKeyWorkItem(root_, toast_activator_reg_path_, - WorkItem::kWow64Default); + ASSERT_TRUE(scoped_com_initializer_.Succeeded()); // Notification_helper.exe is in the build output directory next to this // test executable, as the test build target has a data_deps dependency on // it. base::FilePath dir_exe; ASSERT_TRUE(PathService::Get(base::DIR_EXE, &dir_exe)); - base::FilePath notification_helper = + base::FilePath notification_helper_path = dir_exe.Append(installer::kNotificationHelperExe); - base::string16 toast_activator_server_path = toast_activator_reg_path_; - toast_activator_server_path.append(L"\\LocalServer32"); + work_item_list_ = base::WrapUnique(WorkItem::CreateWorkItemList()); - // Command-line featuring the quoted path to the exe. - base::string16 command(1, L'"'); - command.append(notification_helper.value()).append(1, L'"'); + installer::AddNativeNotificationWorkItems(root_, notification_helper_path, + work_item_list_.get()); - list->AddCreateRegKeyWorkItem(root_, toast_activator_server_path, - WorkItem::kWow64Default); - - list->AddSetRegValueWorkItem(root_, toast_activator_server_path, - WorkItem::kWow64Default, L"", command, true); - - list->AddSetRegValueWorkItem(root_, toast_activator_server_path, - WorkItem::kWow64Default, L"ServerExecutable", - notification_helper.value(), true); - - ASSERT_TRUE(list->Do()); + ASSERT_TRUE(work_item_list_->Do()); } - // Unregisters the server by deleting the registry key installed during the - // test. + // Unregisters the server by rolling back the work item list. void UnregisterServer() { - ASSERT_TRUE(InstallUtil::DeleteRegistryKey(root_, toast_activator_reg_path_, - WorkItem::kWow64Default)); + if (work_item_list_) + work_item_list_->Rollback(); } - // Path to the toast activator registry. - const base::string16 toast_activator_reg_path_; - // Predefined handle to the registry. const HKEY root_; - // Backup of the deleted registry. - RegistryKeyBackup backup_; + // A list of work items on the registry. + std::unique_ptr<WorkItemList> work_item_list_; + + base::win::ScopedCOMInitializer scoped_com_initializer_; DISALLOW_COPY_AND_ASSIGN(NotificationHelperLaunchesChrome); }; @@ -277,9 +246,6 @@ if (base::win::GetVersion() < base::win::VERSION_WIN8) return; - base::win::ScopedWinrtInitializer winrt_initializer; - ASSERT_TRUE(winrt_initializer.Succeeded()); - // There isn't a way to directly correlate the notification_helper.exe server // to this test. So we need to hunt for the server. base::Process notification_helper_process =
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc index c841dbe..e5ff71e 100644 --- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc +++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -457,7 +457,8 @@ params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms); - auto waiter = std::make_unique<PageLoadMetricsWaiter>(params.target_contents); + auto waiter = std::make_unique<PageLoadMetricsWaiter>( + params.navigated_or_inserted_contents); waiter->AddPageExpectation(TimingField::LOAD_EVENT); waiter->Wait();
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc index 1de83933..b1e0f0e 100644 --- a/chrome/browser/password_manager/password_manager_browsertest.cc +++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -35,6 +35,7 @@ #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/common/password_form.h" @@ -70,27 +71,26 @@ namespace { -// Fixture with the Form-Not-Secure in-field warning feature enabled. -class PasswordManagerBrowserTestWarning - : public PasswordManagerBrowserTestBase { +// Test params: +// - bool popup_views_enabled: whether feature AutofillExpandedPopupViews +// is enabled for testing. +class PasswordManagerBrowserTestWithViewsFeature + : public PasswordManagerBrowserTestBase, + public ::testing::WithParamInterface<bool> { public: - PasswordManagerBrowserTestWarning() {} + PasswordManagerBrowserTestWithViewsFeature() = default; + ~PasswordManagerBrowserTestWithViewsFeature() override = default; void SetUpCommandLine(base::CommandLine* command_line) override { PasswordManagerBrowserTestBase::SetUpCommandLine(command_line); - // We need to set the feature state before the render process is created, - // in order for it to inherit the feature state from the browser process. - // SetUp() runs too early, and SetUpOnMainThread() runs too late. - scoped_feature_list_.InitAndEnableFeature( - security_state::kHttpFormWarningFeature); + const bool popup_views_enabled = GetParam(); + scoped_feature_list_.InitWithFeatureState( + autofill::kAutofillExpandedPopupViews, popup_views_enabled); } - protected: - base::test::ScopedFeatureList scoped_feature_list_; - private: - DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTestWarning); + base::test::ScopedFeatureList scoped_feature_list_; }; class MockLoginModelObserver : public password_manager::LoginModelObserver { @@ -234,7 +234,9 @@ namespace password_manager { // Actual tests --------------------------------------------------------------- -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForNormalSubmit) { + +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + PromptForNormalSubmit) { NavigateToFile("/password/password_form.html"); // Fill a form and submit through a <input type="submit"> button. Nothing @@ -257,26 +259,26 @@ base::ASCIIToUTF16("random")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptIfFormReappeared) { NavigateToFile("/password/failed.html"); TestPromptNotShown("normal form", WebContents(), RenderViewHost()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptIfFormReappearedWithPartsHidden) { NavigateToFile("/password/failed_partly_visible.html"); TestPromptNotShown("partly visible form", WebContents(), RenderViewHost()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptIfFormReappearedInputOutsideFor) { NavigateToFile("/password/failed_input_outside.html"); TestPromptNotShown("form with input outside", WebContents(), RenderViewHost()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptIfPasswordFormManagerDestroyed) { NavigateToFile("/password/password_form.html"); // Simulate the Credential Manager API essentially destroying all the @@ -298,7 +300,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForSubmitWithSameDocumentNavigation) { NavigateToFile("/password/password_navigate_before_submit.html"); @@ -316,7 +318,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, LoginSuccessWithUnrelatedForm) { // Log in, see a form on the landing page. That form is not related to the // login form (=has different input fields), so we should offer saving the @@ -335,7 +337,8 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, LoginFailed) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + LoginFailed) { // Log in, see a form on the landing page. That form is not related to the // login form (=has a different action), so we should offer saving the // password. @@ -353,7 +356,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, Redirects) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, Redirects) { NavigateToFile("/password/password_form.html"); // Fill a form and submit through a <input type="submit"> button. The form @@ -377,7 +380,7 @@ EXPECT_TRUE(bubble_observer.IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForSubmitUsingJavaScript) { NavigateToFile("/password/password_form.html"); @@ -396,7 +399,8 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForDynamicForm) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + PromptForDynamicForm) { // Adding a PSL matching form is a workaround explained later. scoped_refptr<password_manager::TestPasswordStore> password_store = static_cast<password_manager::TestPasswordStore*>( @@ -437,7 +441,8 @@ EXPECT_TRUE(BubbleObserver(WebContents()).IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, NoPromptForNavigation) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + NoPromptForNavigation) { NavigateToFile("/password/password_form.html"); // Don't fill the password form, just navigate away. Shouldn't prompt. @@ -450,7 +455,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForSubFrameNavigation) { NavigateToFile("/password/multi_frames.html"); @@ -475,7 +480,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForSameFormWithDifferentAction) { // Log in, see a form on the landing page. That form is related to the login // form (has a different action but has same input fields), so we should not @@ -493,7 +498,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForActionMutation) { NavigateToFile("/password/password_form_action_mutation.html"); @@ -514,7 +519,7 @@ EXPECT_FALSE(prompt_observer.IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForFormWithEnteredUsername) { // Log in, see a form on the landing page. That form is not related to the // login form but has the same username as was entered previously, so we @@ -532,7 +537,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForDifferentFormWithEmptyAction) { // Log in, see a form on the landing page. That form is not related to the // signin form. The signin and the form on the landing page have empty @@ -550,7 +555,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptAfterSubmitWithSubFrameNavigation) { NavigateToFile("/password/multi_frames.html"); @@ -576,8 +581,8 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForFailedLoginFromMainFrameWithMultiFramesSameDocument) { NavigateToFile("/password/multi_frames.html"); @@ -596,8 +601,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForFailedLoginFromSubFrameWithMultiFramesSameDocument) { NavigateToFile("/password/multi_frames.html"); @@ -619,7 +624,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForXHRSubmit) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + PromptForXHRSubmit) { NavigateToFile("/password/password_xhr_submit.html"); // Verify that we show the save password prompt if a form returns false @@ -638,7 +644,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForXHRWithoutOnSubmit) { NavigateToFile("/password/password_xhr_submit.html"); @@ -656,7 +662,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForXHRWithNewPasswordsWithoutOnSubmit) { NavigateToFile("/password/password_xhr_submit.html"); @@ -677,7 +683,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForXHRSubmitWithoutNavigation) { NavigateToFile("/password/password_xhr_submit.html"); @@ -707,7 +713,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForXHRSubmitWithoutNavigation_SignupForm) { NavigateToFile("/password/password_xhr_submit.html"); @@ -738,7 +744,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForXHRSubmitWithoutNavigationWithUnfilledForm) { NavigateToFile("/password/password_xhr_submit.html"); @@ -764,8 +770,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForXHRSubmitWithoutNavigationWithUnfilledForm_SignupForm) { NavigateToFile("/password/password_xhr_submit.html"); @@ -791,7 +797,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForFetchSubmit) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + PromptForFetchSubmit) { NavigateToFile("/password/password_fetch_submit.html"); // Verify that we show the save password prompt if a form returns false @@ -810,7 +817,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForFetchWithoutOnSubmit) { NavigateToFile("/password/password_fetch_submit.html"); @@ -828,7 +835,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForFetchWithNewPasswordsWithoutOnSubmit) { NavigateToFile("/password/password_fetch_submit.html"); @@ -849,7 +856,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForFetchSubmitWithoutNavigation) { NavigateToFile("/password/password_fetch_submit.html"); @@ -879,7 +886,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForFetchSubmitWithoutNavigation_SignupForm) { NavigateToFile("/password/password_fetch_submit.html"); @@ -910,8 +917,8 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForFetchSubmitWithoutNavigationWithUnfilledForm) { NavigateToFile("/password/password_fetch_submit.html"); @@ -937,8 +944,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForFetchSubmitWithoutNavigationWithUnfilledForm_SignupForm) { NavigateToFile("/password/password_fetch_submit.html"); @@ -964,7 +971,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, NoPromptIfLinkClicked) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + NoPromptIfLinkClicked) { NavigateToFile("/password/password_form.html"); // Verify that if the user takes a direct action to leave the page, we don't @@ -981,7 +989,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, VerifyPasswordGenerationUpload) { // Prevent Autofill requests from actually going over the wire. net::TestURLFetcherFactory factory; @@ -1044,7 +1052,7 @@ autofill::test::ReenableSystemServices(); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForSubmitFromIframe) { NavigateToFile("/password/password_submit_from_iframe.html"); @@ -1066,7 +1074,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForInputElementWithoutName) { // Check that the prompt is shown for forms where input elements lack the // "name" attribute but the "id" is present. @@ -1084,7 +1092,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForInputElementWithoutId) { // Check that the prompt is shown for forms where input elements lack the // "id" attribute but the "name" attribute is present. @@ -1102,7 +1110,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForInputElementWithoutIdAndName) { // Check that prompt is shown for forms where the input fields lack both // the "id" and the "name" attributes. @@ -1130,7 +1138,7 @@ } // Test for checking that no prompt is shown for URLs with file: scheme. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForFileSchemeURLs) { GURL url = GetFileURL("password_form.html"); ui_test_utils::NavigateToURL(browser(), url); @@ -1147,7 +1155,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForLandingPageWithHTTPErrorStatusCode) { // Check that no prompt is shown for forms where the landing page has // HTTP status 404. @@ -1165,7 +1173,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, DeleteFrameBeforeSubmit) { NavigateToFile("/password/multi_frames.html"); @@ -1192,7 +1200,7 @@ // The only thing we check here is that there is no use-after-free reported. } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, UsernameAndPasswordValueAccessible) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -1238,7 +1246,7 @@ WaitForElementValue("password_field", "12345"); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PasswordValueAccessibleOnSubmit) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -1280,7 +1288,7 @@ } // Test fix for crbug.com/338650. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, DontPromptForPasswordFormWithDefaultValue) { NavigateToFile("/password/password_form_with_default_value.html"); @@ -1294,7 +1302,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, DontPromptForPasswordFormWithReadonlyPasswordField) { NavigateToFile("/password/password_form_with_password_readonly.html"); @@ -1312,7 +1320,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) { NavigateToFile("/password/password_form.html"); @@ -1330,7 +1338,8 @@ } // Test fix for crbug.com/368690. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, NoPromptWhenReloading) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + NoPromptWhenReloading) { NavigateToFile("/password/password_form.html"); std::string fill = @@ -1351,7 +1360,7 @@ // Test that if a form gets dynamically added between the form parsing and // rendering, and while the main frame still loads, it still is registered, and // thus saving passwords from it works. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, FormsAddedBetweenParsingAndRendering) { NavigateToFile("/password/between_parsing_and_rendering.html"); @@ -1368,7 +1377,7 @@ // Test that if a hidden form gets dynamically added between the form parsing // and rendering, it still is registered, and autofilling works. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, HiddenFormAddedBetweenParsingAndRendering) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -1401,7 +1410,8 @@ // the form. // The fact that the form is hidden isn't super important but reproduces the // actual bug. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, SlowPageFill) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + SlowPageFill) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = static_cast<password_manager::TestPasswordStore*>( @@ -1439,7 +1449,8 @@ // does not think that there were SSL errors on the current page. The test opens // a new tab with a URL for which the embedded test server issues a basic auth // challenge. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, NoLastLoadGoodLastLoad) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + NoLastLoadGoodLastLoad) { // We must use a new test server here because embedded_test_server() is // already started at this point and adding the request handler to it would // not be thread safe. @@ -1501,7 +1512,7 @@ // manage to detect the new one and create a complete matching // PasswordFormManager. Otherwise, the all-but-action matching PFM should be // used. Regardless of the internals the user sees the bubble in 100% cases. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PreferPasswordFormManagerWhichFinishedMatching) { NavigateToFile("/password/create_form_copy_on_submit.html"); @@ -1522,8 +1533,8 @@ // Test that if login fails and content server pushes a different login form // with action URL having different schemes. Heuristic shall be able // identify such cases and *shall not* prompt to save incorrect password. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpToHttps) { std::string path = "/password/separate_login_form_with_onload_submit_script.html"; @@ -1541,8 +1552,8 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpsToHttp) { // This test case cannot inject the scripts via content::ExecuteScript() in // files served through HTTPS. Therefore the scripts are made part of the HTML @@ -1565,8 +1576,8 @@ // Tests whether a attempted submission of a malicious credentials gets blocked. // This simulates a case which is described in http://crbug.com/571580. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, NoPromptForSeperateLoginFormWhenSwitchingFromHttpsToHttp) { std::string path = "/password/password_form.html"; GURL https_url(https_test_server().GetURL(path)); @@ -1627,7 +1638,7 @@ } // Tests that after HTTP -> HTTPS migration the credential is autofilled. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, HttpMigratedCredentialAutofilled) { // Add an http credential to the password store. GURL https_origin = https_test_server().base_url(); @@ -1665,7 +1676,7 @@ // Tests that obsolete HTTP credentials are moved when a site migrated to HTTPS // and has HSTS enabled. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ObsoleteHttpCredentialMovedOnMigrationToHstsSite) { // Add an http credential to the password store. GURL https_origin = https_test_server().base_url(); @@ -1721,7 +1732,7 @@ password_store->stored_passwords().at(https_origin.spec()).empty()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptWhenPasswordFormWithoutUsernameFieldSubmitted) { scoped_refptr<password_manager::TestPasswordStore> password_store = static_cast<password_manager::TestPasswordStore*>( @@ -1748,7 +1759,7 @@ EXPECT_FALSE(password_store->IsEmpty()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForPasswordFormWithoutUsernameField) { std::string submit = "document.getElementById('password').value = 'mypassword';" @@ -1759,7 +1770,7 @@ // Test that if a form gets autofilled, then it gets autofilled on re-creation // as well. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ReCreatedFormsGetFilled) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -1794,7 +1805,7 @@ // Test that if the same dynamic form is created multiple times then all of them // are autofilled and no unnecessary PasswordStore requests are fired. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, DuplicateFormsGetFilled) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -1826,7 +1837,7 @@ WaitForJsElementValue("document.body.children[1].children[1]", "random"); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForPushStateWhenFormDisappears) { NavigateToFile("/password/password_push_state.html"); @@ -1853,7 +1864,7 @@ // Similar to the case above, but this time the form persists after // 'history.pushState()'. And save password prompt should not show up // in this case. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoPromptForPushStateWhenFormPersists) { NavigateToFile("/password/password_push_state.html"); @@ -1876,7 +1887,7 @@ // The password manager should distinguish forms with empty actions. After // successful login, the login form disappears, but the another one shouldn't be // recognized as the login form. The save prompt should appear. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForPushStateWhenFormWithEmptyActionDisappears) { NavigateToFile("/password/password_push_state.html"); @@ -1896,7 +1907,7 @@ // Similar to the case above, but this time the form persists after // 'history.pushState()'. The password manager should find the login form even // if the action of the form is empty. Save password prompt should not show up. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForPushStateWhenFormWithEmptyActionPersists) { NavigateToFile("/password/password_push_state.html"); @@ -1917,8 +1928,8 @@ // Current and target URLs contain different parameters and references. This // test checks that parameters and references in origins are ignored for // form origin comparison. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, PromptForPushStateWhenFormDisappears_ParametersInOrigins) { NavigateToFile("/password/password_push_state.html?login#r"); @@ -1940,9 +1951,8 @@ // 'history.pushState()'. The password manager should find the login form even // if target and current URLs contain different parameters or references. // Save password prompt should not show up. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, - PromptForPushStateWhenFormPersists_ParametersInOrigins) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + PromptForPushStateWhenFormPersists_ParametersInOrigins) { NavigateToFile("/password/password_push_state.html?login#r"); NavigationObserver observer(WebContents()); @@ -1960,7 +1970,7 @@ EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, InFrameNavigationDoesNotClearPopupState) { scoped_refptr<password_manager::TestPasswordStore> password_store = static_cast<password_manager::TestPasswordStore*>( @@ -2017,7 +2027,7 @@ observing_autofill_client->WaitForAutofillPopup(); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ChangePwdFormBubbleShown) { NavigateToFile("/password/password_form.html"); @@ -2035,7 +2045,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ChangePwdFormPushStateBubbleShown) { NavigateToFile("/password/password_push_state.html"); @@ -2054,7 +2064,8 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, NoPromptOnBack) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + NoPromptOnBack) { // Go to a successful landing page through submitting first, so that it is // reachable through going back, and the remembered page transition is form // submit. There is no need to submit non-empty strings. @@ -2087,7 +2098,7 @@ } // Regression test for http://crbug.com/452306 -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ChangingTextToPasswordFieldOnSignupForm) { NavigateToFile("/password/signup_form.html"); @@ -2108,7 +2119,7 @@ } // Regression test for http://crbug.com/451631 -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, SavingOnManyPasswordFieldsTest) { // Simulate Macy's registration page, which contains the normal 2 password // fields for confirming the new password plus 2 more fields for security @@ -2131,7 +2142,7 @@ EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically()); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, SaveWhenIFrameDestroyedOnFormSubmit) { NavigateToFile("/password/frame_detached_on_submit.html"); @@ -2160,7 +2171,7 @@ // Tests that if a site embeds the login and signup forms into one <form>, the // login form still gets autofilled. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForLoginSignupForm) { std::string submit = "document.getElementById('username').value = 'myusername';" @@ -2172,7 +2183,7 @@ // Check that we can fill in cases where <base href> is set and the action of // the form is not set. Regression test for https://crbug.com/360230. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, BaseTagWithNoActionTest) { std::string submit = "document.getElementById('username_field').value = 'myusername';" @@ -2185,7 +2196,7 @@ // Check that a username and password are filled into forms in iframes // that don't share the security origin with the main frame, but have PSL // matched origins. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PSLMatchedCrossSiteFillTest) { GURL main_frame_url = embedded_test_server()->GetURL( "www.foo.com", "/password/password_form_in_crosssite_iframe.html"); @@ -2267,7 +2278,7 @@ // Check that a username and password are not filled in forms in iframes // that don't have PSL matched origins. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PSLUnMatchedCrossSiteFillTest) { GURL main_frame_url = embedded_test_server()->GetURL( "www.foo.com", "/password/password_form_in_crosssite_iframe.html"); @@ -2347,7 +2358,7 @@ // Check that a password form in an iframe of same origin will not be // filled in until user interact with the iframe. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, SameOriginIframeAutoFillTest) { // Visit the sign-up form to store a password for autofill later NavigateToFile("/password/password_form_in_same_origin_iframe.html"); @@ -2401,7 +2412,8 @@ WaitForElementValue("iframe", "password_field", "pa55w0rd"); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, NoFormElementTest) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + NoFormElementTest) { std::string submit = "document.getElementById('username_field').value = 'myusername';" "document.getElementById('password_field').value = 'mypassword';" @@ -2415,7 +2427,7 @@ // The password manager driver will kill processes when they try to access // passwords of sites other than the site the process is dedicated to, under // site isolation. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, CrossSitePasswordEnforcement) { // The code under test is only active under site isolation. if (!content::AreAllSitesIsolatedForTesting()) { @@ -2467,7 +2479,7 @@ iframe_killed.Wait(); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ChangePwdNoAccountStored) { NavigateToFile("/password/password_form.html"); @@ -2496,7 +2508,7 @@ base::ASCIIToUTF16("new_pw")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ChangePwd1AccountStored) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -2539,7 +2551,7 @@ base::ASCIIToUTF16("new_pw")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PasswordOverridenUpdateBubbleShown) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -2582,7 +2594,7 @@ base::ASCIIToUTF16("new_pw")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PasswordNotOverridenUpdateBubbleNotShown) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -2614,7 +2626,7 @@ base::ASCIIToUTF16("pw")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, MultiplePasswordsWithPasswordSelectionEnabled) { NavigateToFile("/password/password_form.html"); NavigationObserver observer(WebContents()); @@ -2660,7 +2672,7 @@ base::ASCIIToUTF16("pass1")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ChangePwdWhenTheFormContainNotUsernameTextfield) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -2703,8 +2715,8 @@ // Test whether the password form with the username and password fields having // ambiguity in id attribute gets autofilled correctly. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForPasswordFormWithAmbiguousIdAttribute) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -2732,8 +2744,8 @@ // Test whether the password form having username and password fields without // name and id attribute gets autofilled correctly. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForPasswordFormWithoutNameOrIdAttribute) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -2761,7 +2773,7 @@ // Test whether the change password form having username and password fields // without name and id attribute gets autofilled correctly. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForChangePwdWithEmptyNames) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -2801,8 +2813,8 @@ // Test whether the change password form having username and password fields // with empty names but having |autocomplete='current-password'| gets autofilled // correctly. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForChangePwdWithEmptyNamesAndAutocomplete) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -2839,8 +2851,8 @@ // Test whether the change password form having username and password fields // with empty names but having only new password fields having // |autocomplete='new-password'| atrribute do not get autofilled. -IN_PROC_BROWSER_TEST_F( - PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForChangePwdWithEmptyNamesButOnlyNewPwdField) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -2893,7 +2905,7 @@ // When there are multiple LoginModelObservers (e.g., multiple HTTP auth dialogs // as in http://crbug.com/537823), ensure that credentials from PasswordStore // distributed to them are filtered by the realm. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, BasicAuthSeparateRealms) { // We must use a new test server here because embedded_test_server() is // already started at this point and adding the request handler to it would @@ -2954,7 +2966,7 @@ // user clicks a sign-in button and a hidden passsword form becomes visible. // This test differs from AutofillSuggestionsForProblematicPasswordForm in that // the form is hidden and in that test only some fields are hidden. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsHiddenPasswordForm) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -2982,7 +2994,7 @@ // Test whether the password form with the problematic invisible password field // gets autofilled correctly. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForProblematicPasswordForm) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -3010,7 +3022,7 @@ // Test whether the password form with the problematic invisible password field // in ambiguous password form gets autofilled correctly. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForProblematicAmbiguousPasswordForm) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::PasswordStore> password_store = @@ -3039,7 +3051,8 @@ } // Check that the internals page contains logs from the renderer. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, InternalsPage_Renderer) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + InternalsPage_Renderer) { // Open the internals page. ui_test_utils::NavigateToURLWithDisposition( browser(), GURL("chrome://password-manager-internals"), @@ -3081,7 +3094,8 @@ } // Check that the internals page contains logs from the browser. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, InternalsPage_Browser) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + InternalsPage_Browser) { ui_test_utils::NavigateToURLWithDisposition( browser(), GURL("chrome://password-manager-internals"), WindowOpenDisposition::CURRENT_TAB, @@ -3105,7 +3119,7 @@ // Tests that submitted credentials are saved on a password form without // username element when there are no stored credentials. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PasswordRetryFormSaveNoUsernameCredentials) { // Check that password save bubble is shown. NavigateToFile("/password/password_form.html"); @@ -3126,7 +3140,7 @@ // Tests that no bubble shown when a password form without username submitted // and there is stored credentials with the same password. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PasswordRetryFormNoBubbleWhenPasswordTheSame) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -3160,7 +3174,7 @@ // Tests that the update bubble shown when a password form without username is // submitted and there are stored credentials but with different password. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PasswordRetryFormUpdateBubbleShown) { // At first let us save credentials to the PasswordManager. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -3196,7 +3210,7 @@ base::ASCIIToUTF16("new_pw")); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, NoCrashWhenNavigatingWithOpenAccountPicker) { // Save credentials with 'skip_zero_click'. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -3228,7 +3242,7 @@ // Tests that the prompt to save the password is still shown if the fields have // the "autocomplete" attribute set off. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, PromptForSubmitWithAutocompleteOff) { NavigateToFile("/password/password_autocomplete_off_test.html"); @@ -3246,7 +3260,7 @@ // Tests that password suggestions still work if the fields have the // "autocomplete" attribute set to off. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AutofillSuggestionsForPasswordFormWithAutocompleteOff) { std::string submit = "document.getElementById('username').value = 'temp';" @@ -3257,9 +3271,9 @@ "mypassword"); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, - SkipZeroClickNotToggledAfterSuccessfulSubmissionWithAPI) -{ +IN_PROC_BROWSER_TEST_P( + PasswordManagerBrowserTestWithViewsFeature, + SkipZeroClickNotToggledAfterSuccessfulSubmissionWithAPI) { // Save credentials with 'skip_zero_click' scoped_refptr<password_manager::TestPasswordStore> password_store = static_cast<password_manager::TestPasswordStore*>( @@ -3304,7 +3318,7 @@ EXPECT_TRUE(form.skip_zero_click); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, SkipZeroClickNotToggledAfterSuccessfulAutofill) { // Save credentials with 'skip_zero_click' scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -3347,7 +3361,8 @@ EXPECT_TRUE(form.skip_zero_click); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, ReattachWebContents) { +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, + ReattachWebContents) { auto detached_web_contents = base::WrapUnique(content::WebContents::Create( content::WebContents::CreateParams(WebContents()->GetBrowserContext()))); NavigationObserver observer(detached_web_contents.get()); @@ -3370,7 +3385,7 @@ TabStripModel::ADD_ACTIVE); } -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, FillWhenFormWithHiddenUsername) { // At first let us save a credential to the password store. scoped_refptr<password_manager::TestPasswordStore> password_store = @@ -3402,123 +3417,13 @@ WaitForElementValue("password", "current_username_password"); } -// Verify the Form-Not-Secure warning is shown on a non-secure username field. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestWarning, - ShowFormNotSecureOnUsernameField) { - ASSERT_TRUE( - base::FeatureList::IsEnabled(security_state::kHttpFormWarningFeature)); - - // We need to serve from a non-localhost context for the form to be treated as - // Not Secure. - NavigationObserver observer(WebContents()); - ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL( - "example.com", "/password/password_form.html")); - observer.Wait(); - - // Mock the autofill client. - password_manager::ContentPasswordManagerDriverFactory* driver_factory = - password_manager::ContentPasswordManagerDriverFactory::FromWebContents( - WebContents()); - ObservingAutofillClient::CreateForWebContents(WebContents()); - ObservingAutofillClient* observing_autofill_client = - ObservingAutofillClient::FromWebContents(WebContents()); - password_manager::ContentPasswordManagerDriver* driver = - driver_factory->GetDriverForFrame(RenderViewHost()->GetMainFrame()); - DCHECK(driver); - driver->GetPasswordAutofillManager()->set_autofill_client( - observing_autofill_client); - - ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture( - RenderFrameHost(), - "var inputRect = document.getElementById('username_field_no_name')" - ".getBoundingClientRect();")); - - // Click on the username field to verify the warning is shown. - int top; - ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractInt( - RenderFrameHost(), "window.domAutomationController.send(inputRect.top);", - &top)); - int left; - ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractInt( - RenderFrameHost(), "window.domAutomationController.send(inputRect.left);", - &left)); - - const char kHistogram[] = - "PasswordManager.ShowedFormNotSecureWarningOnCurrentNavigation"; - base::HistogramTester histograms; - - content::SimulateMouseClickAt(WebContents(), 0, - blink::WebMouseEvent::Button::kLeft, - gfx::Point(left + 1, top + 1)); - // Ensure the warning would be shown. - observing_autofill_client->WaitForAutofillPopup(); - // Ensure the histogram was updated. - histograms.ExpectUniqueSample(kHistogram, true, 1); -} - -// Verify the Form-Not-Secure warning is not shown on a non-credential field. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestWarning, - DoNotShowFormNotSecureOnUnrelatedField) { - ASSERT_TRUE( - base::FeatureList::IsEnabled(security_state::kHttpFormWarningFeature)); - - // We need to serve from a non-localhost context for the form to be treated as - // Not Secure. - NavigationObserver observer(WebContents()); - ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL( - "example.com", "/password/password_form.html")); - observer.Wait(); - - // Mock the autofill client. - password_manager::ContentPasswordManagerDriverFactory* driver_factory = - password_manager::ContentPasswordManagerDriverFactory::FromWebContents( - WebContents()); - ObservingAutofillClient::CreateForWebContents(WebContents()); - ObservingAutofillClient* observing_autofill_client = - ObservingAutofillClient::FromWebContents(WebContents()); - password_manager::ContentPasswordManagerDriver* driver = - driver_factory->GetDriverForFrame(RenderViewHost()->GetMainFrame()); - DCHECK(driver); - driver->GetPasswordAutofillManager()->set_autofill_client( - observing_autofill_client); - - ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture( - RenderFrameHost(), - "var inputRect = document.getElementById('ef_extra')" - ".getBoundingClientRect();")); - - // Click on the non-username text field. - int top; - ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractInt( - RenderFrameHost(), "window.domAutomationController.send(inputRect.top);", - &top)); - int left; - ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractInt( - RenderFrameHost(), "window.domAutomationController.send(inputRect.left);", - &left)); - - const char kHistogram[] = - "PasswordManager.ShowedFormNotSecureWarningOnCurrentNavigation"; - base::HistogramTester histograms; - - content::SimulateMouseClickAt(WebContents(), 0, - blink::WebMouseEvent::Button::kLeft, - gfx::Point(left + 1, top + 1)); - // Force a round-trip. - ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(RenderFrameHost(), - "var noop = 'noop';")); - // Ensure the warning was not triggered. - content::RunAllTasksUntilIdle(); - ASSERT_FALSE(observing_autofill_client->popup_shown()); - // Ensure the histogram remains empty. - histograms.ExpectTotalCount(kHistogram, 0); -} - // Harness for showing dialogs as part of the DialogBrowserTest suite. +// Test params: +// - bool popup_views_enabled: whether feature AutofillExpandedPopupViews +// is enabled for testing. class PasswordManagerDialogBrowserTest - : public SupportsTestDialog<PasswordManagerBrowserTestBase> { + : public SupportsTestDialog<PasswordManagerBrowserTestBase>, + public ::testing::WithParamInterface<bool> { public: PasswordManagerDialogBrowserTest() = default; @@ -3529,6 +3434,15 @@ SupportsTestUi::SetUp(); } + void SetUpCommandLine(base::CommandLine* command_line) override { + SupportsTestDialog<PasswordManagerBrowserTestBase>::SetUpCommandLine( + command_line); + + const bool popup_views_enabled = GetParam(); + scoped_feature_list_.InitWithFeatureState( + autofill::kAutofillExpandedPopupViews, popup_views_enabled); + } + void ShowUi(const std::string& name) override { // Note regarding flakiness: LocationBarBubbleDelegateView::ShowForReason() // uses ShowInactive() unless the bubble is invoked with reason == @@ -3553,16 +3467,18 @@ } private: + base::test::ScopedFeatureList scoped_feature_list_; + DISALLOW_COPY_AND_ASSIGN(PasswordManagerDialogBrowserTest); }; -IN_PROC_BROWSER_TEST_F(PasswordManagerDialogBrowserTest, InvokeUi_normal) { +IN_PROC_BROWSER_TEST_P(PasswordManagerDialogBrowserTest, InvokeUi_normal) { ShowAndVerifyUi(); } // Verify that password manager ignores passwords on forms injected into // about:blank frames. See https://crbug.com/756587. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AboutBlankFramesAreIgnored) { // Start from a page without a password form. NavigateToFile("/password/other.html"); @@ -3594,7 +3510,7 @@ // Verify that password manager ignores passwords on forms injected into // about:blank popups. See https://crbug.com/756587. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, AboutBlankPopupsAreIgnored) { // Start from a page without a password form. NavigateToFile("/password/other.html"); @@ -3633,7 +3549,7 @@ // Verify that previously saved passwords for about:blank frames are not used // for autofill. See https://crbug.com/756587. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, ExistingAboutBlankPasswordsAreNotUsed) { scoped_refptr<password_manager::TestPasswordStore> password_store = static_cast<password_manager::TestPasswordStore*>( @@ -3695,23 +3611,33 @@ EXPECT_TRUE(frame->IsRenderFrameLive()); } +// Test params: +// - bool popup_views_enabled: whether feature AutofillExpandedPopupViews +// is enabled for testing. class SitePerProcessPasswordManagerBrowserTest - : public PasswordManagerBrowserTestBase { + : public PasswordManagerBrowserTestBase, + public ::testing::WithParamInterface<bool> { public: SitePerProcessPasswordManagerBrowserTest() {} void SetUpCommandLine(base::CommandLine* command_line) override { PasswordManagerBrowserTestBase::SetUpCommandLine(command_line); content::IsolateAllSitesForTesting(command_line); + + const bool popup_views_enabled = GetParam(); + scoped_feature_list_.InitWithFeatureState( + autofill::kAutofillExpandedPopupViews, popup_views_enabled); } private: + base::test::ScopedFeatureList scoped_feature_list_; + DISALLOW_COPY_AND_ASSIGN(SitePerProcessPasswordManagerBrowserTest); }; // Verify that there is no renderer kill when filling out a password on a // subframe with a data: URL. -IN_PROC_BROWSER_TEST_F(SitePerProcessPasswordManagerBrowserTest, +IN_PROC_BROWSER_TEST_P(SitePerProcessPasswordManagerBrowserTest, NoRendererKillWithDataURLFrames) { // Start from a page without a password form. NavigateToFile("/password/other.html"); @@ -3745,7 +3671,7 @@ // Test that for HTTP auth (i.e., credentials not put through web forms) the // password manager works even though it should be disabled on the previous // page. -IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, +IN_PROC_BROWSER_TEST_P(PasswordManagerBrowserTestWithViewsFeature, CorrectEntryForHttpAuth) { // The embedded_test_server() is already started at this point and adding the // request handler to it would not be thread safe. Therefore, use a new @@ -3794,4 +3720,16 @@ EXPECT_TRUE(bubble_observer.IsSavePromptShownAutomatically()); } +INSTANTIATE_TEST_CASE_P(All, + PasswordManagerBrowserTestWithViewsFeature, + /*popup_views_enabled=*/::testing::Bool()); + +INSTANTIATE_TEST_CASE_P(All, + PasswordManagerDialogBrowserTest, + /*popup_views_enabled=*/::testing::Bool()); + +INSTANTIATE_TEST_CASE_P(All, + SitePerProcessPasswordManagerBrowserTest, + /*popup_views_enabled=*/::testing::Bool()); + } // namespace password_manager
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 40bdae8..d0e4d8e 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -135,7 +135,7 @@ #include "chrome/browser/extensions/ntp_overridden_bubble_delegate.h" #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h" #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h" -#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" +#include "chrome/browser/ui/webui/extensions/extensions_ui.h" #include "extensions/browser/api/audio/audio_api.h" #include "extensions/browser/api/runtime/runtime_api.h" #include "extensions/browser/extension_prefs.h" @@ -529,6 +529,7 @@ extensions::ActivityLog::RegisterProfilePrefs(registry); extensions::AudioAPI::RegisterUserPrefs(registry); extensions::ExtensionPrefs::RegisterProfilePrefs(registry); + extensions::ExtensionsUI::RegisterProfilePrefs(registry); extensions::launch_util::RegisterProfilePrefs(registry); extensions::NtpOverriddenBubbleDelegate::RegisterPrefs(registry); extensions::RuntimeAPI::RegisterPrefs(registry); @@ -571,7 +572,6 @@ app_list::AppListSyncableService::RegisterProfilePrefs(registry); #endif extensions::CommandService::RegisterProfilePrefs(registry); - extensions::ExtensionSettingsHandler::RegisterProfilePrefs(registry); extensions::TabsCaptureVisibleTabFunction::RegisterProfilePrefs(registry); NewTabUI::RegisterProfilePrefs(registry); PepperFlashSettingsManager::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index dede39e0..a54aa04 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc
@@ -290,11 +290,27 @@ } } +PrerenderManager::Params::Params(NavigateParams* params, + content::WebContents* contents_being_navigated) + : uses_post(params->uses_post), + extra_headers(params->extra_headers), + should_replace_current_entry(params->should_replace_current_entry), + contents_being_navigated(contents_being_navigated) {} + +PrerenderManager::Params::Params(bool uses_post, + const std::string& extra_headers, + bool should_replace_current_entry, + content::WebContents* contents_being_navigated) + : uses_post(uses_post), + extra_headers(extra_headers), + should_replace_current_entry(should_replace_current_entry), + contents_being_navigated(contents_being_navigated) {} + bool PrerenderManager::MaybeUsePrerenderedPage(const GURL& url, - NavigateParams* params) { + Params* params) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - WebContents* web_contents = params->target_contents; + WebContents* web_contents = params->contents_being_navigated; DCHECK(!IsWebContentsPrerendering(web_contents, nullptr)); // Don't prerender if the navigation involves some special parameters that @@ -328,7 +344,7 @@ return false; // Record the new target_contents for the callers. - params->target_contents = new_web_contents; + params->replaced_contents = new_web_contents; return true; }
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index b117e9b..10f37d8f 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h
@@ -154,11 +154,35 @@ // Cancels all active prerenders. void CancelAllPrerenders(); + // Wraps input and output parameters to MaybeUsePrerenderedPage. + struct Params { + Params(NavigateParams* params, + content::WebContents* contents_being_navigated); + Params(bool uses_post, + const std::string& extra_headers, + bool should_replace_current_entry, + content::WebContents* contents_being_navigated); + + // Input parameters. + const bool uses_post; + const std::string extra_headers; + const bool should_replace_current_entry; + content::WebContents* const contents_being_navigated; + + // Output parameters. + content::WebContents* replaced_contents = nullptr; + }; + // If |url| matches a valid prerendered page and |params| are compatible, try - // to swap it and merge browsing histories. Returns |true| and updates - // |params->target_contents| if a prerendered page is swapped in, |false| - // otherwise. - bool MaybeUsePrerenderedPage(const GURL& url, NavigateParams* params); + // to swap it and merge browsing histories. + // + // Returns true if a prerendered page is swapped in. When this happens, the + // PrerenderManager has already swapped out |contents_being_navigated| with + // |replaced_contents| in the WebContents container [e.g. TabStripModel on + // desktop]. + // + // Returns false if nothing is swapped. + bool MaybeUsePrerenderedPage(const GURL& url, Params* params); // Moves a PrerenderContents to the pending delete list from the list of // active prerenders when prerendering should be cancelled.
diff --git a/chrome/browser/previews/previews_browsertest.cc b/chrome/browser/previews/previews_browsertest.cc index 9793973..271e3c3 100644 --- a/chrome/browser/previews/previews_browsertest.cc +++ b/chrome/browser/previews/previews_browsertest.cc
@@ -142,15 +142,16 @@ // Previews InfoBar (which these tests triggers) does not work on Mac. // See crbug.com/782322 for detail. -// Also occasional flakes on win7 (crbug.com/789542). -#if defined(OS_MACOSX) || defined(OS_WIN) +// Also occasional flakes on win7 (crbug.com/789542) and Ubuntu 16.04 +// (crbug.com/831838) +#if defined(OS_ANDROID) #define MAYBE_NoScriptPreviewsEnabled DISABLED_NoScriptPreviewsEnabled #define MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps \ - DISABLED_NoScriptPreviewsEnabledHttpRedirectToHttps + NoScriptPreviewsEnabledHttpRedirectToHttps #else #define MAYBE_NoScriptPreviewsEnabled NoScriptPreviewsEnabled #define MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps \ - NoScriptPreviewsEnabledHttpRedirectToHttps + DISABLED_NoScriptPreviewsEnabledHttpRedirectToHttps #endif // Loads a webpage that has both script and noscript tags and also requests @@ -246,13 +247,14 @@ // Previews InfoBar (which this test triggers) does not work on Mac. // See crbug.com/782322 for detail. -// Also occasional flakes on win7 (crbug.com/789948). -#if defined(OS_MACOSX) || defined(OS_WIN) -#define MAYBE_NoScriptPreviewsEnabledByWhitelist \ - DISABLED_NoScriptPreviewsEnabledByWhitelist -#else +// Also occasional flakes on win7 (crbug.com/789948) and Ubuntu 16.04 +// (crbug.com/831838) +#if defined(OS_ANDROID) #define MAYBE_NoScriptPreviewsEnabledByWhitelist \ NoScriptPreviewsEnabledByWhitelist +#else +#define MAYBE_NoScriptPreviewsEnabledByWhitelist \ + DISABLED_NoScriptPreviewsEnabledByWhitelist #endif IN_PROC_BROWSER_TEST_F(PreviewsOptimizationGuideBrowserTest,
diff --git a/chrome/browser/printing/cloud_print/privet_http_impl.cc b/chrome/browser/printing/cloud_print/privet_http_impl.cc index 4b0befd3..caacea82 100644 --- a/chrome/browser/printing/cloud_print/privet_http_impl.cc +++ b/chrome/browser/printing/cloud_print/privet_http_impl.cc
@@ -14,6 +14,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/location.h" +#include "base/memory/ref_counted_memory.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -480,16 +481,10 @@ url_fetcher_ = privet_client_->CreateURLFetcher(url, net::URLFetcher::POST, this); - if (use_pdf_) { - // TODO(noamsml): Move to file-based upload data? - std::string data_str(reinterpret_cast<const char*>(data_->front()), - data_->size()); - url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str); - } else { - url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster, - pwg_file_path_); - } - + std::string data_str(reinterpret_cast<const char*>(data_->front()), + data_->size()); + url_fetcher_->SetUploadData( + use_pdf_ ? kPrivetContentTypePDF : kPrivetContentTypePWGRaster, data_str); url_fetcher_->Start(); } @@ -573,15 +568,15 @@ } void PrivetLocalPrintOperationImpl::OnPWGRasterConverted( - bool success, - const base::FilePath& pwg_file_path) { - if (!success) { + base::ReadOnlySharedMemoryRegion pwg_region) { + auto data = + base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(pwg_region); + if (!data) { delegate_->OnPrivetPrintingError(this, -1); return; } - DCHECK(!pwg_file_path.empty()); - pwg_file_path_ = pwg_file_path; + data_ = data; StartPrinting(); }
diff --git a/chrome/browser/printing/cloud_print/privet_http_impl.h b/chrome/browser/printing/cloud_print/privet_http_impl.h index 3896350..432a94a 100644 --- a/chrome/browser/printing/cloud_print/privet_http_impl.h +++ b/chrome/browser/printing/cloud_print/privet_http_impl.h
@@ -10,8 +10,8 @@ #include <vector> #include "base/callback.h" -#include "base/files/file_path.h" #include "base/macros.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/printing/cloud_print/privet_http.h" @@ -19,6 +19,10 @@ #include "printing/buildflags/buildflags.h" #include "ui/gfx/geometry/size.h" +namespace base { +class RefCountedMemory; +} + namespace cloud_print { class PrivetHTTPClient; @@ -189,7 +193,7 @@ const base::DictionaryValue* value); void OnCreatejobResponse(bool has_error, const base::DictionaryValue* value); - void OnPWGRasterConverted(bool success, const base::FilePath& pwg_file_path); + void OnPWGRasterConverted(base::ReadOnlySharedMemoryRegion pwg_region); PrivetHTTPClient* const privet_client_; PrivetLocalPrintOperation::Delegate* const delegate_; @@ -200,7 +204,6 @@ cloud_devices::CloudDeviceDescription capabilities_; scoped_refptr<base::RefCountedMemory> data_; - base::FilePath pwg_file_path_; bool use_pdf_ = false; bool has_extended_workflow_ = false;
diff --git a/chrome/browser/printing/cloud_print/privet_http_unittest.cc b/chrome/browser/printing/cloud_print/privet_http_unittest.cc index 85bdf10..4a7fde82 100644 --- a/chrome/browser/printing/cloud_print/privet_http_unittest.cc +++ b/chrome/browser/printing/cloud_print/privet_http_unittest.cc
@@ -358,8 +358,8 @@ return SuccessfulResponseToURL(url, response); } - bool SuccessfulResponseToURLAndFilePath(const GURL& url, - const base::FilePath& file_path, + bool SuccessfulResponseToURLAndFileData(const GURL& url, + const std::string& file_data, const std::string& response) { net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); if (!fetcher) { @@ -369,8 +369,8 @@ EXPECT_EQ(url, fetcher->GetOriginalURL()); - EXPECT_EQ(file_path, fetcher->upload_file_path()); - if (file_path != fetcher->upload_file_path()) + EXPECT_EQ(file_data, fetcher->upload_data()); + if (file_data != fetcher->upload_data()) return false; return SuccessfulResponseToURL(url, response); @@ -767,7 +767,8 @@ } #if BUILDFLAG(ENABLE_PRINT_PREVIEW) -// A note on PWG raster conversion: The PWG raster converter used simply +// A note on PWG raster conversion: The fake PWG raster converter simply returns +// the input as the converted data. The output isn't checked anyway. // converts strings to file paths based on them by appending "test.pdf", since // it's easier to test that way. Instead of using a mock, we simply check if the // request is uploading a file that is based on this pattern. @@ -777,10 +778,17 @@ const printing::PdfRenderSettings& conversion_settings, const printing::PwgRasterSettings& bitmap_settings, ResultCallback callback) override { + base::MappedReadOnlyRegion memory = + base::ReadOnlySharedMemoryRegion::Create(data->size()); + if (!memory.mapping.IsValid()) { + ADD_FAILURE() << "Failed to create pwg raster shared memory."; + std::move(callback).Run(base::ReadOnlySharedMemoryRegion()); + return; + } + + memcpy(memory.mapping.memory(), data->front(), data->size()); bitmap_settings_ = bitmap_settings; - std::string data_str(data->front_as<char>(), data->size()); - std::move(callback).Run( - true, base::FilePath().AppendASCII(data_str + "test.pdf")); + std::move(callback).Run(std::move(memory.region)); } const printing::PwgRasterSettings& bitmap_settings() { @@ -872,8 +880,7 @@ TEST_P(PrivetLocalPrintTest, SuccessfulPWGLocalPrint) { local_print_operation_->SetUsername("sample@gmail.com"); local_print_operation_->SetJobname("Sample job name"); - local_print_operation_->SetData( - RefCountedBytesFromString("path/to/")); + local_print_operation_->SetData(RefCountedBytesFromString("foobar")); local_print_operation_->SetCapabilities(kSampleCapabilitiesResponsePWGOnly); local_print_operation_->Start(); @@ -885,12 +892,11 @@ EXPECT_CALL(local_print_delegate_, OnPrivetPrintingDoneInternal()); - EXPECT_TRUE(SuccessfulResponseToURLAndFilePath( + EXPECT_TRUE(SuccessfulResponseToURLAndFileData( GetUrl("/privet/printer/submitdoc?" "client_name=Chrome&user_name=sample%40gmail.com" "&job_name=Sample+job+name"), - base::FilePath(FILE_PATH_LITERAL("path/to/test.pdf")), - kSampleLocalPrintResponse)); + "foobar", kSampleLocalPrintResponse)); EXPECT_EQ(printing::TRANSFORM_NORMAL, pwg_converter_->bitmap_settings().odd_page_transform); @@ -904,7 +910,7 @@ TEST_P(PrivetLocalPrintTest, SuccessfulPWGLocalPrintDuplex) { local_print_operation_->SetUsername("sample@gmail.com"); local_print_operation_->SetJobname("Sample job name"); - local_print_operation_->SetData(RefCountedBytesFromString("path/to/")); + local_print_operation_->SetData(RefCountedBytesFromString("foobar")); local_print_operation_->SetTicket(kSampleCJTDuplex); local_print_operation_->SetCapabilities( kSampleCapabilitiesResponsePWGSettings); @@ -922,12 +928,11 @@ EXPECT_CALL(local_print_delegate_, OnPrivetPrintingDoneInternal()); - EXPECT_TRUE(SuccessfulResponseToURLAndFilePath( + EXPECT_TRUE(SuccessfulResponseToURLAndFileData( GetUrl("/privet/printer/submitdoc?" "client_name=Chrome&user_name=sample%40gmail.com" "&job_name=Sample+job+name&job_id=1234"), - base::FilePath(FILE_PATH_LITERAL("path/to/test.pdf")), - kSampleLocalPrintResponse)); + "foobar", kSampleLocalPrintResponse)); EXPECT_EQ(printing::TRANSFORM_ROTATE_180, pwg_converter_->bitmap_settings().odd_page_transform); @@ -941,7 +946,7 @@ TEST_P(PrivetLocalPrintTest, SuccessfulPWGLocalPrintMono) { local_print_operation_->SetUsername("sample@gmail.com"); local_print_operation_->SetJobname("Sample job name"); - local_print_operation_->SetData(RefCountedBytesFromString("path/to/")); + local_print_operation_->SetData(RefCountedBytesFromString("foobar")); local_print_operation_->SetTicket(kSampleCJTMono); local_print_operation_->SetCapabilities( kSampleCapabilitiesResponsePWGSettings); @@ -959,12 +964,11 @@ EXPECT_CALL(local_print_delegate_, OnPrivetPrintingDoneInternal()); - EXPECT_TRUE(SuccessfulResponseToURLAndFilePath( + EXPECT_TRUE(SuccessfulResponseToURLAndFileData( GetUrl("/privet/printer/submitdoc?" "client_name=Chrome&user_name=sample%40gmail.com" "&job_name=Sample+job+name&job_id=1234"), - base::FilePath(FILE_PATH_LITERAL("path/to/test.pdf")), - kSampleLocalPrintResponse)); + "foobar", kSampleLocalPrintResponse)); EXPECT_EQ(printing::TRANSFORM_NORMAL, pwg_converter_->bitmap_settings().odd_page_transform); @@ -978,7 +982,7 @@ TEST_P(PrivetLocalPrintTest, SuccessfulPWGLocalPrintMonoToGRAY8Printer) { local_print_operation_->SetUsername("sample@gmail.com"); local_print_operation_->SetJobname("Sample job name"); - local_print_operation_->SetData(RefCountedBytesFromString("path/to/")); + local_print_operation_->SetData(RefCountedBytesFromString("foobar")); local_print_operation_->SetTicket(kSampleCJTMono); local_print_operation_->SetCapabilities( kSampleCapabilitiesResponsePWGSettingsMono); @@ -996,12 +1000,11 @@ EXPECT_CALL(local_print_delegate_, OnPrivetPrintingDoneInternal()); - EXPECT_TRUE(SuccessfulResponseToURLAndFilePath( + EXPECT_TRUE(SuccessfulResponseToURLAndFileData( GetUrl("/privet/printer/submitdoc?" "client_name=Chrome&user_name=sample%40gmail.com" "&job_name=Sample+job+name&job_id=1234"), - base::FilePath(FILE_PATH_LITERAL("path/to/test.pdf")), - kSampleLocalPrintResponse)); + "foobar", kSampleLocalPrintResponse)); EXPECT_EQ(printing::TRANSFORM_NORMAL, pwg_converter_->bitmap_settings().odd_page_transform); @@ -1075,8 +1078,7 @@ local_print_operation_->SetJobname("Sample job name"); local_print_operation_->SetTicket(kSampleCJT); local_print_operation_->SetCapabilities(kSampleCapabilitiesResponse); - local_print_operation_->SetData( - RefCountedBytesFromString("sample/path/")); + local_print_operation_->SetData(RefCountedBytesFromString("sample_data")); local_print_operation_->Start(); EXPECT_TRUE(SuccessfulResponseToURL(GetUrl("/privet/info"), @@ -1093,16 +1095,15 @@ GetUrl("/privet/printer/submitdoc?" "client_name=Chrome&user_name=sample%40gmail.com&" "job_name=Sample+job+name&job_id=1234"), - "sample/path/", kSampleInvalidDocumentTypeResponse)); + "sample_data", kSampleInvalidDocumentTypeResponse)); EXPECT_CALL(local_print_delegate_, OnPrivetPrintingDoneInternal()); - EXPECT_TRUE(SuccessfulResponseToURLAndFilePath( + EXPECT_TRUE(SuccessfulResponseToURLAndFileData( GetUrl("/privet/printer/submitdoc?" "client_name=Chrome&user_name=sample%40gmail.com&" "job_name=Sample+job+name&job_id=1234"), - base::FilePath(FILE_PATH_LITERAL("sample/path/test.pdf")), - kSampleLocalPrintResponse)); + "sample_data", kSampleLocalPrintResponse)); } TEST_P(PrivetLocalPrintTest, LocalPrintRetryOnInvalidJobID) {
diff --git a/chrome/browser/printing/cloud_print/privet_url_fetcher.cc b/chrome/browser/printing/cloud_print/privet_url_fetcher.cc index 44c8544..d8d3d09 100644 --- a/chrome/browser/printing/cloud_print/privet_url_fetcher.cc +++ b/chrome/browser/printing/cloud_print/privet_url_fetcher.cc
@@ -163,16 +163,8 @@ url_fetcher_->SaveResponseToTemporaryFile(GetFileTaskRunner()); // URLFetcher requires us to set upload data for POST requests. - if (request_type_ == net::URLFetcher::POST) { - if (upload_file_path_.empty()) { - url_fetcher_->SetUploadData(upload_content_type_, upload_data_); - } else { - url_fetcher_->SetUploadFilePath( - upload_content_type_, upload_file_path_, 0 /*offset*/, - std::numeric_limits<uint64_t>::max() /*length*/, GetFileTaskRunner()); - } - } - + if (request_type_ == net::URLFetcher::POST) + url_fetcher_->SetUploadData(upload_content_type_, upload_data_); url_fetcher_->Start(); } @@ -196,19 +188,10 @@ void PrivetURLFetcher::SetUploadData(const std::string& upload_content_type, const std::string& upload_data) { - DCHECK(upload_file_path_.empty()); upload_content_type_ = upload_content_type; upload_data_ = upload_data; } -void PrivetURLFetcher::SetUploadFilePath( - const std::string& upload_content_type, - const base::FilePath& upload_file_path) { - DCHECK(upload_data_.empty()); - upload_content_type_ = upload_content_type; - upload_file_path_ = upload_file_path; -} - void PrivetURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) { DVLOG(1) << "Status: " << source->GetStatus().status() << ", ResponseCode: " << source->GetResponseCode();
diff --git a/chrome/browser/printing/cloud_print/privet_url_fetcher.h b/chrome/browser/printing/cloud_print/privet_url_fetcher.h index 91e09bf..5be6c5f5 100644 --- a/chrome/browser/printing/cloud_print/privet_url_fetcher.h +++ b/chrome/browser/printing/cloud_print/privet_url_fetcher.h
@@ -98,9 +98,6 @@ void SetUploadData(const std::string& upload_content_type, const std::string& upload_data); - void SetUploadFilePath(const std::string& upload_content_type, - const base::FilePath& upload_file_path); - const GURL& url() const { return url_fetcher_ ? url_fetcher_->GetOriginalURL() : url_; } @@ -141,7 +138,6 @@ int tries_ = 0; std::string upload_data_; std::string upload_content_type_; - base::FilePath upload_file_path_; std::unique_ptr<net::URLFetcher> url_fetcher_; scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
diff --git a/chrome/browser/printing/pwg_raster_converter.cc b/chrome/browser/printing/pwg_raster_converter.cc index 1e9d3f9..83d0c72 100644 --- a/chrome/browser/printing/pwg_raster_converter.cc +++ b/chrome/browser/printing/pwg_raster_converter.cc
@@ -10,16 +10,8 @@ #include "base/bind_helpers.h" #include "base/cancelable_callback.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/location.h" #include "base/logging.h" #include "base/macros.h" -#include "base/sequenced_task_runner.h" -#include "base/task_scheduler/post_task.h" -#include "base/threading/thread_restrictions.h" -#include "base/threading/thread_task_runner_handle.h" #include "chrome/services/printing/public/mojom/constants.mojom.h" #include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.h" #include "components/cloud_devices/common/cloud_device_description.h" @@ -41,77 +33,9 @@ using content::BrowserThread; -class FileHandlers { - public: - FileHandlers() {} - - ~FileHandlers() { base::AssertBlockingAllowed(); } - - void Init(base::RefCountedMemory* data); - bool IsValid(); - - base::FilePath GetPwgPath() const { - return temp_dir_.GetPath().AppendASCII("output.pwg"); - } - - base::FilePath GetPdfPath() const { - return temp_dir_.GetPath().AppendASCII("input.pdf"); - } - - base::PlatformFile GetPdfForProcess() { - DCHECK(pdf_file_.IsValid()); - return pdf_file_.TakePlatformFile(); - } - - base::PlatformFile GetPwgForProcess() { - DCHECK(pwg_file_.IsValid()); - return pwg_file_.TakePlatformFile(); - } - - private: - base::ScopedTempDir temp_dir_; - base::File pdf_file_; - base::File pwg_file_; - - DISALLOW_COPY_AND_ASSIGN(FileHandlers); -}; - -void FileHandlers::Init(base::RefCountedMemory* data) { - base::AssertBlockingAllowed(); - - if (!temp_dir_.CreateUniqueTempDir()) - return; - - if (static_cast<int>(data->size()) != - base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) { - return; - } - - // Reopen in read only mode. - pdf_file_.Initialize(GetPdfPath(), - base::File::FLAG_OPEN | base::File::FLAG_READ); - pwg_file_.Initialize(GetPwgPath(), - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); -} - -bool FileHandlers::IsValid() { - return pdf_file_.IsValid() && pwg_file_.IsValid(); -} - -// Converts PDF into PWG raster. -// Class uses UI thread and |blocking_task_runner_|. -// Internal workflow is following: -// 1. Create instance on the UI thread. (files_, settings_,) -// 2. Create file on |blocking_task_runner_|. -// 3. Connect to the printing utility service and start the conversion. -// 4. Run result callback on the UI thread. -// 5. Instance is destroyed from any thread that has the last reference. -// 6. FileHandlers destroyed on |blocking_task_runner_|. -// This step posts |FileHandlers| to be destroyed on |blocking_task_runner_|. -// All these steps work sequentially, so no data should be accessed -// simultaneously by several threads. +// Converts PDF into PWG raster. Class lives on the UI thread. class PwgRasterConverterHelper - : public base::RefCountedThreadSafe<PwgRasterConverterHelper> { + : public base::RefCounted<PwgRasterConverterHelper> { public: PwgRasterConverterHelper(const PdfRenderSettings& settings, const PwgRasterSettings& bitmap_settings); @@ -120,21 +44,17 @@ PwgRasterConverter::ResultCallback callback); private: - friend class base::RefCountedThreadSafe<PwgRasterConverterHelper>; + friend class base::RefCounted<PwgRasterConverterHelper>; ~PwgRasterConverterHelper(); - void RunCallback(bool success); - - void OnFilesReadyOnUIThread(); + void RunCallback(base::ReadOnlySharedMemoryRegion region); PdfRenderSettings settings_; PwgRasterSettings bitmap_settings_; mojo::InterfacePtr<printing::mojom::PdfToPwgRasterConverter> pdf_to_pwg_raster_converter_ptr_; PwgRasterConverter::ResultCallback callback_; - const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; - std::unique_ptr<FileHandlers, base::OnTaskRunnerDeleter> files_; DISALLOW_COPY_AND_ASSIGN(PwgRasterConverterHelper); }; @@ -142,14 +62,13 @@ PwgRasterConverterHelper::PwgRasterConverterHelper( const PdfRenderSettings& settings, const PwgRasterSettings& bitmap_settings) - : settings_(settings), - bitmap_settings_(bitmap_settings), - blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE, - base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), - files_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)) {} + : settings_(settings), bitmap_settings_(bitmap_settings) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} -PwgRasterConverterHelper::~PwgRasterConverterHelper() {} +PwgRasterConverterHelper::~PwgRasterConverterHelper() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} void PwgRasterConverterHelper::Convert( base::RefCountedMemory* data, @@ -157,23 +76,6 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); callback_ = std::move(callback); - CHECK(!files_); - files_.reset(new FileHandlers()); - - blocking_task_runner_->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&FileHandlers::Init, base::Unretained(files_.get()), - base::RetainedRef(data)), - base::BindOnce(&PwgRasterConverterHelper::OnFilesReadyOnUIThread, this)); -} - -void PwgRasterConverterHelper::OnFilesReadyOnUIThread() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - if (!files_->IsValid()) { - RunCallback(false); - return; - } content::ServiceManagerConnection::GetForProcess() ->GetConnector() @@ -181,18 +83,29 @@ &pdf_to_pwg_raster_converter_ptr_); pdf_to_pwg_raster_converter_ptr_.set_connection_error_handler( - base::Bind(&PwgRasterConverterHelper::RunCallback, this, false)); + base::BindOnce(&PwgRasterConverterHelper::RunCallback, this, + base::ReadOnlySharedMemoryRegion())); + base::MappedReadOnlyRegion memory = + base::ReadOnlySharedMemoryRegion::Create(data->size()); + if (!memory.region.IsValid() || !memory.mapping.IsValid()) { + RunCallback(base::ReadOnlySharedMemoryRegion()); + return; + } + + // TODO(thestig): Write |data| into shared memory in the first place, to avoid + // this memcpy(). + memcpy(memory.mapping.memory(), data->front(), data->size()); pdf_to_pwg_raster_converter_ptr_->Convert( - mojo::WrapPlatformFile(files_->GetPdfForProcess()), settings_, - bitmap_settings_, mojo::WrapPlatformFile(files_->GetPwgForProcess()), + std::move(memory.region), settings_, bitmap_settings_, base::Bind(&PwgRasterConverterHelper::RunCallback, this)); } -void PwgRasterConverterHelper::RunCallback(bool success) { +void PwgRasterConverterHelper::RunCallback( + base::ReadOnlySharedMemoryRegion region) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (callback_) - std::move(callback_).Run(success, files_->GetPwgPath()); + std::move(callback_).Run(std::move(region)); pdf_to_pwg_raster_converter_ptr_.reset(); } @@ -210,7 +123,8 @@ scoped_refptr<PwgRasterConverterHelper> utility_client_; // Cancelable version of ResultCallback. - base::CancelableOnceCallback<void(bool, const base::FilePath&)> callback_; + base::CancelableOnceCallback<void(base::ReadOnlySharedMemoryRegion)> + callback_; DISALLOW_COPY_AND_ASSIGN(PwgRasterConverterImpl); };
diff --git a/chrome/browser/printing/pwg_raster_converter.h b/chrome/browser/printing/pwg_raster_converter.h index 866f6244..2b2c17f 100644 --- a/chrome/browser/printing/pwg_raster_converter.h +++ b/chrome/browser/printing/pwg_raster_converter.h
@@ -8,13 +8,10 @@ #include <memory> #include "base/callback.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/memory/ref_counted_memory.h" #include "printing/pdf_render_settings.h" -namespace base { -class FilePath; -} - namespace cloud_devices { class CloudDeviceDescription; } @@ -30,12 +27,9 @@ class PwgRasterConverter { public: // Callback for when the PDF is converted to a PWG raster. - // |success| denotes whether the conversion succeeded. - // |temp_file| is the path to the temp file (owned by the converter) that - // contains the PWG raster data. + // |region| contains the PWG raster data. using ResultCallback = - base::OnceCallback<void(bool /*success*/, - const base::FilePath& /*temp_file*/)>; + base::OnceCallback<void(base::ReadOnlySharedMemoryRegion /*region*/)>; virtual ~PwgRasterConverter() {}
diff --git a/chrome/browser/printing/pwg_raster_converter_browsertest.cc b/chrome/browser/printing/pwg_raster_converter_browsertest.cc index d463889..3021e459 100644 --- a/chrome/browser/printing/pwg_raster_converter_browsertest.cc +++ b/chrome/browser/printing/pwg_raster_converter_browsertest.cc
@@ -34,14 +34,11 @@ #endif void ResultCallbackImpl(bool* called, - bool* success_out, - base::FilePath* temp_file_out, + base::ReadOnlySharedMemoryRegion* pwg_region_out, base::Closure quit_closure, - bool success_in, - const base::FilePath& temp_file_in) { + base::ReadOnlySharedMemoryRegion pwg_region_in) { *called = true; - *success_out = success_in; - *temp_file_out = temp_file_in; + *pwg_region_out = std::move(pwg_region_in); quit_closure.Run(); } @@ -57,19 +54,24 @@ *pdf_data = base::RefCountedString::TakeString(&pdf_data_str); } -std::string HashFile(const std::string& file_data) { - std::string sha1 = base::SHA1HashString(file_data); - return base::HexEncode(sha1.c_str(), sha1.length()); +std::string HashData(const char* data, size_t len) { + char hash[base::kSHA1Length]; + base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(data), len, + reinterpret_cast<unsigned char*>(hash)); + return base::HexEncode(hash, base::kSHA1Length); } -void ComparePwgFiles(const base::FilePath& expected, - const base::FilePath& actual) { +void ComparePwgOutput(const base::FilePath& expected_file, + base::ReadOnlySharedMemoryRegion pwg_region) { std::string pwg_expected_data_str; - ASSERT_TRUE(base::ReadFileToString(expected, &pwg_expected_data_str)); - std::string pwg_actual_data_str; - ASSERT_TRUE(base::ReadFileToString(actual, &pwg_actual_data_str)); - EXPECT_EQ(pwg_expected_data_str.length(), pwg_actual_data_str.length()); - EXPECT_EQ(HashFile(pwg_expected_data_str), HashFile(pwg_actual_data_str)); + ASSERT_TRUE(base::ReadFileToString(expected_file, &pwg_expected_data_str)); + + base::ReadOnlySharedMemoryMapping pwg_mapping = pwg_region.Map(); + ASSERT_TRUE(pwg_mapping.IsValid()); + size_t size = pwg_mapping.size(); + ASSERT_EQ(pwg_expected_data_str.length(), size); + EXPECT_EQ(HashData(pwg_expected_data_str.c_str(), size), + HashData(static_cast<const char*>(pwg_mapping.memory()), size)); } class PdfToPwgRasterBrowserTest : public InProcessBrowserTest { @@ -82,16 +84,15 @@ const PdfRenderSettings& conversion_settings, const PwgRasterSettings& bitmap_settings, bool expect_success, - base::FilePath* temp_file) { + base::ReadOnlySharedMemoryRegion* pwg_region) { bool called = false; - bool success = false; base::RunLoop run_loop; converter_->Start(pdf_data, conversion_settings, bitmap_settings, - base::BindOnce(&ResultCallbackImpl, &called, &success, - temp_file, run_loop.QuitClosure())); + base::BindOnce(&ResultCallbackImpl, &called, pwg_region, + run_loop.QuitClosure())); run_loop.Run(); ASSERT_TRUE(called); - EXPECT_EQ(success, expect_success); + EXPECT_EQ(expect_success, pwg_region->IsValid()); } private: @@ -103,9 +104,9 @@ IN_PROC_BROWSER_TEST_F(PdfToPwgRasterBrowserTest, TestFailure) { scoped_refptr<base::RefCountedStaticMemory> bad_pdf_data = base::MakeRefCounted<base::RefCountedStaticMemory>("0123456789", 10); - base::FilePath temp_file; + base::ReadOnlySharedMemoryRegion pwg_region; Convert(bad_pdf_data.get(), PdfRenderSettings(), PwgRasterSettings(), - /*expect_success=*/false, &temp_file); + /*expect_success=*/false, &pwg_region); } IN_PROC_BROWSER_TEST_F(PdfToPwgRasterBrowserTest, TestSuccessColor) { @@ -126,14 +127,13 @@ pwg_settings.reverse_page_order = false; pwg_settings.use_color = true; - base::FilePath temp_file; + base::ReadOnlySharedMemoryRegion pwg_region; Convert(pdf_data.get(), pdf_settings, pwg_settings, - /*expect_success=*/true, &temp_file); - ASSERT_FALSE(temp_file.empty()); + /*expect_success=*/true, &pwg_region); - base::FilePath pwg_file = + base::FilePath expected_pwg_file = test_data_dir.AppendASCII(kPdfToPwgRasterColorTestFile); - ComparePwgFiles(pwg_file, temp_file); + ComparePwgOutput(expected_pwg_file, std::move(pwg_region)); } IN_PROC_BROWSER_TEST_F(PdfToPwgRasterBrowserTest, TestSuccessMono) { @@ -154,14 +154,13 @@ pwg_settings.reverse_page_order = false; pwg_settings.use_color = false; - base::FilePath temp_file; + base::ReadOnlySharedMemoryRegion pwg_region; Convert(pdf_data.get(), pdf_settings, pwg_settings, - /*expect_success=*/true, &temp_file); - ASSERT_FALSE(temp_file.empty()); + /*expect_success=*/true, &pwg_region); - base::FilePath pwg_file = + base::FilePath expected_pwg_file = test_data_dir.AppendASCII(kPdfToPwgRasterMonoTestFile); - ComparePwgFiles(pwg_file, temp_file); + ComparePwgOutput(expected_pwg_file, std::move(pwg_region)); } } // namespace printing
diff --git a/chrome/browser/profile_resetter/reset_report_uploader.cc b/chrome/browser/profile_resetter/reset_report_uploader.cc index c02ef4a1..4c7b445e 100644 --- a/chrome/browser/profile_resetter/reset_report_uploader.cc +++ b/chrome/browser/profile_resetter/reset_report_uploader.cc
@@ -13,9 +13,8 @@ #include "net/base/escape.h" #include "net/base/load_flags.h" #include "net/traffic_annotation/network_traffic_annotation.h" -#include "services/network/public/cpp/resource_request.h" -#include "services/network/public/cpp/simple_url_loader.h" -#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context_getter.h" namespace { const char kResetReportUrl[] = @@ -33,7 +32,9 @@ } // namespace ResetReportUploader::ResetReportUploader(content::BrowserContext* context) - : browser_context_(context) {} + : url_request_context_getter_( + content::BrowserContext::GetDefaultStoragePartition(context)-> + GetURLRequestContext()) {} ResetReportUploader::~ResetReportUploader() {} @@ -71,29 +72,19 @@ "send the data." })"); - auto resource_request = std::make_unique<network::ResourceRequest>(); - resource_request->url = GetClientReportUrl(kResetReportUrl); - resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DISABLE_CACHE; - resource_request->method = "POST"; - std::unique_ptr<network::SimpleURLLoader> simple_url_loader = - network::SimpleURLLoader::Create(std::move(resource_request), - traffic_annotation); - simple_url_loader->AttachStringForUpload(request_data, - "application/octet-stream"); - auto it = simple_url_loaders_.insert(simple_url_loaders_.begin(), - std::move(simple_url_loader)); - simple_url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( - content::BrowserContext::GetDefaultStoragePartition(browser_context_) - ->GetURLLoaderFactoryForBrowserProcess() - .get(), - base::BindOnce(&ResetReportUploader::OnSimpleLoaderComplete, - base::Unretained(this), std::move(it))); + // Note fetcher will be deleted by OnURLFetchComplete. + net::URLFetcher* fetcher = + net::URLFetcher::Create(GetClientReportUrl(kResetReportUrl), + net::URLFetcher::POST, this, traffic_annotation) + .release(); + fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE); + fetcher->SetRequestContext(url_request_context_getter_.get()); + fetcher->SetUploadData("application/octet-stream", request_data); + fetcher->Start(); } -void ResetReportUploader::OnSimpleLoaderComplete( - SimpleURLLoaderList::iterator it, - std::unique_ptr<std::string> response_body) { - simple_url_loaders_.erase(it); +void ResetReportUploader::OnURLFetchComplete(const net::URLFetcher* source) { + delete source; }
diff --git a/chrome/browser/profile_resetter/reset_report_uploader.h b/chrome/browser/profile_resetter/reset_report_uploader.h index 48966da..a710c101 100644 --- a/chrome/browser/profile_resetter/reset_report_uploader.h +++ b/chrome/browser/profile_resetter/reset_report_uploader.h
@@ -5,18 +5,18 @@ #ifndef CHROME_BROWSER_PROFILE_RESETTER_RESET_REPORT_UPLOADER_H_ #define CHROME_BROWSER_PROFILE_RESETTER_RESET_REPORT_UPLOADER_H_ -#include <list> - #include "base/macros.h" #include "base/memory/ref_counted.h" #include "components/keyed_service/core/keyed_service.h" +#include "net/url_request/url_fetcher_delegate.h" namespace content { class BrowserContext; } -namespace network { -class SimpleURLLoader; +namespace net { +class URLFetcher; +class URLRequestContextGetter; } namespace reset_report { @@ -24,7 +24,8 @@ } // Service whose job is up upload ChromeResetReports. -class ResetReportUploader : public KeyedService { +class ResetReportUploader : public KeyedService, + private net::URLFetcherDelegate { public: explicit ResetReportUploader(content::BrowserContext* context); ~ResetReportUploader() override; @@ -32,14 +33,9 @@ void DispatchReport(const reset_report::ChromeResetReport& report); private: - using SimpleURLLoaderList = - std::list<std::unique_ptr<network::SimpleURLLoader>>; + void OnURLFetchComplete(const net::URLFetcher* source) override; - void OnSimpleLoaderComplete(SimpleURLLoaderList::iterator it, - std::unique_ptr<std::string> response_body); - - content::BrowserContext* browser_context_; - SimpleURLLoaderList simple_url_loaders_; + scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_; DISALLOW_COPY_AND_ASSIGN(ResetReportUploader); };
diff --git a/chrome/browser/profiles/profile_downloader.cc b/chrome/browser/profiles/profile_downloader.cc index 179dd3b..7f0caba 100644 --- a/chrome/browser/profiles/profile_downloader.cc +++ b/chrome/browser/profiles/profile_downloader.cc
@@ -32,12 +32,11 @@ #include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_manager.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" #include "google_apis/gaia/gaia_constants.h" #include "net/base/load_flags.h" #include "net/http/http_status_code.h" #include "net/traffic_annotation/network_traffic_annotation.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_request_status.h" #include "skia/ext/image_operations.h" #include "url/gurl.h" @@ -46,8 +45,7 @@ namespace { // Template for optional authorization header when using an OAuth access token. -const char kAuthorizationHeader[] = - "Authorization: Bearer %s"; +constexpr char kAuthorizationHeader[] = "Authorization: Bearer %s"; } // namespace @@ -212,48 +210,55 @@ "profile image." })"); - VLOG(1) << "Fetching profile image from " << image_url_with_size; - profile_image_fetcher_ = - net::URLFetcher::Create(GURL(image_url_with_size), net::URLFetcher::GET, - this, traffic_annotation); - data_use_measurement::DataUseUserData::AttachToFetcher( - profile_image_fetcher_.get(), - data_use_measurement::DataUseUserData::PROFILE_DOWNLOADER); - profile_image_fetcher_->SetRequestContext( - delegate_->GetBrowserProfile()->GetRequestContext()); - profile_image_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES); + VLOG(1) << "Loading profile image from " << image_url_with_size; + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = GURL(image_url_with_size); + resource_request->load_flags = + net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; if (!auth_token_.empty()) { - profile_image_fetcher_->SetExtraRequestHeaders( + resource_request->headers.AddHeadersFromString( base::StringPrintf(kAuthorizationHeader, auth_token_.c_str())); } - profile_image_fetcher_->Start(); + network::mojom::URLLoaderFactory* loader_factory = + content::BrowserContext::GetDefaultStoragePartition( + delegate_->GetBrowserProfile()) + ->GetURLLoaderFactoryForBrowserProcess() + .get(); + + simple_loader_ = network::SimpleURLLoader::Create(std::move(resource_request), + traffic_annotation); + simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( + loader_factory, base::BindOnce(&ProfileDownloader::OnURLLoaderComplete, + base::Unretained(this))); } -void ProfileDownloader::OnURLFetchComplete(const net::URLFetcher* source) { +void ProfileDownloader::OnURLLoaderComplete( + std::unique_ptr<std::string> response_body) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (source->GetResponseCode() == net::HTTP_OK) { - std::string data; - source->GetResponseAsString(&data); - profile_image_fetcher_.reset(); + int response_code = -1; + if (simple_loader_->ResponseInfo() && simple_loader_->ResponseInfo()->headers) + response_code = simple_loader_->ResponseInfo()->headers->response_code(); + + if (response_body) { + simple_loader_.reset(); DVLOG(1) << "Decoding the image..."; - ImageDecoder::Start(this, data); - } else if (source->GetResponseCode() == net::HTTP_NOT_FOUND) { - profile_image_fetcher_.reset(); + ImageDecoder::Start(this, *response_body); + } else if (response_code == net::HTTP_NOT_FOUND) { + simple_loader_.reset(); VLOG(1) << "Got 404, using default picture..."; picture_status_ = PICTURE_DEFAULT; delegate_->OnProfileDownloadSuccess(this); } else { - LOG(WARNING) << "Fetching profile data failed"; - DVLOG(1) << " Status: " << source->GetStatus().status(); - DVLOG(1) << " Error: " << source->GetStatus().error(); - DVLOG(1) << " Response code: " << source->GetResponseCode(); - DVLOG(1) << " Url: " << source->GetURL().spec(); + LOG(WARNING) << "Loading profile data failed"; + DVLOG(1) << " Error: " << simple_loader_->NetError(); + DVLOG(1) << " Response code: " << response_code; + DVLOG(1) << " Url: " << simple_loader_->GetFinalURL().spec(); + // Handle miscellaneous 400/500 errors. bool network_error = - source->GetStatus().status() != net::URLRequestStatus::SUCCESS; - profile_image_fetcher_.reset(); + response_code == -1 || (response_code >= 400 && response_code < 600); + simple_loader_.reset(); delegate_->OnProfileDownloadFailure(this, network_error ? ProfileDownloaderDelegate::NETWORK_ERROR : ProfileDownloaderDelegate::SERVICE_ERROR);
diff --git a/chrome/browser/profiles/profile_downloader.h b/chrome/browser/profiles/profile_downloader.h index 80d62ed..43c65f6 100644 --- a/chrome/browser/profiles/profile_downloader.h +++ b/chrome/browser/profiles/profile_downloader.h
@@ -15,19 +15,15 @@ #include "components/signin/core/browser/account_info.h" #include "components/signin/core/browser/account_tracker_service.h" #include "google_apis/gaia/oauth2_token_service.h" -#include "net/url_request/url_fetcher_delegate.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "third_party/skia/include/core/SkBitmap.h" class ProfileDownloaderDelegate; -namespace net { -class URLFetcher; -} // namespace net - // Downloads user profile information. The profile picture is decoded in a // sandboxed process. -class ProfileDownloader : public net::URLFetcherDelegate, - public ImageDecoder::ImageRequest, +class ProfileDownloader : public ImageDecoder::ImageRequest, public OAuth2TokenService::Observer, public OAuth2TokenService::Consumer, public AccountTrackerService::Observer { @@ -87,8 +83,7 @@ void FetchImageData(); - // Overriden from net::URLFetcherDelegate: - void OnURLFetchComplete(const net::URLFetcher* source) override; + void OnURLLoaderComplete(std::unique_ptr<std::string> response_body); // Overriden from ImageDecoder::ImageRequest: void OnImageDecoded(const SkBitmap& decoded_image) override; @@ -121,7 +116,7 @@ ProfileDownloaderDelegate* delegate_; std::string account_id_; std::string auth_token_; - std::unique_ptr<net::URLFetcher> profile_image_fetcher_; + std::unique_ptr<network::SimpleURLLoader> simple_loader_; std::unique_ptr<OAuth2TokenService::Request> oauth2_access_token_request_; AccountInfo account_info_; SkBitmap profile_picture_;
diff --git a/chrome/browser/resources/chromeos/chromevox/common/command_store.js b/chrome/browser/resources/chromeos/chromevox/common/command_store.js index bf39148..40bd59f 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/command_store.js +++ b/chrome/browser/resources/chromeos/chromevox/common/command_store.js
@@ -354,19 +354,12 @@ category: 'navigation' }, - 'performDefaultAction': { - disallowContinuation: true, - msgId: 'perform_default_action', - doDefault: true, - skipInput: true, - category: 'navigation' - }, 'forceClickOnCurrentItem': { announce: true, disallowContinuation: true, allowEvents: true, msgId: 'force_click_on_current_item', - category: 'navigation' + category: 'actions' }, 'forceDoubleClickOnCurrentItem': {announce: true, allowEvents: true, disallowContinuation: true}, @@ -383,7 +376,7 @@ 'speakTimeAndDate': {announce: false, msgId: 'speak_time_and_date', category: 'information'}, 'toggleSelection': - {announce: true, msgId: 'toggle_selection', category: 'information'}, + {announce: true, msgId: 'toggle_selection', category: 'actions'}, 'toggleSearchWidget': { announce: false,
diff --git a/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs index 94e5c16..a1f9cfdc 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs +++ b/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs
@@ -24,15 +24,16 @@ TEST_F('CvoxCommandStoreUnitTest', 'TableData', function() { var categories = cvox.CommandStore.categories(); - assertEquals(10, categories.length); + assertEquals(11, categories.length); assertEquals('modifier_keys', categories[0]); assertEquals('controlling_speech', categories[1]); assertEquals('help_commands', categories[2]); assertEquals('navigation', categories[3]); - assertEquals('information', categories[4]); - assertEquals('overview', categories[5]); - assertEquals('jump_commands', categories[6]); - assertEquals('tables', categories[7]); + assertEquals('actions', categories[4]); + assertEquals('information', categories[5]); + assertEquals('overview', categories[6]); + assertEquals('jump_commands', categories[7]); + assertEquals('tables', categories[8]); assertEquals('stop_speech_key', cvox.CommandStore.messageForCommand('stopSpeech'));
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js index 540590e..ac67c15c 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -1807,10 +1807,11 @@ this.append_(buff, Msgs.getMsg('access_key', [node.accessKey])); // Ancestry based hints. - if (uniqueAncestors.find(AutomationPredicate.table)) - this.format_(node, '@hint_table', buff); if (uniqueAncestors.find( - AutomationPredicate.roles([RoleType.MENU, RoleType.MENU_BAR]))) + /** @type {function(?) : boolean} */ (AutomationPredicate.table))) + this.format_(node, '@hint_table', buff); + if (uniqueAncestors.find(/** @type {function(?) : boolean} */ ( + AutomationPredicate.roles([RoleType.MENU, RoleType.MENU_BAR])))) this.format_(node, '@hint_menu', buff); },
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js index c2966c1..9c253816 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js
@@ -46,6 +46,15 @@ search: {title: 'panel_title', location: '#focus'} }; +Panel.ACTION_TO_MSG_ID = { + decrement: 'action_decrement_description', + doDefault: 'perform_default_action', + increment: 'action_increment_description', + scrollBackward: 'action_scroll_backward_description', + scrollForward: 'action_scroll_forward_description', + showContextMenu: 'show_context_menu' +}; + /** * A callback function to be executed to perform the action from selecting * a menu item after the menu has been closed and focus has been restored @@ -335,6 +344,7 @@ var speechMenu = Panel.addMenu('panel_menu_speech'); var tabsMenu = Panel.addMenu('panel_menu_tabs'); var chromevoxMenu = Panel.addMenu('panel_menu_chromevox'); + var actionsMenu = Panel.addMenu('panel_menu_actions'); // Create a mapping between categories from CommandStore, and our // top-level menus. Some categories aren't mapped to any menu. @@ -347,6 +357,7 @@ 'information': speechMenu, 'modifier_keys': chromevoxMenu, 'help_commands': chromevoxMenu, + 'actions': actionsMenu, 'braille': null, 'developer': null @@ -447,13 +458,25 @@ Panel.addNodeMenu(menuTitle, node, predicate, async); } - // Add actions menu if there are custom actions. - if (node.customActions && node.customActions.length > 0) { - var actionsMenu = Panel.addMenu('panel_menu_actions'); + if (node.standardActions) { + for (var i = 0; i < node.standardActions.length; i++) { + var standardAction = node.standardActions[i]; + var actionMsg = Panel.ACTION_TO_MSG_ID[standardAction]; + if (!actionMsg) + continue; + var actionDesc = Msgs.getMsg(actionMsg); + actionsMenu.addMenuItem( + actionDesc, '' /* menuItemShortcut */, '' /* menuItemBraille */, + node.performStandardAction.bind(node, standardAction)); + } + } + + if (node.customActions) { for (var i = 0; i < node.customActions.length; i++) { var customAction = node.customActions[i]; - actionsMenu.addMenuItem(customAction.description, - '' /* menuItemShortcut */, '' /* menuItemBraille */, + actionsMenu.addMenuItem( + customAction.description, '' /* menuItemShortcut */, + '' /* menuItemBraille */, node.performCustomAction.bind(node, customAction.id)); } }
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd index 317b955..ae6a4ef 100644 --- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd +++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -2816,6 +2816,18 @@ <message desc="A hint to the user for interacting with the menu control." name="IDS_CHROMEVOX_HINT_MENU"> Press up or down arrow to navigate; enter to activate. </message> + <message desc="Describes the action of decrementing a control." name="IDS_CHROMEVOX_ACTION_DECREMENT_DESCRIPTION"> + Decrease value + </message> + <message desc="Describes the action of incrementing a control." name="IDS_CHROMEVOX_ACTION_INCREMENT_DESCRIPTION"> + Increase value + </message> + <message desc="Describes the action of scrolling backward." name="IDS_CHROMEVOX_ACTION_SCROLL_BACKWARD_DESCRIPTION"> + Scroll back + </message> + <message desc="Describes the action of scrolling forward." name="IDS_CHROMEVOX_ACTION_SCROLL_FORWARD_DESCRIPTION"> + Scroll forward + </message> </messages> </release> </grit>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/button_navigation_behavior.js b/chrome/browser/resources/chromeos/multidevice_setup/button_navigation_behavior.js deleted file mode 100644 index 85b5ac2..0000000 --- a/chrome/browser/resources/chromeos/multidevice_setup/button_navigation_behavior.js +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** @polymerBehavior */ -const ButtonNavigationBehaviorImpl = { - properties: { - /** - * ID for forward button label, which must be translated for display. - * - * Undefined if the visible page has no forward-navigation button. - * - * @type {string|undefined} - */ - forwardButtonTextId: String, - - /** - * ID for backward button label, which must be translated for display. - * - * Undefined if the visible page has no backward-navigation button. - * - * @type {string|undefined} - */ - backwardButtonTextId: String, - - /** - * Translated text to display on the forward-naviation button. - * - * Undefined if the visible page has no forward-navigation button. - * - * @type {string|undefined} - */ - forwardButtonText: { - type: String, - computed: 'computeButtonText_(forwardButtonTextId)', - }, - - /** - * Translated text to display on the backward-naviation button. - * - * Undefined if the visible page has no backward-navigation button. - * - * @type {string|undefined} - */ - backwardButtonText: { - type: String, - computed: 'computeButtonText_(backwardButtonTextId)', - }, - }, - - /** - * @param {string} buttonTextId Key for the localized string to appear on a - * button. - * @return {string|undefined} The localized string corresponding to the key - * buttonTextId. Return value is undefined if buttonTextId is not a key - * for any localized string. Note: this includes the case in which - * buttonTextId is undefined. - * @private - */ - computeButtonText_(buttonTextId) { - return this.i18nExists(buttonTextId) ? this.i18n(buttonTextId) : undefined; - }, -}; - -/** @polymerBehavior */ -const ButtonNavigationBehavior = [ - I18nBehavior, - ButtonNavigationBehaviorImpl, -];
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/compiled_resources2.gyp b/chrome/browser/resources/chromeos/multidevice_setup/compiled_resources2.gyp index 4fa64d95..7757cac 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/compiled_resources2.gyp +++ b/chrome/browser/resources/chromeos/multidevice_setup/compiled_resources2.gyp
@@ -11,15 +11,6 @@ }, { - 'target_name' : 'button_navigation_behavior', - 'dependencies' : [ - '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', - ], - 'includes' : - ['../../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - - { 'target_name' : 'multidevice_setup', 'dependencies' : [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', @@ -35,7 +26,7 @@ { 'target_name' : 'setup_failed_page', 'dependencies' : [ - 'button_navigation_behavior', + 'ui_page_container_behavior', ], 'includes' : ['../../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -44,7 +35,7 @@ { 'target_name' : 'setup_succeeded_page', 'dependencies' : [ - 'button_navigation_behavior', + 'ui_page_container_behavior', ], 'includes' : ['../../../../../third_party/closure_compiler/compile_js2.gypi'], @@ -53,7 +44,17 @@ { 'target_name' : 'start_setup_page', 'dependencies' : [ - 'button_navigation_behavior', + 'ui_page_container_behavior', + ], + 'includes' : + ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + + { + 'target_name' : 'ui_page_container_behavior', + 'dependencies' : [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', ], 'includes' : ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup.html index 2a0752b7..6ff7cfd 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup.html +++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup.html
@@ -3,8 +3,6 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html"> <link rel="import" href="button_bar.html"> -<link rel="import" href="button_navigation_behavior.html"> -<link rel="import" href="setting_up_page.html"> <link rel="import" href="setup_failed_page.html"> <link rel="import" href="setup_succeeded_page.html"> <link rel="import" href="start_setup_page.html"> @@ -12,7 +10,6 @@ <dom-module id="multidevice-setup"> <template> <iron-pages - id="pages" attr-for-selected="is" selected="[[visiblePageName_]]" selected-item="{{visiblePage_}}">
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd index a10e30a..cd24518 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd +++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd
@@ -18,12 +18,6 @@ <structure name="IDR_MULTIDEVICE_SETUP_BUTTON_BAR_JS" file="button_bar.js" type="chrome_html" /> - <structure name="IDR_MULTIDEVICE_SETUP_BUTTON_NAVIGATION_BEHAVIOR_HTML" - file="button_navigation_behavior.html" - type="chrome_html" /> - <structure name="IDR_MULTIDEVICE_SETUP_BUTTON_NAVIGATION_BEHAVIOR_JS" - file="button_navigation_behavior.js" - type="chrome_html" /> <structure name="IDR_MULTIDEVICE_SETUP_I18N_SETUP_HTML" file="i18n_setup.html" type="chrome_html" /> @@ -56,6 +50,18 @@ <structure name="IDR_MULTIDEVICE_SETUP_START_SETUP_PAGE_JS" file="start_setup_page.js" type="chrome_html" /> + <structure name="IDR_MULTIDEVICE_SETUP_UI_PAGE_CONTAINER_BEHAVIOR_HTML" + file="ui_page_container_behavior.html" + type="chrome_html" /> + <structure name="IDR_MULTIDEVICE_SETUP_UI_PAGE_CONTAINER_BEHAVIOR_JS" + file="ui_page_container_behavior.js" + type="chrome_html" /> + <structure name="IDR_MULTIDEVICE_SETUP_UI_PAGE_HTML" + file="ui_page.html" + type="chrome_html" /> + <structure name="IDR_MULTIDEVICE_SETUP_UI_PAGE_JS" + file="ui_page.js" + type="chrome_html" /> </structures> </release> </grit>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.html b/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.html index f8ecec6..47d837dc 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.html +++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.html
@@ -1,11 +1,17 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="button_navigation_behavior.html"> +<link rel="import" href="ui_page_container_behavior.html"> +<link rel="import" href="ui_page.html"> <dom-module id="setup-failed-page"> <template> - --------------Setup Failed Placeholder Text-------------- + <ui-page header-text="[[headerText]]"> + <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span> + <div slot="additional-content"> + This is empty... (PlAcEhOlDeR tExT!!) + </div> + </ui-page> </template> <script src="setup_failed_page.js"></script> </dom-module>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.js b/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.js index 1c88f612..34dd4ee9 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.js +++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_failed_page.js
@@ -6,24 +6,32 @@ is: 'setup-failed-page', properties: { - /** - * Overriden from ButtonNavigationBehavior - */ + /** Overridden from UiPageContainerBehavior. */ forwardButtonTextId: { type: String, value: 'tryAgain', }, - /** - * Overriden from ButtonNavigationBehavior - */ + /** Overridden from UiPageContainerBehavior. */ backwardButtonTextId: { type: String, value: 'cancel', }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'setupFailedPageHeader', + }, + + /** Overridden from UiPageContainerBehavior. */ + messageId: { + type: String, + value: 'setupFailedPageMessage', + }, }, behaviors: [ - ButtonNavigationBehavior, + UiPageContainerBehavior, ], });
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html index d45962c..98764fd7 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html +++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html
@@ -1,11 +1,17 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="button_navigation_behavior.html"> +<link rel="import" href="ui_page.html"> +<link rel="import" href="ui_page_container_behavior.html"> <dom-module id="setup-succeeded-page"> <template> - --------------Setup Succeeded Placeholder Text-------------- + <ui-page header-text="[[headerText]]"> + <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span> + <div slot="additional-content"> + This is empty... (PlAcEhOlDeR tExT!!) + </div> + </ui-page> </template> <script src="setup_succeeded_page.js"></script> </dom-module>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.js b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.js index b116d70..6fa48b60 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.js +++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.js
@@ -6,16 +6,41 @@ is: 'setup-succeeded-page', properties: { - /** - * Overriden from ButtonNavigationBehavior - */ + /** Overridden from UiPageContainerBehavior. */ forwardButtonTextId: { type: String, value: 'done', }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'setupSucceededPageHeader', + }, + + /** Overridden from UiPageContainerBehavior. */ + messageId: { + type: String, + value: 'setupSucceededPageMessage', + }, }, behaviors: [ - ButtonNavigationBehavior, + UiPageContainerBehavior, ], + + /** @private */ + openSettings_: function() { + // TODO(jordynass): Open MultiDevice settings when that page is built. + console.log('Opening MultiDevice Settings'); + // This method is just for testing that the method was called + this.fire('settings-opened'); + }, + + /** @override */ + ready: function() { + let linkElement = this.$$('#settings-link'); + linkElement.setAttribute('href', '#'); + linkElement.addEventListener('click', () => this.openSettings_()); + } });
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html index 9aab421..7fcdd5a2 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html +++ b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html
@@ -1,11 +1,17 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="button_navigation_behavior.html"> +<link rel="import" href="ui_page.html"> +<link rel="import" href="ui_page_container_behavior.html"> <dom-module id="start-setup-page"> <template> - --------------Start Setup Placeholder Text-------------- + <ui-page header-text="[[headerText]]"> + <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span> + <div slot="additional-content"> + This is a dropdown or a spinner... (PlAcEhOlDeR tExT!!) + </div> + </ui-page> </template> <script src="start_setup_page.js"></script> </dom-module>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.js b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.js index 1d199a8..7b25215 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.js +++ b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.js
@@ -6,24 +6,32 @@ is: 'start-setup-page', properties: { - /** - * Overriden from ButtonNavigationBehavior - */ + /** Overridden from UiPageContainerBehavior. */ forwardButtonTextId: { type: String, value: 'accept', }, - /** - * Overriden from ButtonNavigationBehavior - */ + /** Overridden from UiPageContainerBehavior. */ backwardButtonTextId: { type: String, value: 'cancel', }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'startSetupPageHeader', + }, + + /** Overridden from UiPageContainerBehavior. */ + messageId: { + type: String, + value: 'startSetupPageMessage', + }, }, behaviors: [ - ButtonNavigationBehavior, + UiPageContainerBehavior, ], });
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/ui_page.html b/chrome/browser/resources/chromeos/multidevice_setup/ui_page.html new file mode 100644 index 0000000..8649eb0a --- /dev/null +++ b/chrome/browser/resources/chromeos/multidevice_setup/ui_page.html
@@ -0,0 +1,10 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<dom-module id="ui-page"> + <template> + <div>[[headerText]]</div> + <slot name="message"></slot> + <slot name="additional-content"></slot> + </template> + <script src="ui_page.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js b/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js new file mode 100644 index 0000000..0cc2d39 --- /dev/null +++ b/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js
@@ -0,0 +1,19 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * An element that encapsulates the structure common to all pages in the WebUI. + */ +Polymer({ + is: 'ui-page', + + properties: { + /** + * Main heading for the page. + * + * @type {string} + */ + headerText: String, + }, +});
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/button_navigation_behavior.html b/chrome/browser/resources/chromeos/multidevice_setup/ui_page_container_behavior.html similarity index 70% rename from chrome/browser/resources/chromeos/multidevice_setup/button_navigation_behavior.html rename to chrome/browser/resources/chromeos/multidevice_setup/ui_page_container_behavior.html index 1a749867..80d80e58 100644 --- a/chrome/browser/resources/chromeos/multidevice_setup/button_navigation_behavior.html +++ b/chrome/browser/resources/chromeos/multidevice_setup/ui_page_container_behavior.html
@@ -1,4 +1,4 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> -<script src="button_navigation_behavior.js"></script> +<script src="ui_page_container_behavior.js"></script>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/ui_page_container_behavior.js b/chrome/browser/resources/chromeos/multidevice_setup/ui_page_container_behavior.js new file mode 100644 index 0000000..701f7f6a --- /dev/null +++ b/chrome/browser/resources/chromeos/multidevice_setup/ui_page_container_behavior.js
@@ -0,0 +1,107 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @polymerBehavior */ +const UiPageContainerBehaviorImpl = { + properties: { + /** + * ID for forward button label, which must be translated for display. + * + * Undefined if the visible page has no forward-navigation button. + * + * @type {string|undefined} + */ + forwardButtonTextId: String, + + /** + * ID for backward button label, which must be translated for display. + * + * Undefined if the visible page has no backward-navigation button. + * + * @type {string|undefined} + */ + backwardButtonTextId: String, + + /** + * ID for text of main UI Page heading. + * + * @type {string} + */ + headerId: String, + + /** + * ID for text of main UI Page message body. + * + * @type {string} + */ + messageId: String, + + /** + * Translated text to display on the forward-naviation button. + * + * Undefined if the visible page has no forward-navigation button. + * + * @type {string|undefined} + */ + forwardButtonText: { + type: String, + computed: 'computeLocalizedText_(forwardButtonTextId)', + }, + + /** + * Translated text to display on the backward-naviation button. + * + * Undefined if the visible page has no backward-navigation button. + * + * @type {string|undefined} + */ + backwardButtonText: { + type: String, + computed: 'computeLocalizedText_(backwardButtonTextId)', + }, + + /** + * Translated text of main UI Page heading. + * + * @type {string|undefined} + */ + headerText: { + type: String, + computed: 'computeLocalizedText_(headerId)', + }, + + /** + * Translated text of main UI Page heading. In general this can include + * some markup. + * + * @type {string|undefined} + */ + messageHtml: { + type: String, + computed: 'computeLocalizedText_(messageId)', + }, + }, + + /** + * @param {string} textId Key for the localized string to appear on a + * button. + * @return {string|undefined} The localized string corresponding to the key + * textId. Return value is undefined if textId is not a key + * for any localized string. Note: this includes the case in which + * textId is undefined. + * @private + */ + computeLocalizedText_: function(textId) { + if (!this.i18nExists(textId)) + return; + return this.i18nAdvanced( + textId, {attrs: {'id': (node, value) => node.tagName == 'A'}}); + }, +}; + +/** @polymerBehavior */ +const UiPageContainerBehavior = [ + I18nBehavior, + UiPageContainerBehaviorImpl, +];
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css b/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css index 8914d86..ff35ec5 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css +++ b/chrome/browser/resources/chromeos/wallpaper_manager/css/wallpaper_manager.css
@@ -387,7 +387,6 @@ /**** New wallpaper picker rules **********************************************/ body.v2 { - -webkit-app-region: no-drag; background-color: transparent; } @@ -437,7 +436,7 @@ } .v2 #categories-list > li > div { - color: rgb(128, 134, 139); + color: rgb(95, 99, 104); font-family: 'Roboto'; font-size: 13px; font-weight: 500; @@ -478,49 +477,62 @@ } .v2 .image-picker [role=listitem][selected]:not(.daily-refresh-item) { - -webkit-margin-end: 24px; - -webkit-margin-start: 16px; - height: 128px; - margin-bottom: 24px; - margin-top: 16px; - width: 128px; + border-radius: none; +} + +.v2 .image-picker:not([disabled]) [role=listitem]:hover { + outline: none; } .v2 .image-picker img { height: unset; - overflow: hidden; position: absolute; width: unset; } +.v2 .image-picker .check { + background-image: url(../images/ui/selected_wallpaper_checkmark.svg); + background-repeat: no-repeat; + left: 128px; + top: 8px; +} + +.v2 .image-picker .selected-grid-border { + border: solid 16px #fff; + box-sizing: border-box; + height: 100%; + position: absolute; + width: 100%; +} + .v2 #window-close-button { display: none; } -.v2 #minimize-button { - background-image: url(../images/ui/button_minimize.png); +.v2 #dialog-header > div { + -webkit-app-region: no-drag; display: block; height: 24px; position: absolute; - right: 38px; width: 24px; } +.v2 #minimize-button { + background-image: url(../images/ui/button_minimize.png); + right: 38px; +} + .v2 #close-button { background-image: url(../images/ui/button_close.png); - display: block; - height: 24px; - position: absolute; right: 8px; - width: 24px; } -.v2 .image-picker .check, .v2 .bottom-bar { display: none; } .v2 #top-header { + -webkit-app-region: no-drag; background-color: #fff; border-radius: 0 0 48px 48px; display: flex; @@ -555,8 +567,8 @@ -webkit-padding-start: 32px; background-image: url(../images/ui/left_arrow.svg); background-repeat: no-repeat; - background-size: 16px 16px; - margin-top: 14px; + background-size: 20px 20px; + margin-top: 12px; } .v2 .top-header-contents #image-title { @@ -724,7 +736,7 @@ .v2 #current-wallpaper-info-bar { background-color: #fff; border-radius: 4px; - bottom: 0; + bottom: 8px; box-shadow: 0 0 60px; display: flex; flex-direction: row; @@ -766,6 +778,7 @@ .v2 #current-wallpaper-description { color: rgb(32, 33, 36); font-size: 12px; + font-weight: 400; padding-top: 14px; width: 320px; } @@ -778,6 +791,10 @@ font-size: 12px; } +.v2 #current-wallpaper-more-options.online-wallpaper { + -webkit-margin-start: 54px; +} + .v2 #current-wallpaper-more-options > div { border: 1px solid; border-radius: 32px; @@ -826,7 +843,7 @@ visibility: visible; } -body:not([surprise-me-disabled]).v2 [role=listitem]:not(.daily-refresh-item) { - opacity: 0.6; - pointer-events: none; +.v2 .progress-bar { + bottom: 0; + top: unset; }
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/left_arrow.svg b/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/left_arrow.svg index 19ba2936..b6c74718 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/left_arrow.svg +++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/left_arrow.svg
@@ -1,3 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> - <path fill="#1A73E8" fill-rule="evenodd" d="M18.0415385,11 L2.76923077,11 C2.32122209,11.1648505 2,11.4746322 2,11.9968 C2,12.5189678 2.32122209,12.8351495 2.76923077,13 L18.0307692,13 L11.5676923,19.9184 C11.3685877,20.1551278 11.2338462,20.4615817 11.2338462,20.8016 C11.2338462,21.464 11.7476923,22 12.3830769,22 C12.7744633,22 13.1206827,21.7956341 13.3279245,21.4829586 L21.4954803,13 C21.7999809,12.7838963 22,12.4088436 22,11.9968 C22,11.599147 21.8137082,11.2183924 21.5269978,11 L13.2824295,2.45372669 C13.0720283,2.17730426 12.7470873,2 12.3830769,2 C11.7476923,2 11.2338462,2.5376 11.2338462,3.2 C11.2338462,3.5377701 11.3674549,3.84309007 11.5826573,4.0612278 L18.0415385,10.7968 L18.0415385,11 Z" transform="matrix(-1 0 0 1 24 0)"/> + <g fill="none" fill-rule="evenodd"> + <polygon points="0 0 24 0 24 24 0 24"/> + <polygon fill="#1A73E8" fill-rule="nonzero" points="20 11 7.83 11 13.42 5.41 12 4 4 12 12 20 13.41 18.59 7.83 13 20 13"/> + </g> </svg>
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/selected_wallpaper_checkmark.svg b/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/selected_wallpaper_checkmark.svg new file mode 100644 index 0000000..cc5d5c7 --- /dev/null +++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/ui/selected_wallpaper_checkmark.svg
@@ -0,0 +1,18 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> + <defs> + <filter id="ic_check_circle-1x-a" width="154.2%" height="154.2%" x="-27.1%" y="-22.9%" filterUnits="objectBoundingBox"> + <feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="2"/> + <feColorMatrix in="shadowBlurOuter1" result="shadowMatrixOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.34 0"/> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"/> + <feMergeNode in="SourceGraphic"/> + </feMerge> + </filter> + </defs> + <g fill="none" fill-rule="evenodd" filter="url(#ic_check_circle-1x-a)"> + <polygon points="0 0 24 0 24 24 0 24"/> + <path fill="#1A73E8" d="M12,2 C6.48,2 2,6.48 2,12 C2,17.52 6.48,22 12,22 C17.52,22 22,17.52 22,12 C22,6.48 17.52,2 12,2 L12,2 Z"/> + <polygon fill="#FFF" points="10 17 5 12 6.41 10.59 10 14.17 17.59 6.58 19 8"/> + </g> +</svg>
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js index a58cc15..c64081bd 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js +++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
@@ -81,7 +81,7 @@ if (this.thumbnail_) { this.appendChild(this.thumbnail_); - this.callback_(this.dataModelId_); + this.callback_(this.dataModelId_, this.dataItem.wallpaperId); return; } @@ -488,15 +488,47 @@ pendingItemComplete: function(dataModelId, opt_wallpaperId, opt_thumbnail) { if (dataModelId != this.dataModelId_) return; - this.pendingItems_--; - if (opt_wallpaperId != null) + --this.pendingItems_; + if (opt_wallpaperId && opt_thumbnail) this.thumbnailList_[opt_wallpaperId] = opt_thumbnail; - if (opt_thumbnail && this.useNewWallpaperPicker_) { + if (opt_thumbnail && this.useNewWallpaperPicker_) this.cropImageToFitGrid_(opt_thumbnail); - if (this.isShowingDailyRefresh) - this.cacheDailyRefreshThumbnailImages_(opt_thumbnail); + if (this.isShowingDailyRefresh) { + var dailyRefreshItemReady = this.dailyRefreshItem && + this.dailyRefreshImages.length >= DAILY_REFRESH_IMAGES_NUM; + if (!dailyRefreshItemReady) { + // The daily refresh item may not be created at this point (e.g. when + // user starts with the bottom of the list and scrolls upward). This + // list is used to store the wallpaper ids that will be added to the + // daily refresh item later. + if (!this.pendingImageIds_) + this.pendingImageIds_ = []; + + if (opt_wallpaperId && opt_thumbnail) { + this.pendingImageIds_.push(opt_wallpaperId); + // Cache the image thumbnail so that it can be retrieved when the + // daily refresh item is created, or the next time the user clicks + // on this collection. + var slideShowImage = opt_thumbnail.cloneNode(true /*deep=*/); + this.dailyRefreshCacheList_[opt_wallpaperId] = slideShowImage; + } else if ( + opt_wallpaperId && + opt_wallpaperId in this.dailyRefreshCacheList_) { + // The thumbnail is expected to exist in the cache when the user + // clicks on a collection more than once. + this.pendingImageIds_.push(opt_wallpaperId); + } + + if (this.dailyRefreshItem) { + for (var wallpaperId of this.pendingImageIds_) { + this.addImageToDailyRefreshItem_( + this.dailyRefreshCacheList_[wallpaperId]); + } + this.pendingImageIds_ = []; + } + } } if (this.pendingItems_ == 0) { @@ -504,15 +536,10 @@ window.clearTimeout(this.spinnerTimeout_); this.spinnerTimeout_ = 0; $('spinner-container').hidden = true; - if (this.useNewWallpaperPicker_) { - // TODO(crbug.com/812725): Decide what to show in the top header bar - // if the current wallpaper in use was not selected from the picker. - // For now, show the info of the first wallpaper in this collection. - var startingIndex = this.isShowingDailyRefresh ? 1 : 0; - wallpaperManager.setWallpaperAttribution( - this.dataModel.item(startingIndex)); - if (this.isShowingDailyRefresh) - this.decorateDailyRefreshItem_(); + // Start the slideshow. + if (this.dailyRefreshItem) { + window.clearTimeout(this.dailyRefreshTimer_); + this.showNextImage_(0); } } }, @@ -603,11 +630,22 @@ } // Clears previous checkmark. - if (this.checkmark_.parentNode) - this.checkmark_.parentNode.removeChild(this.checkmark_); - + var previousSelectedGridItem = this.checkmark_.parentNode; + if (previousSelectedGridItem) { + previousSelectedGridItem.removeChild(this.checkmark_); + var border = + previousSelectedGridItem.querySelector('.selected-grid-border'); + if (border) + previousSelectedGridItem.removeChild(border); + } if (!selectedGridItem) return; + + if (this.useNewWallpaperPicker_) { + var selectedGridBorder = cr.doc.createElement('div'); + selectedGridBorder.classList.add('selected-grid-border'); + selectedGridItem.appendChild(selectedGridBorder); + } selectedGridItem.appendChild(this.checkmark_); }, @@ -629,31 +667,13 @@ }, /** - * Initializes the UI of the daily refresh item. + * Adds an image to the daily refresh slideshow. + * @param {Object} slideShowImage The image to be used in the slideshow. * @private */ - decorateDailyRefreshItem_: function() { - if (!this.isShowingDailyRefresh || !this.dailyRefreshItem || - this.dailyRefreshImages.length >= DAILY_REFRESH_IMAGES_NUM || - this.dailyRefreshCacheList_.length == 0) { - return; - } - + addImageToDailyRefreshItem_: function(slideShowImage) { this.dailyRefreshItem.classList.add('daily-refresh-item'); - // Randomly select images from the cache list. - var startingIndex = - Math.floor(this.dailyRefreshCacheList_.length * Math.random()); - var imageCount = Math.min( - DAILY_REFRESH_IMAGES_NUM, this.dailyRefreshCacheList_.length); - for (var i = 0; i < imageCount; ++i) { - var index = (startingIndex + i) % this.dailyRefreshCacheList_.length; - var image = this.dailyRefreshCacheList_[index]; - image.classList.add('slide-show'); - image.style.opacity = 0; - this.dailyRefreshItem.appendChild(image); - } - // Add the daily refresh label and toggle. if (!this.dailyRefreshItem.querySelector('.daily-refresh-banner')) { var dailyRefreshBanner = document.querySelector('.daily-refresh-banner') @@ -667,8 +687,11 @@ this.dailyRefreshItem.appendChild(dailyRefreshBanner); } - window.clearTimeout(this.dailyRefreshTimer_); - this.showNextImage_(0); + slideShowImage.style.opacity = + this.dailyRefreshImages.length == 0 ? 1 : 0; + slideShowImage.classList.add('slide-show'); + this.dailyRefreshItem.insertBefore( + slideShowImage, this.dailyRefreshItem.firstChild); }, /** @@ -702,9 +725,8 @@ // The active thumbnail maybe deleted in the above redraw(). Sets it again // to make sure checkmark shows correctly. this.updateActiveThumb_(); - // Show the info bar only when the scroll bar is at the top. - $('current-wallpaper-info-bar') - .classList.toggle('show-info-bar', this.scrollTop == 0); + if (this.useNewWallpaperPicker_) + wallpaperManager.onScrollPositionChanged(this.scrollTop); } };
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js index 73cdebb..35e24058 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js +++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
@@ -439,6 +439,16 @@ $('close-button').addEventListener('click', function() { window.close(); }); + this.document_.querySelector('.dialog-topbar') + .addEventListener('mouseover', () => { + this.isHoveringOverCollection_ = true; + this.updateInfoBarVisibility_(); + }); + this.document_.querySelector('.dialog-topbar') + .addEventListener('mouseleave', () => { + this.isHoveringOverCollection_ = false; + this.updateInfoBarVisibility_(); + }); } else { $('window-close-button').addEventListener('click', function() { window.close(); @@ -565,8 +575,7 @@ }); } - if (this.useNewWallpaperPicker_) - this.decorateCurrentWallpaperInfoBar_(); + this.decorateCurrentWallpaperInfoBar_(); this.onResize_(); this.initContextMenuAndCommand_(); WallpaperUtil.testSendMessage('launched'); @@ -702,6 +711,9 @@ * @private */ WallpaperManager.prototype.decorateCurrentWallpaperInfoBar_ = function() { + // The info bar only exists in new wallpaper picker. + if (!this.useNewWallpaperPicker_) + return; var image = $('current-wallpaper-image'); var currentWallpaperInfo; Object.values(this.imagesInfoMap_).forEach(imagesInfo => { @@ -768,7 +780,7 @@ } $('current-wallpaper-more-options') - .classList.toggle('custom-wallpaper', !foundImageInList); + .classList.toggle('online-wallpaper', foundImageInList); if (foundImageInList) { // Initialize the image title and description. $('current-wallpaper-title').textContent = @@ -800,6 +812,8 @@ onError); }, onError); } + + this.updateInfoBarVisibility_(); }; /** @@ -856,6 +870,7 @@ activeItem, currentWallpaperURL) { this.wallpaperGrid_.activeItem = activeItem; this.currentWallpaper_ = currentWallpaperURL; + this.decorateCurrentWallpaperInfoBar_(); // Hides the wallpaper set by message. $('wallpaper-set-by-message').textContent = ''; $('wallpaper-grid').classList.remove('small'); @@ -1519,14 +1534,12 @@ else this.document_.body.setAttribute('surprise-me-disabled', ''); - if (this.useNewWallpaperPicker_) { - // The surprise me state affects the UI of the current wallpaper info bar. - this.decorateCurrentWallpaperInfoBar_(); - } else { - // On the old wallpaper picker, the wallpaper grid should be disabled when - // surprise me is enabled. + // The surprise me state affects the UI of the current wallpaper info bar. + this.decorateCurrentWallpaperInfoBar_(); + // On the old wallpaper picker, the wallpaper grid should be disabled when + // surprise me is enabled. + if (!this.useNewWallpaperPicker_) $('wallpaper-grid').disabled = enabled; - } }; /** @@ -1723,4 +1736,29 @@ chrome.app.window.current().isMaximized()); }; +/** + * Notifies the wallpaper manager that the scroll bar position changes. + * @param {number} scrollTop The distance between the scroll bar and the top. + */ +WallpaperManager.prototype.onScrollPositionChanged = function(scrollTop) { + var isScrollAtTop = scrollTop == 0; + if (this.isScrollAtTop_ !== isScrollAtTop) { + this.isScrollAtTop_ = isScrollAtTop; + this.updateInfoBarVisibility_(); + } +}; + +/** + * Updates the visibility of the current wallpaper info bar. + * @private + */ +WallpaperManager.prototype.updateInfoBarVisibility_ = function() { + var isWindowMaximized = chrome.app.window.current().isMaximized() || + chrome.app.window.current().isFullscreen(); + var shouldShowInfoBar = this.useNewWallpaperPicker_ && this.isScrollAtTop_ && + (!this.isHoveringOverCollection_ || isWindowMaximized); + $('current-wallpaper-info-bar') + .classList.toggle('show-info-bar', shouldShowInfoBar); +}; + })();
diff --git a/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html b/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html index b5032db..97ddcc1 100644 --- a/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html +++ b/chrome/browser/resources/pdf/elements/viewer-error-screen/viewer-error-screen.html
@@ -1,18 +1,16 @@ <link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> -<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <dom-module id="viewer-error-screen"> <template> - <style include="cr-shared-style"></style> + <style include="paper-button-style cr-hidden-style"></style> <cr-dialog id="dialog" no-cancel> - <div slot="title"> - [[strings.errorDialogTitle]] - </div> - <div slot="body"> - [[strings.pageLoadFailed]] - </div> + <div slot="title">[[strings.errorDialogTitle]]</div> + <div slot="body">[[strings.pageLoadFailed]]</div> <div slot="button-container" hidden$="[[!reloadFn]]"> <paper-button class="action-button" on-click="reload"> [[strings.pageReload]]
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html index fd848a6..7cb5fa0d 100644 --- a/chrome/browser/resources/print_preview/new/app.html +++ b/chrome/browser/resources/print_preview/new/app.html
@@ -100,6 +100,10 @@ disabled="[[controlsDisabled_]]" available="[[settings.color.available]]"> </print-preview-color-settings> + <print-preview-more-settings settings-expanded="{{settingsExpanded_}}" + disabled="[[controlsDisabled_]]" + available="[[shouldShowMoreSettings_]]"> + </print-preview-more-settings> <print-preview-media-size-settings settings="{{settings}}" capability="[[destination_.capabilities.printer.media_size]]" disabled="[[controlsDisabled_]]" @@ -116,19 +120,16 @@ </print-preview-dpi-settings> <print-preview-scaling-settings settings="{{settings}}" document-info="[[documentInfo_]]" disabled="[[controlsDisabled_]]" - available="[[settings.scaling.available]]"> + available="[[settings.scaling.available]]" collapsible> </print-preview-scaling-settings> <print-preview-other-options-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - available="[[settings.otherOptions.available]]"> + available="[[settings.otherOptions.available]]" collapsible> </print-preview-other-options-settings> <print-preview-advanced-options-settings settings="{{settings}}" destination="[[destination_]]" disabled="[[controlsDisabled_]]" available="[[settings.vendorItems.available]]" collapsible> </print-preview-advanced-options-settings> - <print-preview-more-settings settings-expanded="{{settingsExpanded_}}" - hidden$="[[!shouldShowMoreSettings_]]" available> - </print-preview-more-settings> <if expr="not chromeos"> <print-preview-link-container destination="[[destination_]]" app-kiosk-mode="[[isInAppKioskMode_]]"
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js index ff4979d..229656f 100644 --- a/chrome/browser/resources/print_preview/new/app.js +++ b/chrome/browser/resources/print_preview/new/app.js
@@ -520,7 +520,7 @@ section.hide(); return; } - section.show(this.settingsExpanded_); + section.show(); }); },
diff --git a/chrome/browser/resources/print_preview/new/destination_dialog.js b/chrome/browser/resources/print_preview/new/destination_dialog.js index c8858ac3..64e24b2 100644 --- a/chrome/browser/resources/print_preview/new/destination_dialog.js +++ b/chrome/browser/resources/print_preview/new/destination_dialog.js
@@ -90,6 +90,8 @@ attrs: { 'is': (node, v) => v == 'action-link', 'class': (node, v) => v == 'sign-in', + 'tabindex': (node, v) => v == '0', + 'role': (node, v) => v == 'link', }, }); },
diff --git a/chrome/browser/resources/print_preview/new/link_container.html b/chrome/browser/resources/print_preview/new/link_container.html index 606aa34..48b03a85 100644 --- a/chrome/browser/resources/print_preview/new/link_container.html +++ b/chrome/browser/resources/print_preview/new/link_container.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/html/action_link.html"> <link rel="import" href="chrome://resources/html/action_link_css.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="../data/destination.html">
diff --git a/chrome/browser/resources/print_preview/new/more_settings.html b/chrome/browser/resources/print_preview/new/more_settings.html index bde1584..2e8e078 100644 --- a/chrome/browser/resources/print_preview/new/more_settings.html +++ b/chrome/browser/resources/print_preview/new/more_settings.html
@@ -1,28 +1,28 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/html/action_link.html"> <link rel="import" href="chrome://resources/html/action_link_css.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="settings_section_behavior.html"> <dom-module id="print-preview-more-settings"> <template> - <style include="action-link"> - :host { + <style include="action-link cr-hidden-style"> + :host([available]) { align-items: center; - background-color: #f6f6f6; - border-bottom: 1px solid #e1e1e1; - border-top: 1px solid #e1e1e1; display: flex; margin-top: 4px; - min-height: 51px; + min-height: 38px; + padding: 5px 20px; + } + + :host #label { + margin: 0 10px; } :host #icon { - -webkit-margin-end: 15px; - -webkit-margin-start: 20px; height: 11px; - margin-bottom: 20px; - margin-top: 20px; width: 11px; } @@ -39,7 +39,7 @@ } </style> <div id="icon" class$="[[getIconClass_(settingsExpanded)]]"></div> - <a class="label" is="action-link" on-click="onMoreSettingsClick_"> + <a id="label" is="action-link" disabled="[[disabled]]"> [[getLabelText_(settingsExpanded)]] </a> </template>
diff --git a/chrome/browser/resources/print_preview/new/more_settings.js b/chrome/browser/resources/print_preview/new/more_settings.js index 7dea7cc..5efde4a 100644 --- a/chrome/browser/resources/print_preview/new/more_settings.js +++ b/chrome/browser/resources/print_preview/new/more_settings.js
@@ -12,6 +12,12 @@ type: Boolean, notify: true, }, + + disabled: Boolean, + }, + + listeners: { + 'click': 'onMoreSettingsClick_', }, /**
diff --git a/chrome/browser/resources/print_preview/new/number_settings_section.html b/chrome/browser/resources/print_preview/new/number_settings_section.html index 4e86bc5..7eeb79e 100644 --- a/chrome/browser/resources/print_preview/new/number_settings_section.html +++ b/chrome/browser/resources/print_preview/new/number_settings_section.html
@@ -1,6 +1,5 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="input_css.html"> <link rel="import" href="input_behavior.html"> <link rel="import" href="print_preview_shared_css.html"> @@ -8,7 +7,7 @@ <dom-module id="print-preview-number-settings-section"> <template> - <style include="print-preview-shared input cr-hidden-style"> + <style include="print-preview-shared input"> input[type='number'] { -webkit-margin-end: 16px; margin-bottom: 2.5px; @@ -33,7 +32,7 @@ <span slot="title" id="section-title">[[inputLabel]]</span> <div slot="controls"> <slot name="opt-outside-content"></slot> - <span class="input-wrapper" hidden="[[hideInput]]"> + <span class="input-wrapper"> <input id="userValue" class="user-value" type="number" max="[[maxValue]]" min="[[minValue]]" data-timeout-delay="250" disabled$="[[getDisabled_(inputValid, disabled)]]"
diff --git a/chrome/browser/resources/print_preview/new/number_settings_section.js b/chrome/browser/resources/print_preview/new/number_settings_section.js index f055173..1e52be5 100644 --- a/chrome/browser/resources/print_preview/new/number_settings_section.js +++ b/chrome/browser/resources/print_preview/new/number_settings_section.js
@@ -40,8 +40,6 @@ hintMessage: String, disabled: Boolean, - - hideInput: Boolean, }, listeners: {
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.html b/chrome/browser/resources/print_preview/new/other_options_settings.html index 8e2196d..1484f097 100644 --- a/chrome/browser/resources/print_preview/new/other_options_settings.html +++ b/chrome/browser/resources/print_preview/new/other_options_settings.html
@@ -17,34 +17,38 @@ <print-preview-settings-section class="multirow-controls"> <span slot="title" id="options-label">$i18n{optionsLabel}</span> <div slot="controls" class="checkbox"> - <label id="headerFooterContainer" aria-live="polite"> + <label hidden$="[[!settings.headerFooter.available]]" + aria-live="polite"> <input type="checkbox" id="headerFooter" disabled$="[[disabled]]" on-change="onHeaderFooterChange_" checked$="[[settings.headerFooter.value]]"> <span>$i18n{optionHeaderFooter}</span> </label> - <label id="duplexContainer" aria-live="polite"> + <label hidden$="[[!settings.duplex.available]]" + aria-live="polite"> <input type="checkbox" id="duplex" on-change="onDuplexChange_" disabled$="[[disabled]]" checked$="[[settings.duplex.value]]"> <span>$i18n{optionTwoSided}</span> </label> - <label id="cssBackgroundContainer" aria-live="polite"> + <label hidden$="[[!settings.cssBackground.available]]" + aria-live="polite"> <input type="checkbox" id="cssBackground" on-change="onCssBackgroundChange_" disabled$="[[disabled]]" checked$="[[settings.cssBackground.value]]"> <span>$i18n{optionBackgroundColorsAndImages}</span> </label> - <label id="rasterizeContainer" aria-live="polite"> + <label hidden$="[[!settings.rasterize.available]]" + aria-live="polite"> <input type="checkbox" id="rasterize" disabled$="[[disabled]]" on-change="onRasterizeChange_" checked$="[[settings.rasterize.value]]"> <span>$i18n{optionRasterize}</span> </label> - <label id="selectionOnlyContainer" aria-live="polite" - hidden$="[[!settings.selectionOnly.available]]"> + <label hidden$="[[!settings.selectionOnly.available]]" + aria-live="polite"> <input type="checkbox" id="selectionOnly" disabled$="[[disabled]]" on-change="onSelectionOnlyChange_"
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.js b/chrome/browser/resources/print_preview/new/other_options_settings.js index cf685686..efb0cae 100644 --- a/chrome/browser/resources/print_preview/new/other_options_settings.js +++ b/chrome/browser/resources/print_preview/new/other_options_settings.js
@@ -20,28 +20,6 @@ ], /** - * @param {boolean} showCollapsible Whether collapsible content should be - * shown. - */ - show: function(showCollapsible) { - const duplexAvailable = this.getSetting('duplex').available; - if (!showCollapsible && !duplexAvailable) { - this.hidden = true; - return; - } - this.$.headerFooterContainer.hidden = - !this.getSetting('headerFooter').available || !showCollapsible; - this.$.duplexContainer.hidden = !duplexAvailable; - this.$.cssBackgroundContainer.hidden = - !this.getSetting('cssBackground').available || !showCollapsible; - this.$.rasterizeContainer.hidden = - !this.getSetting('rasterize').available || !showCollapsible; - this.$.selectionOnlyContainer.hidden = - !this.getSetting('selectionOnly').available || !showCollapsible; - this.hidden = false; - }, - - /** * @param {boolean} value The new value of the header footer setting. * @private */
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.html b/chrome/browser/resources/print_preview/new/scaling_settings.html index b7c5bc5..1c4208a 100644 --- a/chrome/browser/resources/print_preview/new/scaling_settings.html +++ b/chrome/browser/resources/print_preview/new/scaling_settings.html
@@ -15,8 +15,9 @@ default-value="100" input-label="$i18n{scalingLabel}" disabled="[[disabled]]" current-value="{{currentValue_}}" input-valid="{{inputValid_}}" hint-message="$i18n{scalingInstruction}" - hide-input="[[hideInput_]]" class="multirow-controls"> - <div slot="opt-outside-content" class="checkbox" id="fitToPageContainer"> + class="multirow-controls"> + <div slot="opt-outside-content" class="checkbox" + hidden$="[[!settings.fitToPage.available]]"> <label aria-live="polite"> <input type="checkbox" id="fit-to-page-checkbox" disabled$="[[getDisabled_(disabled, inputValid_)]]"
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.js b/chrome/browser/resources/print_preview/new/scaling_settings.js index 171309a..6f33df9 100644 --- a/chrome/browser/resources/print_preview/new/scaling_settings.js +++ b/chrome/browser/resources/print_preview/new/scaling_settings.js
@@ -36,21 +36,6 @@ 'onScalingSettingChanged_(settings.scaling.value)', ], - /** - * @param {boolean} showCollapsible Whether collapsible content should be - * shown. - */ - show: function(showCollapsible) { - const fitToPageAvailable = this.getSetting('fitToPage').available; - if (!fitToPageAvailable && !showCollapsible) { - this.hidden = true; - return; - } - this.$.fitToPageContainer.hidden = !fitToPageAvailable; - this.hideInput_ = !showCollapsible || !this.getSetting('scaling').available; - this.hidden = false; - }, - /** @private */ onFitToPageSettingChange_: function() { const fitToPage = this.getSetting('fitToPage');
diff --git a/chrome/browser/resources/print_preview/new/settings_section_behavior.js b/chrome/browser/resources/print_preview/new/settings_section_behavior.js index 1f58c5d..74d7e5a 100644 --- a/chrome/browser/resources/print_preview/new/settings_section_behavior.js +++ b/chrome/browser/resources/print_preview/new/settings_section_behavior.js
@@ -21,11 +21,7 @@ }, }, - /** - * @param {boolean} showCollapsible Whether collapsible subsections - * should be shown. - */ - show: function(showCollapsible) { + show: function() { this.hidden = false; },
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc index 49cef0d..6d18cbb0 100644 --- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc +++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -155,7 +155,7 @@ BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&CheckClientDownloadRequest::Cancel, - weakptr_factory_.GetWeakPtr()), + weakptr_factory_.GetWeakPtr(), false), base::TimeDelta::FromMilliseconds( service_->download_request_timeout_ms())); } @@ -163,7 +163,7 @@ // Canceling a request will cause us to always report the result as // DownloadCheckResult::UNKNOWN unless a pending request is about to call // FinishRequest. -void CheckClientDownloadRequest::Cancel() { +void CheckClientDownloadRequest::Cancel(bool download_destroyed) { DCHECK_CURRENTLY_ON(BrowserThread::UI); cancelable_task_tracker_.TryCancelAll(); if (loader_.get()) { @@ -176,7 +176,9 @@ // reference to this object. We'll eventually wind up in some method on // the UI thread that will call FinishRequest() again. If FinishRequest() // is called a second time, it will be a no-op. - FinishRequest(DownloadCheckResult::UNKNOWN, REASON_REQUEST_CANCELED); + FinishRequest(DownloadCheckResult::UNKNOWN, download_destroyed + ? REASON_DOWNLOAD_DESTROYED + : REASON_REQUEST_CANCELED); // Calling FinishRequest might delete this object, we may be deleted by // this point. } @@ -184,7 +186,7 @@ // download::DownloadItem::Observer implementation. void CheckClientDownloadRequest::OnDownloadDestroyed( download::DownloadItem* download) { - Cancel(); + Cancel(/*download_destroyed=*/true); DCHECK(item_ == NULL); } @@ -1061,7 +1063,8 @@ << " result:" << static_cast<int>(result); UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", reason, REASON_MAX); - callback_.Run(result); + if (reason != REASON_DOWNLOAD_DESTROYED) + callback_.Run(result); item_->RemoveObserver(this); item_ = NULL; DownloadProtectionService* service = service_;
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h index b98642e..8c6b2c91 100644 --- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h +++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
@@ -57,7 +57,10 @@ bool ShouldSampleUnsupportedFile(const base::FilePath& filename); void Start(); void StartTimeout(); - void Cancel(); + + // |download_destroyed| indicates if cancellation is due to the destruction of + // the download item. + void Cancel(bool download_destroyed); void OnDownloadDestroyed(download::DownloadItem* download) override; void OnURLLoaderComplete(std::unique_ptr<std::string> response_body); static bool IsSupportedDownload(const download::DownloadItem& item,
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc index 67e32ec..097cfb6 100644 --- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc +++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -200,7 +200,7 @@ // We need to advance the iterator before we cancel because canceling // the request will invalidate it when RequestFinished is called below. scoped_refptr<CheckClientDownloadRequest> tmp = *it++; - tmp->Cancel(); + tmp->Cancel(/*download_destropyed=*/false); } DCHECK(download_requests_.empty());
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc index 179c3f4..1d00980d 100644 --- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc +++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -2026,7 +2026,9 @@ // notification. } - EXPECT_TRUE(IsResult(DownloadCheckResult::UNKNOWN)); + // When download is destroyed, no need to check for client download request + // result. + EXPECT_FALSE(has_result_); EXPECT_FALSE(HasClientDownloadRequest()); } @@ -2054,13 +2056,13 @@ tmp_path_, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(0); - RunLoop run_loop; download_service_->CheckClientDownload( - item.get(), base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, - base::Unretained(this), run_loop.QuitClosure())); + item.get(), + base::BindRepeating(&DownloadProtectionServiceTest::SyncCheckDoneCallback, + base::Unretained(this))); + base::RunLoop().RunUntilIdle(); - run_loop.Run(); - EXPECT_TRUE(IsResult(DownloadCheckResult::UNKNOWN)); + EXPECT_FALSE(has_result_); EXPECT_FALSE(HasClientDownloadRequest()); }
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_util.h b/chrome/browser/safe_browsing/download_protection/download_protection_util.h index 0874e8dd..c8740c51 100644 --- a/chrome/browser/safe_browsing/download_protection/download_protection_util.h +++ b/chrome/browser/safe_browsing/download_protection/download_protection_util.h
@@ -53,6 +53,7 @@ REASON_REMOTE_FILE = 25, REASON_SAMPLED_UNSUPPORTED_FILE = 26, REASON_VERDICT_UNKNOWN = 27, + REASON_DOWNLOAD_DESTROYED = 28, REASON_MAX // Always add new values before this one. };
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc index 6a818b0..41c4825 100644 --- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc +++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
@@ -61,7 +61,7 @@ void PreferenceValidationDelegate::OnAtomicPreferenceValidation( const std::string& pref_path, - std::unique_ptr<base::Value> value, + base::Optional<base::Value> value, ValueState value_state, ValueState external_validation_value_state, bool is_personal) { @@ -71,9 +71,9 @@ std::unique_ptr<TPIncident> incident( new ClientIncidentReport_IncidentData_TrackedPreferenceIncident()); incident->set_path(pref_path); - if (!value || - (!value->GetAsString(incident->mutable_atomic_value()) && - !base::JSONWriter::Write(*value, incident->mutable_atomic_value()))) { + if (!value || (!value->GetAsString(incident->mutable_atomic_value()) && + !base::JSONWriter::Write( + std::move(*value), incident->mutable_atomic_value()))) { incident->clear_atomic_value(); } incident->set_value_state(proto_value_state);
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h index 2cf2027..c7cbe220 100644 --- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h +++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h
@@ -9,6 +9,8 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/optional.h" +#include "base/values.h" #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h" class Profile; @@ -32,7 +34,7 @@ // TrackedPreferenceValidationDelegate methods. void OnAtomicPreferenceValidation( const std::string& pref_path, - std::unique_ptr<base::Value> value, + base::Optional<base::Value> value, prefs::mojom::TrackedPreferenceValidationDelegate::ValueState value_state, prefs::mojom::TrackedPreferenceValidationDelegate::ValueState external_validation_value_state,
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc index 60c4ad2..8028da8 100644 --- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc +++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
@@ -100,9 +100,9 @@ // Tests that a NULL value results in an incident with no value. TEST_F(PreferenceValidationDelegateTest, NullValue) { - instance_->OnAtomicPreferenceValidation(kPrefPath, NULL, ValueState::CLEARED, - ValueState::UNSUPPORTED, - false /* is_personal */); + instance_->OnAtomicPreferenceValidation( + kPrefPath, base::nullopt, ValueState::CLEARED, ValueState::UNSUPPORTED, + false /* is_personal */); std::unique_ptr<safe_browsing::ClientIncidentReport_IncidentData> incident( incidents_.back()->TakePayload()); EXPECT_FALSE(incident->tracked_preference().has_atomic_value()); @@ -126,36 +126,35 @@ expected_value_ = std::get<1>(GetParam()); } - static std::unique_ptr<base::Value> MakeValue(base::Value::Type value_type) { + static base::Value MakeValue(base::Value::Type value_type) { using base::Value; switch (value_type) { case Value::Type::NONE: - return std::make_unique<base::Value>(); + return Value(); case Value::Type::BOOLEAN: - return std::unique_ptr<Value>(new base::Value(false)); + return Value(false); case Value::Type::INTEGER: - return std::unique_ptr<Value>(new base::Value(47)); + return Value(47); case Value::Type::DOUBLE: - return std::unique_ptr<Value>(new base::Value(0.47)); + return Value(0.47); case Value::Type::STRING: - return std::unique_ptr<Value>(new base::Value("i have a spleen")); + return Value("i have a spleen"); case Value::Type::DICTIONARY: { - std::unique_ptr<base::DictionaryValue> value( - new base::DictionaryValue()); - value->SetInteger("twenty-two", 22); - value->SetInteger("forty-seven", 47); - return std::move(value); + Value value(base::Value::Type::DICTIONARY); + value.SetKey("twenty-two", Value(22)); + value.SetKey("forty-seven", Value(47)); + return value; } case Value::Type::LIST: { - std::unique_ptr<base::ListValue> value(new base::ListValue()); - value->AppendInteger(22); - value->AppendInteger(47); - return std::move(value); + Value value(base::Value::Type::LIST); + value.GetList().emplace_back(22); + value.GetList().emplace_back(47); + return value; } default: ADD_FAILURE() << "unsupported value type " << value_type; } - return std::unique_ptr<Value>(); + return Value(); } base::Value::Type value_type_; @@ -209,7 +208,7 @@ TEST_P(PreferenceValidationDelegateNoIncident, Atomic) { instance_->OnAtomicPreferenceValidation( - kPrefPath, std::make_unique<base::Value>(), value_state_, + kPrefPath, base::make_optional<base::Value>(), value_state_, external_validation_value_state_, false /* is_personal */); EXPECT_EQ(0U, incidents_.size()); } @@ -252,7 +251,7 @@ TEST_P(PreferenceValidationDelegateWithIncident, Atomic) { instance_->OnAtomicPreferenceValidation( - kPrefPath, std::make_unique<base::Value>(), value_state_, + kPrefPath, base::make_optional<base::Value>(), value_state_, external_validation_value_state_, is_personal_); ASSERT_EQ(1U, incidents_.size()); std::unique_ptr<safe_browsing::ClientIncidentReport_IncidentData> incident(
diff --git a/chrome/browser/signin/unified_consent_helper.cc b/chrome/browser/signin/unified_consent_helper.cc index 94a50b2..bc83ff1b 100644 --- a/chrome/browser/signin/unified_consent_helper.cc +++ b/chrome/browser/signin/unified_consent_helper.cc
@@ -7,15 +7,30 @@ #include "base/feature_list.h" #include "build/buildflag.h" #include "chrome/browser/signin/account_consistency_mode_manager.h" -#include "components/signin/core/browser/profile_management_switches.h" #include "components/signin/core/browser/signin_buildflags.h" -bool IsUnifiedConsentEnabled(Profile* profile) { +signin::UnifiedConsentFeatureState GetUnifiedConsentFeatureState( + Profile* profile) { + DCHECK(profile); #if BUILDFLAG(ENABLE_DICE_SUPPORT) // On Dice platforms, unified consent requires Dice to be enabled first. if (!AccountConsistencyModeManager::IsDiceEnabledForProfile(profile)) - return false; + return signin::UnifiedConsentFeatureState::kDisabled; #endif // BUILDFLAG(ENABLE_DICE_SUPPORT) - return base::FeatureList::IsEnabled(signin::kUnifiedConsent); + return signin::GetUnifiedConsentFeatureState(); +} + +bool IsUnifiedConsentEnabled(Profile* profile) { + DCHECK(profile); + signin::UnifiedConsentFeatureState feature_state = + GetUnifiedConsentFeatureState(profile); + return feature_state != signin::UnifiedConsentFeatureState::kDisabled; +} + +bool IsUnifiedConsentBumpEnabled(Profile* profile) { + DCHECK(profile); + signin::UnifiedConsentFeatureState feature_state = + GetUnifiedConsentFeatureState(profile); + return feature_state == signin::UnifiedConsentFeatureState::kEnabledWithBump; }
diff --git a/chrome/browser/signin/unified_consent_helper.h b/chrome/browser/signin/unified_consent_helper.h index 7e0481f..b8c4668 100644 --- a/chrome/browser/signin/unified_consent_helper.h +++ b/chrome/browser/signin/unified_consent_helper.h
@@ -5,9 +5,20 @@ #ifndef CHROME_BROWSER_SIGNIN_UNIFIED_CONSENT_HELPER_H_ #define CHROME_BROWSER_SIGNIN_UNIFIED_CONSENT_HELPER_H_ +#include "components/signin/core/browser/profile_management_switches.h" + class Profile; -// Returns true if unified consent is enabled for |profile|. +// Returns the state of the "Unified Consent" feature for |profile|. +signin::UnifiedConsentFeatureState GetUnifiedConsentFeatureState( + Profile* profile); + +// Returns true if the unified consent feature state is kEnabledNoBump or +// kEnabledWithBump. Note that the bump may not be enabled, even if this returns +// true. To check if the bump is enabled, use IsUnifiedConsentBumpEnabled(). bool IsUnifiedConsentEnabled(Profile* profile); +// Returns true if the unified consent feature state is kEnabledWithBump. +bool IsUnifiedConsentBumpEnabled(Profile* profile); + #endif // CHROME_BROWSER_SIGNIN_UNIFIED_CONSENT_HELPER_H_
diff --git a/chrome/browser/signin/unified_consent_helper_unittest.cc b/chrome/browser/signin/unified_consent_helper_unittest.cc new file mode 100644 index 0000000..85d370af --- /dev/null +++ b/chrome/browser/signin/unified_consent_helper_unittest.cc
@@ -0,0 +1,81 @@ +// Copyright 2018 The Chromium 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/signin/unified_consent_helper.h" + +#include "build/buildflag.h" +#include "chrome/test/base/testing_profile.h" +#include "components/signin/core/browser/scoped_account_consistency.h" +#include "components/signin/core/browser/scoped_unified_consent.h" +#include "components/signin/core/browser/signin_buildflags.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + +// On Dice platforms, unified consent can only be enabled for Dice profiles. +TEST(UnifiedConsentHelperTest, DiceDisabled) { + // Disable Dice. + signin::ScopedAccountConsistencyDiceFixAuthErrors dice_fix_auth_errors; + + content::TestBrowserThreadBundle thread_bundle; + TestingProfile profile; + + for (signin::UnifiedConsentFeatureState state : + {signin::UnifiedConsentFeatureState::kDisabled, + signin::UnifiedConsentFeatureState::kEnabledNoBump, + signin::UnifiedConsentFeatureState::kEnabledWithBump}) { + signin::ScopedUnifiedConsent scoped_state(state); + EXPECT_EQ(signin::UnifiedConsentFeatureState::kDisabled, + GetUnifiedConsentFeatureState(&profile)); + EXPECT_FALSE(IsUnifiedConsentEnabled(&profile)); + EXPECT_FALSE(IsUnifiedConsentBumpEnabled(&profile)); + } +} + +#endif + +// Checks that the feature state for the profile is the same as the global +// feature state. +TEST(UnifiedConsentHelperTest, FeatureState) { +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + // Enable Dice. + signin::ScopedAccountConsistencyDice dice; +#endif + + content::TestBrowserThreadBundle thread_bundle; + TestingProfile profile; + + // Unified consent is disabled by default. + EXPECT_EQ(signin::UnifiedConsentFeatureState::kDisabled, + GetUnifiedConsentFeatureState(&profile)); + + // The feature state for the profile is the same as the global feature state. + { + signin::ScopedUnifiedConsent scoped_disabled( + signin::UnifiedConsentFeatureState::kDisabled); + EXPECT_EQ(signin::UnifiedConsentFeatureState::kDisabled, + GetUnifiedConsentFeatureState(&profile)); + EXPECT_FALSE(IsUnifiedConsentEnabled(&profile)); + EXPECT_FALSE(IsUnifiedConsentBumpEnabled(&profile)); + } + + { + signin::ScopedUnifiedConsent scoped_no_bump( + signin::UnifiedConsentFeatureState::kEnabledNoBump); + EXPECT_EQ(signin::UnifiedConsentFeatureState::kEnabledNoBump, + GetUnifiedConsentFeatureState(&profile)); + EXPECT_TRUE(IsUnifiedConsentEnabled(&profile)); + EXPECT_FALSE(IsUnifiedConsentBumpEnabled(&profile)); + } + + { + signin::ScopedUnifiedConsent scoped_bump( + signin::UnifiedConsentFeatureState::kEnabledWithBump); + EXPECT_EQ(signin::UnifiedConsentFeatureState::kEnabledWithBump, + GetUnifiedConsentFeatureState(&profile)); + EXPECT_TRUE(IsUnifiedConsentEnabled(&profile)); + EXPECT_TRUE(IsUnifiedConsentBumpEnabled(&profile)); + } +}
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc index 436cec5..5535fb5 100644 --- a/chrome/browser/ssl/ssl_browsertest.cc +++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -2756,7 +2756,7 @@ content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); Navigate(¶ms); - WebContents* tab2 = params.target_contents; + WebContents* tab2 = params.navigated_or_inserted_contents; observer.Wait(); // The new tab has insecure content. @@ -2798,7 +2798,7 @@ content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); Navigate(¶ms); - WebContents* tab2 = params.target_contents; + WebContents* tab2 = params.navigated_or_inserted_contents; observer.Wait(); // Both tabs should have the same process.
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc index 4219504..e080ac8 100644 --- a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc +++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
@@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" +#include "base/test/bind_test_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/profile_sync_service_factory.h" @@ -176,7 +177,9 @@ IdentityManagerFactory::GetForProfile(profile_); identity_manager->SetPrimaryAccountSynchronouslyForTests( gaia_id_, username_, GenerateFakeOAuth2RefreshTokenString()); - service()->OnPrimaryAccountSet(identity_manager->GetPrimaryAccountInfo()); + std::string account_id = + identity_manager->GetPrimaryAccountInfo().account_id; + service()->GoogleSigninSucceeded(account_id, username_); } else { LOG(ERROR) << "Unsupported profile signin type."; } @@ -279,7 +282,20 @@ void ProfileSyncServiceHarness::SignoutSyncService() { DCHECK(!username_.empty()); - service()->OnPrimaryAccountCleared(service()->GetAuthenticatedAccountInfo()); + service()->GoogleSignedOut( + service()->GetAuthenticatedAccountInfo().account_id, username_); +} + +bool ProfileSyncServiceHarness::HasUnsyncedItems() { + base::RunLoop loop; + bool result = false; + service()->HasUnsyncedItemsForTest( + base::BindLambdaForTesting([&](bool has_unsynced_items) { + result = has_unsynced_items; + loop.Quit(); + })); + loop.Run(); + return result; } bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion( @@ -512,8 +528,7 @@ ProfileSyncService::Status status; service()->QueryDetailedSyncStatus(&status); // Capture select info from the sync session snapshot and syncer status. - os << ", has_unsynced_items: " - << (service()->IsSyncActive() ? service()->HasUnsyncedItemsForTest() : 0) + os << ", has_unsynced_items: " << snap.has_remaining_local_changes() << ", did_commit: " << (snap.model_neutral_state().num_successful_commits == 0 && snap.model_neutral_state().commit_result == syncer::SYNCER_OK)
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.h b/chrome/browser/sync/test/integration/profile_sync_service_harness.h index 108c524a..bd56e5b8a 100644 --- a/chrome/browser/sync/test/integration/profile_sync_service_harness.h +++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
@@ -72,6 +72,11 @@ // Sign out of sync service. void SignoutSyncService(); + // Returns whether this client has unsynced items. Avoid verifying false + // return values, because tests typically shouldn't make assumptions about + // other datatypes. + bool HasUnsyncedItems(); + // Calling this acts as a barrier and blocks the caller until |this| and // |partner| have both completed a sync cycle. When calling this method, // the |partner| should be the passive responder who responds to the actions
diff --git a/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc b/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc index e5ea4ad4..8d0a62d 100644 --- a/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc +++ b/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc
@@ -10,6 +10,7 @@ #include "base/scoped_observer.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" +#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h" #include "components/browser_sync/profile_sync_service.h" #include "components/sync/engine/cycle/sync_cycle_snapshot.h" @@ -61,100 +62,35 @@ } // namespace -// A helper class to keep an eye on a particular ProfileSyncService's -// "HasLatestProgressMarkers()" state. -// -// This is a work-around for the HasLatestProgressMarkers check's inherent -// flakiness. It's not safe to check that condition whenever we want. The -// safest time to check it is when the ProfileSyncService emits an -// OnStateChanged() event. This class waits for those events and updates its -// cached HasLatestProgressMarkers state every time that event occurs. -// -// See the comments in UpdatedProgressMarkerChecker for more details. -// -// The long-term plan is to deprecate this hack by replacing all its usees with -// more reliable status checkers. -class ProgressMarkerWatcher : public syncer::SyncServiceObserver { +// Variation of UpdateProgressMarkerChecker that intercepts calls to +// CheckExitCondition() and forwards them to a parent checker. +class QuiesceStatusChangeChecker::NestedUpdatedProgressMarkerChecker + : public UpdatedProgressMarkerChecker { public: - ProgressMarkerWatcher(browser_sync::ProfileSyncService* service, - QuiesceStatusChangeChecker* quiesce_checker); - ~ProgressMarkerWatcher() override; - void OnStateChanged(syncer::SyncService* sync) override; + NestedUpdatedProgressMarkerChecker( + browser_sync::ProfileSyncService* service, + const base::RepeatingClosure& check_exit_condition_cb) + : UpdatedProgressMarkerChecker(service), + check_exit_condition_cb_(check_exit_condition_cb) {} - bool HasLatestProgressMarkers(); - bool IsSyncDisabled(); + ~NestedUpdatedProgressMarkerChecker() override {} + + protected: + void CheckExitCondition() override { check_exit_condition_cb_.Run(); } private: - void UpdateHasLatestProgressMarkers(); - - browser_sync::ProfileSyncService* service_; - QuiesceStatusChangeChecker* quiesce_checker_; - ScopedObserver<browser_sync::ProfileSyncService, ProgressMarkerWatcher> - scoped_observer_; - bool probably_has_latest_progress_markers_; + const base::RepeatingClosure check_exit_condition_cb_; }; -ProgressMarkerWatcher::ProgressMarkerWatcher( - browser_sync::ProfileSyncService* service, - QuiesceStatusChangeChecker* quiesce_checker) - : service_(service), - quiesce_checker_(quiesce_checker), - scoped_observer_(this), - probably_has_latest_progress_markers_(false) { - scoped_observer_.Add(service); - UpdateHasLatestProgressMarkers(); -} - -ProgressMarkerWatcher::~ProgressMarkerWatcher() { } - -void ProgressMarkerWatcher::OnStateChanged(syncer::SyncService* sync) { - UpdateHasLatestProgressMarkers(); - quiesce_checker_->OnServiceStateChanged(service_); -} - -void ProgressMarkerWatcher::UpdateHasLatestProgressMarkers() { - if (IsSyncDisabled()) { - probably_has_latest_progress_markers_ = false; - return; - } - - // This is the same progress marker check as used by the - // UpdatedProgressMarkerChecker. It has the samed drawbacks and potential for - // flakiness. See the comment in - // UpdatedProgressMarkerChecker::IsExitConditionSatisfied() for more - // information. - // - // The QuiesceStatusChangeChecker attempts to work around the limitations of - // this progress marker checking method. It tries to update the progress - // marker status only in the OnStateChanged() callback, where the snapshot is - // freshest. - // - // It also checks the progress marker status when it is first initialized, and - // that's where it's most likely that we could return a false positive. We - // need to check these service at startup, since not every service is - // guaranteed to generate OnStateChanged() events while we're waiting for - // quiescence. - const syncer::SyncCycleSnapshot& snap = service_->GetLastCycleSnapshot(); - probably_has_latest_progress_markers_ = - snap.model_neutral_state().num_successful_commits == 0 && - !service_->HasUnsyncedItemsForTest(); -} - -bool ProgressMarkerWatcher::HasLatestProgressMarkers() { - return probably_has_latest_progress_markers_; -} - -bool ProgressMarkerWatcher::IsSyncDisabled() { - return ::IsSyncDisabled(service_); -} - QuiesceStatusChangeChecker::QuiesceStatusChangeChecker( std::vector<browser_sync::ProfileSyncService*> services) - : services_(services) { - DCHECK_LE(1U, services_.size()); - for (size_t i = 0; i < services_.size(); ++i) { - observers_.push_back( - std::make_unique<ProgressMarkerWatcher>(services[i], this)); + : MultiClientStatusChangeChecker(services) { + DCHECK_LE(1U, services.size()); + for (size_t i = 0; i < services.size(); ++i) { + checkers_.push_back(std::make_unique<NestedUpdatedProgressMarkerChecker>( + services[i], + base::BindRepeating(&QuiesceStatusChangeChecker::CheckExitCondition, + base::Unretained(this)))); } } @@ -162,45 +98,26 @@ bool QuiesceStatusChangeChecker::IsExitConditionSatisfied() { // Check that all progress markers are up to date. - for (auto it = observers_.begin(); it != observers_.end(); ++it) { - if ((*it)->IsSyncDisabled()) { + std::vector<browser_sync::ProfileSyncService*> enabled_services; + for (const auto& checker : checkers_) { + if (IsSyncDisabled(checker->service())) { continue; // Skip disabled services. } - if (!(*it)->HasLatestProgressMarkers()) { + enabled_services.push_back(checker->service()); + + if (!checker->IsExitConditionSatisfied()) { DVLOG(1) << "Not quiesced: Progress markers are old."; return false; } } - std::vector<browser_sync::ProfileSyncService*> enabled_services; - for (std::vector<browser_sync::ProfileSyncService*>::const_iterator it = - services_.begin(); - it != services_.end(); ++it) { - if (!IsSyncDisabled(*it)) { - enabled_services.push_back(*it); - } - } - - // Return true if we have nothing to compare against. - if (enabled_services.size() <= 1) { - return true; - } - - std::vector<browser_sync::ProfileSyncService*>::const_iterator it1 = - enabled_services.begin(); - std::vector<browser_sync::ProfileSyncService*>::const_iterator it2 = - enabled_services.begin(); - it2++; - - while (it2 != enabled_services.end()) { + for (size_t i = 1; i < enabled_services.size(); ++i) { // Return false if there is a progress marker mismatch. - if (!ProgressMarkersMatch(*it1, *it2)) { + if (!ProgressMarkersMatch(enabled_services[i - 1], enabled_services[i])) { DVLOG(1) << "Not quiesced: Progress marker mismatch."; return false; } - it1++; - it2++; } return true; @@ -208,10 +125,5 @@ std::string QuiesceStatusChangeChecker::GetDebugMessage() const { return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients", - services_.size()); -} - -void QuiesceStatusChangeChecker::OnServiceStateChanged( - browser_sync::ProfileSyncService* service) { - CheckExitCondition(); + checkers_.size()); }
diff --git a/chrome/browser/sync/test/integration/quiesce_status_change_checker.h b/chrome/browser/sync/test/integration/quiesce_status_change_checker.h index 05537e30..d0cf747 100644 --- a/chrome/browser/sync/test/integration/quiesce_status_change_checker.h +++ b/chrome/browser/sync/test/integration/quiesce_status_change_checker.h
@@ -12,9 +12,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/time/time.h" -#include "chrome/browser/sync/test/integration/status_change_checker.h" - -class ProgressMarkerWatcher; +#include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h" namespace browser_sync { class ProfileSyncService; @@ -26,28 +24,19 @@ // This requires that "self-notifications" be enabled. Otherwise the clients // will not fetch the latest progress markers on their own, and the latest // progress markers are needed to confirm that clients are in sync. -// -// There is a race condition here. If we manage to perform the check at -// precisely the wrong time, we could end up seeing stale snapshot state -// (crbug.com/95742), which would make us think that the client has finished -// syncing when it hasn't. In practice, this race is rare enough that it -// doesn't cause test failures. -class QuiesceStatusChangeChecker : public StatusChangeChecker { +class QuiesceStatusChangeChecker : public MultiClientStatusChangeChecker { public: explicit QuiesceStatusChangeChecker( std::vector<browser_sync::ProfileSyncService*> services); ~QuiesceStatusChangeChecker() override; - // A callback function for some helper objects. - void OnServiceStateChanged(browser_sync::ProfileSyncService* service); - // Implementation of StatusChangeChecker. bool IsExitConditionSatisfied() override; std::string GetDebugMessage() const override; private: - std::vector<browser_sync::ProfileSyncService*> services_; - std::vector<std::unique_ptr<ProgressMarkerWatcher>> observers_; + class NestedUpdatedProgressMarkerChecker; + std::vector<std::unique_ptr<NestedUpdatedProgressMarkerChecker>> checkers_; DISALLOW_COPY_AND_ASSIGN(QuiesceStatusChangeChecker); };
diff --git a/chrome/browser/sync/test/integration/sessions_helper.cc b/chrome/browser/sync/test/integration/sessions_helper.cc index 9191e04..c6c659b4 100644 --- a/chrome/browser/sync/test/integration/sessions_helper.cc +++ b/chrome/browser/sync/test/integration/sessions_helper.cc
@@ -163,7 +163,7 @@ params.disposition = WindowOpenDisposition::CURRENT_TAB; ui_test_utils::NavigateToURL(¶ms); - return WaitForTabToLoad(index, url, params.target_contents); + return WaitForTabToLoad(index, url, params.navigated_or_inserted_contents); } void NavigateTabBack(int index) {
diff --git a/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc b/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc index f2fd8d2e..4b41c7e 100644 --- a/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_printers_sync_test.cc
@@ -32,11 +32,6 @@ // Verify that printers aren't added with a sync call. IN_PROC_BROWSER_TEST_F(SingleClientPrintersSyncTest, NoPrinters) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; - // TODO(sync): Should not use UpdatedProgressMarkerChecker here since - // UpdatedProgressMarkerChecker is for SyncableService datatype, but - // syncer::PRINTERS is ModelTypeSyncBridge datatype. So maybe we should create - // another checker for ModelTypeSyncBridge, or some other things to wait for - // ModelTypeSyncBridge datatype. ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait()); EXPECT_TRUE(ProfileContainsSamePrintersAsVerifier(0)); }
diff --git a/chrome/browser/sync/test/integration/single_client_status_change_checker.h b/chrome/browser/sync/test/integration/single_client_status_change_checker.h index 91b6653..d537ed942 100644 --- a/chrome/browser/sync/test/integration/single_client_status_change_checker.h +++ b/chrome/browser/sync/test/integration/single_client_status_change_checker.h
@@ -30,7 +30,6 @@ bool IsExitConditionSatisfied() override = 0; std::string GetDebugMessage() const override = 0; - protected: browser_sync::ProfileSyncService* service(); };
diff --git a/chrome/browser/sync/test/integration/status_change_checker.h b/chrome/browser/sync/test/integration/status_change_checker.h index d78ca105..71896dde 100644 --- a/chrome/browser/sync/test/integration/status_change_checker.h +++ b/chrome/browser/sync/test/integration/status_change_checker.h
@@ -51,7 +51,7 @@ // Checks IsExitConditionSatisfied() and calls StopWaiting() if it returns // true. - void CheckExitCondition(); + virtual void CheckExitCondition(); private: // Helper function to start running the nested run loop (run_loop_).
diff --git a/chrome/browser/sync/test/integration/sync_auth_test.cc b/chrome/browser/sync/test/integration/sync_auth_test.cc index 1a20ad9..ffde283 100644 --- a/chrome/browser/sync/test/integration/sync_auth_test.cc +++ b/chrome/browser/sync/test/integration/sync_auth_test.cc
@@ -47,7 +47,8 @@ const char kMalformedOAuth2Token[] = "{ \"foo\": "; -class TestForAuthError : public SingleClientStatusChangeChecker { +// Waits until local changes are committed or an auth error is encountered. +class TestForAuthError : public UpdatedProgressMarkerChecker { public: explicit TestForAuthError(browser_sync::ProfileSyncService* service); @@ -57,12 +58,12 @@ }; TestForAuthError::TestForAuthError(browser_sync::ProfileSyncService* service) - : SingleClientStatusChangeChecker(service) {} + : UpdatedProgressMarkerChecker(service) {} bool TestForAuthError::IsExitConditionSatisfied() { - return !service()->HasUnsyncedItemsForTest() || - (service()->GetSyncTokenStatus().last_get_token_error.state() != - GoogleServiceAuthError::NONE); + return (service()->GetSyncTokenStatus().last_get_token_error.state() != + GoogleServiceAuthError::NONE) || + UpdatedProgressMarkerChecker::IsExitConditionSatisfied(); } std::string TestForAuthError::GetDebugMessage() const {
diff --git a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc index e782d871..75d95e9e 100644 --- a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc
@@ -186,8 +186,7 @@ // Client 1 now deletes client 0's tabs. This frees the memory of sessions1. DeleteForeignSession(1, sessions1[0]->session_tag); - ASSERT_FALSE(GetClient(0)->service()->HasUnsyncedItemsForTest()); - ASSERT_TRUE(GetClient(1)->service()->HasUnsyncedItemsForTest()); + ASSERT_TRUE(GetClient(1)->HasUnsyncedItems()); ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0))); EXPECT_FALSE(GetSessionData(1, &sessions1)); } @@ -211,8 +210,7 @@ // Client 1 now deletes client 0's tabs. This frees the memory of sessions1. DeleteForeignSession(1, sessions1[0]->session_tag); - ASSERT_FALSE(GetClient(0)->service()->HasUnsyncedItemsForTest()); - ASSERT_TRUE(GetClient(1)->service()->HasUnsyncedItemsForTest()); + ASSERT_TRUE(GetClient(1)->HasUnsyncedItems()); ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0))); ASSERT_FALSE(GetSessionData(1, &sessions1));
diff --git a/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc b/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc index 355b20bc..ece2e84 100644 --- a/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc +++ b/chrome/browser/sync/test/integration/updated_progress_marker_checker.cc
@@ -9,36 +9,47 @@ UpdatedProgressMarkerChecker::UpdatedProgressMarkerChecker( browser_sync::ProfileSyncService* service) - : SingleClientStatusChangeChecker(service) {} + : SingleClientStatusChangeChecker(service), weak_ptr_factory_(this) { + // HasUnsyncedItemsForTest() posts a task to the sync thread which guarantees + // that all tasks posted to the sync thread before this constructor have been + // processed. + service->HasUnsyncedItemsForTest( + base::BindOnce(&UpdatedProgressMarkerChecker::GotHasUnsyncedItems, + weak_ptr_factory_.GetWeakPtr())); +} + +UpdatedProgressMarkerChecker::~UpdatedProgressMarkerChecker() {} bool UpdatedProgressMarkerChecker::IsExitConditionSatisfied() { - // Checks to see if our self-notify sync cycle has completed and - // there's nothing to commit. - // - // If we assume that no one else is committing at this time and that the - // current client did not commit anything in its previous sync cycle, then - // this client has the latest progress markers. - // - // The !service()->HasUnsyncedItemsForTest() check makes sure that we have - // nothing to commit. - // - // There is a subtle race condition here. While committing items, the syncer - // will unset the IS_UNSYNCED bits in the directory. However, the evidence of - // this current sync cycle won't be available from GetLastCycleSnapshot() - // until the sync cycle completes. If we query this condition between the - // commit response processing and the end of the sync cycle, we could return a - // false positive. - // - // In practice, this doesn't happen very often because we only query the - // status when the waiting first starts and when we receive notification of a - // sync session complete or other significant event from the - // ProfileSyncService. If we're calling this right after the sync session - // completes, then the snapshot is much more likely to be up to date. + if (!has_unsynced_items_.has_value()) { + return false; + } + const syncer::SyncCycleSnapshot& snap = service()->GetLastCycleSnapshot(); return snap.model_neutral_state().num_successful_commits == 0 && - service()->IsSyncActive() && !service()->HasUnsyncedItemsForTest(); + service()->IsSyncActive() && !has_unsynced_items_.value(); +} + +void UpdatedProgressMarkerChecker::GotHasUnsyncedItems( + bool has_unsynced_items) { + has_unsynced_items_ = has_unsynced_items; + CheckExitCondition(); } std::string UpdatedProgressMarkerChecker::GetDebugMessage() const { return "Waiting for progress markers"; } + +void UpdatedProgressMarkerChecker::OnSyncCycleCompleted( + syncer::SyncService* sync) { + // Ignore sync cycles that were started before our constructor posted + // HasUnsyncedItemsForTest() to the sync thread. + if (!has_unsynced_items_.has_value()) { + return; + } + + // Override |has_unsynced_items_| with the result of the sync cycle. + const syncer::SyncCycleSnapshot& snap = service()->GetLastCycleSnapshot(); + has_unsynced_items_ = snap.has_remaining_local_changes(); + CheckExitCondition(); +}
diff --git a/chrome/browser/sync/test/integration/updated_progress_marker_checker.h b/chrome/browser/sync/test/integration/updated_progress_marker_checker.h index efeffff53..2529e7a 100644 --- a/chrome/browser/sync/test/integration/updated_progress_marker_checker.h +++ b/chrome/browser/sync/test/integration/updated_progress_marker_checker.h
@@ -7,16 +7,17 @@ #include <string> +#include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h" +#include "components/sync/driver/sync_service_observer.h" -// Waits until the latest progress markers are available. +// Waits until all local changes have been committed and progress markers are +// updated. This includes local changes posted to the sync thread before the +// construction of this object. // -// There are several limitations to this checker: -// - It assumes that this client is the only one committing at this time. -// - It relies on the test-only 'self-notify' to trigger an extra GetUpdate -// cycle after every commit. -// - It's flaky. In some rare cases, the IsExitConditionSatisifed() call could -// return a false positive. See comments in the .cc file for details. +// It relies on the test-only 'self-notify' to trigger an extra GetUpdate cycle +// after every commit. // // Because of these limitations, we intend to eventually migrate all tests off // of this checker. Please do not use it in new tests. @@ -24,10 +25,21 @@ public: explicit UpdatedProgressMarkerChecker( browser_sync::ProfileSyncService* service); + ~UpdatedProgressMarkerChecker() override; // StatusChangeChecker implementation. bool IsExitConditionSatisfied() override; std::string GetDebugMessage() const override; + + // syncer::SyncServiceObserver implementation. + void OnSyncCycleCompleted(syncer::SyncService* sync) override; + + private: + void GotHasUnsyncedItems(bool has_unsynced_items); + + base::Optional<bool> has_unsynced_items_; + + base::WeakPtrFactory<UpdatedProgressMarkerChecker> weak_ptr_factory_; }; #endif // CHROME_BROWSER_SYNC_TEST_INTEGRATION_UPDATED_PROGRESS_MARKER_CHECKER_H_
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 2d2af58..1a57bfa 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1539,10 +1539,6 @@ "webui/constrained_web_dialog_delegate_base.h", "webui/devtools_ui.cc", "webui/devtools_ui.h", - "webui/extensions/extension_loader_handler.cc", - "webui/extensions/extension_loader_handler.h", - "webui/extensions/extension_settings_handler.cc", - "webui/extensions/extension_settings_handler.h", "webui/extensions/extensions_ui.cc", "webui/extensions/extensions_ui.h", "webui/extensions/install_extension_handler.cc", @@ -1838,6 +1834,8 @@ "ash/network/enrollment_dialog_view.h", "ash/network/network_connect_delegate_chromeos.cc", "ash/network/network_connect_delegate_chromeos.h", + "ash/network/network_portal_notification_controller.cc", + "ash/network/network_portal_notification_controller.h", "ash/network/network_state_notifier.cc", "ash/network/network_state_notifier.h", "ash/network/networking_config_delegate_chromeos.cc", @@ -2111,6 +2109,7 @@ "//chromeos/components/proximity_auth/webui", "//chromeos/components/tether", "//components/arc", + "//components/captive_portal", "//components/consent_auditor:consent_auditor", "//components/cryptauth", "//components/drive:drive_chromeos", @@ -2529,8 +2528,6 @@ "cocoa/password_reuse_warning_view_controller.h", "cocoa/password_reuse_warning_view_controller.mm", "cocoa/simple_message_box_bridge_views.mm", - "cocoa/simple_message_box_cocoa.h", - "cocoa/simple_message_box_cocoa.mm", "cocoa/subresource_filter/subresource_filter_bubble_controller.h", "cocoa/subresource_filter/subresource_filter_bubble_controller.mm", "javascript_dialogs/javascript_dialog_cocoa.h", @@ -3614,6 +3611,8 @@ "app_list/arc/arc_vpn_provider_manager_factory.h", "app_list/crostini/crostini_app_context_menu.cc", "app_list/crostini/crostini_app_context_menu.h", + "app_list/crostini/crostini_app_icon.cc", + "app_list/crostini/crostini_app_icon.h", "app_list/crostini/crostini_app_icon_loader.cc", "app_list/crostini/crostini_app_icon_loader.h", "app_list/crostini/crostini_app_item.cc",
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc index c8b0e3b9..c90c8aa 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service.cc +++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -439,7 +439,12 @@ void AppListSyncableService::SetOemFolderName(const std::string& name) { oem_folder_name_ = name; - model_updater_->SetItemName(ash::kOemFolderId, oem_folder_name_); + // Update OEM folder item if it was already created. If it is not created yet + // then on creation it will take right name. + ChromeAppListItem* oem_folder_item = + model_updater_->FindItem(ash::kOemFolderId); + if (oem_folder_item) + oem_folder_item->SetName(oem_folder_name_); } AppListModelUpdater* AppListSyncableService::GetModelUpdater() {
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.cc b/chrome/browser/ui/app_list/chrome_app_list_item.cc index 21fd069..68acbf5 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_item.cc +++ b/chrome/browser/ui/app_list/chrome_app_list_item.cc
@@ -19,6 +19,14 @@ AppListControllerDelegate* g_controller_for_test = nullptr; +ash::mojom::AppListItemMetadataPtr CreateDefaultMetadata( + const std::string& app_id) { + return ash::mojom::AppListItemMetadata::New( + app_id, std::string() /* name */, std::string() /* short_name */, + std::string() /* folder_id */, syncer::StringOrdinal(), + false /* is_folder */, gfx::ImageSkia() /* icon */); +} + } // namespace // static @@ -49,15 +57,14 @@ // ChromeAppListItem ChromeAppListItem::ChromeAppListItem(Profile* profile, const std::string& app_id) - : metadata_( - ash::mojom::AppListItemMetadata::New(app_id, - std::string() /* name */, - std::string() /* short_name */, - std::string() /* folder_id */, - syncer::StringOrdinal(), - false /* is_folder */, - gfx::ImageSkia() /* icon */)), - profile_(profile) {} + : metadata_(CreateDefaultMetadata(app_id)), profile_(profile) {} + +ChromeAppListItem::ChromeAppListItem(Profile* profile, + const std::string& app_id, + AppListModelUpdater* model_updater) + : metadata_(CreateDefaultMetadata(app_id)), + profile_(profile), + model_updater_(model_updater) {} ChromeAppListItem::~ChromeAppListItem() { }
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.h b/chrome/browser/ui/app_list/chrome_app_list_item.h index d6bf72e..9198b8e 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_item.h +++ b/chrome/browser/ui/app_list/chrome_app_list_item.h
@@ -41,7 +41,9 @@ ChromeAppListItem* const item_; }; - ChromeAppListItem(Profile* profile, const std::string& app_id); + ChromeAppListItem(Profile* profile, + const std::string& app_id, + AppListModelUpdater* model_updater); virtual ~ChromeAppListItem(); // AppListControllerDelegate is not properly implemented in tests. Use mock @@ -106,6 +108,8 @@ std::string ToDebugString() const; protected: + ChromeAppListItem(Profile* profile, const std::string& app_id); + Profile* profile() const { return profile_; } extensions::AppSorting* GetAppSorting();
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc index ca3d594..01e9f5d 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc +++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -448,7 +448,7 @@ ChromeAppListItem* oem_folder = FindFolderItem(oem_folder_id); if (!oem_folder) { std::unique_ptr<ChromeAppListItem> new_oem_folder = - std::make_unique<ChromeAppListItem>(profile_, oem_folder_id); + std::make_unique<ChromeAppListItem>(profile_, oem_folder_id, this); oem_folder = AddChromeItem(std::move(new_oem_folder)); oem_folder->SetChromeIsFolder(true); } @@ -504,9 +504,10 @@ // Otherwise, we detect an item is created in Ash which is not added into our // Chrome list yet. This only happens when a folder is created. std::unique_ptr<ChromeAppListItem> new_item = - std::make_unique<ChromeAppListItem>(profile_, item->id); + std::make_unique<ChromeAppListItem>(profile_, item->id, this); chrome_item = AddChromeItem(std::move(new_item)); chrome_item->SetMetadata(std::move(item)); + if (delegate_) delegate_->OnAppListItemAdded(chrome_item); }
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc b/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc new file mode 100644 index 0000000..c39a8ab3 --- /dev/null +++ b/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc
@@ -0,0 +1,344 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/app_list/crostini/crostini_app_icon.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/no_destructor.h" +#include "base/task_scheduler/post_task.h" +#include "chrome/browser/chromeos/crostini/crostini_registry_service.h" +#include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" +#include "chrome/browser/chromeos/crostini/crostini_util.h" +#include "chrome/browser/image_decoder.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/grit/chrome_unscaled_resources.h" +#include "content/public/browser/browser_thread.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/image/image_skia_source.h" + +namespace { +void InstallIconFromFileThread(const base::FilePath& icon_path, + const std::vector<unsigned char>& content_png) { + DCHECK(!content_png.empty()); + + base::CreateDirectory(icon_path.DirName()); + + int wrote = base::WriteFile(icon_path, + reinterpret_cast<const char*>(content_png.data()), + content_png.size()); + if (wrote != static_cast<int>(content_png.size())) { + VLOG(2) << "Failed to write Crostini icon file: " + << icon_path.MaybeAsASCII(); + if (!base::DeleteFile(icon_path, false)) { + VLOG(2) << "Couldn't delete broken icon file" << icon_path.MaybeAsASCII(); + } + } +} +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// CrostiniAppIcon::ReadResult + +struct CrostiniAppIcon::ReadResult { + ReadResult(bool error, + bool request_to_install, + ui::ScaleFactor scale_factor, + std::string unsafe_icon_data) + : error(error), + request_to_install(request_to_install), + scale_factor(scale_factor), + unsafe_icon_data(unsafe_icon_data) {} + + bool error; + bool request_to_install; + ui::ScaleFactor scale_factor; + std::string unsafe_icon_data; +}; + +//////////////////////////////////////////////////////////////////////////////// +// CrostiniAppIcon::Source + +class CrostiniAppIcon::Source : public gfx::ImageSkiaSource { + public: + Source(const base::WeakPtr<CrostiniAppIcon>& host, int resource_size_in_dip); + ~Source() override; + + private: + // gfx::ImageSkiaSource overrides: + gfx::ImageSkiaRep GetImageForScale(float scale) override; + + // Used to load images asynchronously. NULLed out when the CrostiniAppIcon is + // destroyed. + base::WeakPtr<CrostiniAppIcon> host_; + + const int resource_size_in_dip_; + + DISALLOW_COPY_AND_ASSIGN(Source); +}; + +CrostiniAppIcon::Source::Source(const base::WeakPtr<CrostiniAppIcon>& host, + int resource_size_in_dip) + : host_(host), resource_size_in_dip_(resource_size_in_dip) {} + +CrostiniAppIcon::Source::~Source() = default; + +gfx::ImageSkiaRep CrostiniAppIcon::Source::GetImageForScale(float scale) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // Host loads icon asynchronously, so use default icon so far. + int resource_id; + if (host_ && host_->app_id() == kCrostiniTerminalId) { + // Don't initiate the icon request from the container because we have this + // one already. + resource_id = IDR_LOGO_CROSTINI_TERMINAL; + } else { + if (host_) + host_->LoadForScaleFactor(ui::GetSupportedScaleFactor(scale)); + resource_id = IDR_LOGO_CROSTINI_DEFAULT; + } + + // A map from a pair of a resource ID and size in DIP to an image. This + // is a cache to avoid resizing IDR icons in GetImageForScale every time. + static base::NoDestructor<std::map<std::pair<int, int>, gfx::ImageSkia>> + default_icons_cache_; + + // Check |default_icons_cache_| and returns the existing one if possible. + const auto key = std::make_pair(resource_id, resource_size_in_dip_); + const auto it = default_icons_cache_->find(key); + if (it != default_icons_cache_->end()) + return it->second.GetRepresentation(scale); + + const gfx::ImageSkia* default_image = + ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); + gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage( + *default_image, skia::ImageOperations::RESIZE_BEST, + gfx::Size(resource_size_in_dip_, resource_size_in_dip_)); + + // Add the resized image to the cache to avoid executing the expensive resize + // operation many times. Caching the result is safe because unlike Crostini + // icons that can be updated dynamically, IDR icons are static. + default_icons_cache_->insert(std::make_pair(key, resized_image)); + return resized_image.GetRepresentation(scale); +} + +class CrostiniAppIcon::DecodeRequest : public ImageDecoder::ImageRequest { + public: + DecodeRequest(const base::WeakPtr<CrostiniAppIcon>& host, + int dimension, + ui::ScaleFactor scale_factor); + ~DecodeRequest() override; + + // ImageDecoder::ImageRequest + void OnImageDecoded(const SkBitmap& bitmap) override; + void OnDecodeImageFailed() override; + + private: + base::WeakPtr<CrostiniAppIcon> host_; + int dimension_; + ui::ScaleFactor scale_factor_; + + DISALLOW_COPY_AND_ASSIGN(DecodeRequest); +}; + +//////////////////////////////////////////////////////////////////////////////// +// CrostiniAppIcon::DecodeRequest + +CrostiniAppIcon::DecodeRequest::DecodeRequest( + const base::WeakPtr<CrostiniAppIcon>& host, + int dimension, + ui::ScaleFactor scale_factor) + : host_(host), dimension_(dimension), scale_factor_(scale_factor) {} + +CrostiniAppIcon::DecodeRequest::~DecodeRequest() = default; + +void CrostiniAppIcon::DecodeRequest::OnImageDecoded(const SkBitmap& bitmap) { + DCHECK(!bitmap.isNull() && !bitmap.empty()); + + if (!host_) + return; + + int expected_dim = static_cast<int>( + ui::GetScaleForScaleFactor(scale_factor_) * dimension_ + 0.5f); + if (bitmap.width() == expected_dim && bitmap.height() == expected_dim) { + host_->Update(scale_factor_, bitmap); + host_->DiscardDecodeRequest(this); + return; + } + + // We won't always get back from Crostini the icon size we asked for, so it + // is expected that sometimes we need to rescale it. When that happens we + // also want to store that result as the PNG to avoid having to do this + // rescale every time the icon is loaded from the file. + SkBitmap resized_image = skia::ImageOperations::Resize( + bitmap, skia::ImageOperations::RESIZE_BEST, expected_dim, expected_dim); + + std::vector<unsigned char> png_data; + bool encode_result; + if (resized_image.colorType() == kAlpha_8_SkColorType) { + encode_result = gfx::PNGCodec::EncodeA8SkBitmap(resized_image, &png_data); + } else { + encode_result = gfx::PNGCodec::EncodeBGRASkBitmap( + resized_image, false /* discard_transparency */, &png_data); + } + if (encode_result) { + // Now save this so we can reload it later when needed. + crostini::CrostiniRegistryService* registry_service = + crostini::CrostiniRegistryServiceFactory::GetForProfile( + host_->profile()); + DCHECK(registry_service); + + const base::FilePath path = + registry_service->GetIconPath(host_->app_id(), scale_factor_); + DCHECK(!path.empty()); + base::PostTaskWithTraits( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + base::BindOnce(&InstallIconFromFileThread, path, std::move(png_data))); + } else { + LOG(ERROR) << "Failed encoding resized SkBitmap as PNG for Crostini icon"; + } + + host_->Update(scale_factor_, std::move(resized_image)); + host_->DiscardDecodeRequest(this); +} + +void CrostiniAppIcon::DecodeRequest::OnDecodeImageFailed() { + VLOG(2) << "Failed to decode Crostini icon."; + + if (!host_) + return; + + host_->MaybeRequestIcon(scale_factor_); + host_->DiscardDecodeRequest(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// CrostiniAppIcon + +CrostiniAppIcon::CrostiniAppIcon(Profile* profile, + const std::string& app_id, + int resource_size_in_dip, + Observer* observer) + : profile_(profile), + app_id_(app_id), + resource_size_in_dip_(resource_size_in_dip), + observer_(observer), + weak_ptr_factory_(this) { + DCHECK_NE(observer_, nullptr); + auto source = std::make_unique<Source>(weak_ptr_factory_.GetWeakPtr(), + resource_size_in_dip); + gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip); + image_skia_ = gfx::ImageSkia(std::move(source), resource_size); +} + +CrostiniAppIcon::~CrostiniAppIcon() = default; + +void CrostiniAppIcon::LoadForScaleFactor(ui::ScaleFactor scale_factor) { + crostini::CrostiniRegistryService* registry_service = + crostini::CrostiniRegistryServiceFactory::GetForProfile(profile_); + DCHECK(registry_service); + + const base::FilePath path = + registry_service->GetIconPath(app_id_, scale_factor); + DCHECK(!path.empty()); + + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + base::BindOnce(&CrostiniAppIcon::ReadOnFileThread, scale_factor, path), + base::BindOnce(&CrostiniAppIcon::OnIconRead, + weak_ptr_factory_.GetWeakPtr())); +} + +void CrostiniAppIcon::MaybeRequestIcon(ui::ScaleFactor scale_factor) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + crostini::CrostiniRegistryService* registry_service = + crostini::CrostiniRegistryServiceFactory::GetForProfile(profile_); + DCHECK(registry_service); + + // CrostiniRegistryService notifies CrostiniAppModelBuilder via Observer when + // icon is ready and CrostiniAppModelBuilder refreshes the icon of the + // corresponding item by calling LoadScaleFactor. + registry_service->MaybeRequestIcon(app_id_, scale_factor); +} + +// static +std::unique_ptr<CrostiniAppIcon::ReadResult> CrostiniAppIcon::ReadOnFileThread( + ui::ScaleFactor scale_factor, + const base::FilePath& path) { + DCHECK(!path.empty()); + + if (!base::PathExists(path)) { + return std::make_unique<CrostiniAppIcon::ReadResult>( + false, true, scale_factor, std::string()); + } + + // Read the file from disk. + std::string unsafe_icon_data; + if (!base::ReadFileToString(path, &unsafe_icon_data) || + unsafe_icon_data.empty()) { + VLOG(2) << "Failed to read a Crostini icon from file " + << path.MaybeAsASCII(); + + // If |unsafe_icon_data| is empty typically means we have a file corruption + // on cached icon file. Send request to re install the icon. + return std::make_unique<CrostiniAppIcon::ReadResult>( + true, unsafe_icon_data.empty(), scale_factor, std::string()); + } + + return std::make_unique<CrostiniAppIcon::ReadResult>( + false, false, scale_factor, unsafe_icon_data); +} + +void CrostiniAppIcon::OnIconRead( + std::unique_ptr<CrostiniAppIcon::ReadResult> read_result) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (read_result->request_to_install) { + MaybeRequestIcon(read_result->scale_factor); + } + + if (!read_result->unsafe_icon_data.empty()) { + decode_requests_.push_back(std::make_unique<DecodeRequest>( + weak_ptr_factory_.GetWeakPtr(), resource_size_in_dip_, + read_result->scale_factor)); + ImageDecoder::Start(decode_requests_.back().get(), + read_result->unsafe_icon_data); + } +} + +void CrostiniAppIcon::Update(ui::ScaleFactor scale_factor, + const SkBitmap& bitmap) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + gfx::ImageSkiaRep image_rep(bitmap, ui::GetScaleForScaleFactor(scale_factor)); + DCHECK(ui::IsSupportedScale(image_rep.scale())); + + image_skia_.RemoveRepresentation(image_rep.scale()); + image_skia_.AddRepresentation(image_rep); + image_skia_.RemoveUnsupportedRepresentationsForScale(image_rep.scale()); + + observer_->OnIconUpdated(this); +} + +void CrostiniAppIcon::DiscardDecodeRequest(DecodeRequest* request) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + auto it = std::find_if(decode_requests_.begin(), decode_requests_.end(), + [request](const std::unique_ptr<DecodeRequest>& ptr) { + return ptr.get() == request; + }); + CHECK(it != decode_requests_.end()); + decode_requests_.erase(it); +}
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_icon.h b/chrome/browser/ui/app_list/crostini/crostini_app_icon.h new file mode 100644 index 0000000..c68e4e4 --- /dev/null +++ b/chrome/browser/ui/app_list/crostini/crostini_app_icon.h
@@ -0,0 +1,100 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_APP_ICON_H_ +#define CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_APP_ICON_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "ui/base/layout.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" + +namespace base { +class FilePath; +} + +class Profile; + +// A class that provides an ImageSkia for UI code to use. It handles Crostini +// app icon resource loading, screen scale factor change etc. UI code that uses +// Crostini app icon should host this class. +class CrostiniAppIcon { + public: + class Observer { + public: + // Invoked when a new image rep for an additional scale factor + // is loaded and added to |image|. + virtual void OnIconUpdated(CrostiniAppIcon* icon) = 0; + + protected: + virtual ~Observer() {} + }; + + CrostiniAppIcon(Profile* profile, + const std::string& app_id, + int resource_size_in_dip, + Observer* observer); + ~CrostiniAppIcon(); + + const std::string& app_id() const { return app_id_; } + const gfx::ImageSkia& image_skia() const { return image_skia_; } + Profile* profile() const { return profile_; } + + // Icon loading is performed in several steps. It is initiated by + // LoadImageForScaleFactor request that specifies a required scale factor. + // CrostiniRegistryService is used to resolve a path to resource. Content of + // file is asynchronously read in context of browser file thread. On + // successful read, an icon data is decoded to an image in the special utility + // process. DecodeRequest is used to interact with the utility process, and + // each active request is stored at |decode_requests_| vector. When decoding + // is complete, results are returned in context of UI thread, and + // corresponding request is removed from |decode_requests_|. In case of some + // requests are not completed by the time of deleting this icon, they are + // automatically canceled. In case of the icon file is not available this + // requests CrostiniRegistryService to install required resource from Crostini + // side. CrostiniRegistryService notifies UI items that new icon is available + // and corresponding item should invoke LoadImageForScaleFactor again. + void LoadForScaleFactor(ui::ScaleFactor scale_factor); + + private: + class Source; + class DecodeRequest; + struct ReadResult; + + // Makes a request back to the registry service to request an icon if one + // isn't already in progress. + void MaybeRequestIcon(ui::ScaleFactor scale_factor); + // Reads the icon data in and returns the result. + static std::unique_ptr<CrostiniAppIcon::ReadResult> ReadOnFileThread( + ui::ScaleFactor scale_factor, + const base::FilePath& path); + // Callback handler for when we have read an icon file from storage. + void OnIconRead(std::unique_ptr<CrostiniAppIcon::ReadResult> read_result); + // Called when we have updated an icon and we should notify our observer + // about the change. + void Update(ui::ScaleFactor scale_factor, const SkBitmap& bitmap); + // Removed the corresponding |request| from our list. + void DiscardDecodeRequest(DecodeRequest* request); + + Profile* profile_; + const std::string app_id_; + const int resource_size_in_dip_; + Observer* const observer_; + + gfx::ImageSkia image_skia_; + + // Contains pending image decode requests. + std::vector<std::unique_ptr<DecodeRequest>> decode_requests_; + + base::WeakPtrFactory<CrostiniAppIcon> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CrostiniAppIcon); +}; + +#endif // CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_APP_ICON_H_
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.cc b/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.cc index 81b15b1..779d7a5 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.cc
@@ -8,34 +8,38 @@ #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/grit/chrome_unscaled_resources.h" +#include "ui/app_list/app_list_constants.h" #include "ui/base/resource/resource_bundle.h" CrostiniAppIconLoader::CrostiniAppIconLoader(Profile* profile, int resource_size_in_dip, AppIconLoaderDelegate* delegate) : AppIconLoader(profile, resource_size_in_dip, delegate), - profile_(profile) {} + registry_service_( + crostini::CrostiniRegistryServiceFactory::GetForProfile(profile)) { + DCHECK(registry_service_); + registry_service_->AddObserver(this); +} -CrostiniAppIconLoader::~CrostiniAppIconLoader() = default; +CrostiniAppIconLoader::~CrostiniAppIconLoader() { + registry_service_->RemoveObserver(this); +} bool CrostiniAppIconLoader::CanLoadImageForApp(const std::string& app_id) { if (icon_map_.find(app_id) != icon_map_.end()) return true; - crostini::CrostiniRegistryService* registry_service = - crostini::CrostiniRegistryServiceFactory::GetForProfile(profile_); - return registry_service->IsCrostiniShelfAppId(app_id); + return registry_service_->IsCrostiniShelfAppId(app_id); } void CrostiniAppIconLoader::FetchImage(const std::string& app_id) { if (icon_map_.find(app_id) != icon_map_.end()) return; - // TODO(timzheng): Use a real specific icon for this app. - std::unique_ptr<gfx::ImageSkia> image_skia = std::make_unique<gfx::ImageSkia>( - *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - IDR_LOGO_CROSTINI_DEFAULT)); - icon_map_[app_id] = std::move(image_skia); + std::unique_ptr<CrostiniAppIcon> icon = std::make_unique<CrostiniAppIcon>( + profile(), app_id, app_list::kTileIconSize, this); + icon->image_skia().EnsureRepsForSupportedScales(); + icon_map_[app_id] = std::move(icon); UpdateImage(app_id); } @@ -48,5 +52,17 @@ if (it == icon_map_.end()) return; - delegate()->OnAppImageUpdated(app_id, *(it->second)); + delegate()->OnAppImageUpdated(app_id, it->second->image_skia()); +} + +void CrostiniAppIconLoader::OnIconUpdated(CrostiniAppIcon* icon) { + UpdateImage(icon->app_id()); +} + +void CrostiniAppIconLoader::OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) { + AppIDToIconMap::const_iterator it = icon_map_.find(app_id); + if (it == icon_map_.end()) + return; + it->second->LoadForScaleFactor(scale_factor); }
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.h b/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.h index dfe3fd7..6ca2163 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.h +++ b/chrome/browser/ui/app_list/crostini/crostini_app_icon_loader.h
@@ -10,13 +10,18 @@ #include <string> #include "base/macros.h" +#include "chrome/browser/chromeos/crostini/crostini_registry_service.h" #include "chrome/browser/ui/app_icon_loader.h" +#include "chrome/browser/ui/app_list/crostini/crostini_app_icon.h" #include "ui/gfx/image/image_skia.h" class Profile; // An AppIconLoader that loads icons for Crostini apps. -class CrostiniAppIconLoader : public AppIconLoader { +class CrostiniAppIconLoader + : public AppIconLoader, + public crostini::CrostiniRegistryService::Observer, + public CrostiniAppIcon::Observer { public: CrostiniAppIconLoader(Profile* profile, int resource_size_in_dip, @@ -29,11 +34,19 @@ void ClearImage(const std::string& app_id) override; void UpdateImage(const std::string& app_id) override; + // CrostiniRegistryService::Observer: + void OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) override; + + // CrostiniAppIcon::Observer: + void OnIconUpdated(CrostiniAppIcon* icon) override; + private: - using AppIDToIconMap = std::map<std::string, std::unique_ptr<gfx::ImageSkia>>; + using AppIDToIconMap = + std::map<std::string, std::unique_ptr<CrostiniAppIcon>>; // Not owned. - Profile* profile_; + crostini::CrostiniRegistryService* registry_service_; // Maps from Crostini app id to icon. AppIDToIconMap icon_map_;
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_item.cc b/chrome/browser/ui/app_list/crostini/crostini_app_item.cc index 309c2d8..5bfa195 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_item.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_item.cc
@@ -10,29 +10,33 @@ #include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/ui/app_list/app_list_controller_delegate.h" #include "chrome/browser/ui/app_list/crostini/crostini_app_context_menu.h" +#include "content/public/browser/browser_thread.h" #include "ui/app_list/app_list_constants.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/gfx/image/image_skia_operations.h" // static const char CrostiniAppItem::kItemType[] = "CrostiniAppItem"; CrostiniAppItem::CrostiniAppItem( Profile* profile, + AppListModelUpdater* model_updater, const app_list::AppListSyncableService::SyncItem* sync_item, const std::string& id, - const std::string& name, - const gfx::ImageSkia* image_skia) + const std::string& name) : ChromeAppListItem(profile, id) { - SetIcon(gfx::ImageSkiaOperations::CreateResizedImage( - *image_skia, skia::ImageOperations::RESIZE_BEST, - gfx::Size(app_list::kTileIconSize, app_list::kTileIconSize))); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + crostini_app_icon_.reset( + new CrostiniAppIcon(profile, id, app_list::kTileIconSize, this)); + SetName(name); + UpdateIcon(); if (sync_item && sync_item->item_ordinal.IsValid()) { UpdateFromSync(sync_item); } else { SetDefaultPositionIfApplicable(); } + + // Set model updater last to avoid being called during construction. + set_model_updater(model_updater); } CrostiniAppItem::~CrostiniAppItem() {} @@ -56,3 +60,12 @@ app_list::AppContextMenu* CrostiniAppItem::GetAppContextMenu() { return context_menu_.get(); } + +void CrostiniAppItem::UpdateIcon() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + SetIcon(crostini_app_icon_->image_skia()); +} + +void CrostiniAppItem::OnIconUpdated(CrostiniAppIcon* icon) { + UpdateIcon(); +}
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_item.h b/chrome/browser/ui/app_list/crostini/crostini_app_item.h index 9b09154..3c0eb120 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_item.h +++ b/chrome/browser/ui/app_list/crostini/crostini_app_item.h
@@ -6,23 +6,26 @@ #define CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_APP_ITEM_H_ #include "chrome/browser/ui/app_list/chrome_app_list_item.h" +#include "chrome/browser/ui/app_list/crostini/crostini_app_icon.h" class CrostiniAppContextMenu; -namespace gfx { -class ImageSkia; -} - -class CrostiniAppItem : public ChromeAppListItem { +class CrostiniAppItem : public ChromeAppListItem, + public CrostiniAppIcon::Observer { public: static const char kItemType[]; CrostiniAppItem(Profile* profile, + AppListModelUpdater* model_updater, const app_list::AppListSyncableService::SyncItem* sync_item, const std::string& id, - const std::string& name, - const gfx::ImageSkia* image_skia); + const std::string& name); ~CrostiniAppItem() override; + CrostiniAppIcon* crostini_app_icon() { return crostini_app_icon_.get(); } + + // CrostiniAppIcon::Observer + void OnIconUpdated(CrostiniAppIcon* icon) override; + private: // ChromeAppListItem: void Activate(int event_flags) override; @@ -30,6 +33,9 @@ void GetContextMenuModel(GetMenuModelCallback callback) override; app_list::AppContextMenu* GetAppContextMenu() override; + void UpdateIcon(); + + std::unique_ptr<CrostiniAppIcon> crostini_app_icon_; std::unique_ptr<CrostiniAppContextMenu> context_menu_; DISALLOW_COPY_AND_ASSIGN(CrostiniAppItem);
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc index 12da86e..32d8126f 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc
@@ -44,12 +44,8 @@ const std::string& localized_name = crostini::CrostiniRegistryService::Registration::Localize( registration->name); - // TODO(timloh): Get an icon from the registry. InsertApp(std::make_unique<CrostiniAppItem>( - profile(), GetSyncItem(app_id), app_id, localized_name, - ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - app_id == kCrostiniTerminalId ? IDR_LOGO_CROSTINI_TERMINAL - : IDR_LOGO_CROSTINI_DEFAULT))); + profile(), model_updater(), GetSyncItem(app_id), app_id, localized_name)); } void CrostiniAppModelBuilder::OnRegistryUpdated( @@ -67,3 +63,16 @@ for (const std::string& app_id : inserted_apps) InsertCrostiniAppItem(registry_service, app_id); } + +void CrostiniAppModelBuilder::OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) { + CrostiniAppItem* app_item = static_cast<CrostiniAppItem*>(GetAppItem(app_id)); + if (!app_item) { + VLOG(2) << "Could not update the icon of Crostini app (" << app_id + << ") because it was not found"; + return; + } + + // Initiate async icon reloading. + app_item->crostini_app_icon()->LoadForScaleFactor(scale_factor); +}
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h index f05e1f9..9c6f0d82 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h +++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h
@@ -28,6 +28,8 @@ const std::vector<std::string>& updated_apps, const std::vector<std::string>& removed_apps, const std::vector<std::string>& inserted_apps) override; + void OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) override; void InsertCrostiniAppItem( const crostini::CrostiniRegistryService* registry_service,
diff --git a/chrome/browser/ui/app_list/search/answer_card/answer_card_web_contents.cc b/chrome/browser/ui/app_list/search/answer_card/answer_card_web_contents.cc index 8fcc7895..f2de4a2 100644 --- a/chrome/browser/ui/app_list/search/answer_card/answer_card_web_contents.cc +++ b/chrome/browser/ui/app_list/search/answer_card/answer_card_web_contents.cc
@@ -232,7 +232,7 @@ base::RecordAction(base::UserMetricsAction("SearchAnswer_OpenedUrl")); - return new_tab_params.target_contents; + return new_tab_params.navigated_or_inserted_contents; } bool AnswerCardWebContents::HandleContextMenu(
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc index 5a8c85d..631a3db 100644 --- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc +++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
@@ -157,7 +157,7 @@ oem_folder->SetMetadata(std::move(folder_data)); } else { std::unique_ptr<ChromeAppListItem> new_folder = - std::make_unique<ChromeAppListItem>(nullptr, oem_folder_id); + std::make_unique<ChromeAppListItem>(nullptr, oem_folder_id, nullptr); oem_folder = new_folder.get(); AddItem(std::move(new_folder)); ash::mojom::AppListItemMetadataPtr folder_data =
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc index da9ddf8..a3fd28a 100644 --- a/chrome/browser/ui/apps/chrome_app_delegate.cc +++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -77,7 +77,7 @@ new_tab_params.initiating_profile = Profile::FromBrowserContext(context); Navigate(&new_tab_params); - return new_tab_params.target_contents; + return new_tab_params.navigated_or_inserted_contents; } void OpenURLAfterCheckIsDefaultBrowser(
diff --git a/chrome/browser/ui/ash/README.md b/chrome/browser/ui/ash/README.md new file mode 100644 index 0000000..adbdf51 --- /dev/null +++ b/chrome/browser/ui/ash/README.md
@@ -0,0 +1,14 @@ +chrome/browser/ui/ash +===================== + +This directory should contain Chrome OS UI (Ash) code that has `src/chrome` +dependencies. + +Code in this directory may have `ash/public` dependencies. +*Any non-public `ash/` dependencies will need to be removed.* + +See [ash/README.md#Mustash](/ash/README.md#Mustash) for more information about +the Mustash project. + +Code in this directory may depend on code in +[`chrome/browser/chromeos`](/chrome/browser/chromeos/README.md).
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc index 36ef6bf31..1e4b685 100644 --- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc +++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -21,7 +21,6 @@ #include "base/command_line.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/ash_config.h" -#include "chrome/browser/chromeos/net/network_portal_notification_controller.h" #include "chrome/browser/chromeos/night_light/night_light_client.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/app_list/app_list_client_impl.h" @@ -37,6 +36,7 @@ #include "chrome/browser/ui/ash/media_client.h" #include "chrome/browser/ui/ash/network/data_promo_notification.h" #include "chrome/browser/ui/ash/network/network_connect_delegate_chromeos.h" +#include "chrome/browser/ui/ash/network/network_portal_notification_controller.h" #include "chrome/browser/ui/ash/session_controller_client.h" #include "chrome/browser/ui/ash/system_tray_client.h" #include "chrome/browser/ui/ash/tab_scrubber.h"
diff --git a/chrome/browser/ui/ash/network/DEPS b/chrome/browser/ui/ash/network/DEPS new file mode 100644 index 0000000..1601c9d --- /dev/null +++ b/chrome/browser/ui/ash/network/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/captive_portal", +]
diff --git a/chrome/browser/ui/ash/network/data_promo_notification_unittest.cc b/chrome/browser/ui/ash/network/data_promo_notification_unittest.cc index 268eea3..4a3749b 100644 --- a/chrome/browser/ui/ash/network/data_promo_notification_unittest.cc +++ b/chrome/browser/ui/ash/network/data_promo_notification_unittest.cc
@@ -124,7 +124,8 @@ home_provider.SetString("name", "Cellular1_Provider"); home_provider.SetString("country", "us"); device_test->SetDeviceProperty(kCellularDevicePath, - shill::kHomeProviderProperty, home_provider); + shill::kHomeProviderProperty, home_provider, + /*notify_changed=*/true); // Create a cellular network and activate it. chromeos::ShillServiceClient::TestInterface* service_test =
diff --git a/chrome/browser/chromeos/net/network_portal_notification_controller.cc b/chrome/browser/ui/ash/network/network_portal_notification_controller.cc similarity index 95% rename from chrome/browser/chromeos/net/network_portal_notification_controller.cc rename to chrome/browser/ui/ash/network/network_portal_notification_controller.cc index c83cb4c..1a957e9 100644 --- a/chrome/browser/chromeos/net/network_portal_notification_controller.cc +++ b/chrome/browser/ui/ash/network/network_portal_notification_controller.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/chromeos/net/network_portal_notification_controller.h" +#include "chrome/browser/ui/ash/network/network_portal_notification_controller.h" #include <stdint.h> @@ -10,8 +10,6 @@ #include <vector> #include "ash/public/cpp/vector_icons/vector_icons.h" -#include "ash/shell.h" -#include "ash/system/tray/system_tray_notifier.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/macros.h" @@ -21,11 +19,8 @@ #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/chromeos/mobile/mobile_activator.h" #include "chrome/browser/chromeos/net/network_portal_web_dialog.h" -#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/notifications/notification_handler.h" #include "chrome/browser/notifications/system_notification_helper.h" @@ -35,7 +30,6 @@ #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" -#include "chrome/grit/theme_resources.h" #include "chromeos/chromeos_switches.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" @@ -48,7 +42,6 @@ #include "extensions/browser/api/networking_config/networking_config_service_factory.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/message_center/public/cpp/notification.h" #include "ui/message_center/public/cpp/notification_types.h" #include "ui/message_center/public/cpp/notifier_id.h" @@ -101,8 +94,8 @@ networking_config_service->LookupExtensionIdForHexSsid(hex_ssid); if (extension_id.empty()) return nullptr; - return extensions::ExtensionRegistry::Get(profile) - ->GetExtensionById(extension_id, extensions::ExtensionRegistry::ENABLED); + return extensions::ExtensionRegistry::Get(profile)->GetExtensionById( + extension_id, extensions::ExtensionRegistry::ENABLED); } class NetworkPortalNotificationControllerDelegate @@ -188,9 +181,8 @@ const bool use_incognito_profile = disable_bypass_proxy_switch_present ? false - : (profile && - profile->GetPrefs()->GetBoolean( - prefs::kCaptivePortalAuthenticationIgnoresProxy)); + : (profile && profile->GetPrefs()->GetBoolean( + prefs::kCaptivePortalAuthenticationIgnoresProxy)); if (use_incognito_profile) { if (controller_) @@ -296,11 +288,6 @@ return; last_network_guid_ = network->guid(); - if (ash::Shell::HasInstance()) { - ash::Shell::Get()->system_tray_notifier()->NotifyOnCaptivePortalDetected( - network->guid()); - } - SystemNotificationHelper::GetInstance()->Display( *GetNotification(network, state)); UMA_HISTOGRAM_ENUMERATION(
diff --git a/chrome/browser/chromeos/net/network_portal_notification_controller.h b/chrome/browser/ui/ash/network/network_portal_notification_controller.h similarity index 94% rename from chrome/browser/chromeos/net/network_portal_notification_controller.h rename to chrome/browser/ui/ash/network/network_portal_notification_controller.h index 30bacf6..03faaa9b 100644 --- a/chrome/browser/chromeos/net/network_portal_notification_controller.h +++ b/chrome/browser/ui/ash/network/network_portal_notification_controller.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_NOTIFICATION_CONTROLLER_H_ -#define CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_NOTIFICATION_CONTROLLER_H_ +#ifndef CHROME_BROWSER_UI_ASH_NETWORK_NETWORK_PORTAL_NOTIFICATION_CONTROLLER_H_ +#define CHROME_BROWSER_UI_ASH_NETWORK_NETWORK_PORTAL_NOTIFICATION_CONTROLLER_H_ #include <string> @@ -17,7 +17,7 @@ namespace extensions { class Extension; class NetworkingConfigService; -} +} // namespace extensions namespace chromeos { @@ -140,4 +140,4 @@ } // namespace chromeos -#endif // CHROME_BROWSER_CHROMEOS_NET_NETWORK_PORTAL_NOTIFICATION_CONTROLLER_H_ +#endif // CHROME_BROWSER_UI_ASH_NETWORK_NETWORK_PORTAL_NOTIFICATION_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc b/chrome/browser/ui/ash/network/network_portal_notification_controller_unittest.cc similarity index 98% rename from chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc rename to chrome/browser/ui/ash/network/network_portal_notification_controller_unittest.cc index ace13b00..4615324 100644 --- a/chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc +++ b/chrome/browser/ui/ash/network/network_portal_notification_controller_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/chromeos/net/network_portal_notification_controller.h" +#include "chrome/browser/ui/ash/network/network_portal_notification_controller.h" #include "base/command_line.h" #include "base/macros.h" #include "chrome/browser/chromeos/profiles/profile_helper.h"
diff --git a/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc b/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc index 89d3205..6a8e81e 100644 --- a/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc +++ b/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
@@ -6,7 +6,9 @@ #include "base/command_line.h" #include "chrome/browser/ui/ash/tablet_mode_client.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/url_constants.h" #include "chrome/test/base/in_process_browser_test.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" @@ -106,4 +108,61 @@ false /* tablet_mode_enabled */); } +IN_PROC_BROWSER_TEST_F(TabletModePageBehaviorTest, ExcludeInternalPages) { + constexpr char kSettingsUrl[] = "chrome://settings/"; + AddTabAtIndexToBrowser(browser(), 0, GURL(kSettingsUrl), + ui::PAGE_TRANSITION_LINK, + false /* check_navigation_success */); + auto* web_contents = GetActiveWebContents(browser()); + ASSERT_TRUE(web_contents); + EXPECT_STREQ(web_contents->GetLastCommittedURL().spec().c_str(), + kSettingsUrl); + + // Now enable tablet mode, and expect that this internal page's web prefs + // remain unaffected as if tablet mode is off. + ToggleTabletMode(); + ASSERT_TRUE(GetTabletModeEnabled()); + ValidateWebPrefs(GetWebKitPreferences(web_contents), + false /* tablet_mode_enabled */); +} + +IN_PROC_BROWSER_TEST_F(TabletModePageBehaviorTest, ExcludeHostedApps) { + browser()->window()->Close(); + + // Open a new app window. + Browser::CreateParams params = Browser::CreateParams::CreateForApp( + "test_browser_app", true /* trusted_source */, gfx::Rect(), + browser()->profile(), true); + params.initial_show_state = ui::SHOW_STATE_DEFAULT; + Browser* browser = new Browser(params); + AddBlankTabAndShow(browser); + + ASSERT_TRUE(browser->is_app()); + auto* web_contents = GetActiveWebContents(browser); + ASSERT_TRUE(web_contents); + + // Now enable tablet mode, and expect that the page's web prefs of this hosted + // app remain unaffected as if tablet mode is off. + ToggleTabletMode(); + ASSERT_TRUE(GetTabletModeEnabled()); + ValidateWebPrefs(GetWebKitPreferences(web_contents), + false /* tablet_mode_enabled */); +} + +IN_PROC_BROWSER_TEST_F(TabletModePageBehaviorTest, ExcludeNTPs) { + AddTabAtIndexToBrowser(browser(), 0, GURL(chrome::kChromeSearchLocalNtpUrl), + ui::PAGE_TRANSITION_LINK, + false /* check_navigation_success */); + auto* web_contents = GetActiveWebContents(browser()); + ASSERT_TRUE(web_contents); + EXPECT_STREQ(web_contents->GetLastCommittedURL().spec().c_str(), + chrome::kChromeSearchLocalNtpUrl); + + // NTPs should not be affected in tablet mode. + ToggleTabletMode(); + ASSERT_TRUE(GetTabletModeEnabled()); + ValidateWebPrefs(GetWebKitPreferences(web_contents), + false /* tablet_mode_enabled */); +} + } // namespace
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc index 7ba44f54..bccb99f7 100644 --- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc +++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
@@ -42,10 +42,10 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(PopupBlockerTabHelper); struct PopupBlockerTabHelper::BlockedRequest { - BlockedRequest(const NavigateParams& params, + BlockedRequest(NavigateParams&& params, const blink::mojom::WindowFeatures& window_features, PopupBlockType block_type) - : params(params), + : params(std::move(params)), window_features(window_features), block_type(block_type) {} @@ -109,15 +109,15 @@ bool PopupBlockerTabHelper::MaybeBlockPopup( content::WebContents* web_contents, const base::Optional<GURL>& opener_url, - const NavigateParams& params, + NavigateParams* params, const content::OpenURLParams* open_url_params, const blink::mojom::WindowFeatures& window_features) { DCHECK(!open_url_params || - open_url_params->user_gesture == params.user_gesture); + open_url_params->user_gesture == params->user_gesture); LogAction(Action::kInitiated); - const bool user_gesture = params.user_gesture; + const bool user_gesture = params->user_gesture; if (!web_contents) return false; @@ -165,7 +165,7 @@ } void PopupBlockerTabHelper::AddBlockedPopup( - const NavigateParams& params, + NavigateParams* params, const blink::mojom::WindowFeatures& window_features, PopupBlockType block_type) { LogAction(Action::kBlocked); @@ -174,12 +174,12 @@ int id = next_id_; next_id_++; - blocked_popups_[id] = - std::make_unique<BlockedRequest>(params, window_features, block_type); + blocked_popups_[id] = std::make_unique<BlockedRequest>( + std::move(*params), window_features, block_type); TabSpecificContentSettings::FromWebContents(web_contents())-> OnContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS); for (auto& observer : observers_) - observer.BlockedPopupAdded(id, params.url); + observer.BlockedPopupAdded(id, blocked_popups_[id]->params.url); } void PopupBlockerTabHelper::ShowBlockedPopup( @@ -207,13 +207,13 @@ #else Navigate(&popup->params); #endif - if (popup->params.target_contents) { - PopupTracker::CreateForWebContents(popup->params.target_contents, - web_contents()); + if (popup->params.navigated_or_inserted_contents) { + PopupTracker::CreateForWebContents( + popup->params.navigated_or_inserted_contents, web_contents()); if (popup->params.disposition == WindowOpenDisposition::NEW_POPUP) { content::RenderFrameHost* host = - popup->params.target_contents->GetMainFrame(); + popup->params.navigated_or_inserted_contents->GetMainFrame(); DCHECK(host); chrome::mojom::ChromeRenderFrameAssociatedPtr client; host->GetRemoteAssociatedInterfaces()->GetInterface(&client);
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h index 3a841bf2..594503c 100644 --- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h +++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h
@@ -91,10 +91,13 @@ // |opener_url| is an optional parameter used to compute how the popup // permission will behave. If it is not set the current committed URL will be // used instead. + // + // If this function returns true, then the contents of |params| is moved to + // |blocked_popups_|. static bool MaybeBlockPopup( content::WebContents* web_contents, const base::Optional<GURL>& opener_url, - const NavigateParams& params, + NavigateParams* params, const content::OpenURLParams* open_url_params, const blink::mojom::WindowFeatures& window_features); @@ -118,7 +121,7 @@ explicit PopupBlockerTabHelper(content::WebContents* web_contents); - void AddBlockedPopup(const NavigateParams& params, + void AddBlockedPopup(NavigateParams* params, const blink::mojom::WindowFeatures& window_features, PopupBlockType block_type);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index a6ad2a7c..b0727a2 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -1466,17 +1466,18 @@ bool is_popup = source && PopupBlockerTabHelper::ConsiderForPopupBlocking( params.disposition); if (is_popup && PopupBlockerTabHelper::MaybeBlockPopup( - source, base::Optional<GURL>(), nav_params, ¶ms, + source, base::Optional<GURL>(), &nav_params, ¶ms, blink::mojom::WindowFeatures())) { return nullptr; } Navigate(&nav_params); - if (is_popup && nav_params.target_contents) - PopupTracker::CreateForWebContents(nav_params.target_contents, source); + if (is_popup && nav_params.navigated_or_inserted_contents) + PopupTracker::CreateForWebContents( + nav_params.navigated_or_inserted_contents, source); - return nav_params.target_contents; + return nav_params.navigated_or_inserted_contents; } void Browser::NavigationStateChanged(WebContents* source,
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc index 787b325..e817173df 100644 --- a/chrome/browser/ui/browser_navigator.cc +++ b/chrome/browser/ui/browser_navigator.cc
@@ -101,7 +101,7 @@ // Returns true on success. Otherwise, if changing params leads the browser into // an erroneous state, returns false. bool AdjustNavigateParamsForURL(NavigateParams* params) { - if (params->target_contents != NULL || + if (params->contents_to_insert || params->switch_to_singleton_tab || IsURLAllowedInIncognito(params->url, params->initiating_profile) || params->initiating_profile->IsGuestSession()) { return true; @@ -345,7 +345,9 @@ // by the time it goes out of scope, provided |params| wants it to be shown. class ScopedBrowserShower { public: - explicit ScopedBrowserShower(NavigateParams* params) : params_(params) {} + explicit ScopedBrowserShower(NavigateParams* params, + content::WebContents** contents) + : params_(params), contents_(contents) {} ~ScopedBrowserShower() { if (params_->window_action == NavigateParams::SHOW_WINDOW_INACTIVE) { params_->browser->window()->ShowInactive(); @@ -355,8 +357,8 @@ // If a user gesture opened a popup window, focus the contents. if (params_->user_gesture && params_->disposition == WindowOpenDisposition::NEW_POPUP && - params_->target_contents) { - params_->target_contents->Focus(); + *contents_) { + (*contents_)->Focus(); window->Activate(); } } @@ -364,44 +366,13 @@ private: NavigateParams* params_; + content::WebContents** contents_; DISALLOW_COPY_AND_ASSIGN(ScopedBrowserShower); }; -// This class manages the lifetime of a WebContents created by the -// Navigate() function. When Navigate() creates a WebContents for a URL, -// an instance of this class takes ownership of it via TakeOwnership() until the -// WebContents is added to a tab strip at which time ownership is -// relinquished via ReleaseOwnership(). If this object goes out of scope without -// being added to a tab strip, the created WebContents is deleted to -// avoid a leak and the params->target_contents field is set to NULL. -class ScopedTargetContentsOwner { - public: - explicit ScopedTargetContentsOwner(NavigateParams* params) - : params_(params) {} - ~ScopedTargetContentsOwner() { - if (target_contents_owner_.get()) - params_->target_contents = NULL; - } - - // Assumes ownership of |params_|' target_contents until ReleaseOwnership - // is called. - void TakeOwnership() { - target_contents_owner_.reset(params_->target_contents); - } - - // Relinquishes ownership of |params_|' target_contents. - WebContents* ReleaseOwnership() { - return target_contents_owner_.release(); - } - - private: - NavigateParams* params_; - std::unique_ptr<WebContents> target_contents_owner_; - DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner); -}; - -content::WebContents* CreateTargetContents(const NavigateParams& params, - const GURL& url) { +std::unique_ptr<content::WebContents> CreateTargetContents( + const NavigateParams& params, + const GURL& url) { // Always create the new WebContents in a new SiteInstance (and therefore a // new BrowsingInstance), *unless* there's a |params.opener|. // @@ -441,26 +412,31 @@ } #endif - WebContents* target_contents = WebContents::Create(create_params); + std::unique_ptr<WebContents> target_contents = + base::WrapUnique(WebContents::Create(create_params)); // New tabs can have WebUI URLs that will make calls back to arbitrary // tab helpers, so the entire set of tab helpers needs to be set up // immediately. - BrowserNavigatorWebContentsAdoption::AttachTabHelpers(target_contents); + BrowserNavigatorWebContentsAdoption::AttachTabHelpers(target_contents.get()); #if BUILDFLAG(ENABLE_EXTENSIONS) - extensions::TabHelper::FromWebContents(target_contents)-> - SetExtensionAppById(params.extension_app_id); + extensions::TabHelper::FromWebContents(target_contents.get()) + ->SetExtensionAppById(params.extension_app_id); #endif return target_contents; } -// If a prerendered page exists for |url|, replace the page at -// |params->target_contents| with it and update to point to the swapped-in -// WebContents. -bool SwapInPrerender(const GURL& url, NavigateParams* params) { - Profile* profile = - Profile::FromBrowserContext(params->target_contents->GetBrowserContext()); +// If a prerendered page exists for |url|, then replace +// params.contents_being_navigated with it. When this occurs, the new page is +// stored in params.replaced_contents. +// This method updates the underlying storage mechanism as well. e.g. On +// Desktop, |contents_being_navigated| is replaced in the tabstrip by +// |replaced_contents|. +bool SwapInPrerender(const GURL& url, + prerender::PrerenderManager::Params* params) { + Profile* profile = Profile::FromBrowserContext( + params->contents_being_navigated->GetBrowserContext()); prerender::PrerenderManager* prerender_manager = prerender::PrerenderManagerFactory::GetForBrowserContext(profile); return prerender_manager && @@ -511,13 +487,20 @@ params->source_contents = params->browser->tab_strip_model()->GetActiveWebContents(); } + + WebContents* contents_to_navigate_or_insert = + params->contents_to_insert.get(); + if (params->switch_to_singleton_tab) { + DCHECK_EQ(params->disposition, WindowOpenDisposition::SINGLETON_TAB); + contents_to_navigate_or_insert = params->switch_to_singleton_tab; + } int singleton_index; std::tie(params->browser, singleton_index) = GetBrowserAndTabForDisposition(*params); if (!params->browser) return; if (singleton_index != -1) { - params->target_contents = + contents_to_navigate_or_insert = params->browser->tab_strip_model()->GetWebContentsAt(singleton_index); } #if defined(OS_CHROMEOS) @@ -555,11 +538,12 @@ } // Make sure the Browser is shown if params call for it. - ScopedBrowserShower shower(params); + ScopedBrowserShower shower(params, &contents_to_navigate_or_insert); // Makes sure any WebContents created by this function is destroyed if // not properly added to a tab strip. - ScopedTargetContentsOwner target_contents_owner(params); + std::unique_ptr<WebContents> contents_to_insert = + std::move(params->contents_to_insert); // Some dispositions need coercion to base types. NormalizeDisposition(params); @@ -601,27 +585,29 @@ // If no target WebContents was specified (and we didn't seek and find a // singleton), we need to construct one if we are supposed to target a new // tab. - if (!params->target_contents) { + if (!contents_to_navigate_or_insert) { DCHECK(!params->url.is_empty()); if (params->disposition != WindowOpenDisposition::CURRENT_TAB) { - params->target_contents = CreateTargetContents(*params, params->url); - - // This function takes ownership of |params->target_contents| until it - // is added to a TabStripModel. - target_contents_owner.TakeOwnership(); + contents_to_insert = CreateTargetContents(*params, params->url); + contents_to_navigate_or_insert = contents_to_insert.get(); } else { // ... otherwise if we're loading in the current tab, the target is the // same as the source. DCHECK(params->source_contents); - params->target_contents = params->source_contents; + contents_to_navigate_or_insert = params->source_contents; + + prerender::PrerenderManager::Params prerender_params( + params, params->source_contents); // Prerender can only swap in CURRENT_TAB navigations; others have // different sessionStorage namespaces. - swapped_in_prerender = SwapInPrerender(params->url, params); + swapped_in_prerender = SwapInPrerender(params->url, &prerender_params); + if (swapped_in_prerender) + contents_to_navigate_or_insert = prerender_params.replaced_contents; } if (user_initiated) - params->target_contents->NavigatedByUser(); + contents_to_navigate_or_insert->NavigatedByUser(); if (!swapped_in_prerender) { // Try to handle non-navigational URLs that popup dialogs and such, these @@ -630,13 +616,13 @@ // Perform the actual navigation, tracking whether it came from the // renderer. - LoadURLInContents(params->target_contents, params->url, params); + LoadURLInContents(contents_to_navigate_or_insert, params->url, params); } } } else { - // |target_contents| was specified non-NULL, and so we assume it has already - // been navigated appropriately. We need to do nothing more other than - // add it to the appropriate tabstrip. + // |contents_to_navigate_or_insert| was specified non-NULL, and so we assume + // it has already been navigated appropriately. We need to do nothing more + // other than add it to the appropriate tabstrip. } // If the user navigated from the omnibox, and the selected tab is going to @@ -648,28 +634,24 @@ (params->tabstrip_add_types & TabStripModel::ADD_INHERIT_OPENER)) params->source_contents->Focus(); - if (params->source_contents == params->target_contents || + if (params->source_contents == contents_to_navigate_or_insert || (swapped_in_prerender && params->disposition == WindowOpenDisposition::CURRENT_TAB)) { // The navigation occurred in the source tab. params->browser->UpdateUIForNavigationInTab( - params->target_contents, params->transition, params->window_action, - user_initiated); + contents_to_navigate_or_insert, params->transition, + params->window_action, user_initiated); } else if (singleton_index == -1) { // If some non-default value is set for the index, we should tell the // TabStripModel to respect it. if (params->tabstrip_index != -1) params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX; + DCHECK(contents_to_insert); // The navigation should insert a new tab into the target Browser. params->browser->tab_strip_model()->AddWebContents( - base::WrapUnique(params->target_contents), params->tabstrip_index, + std::move(contents_to_insert), params->tabstrip_index, params->transition, params->tabstrip_add_types); - - // TODO(erikchen): Fix ownership semantics here. https://crbug.com/832879. - // Now that the |params->target_contents| is safely owned by the target - // Browser's TabStripModel, we can release ownership. - target_contents_owner.ReleaseOwnership(); } if (singleton_index >= 0) { @@ -678,16 +660,16 @@ params->browser != source_browser) params->window_action = NavigateParams::SHOW_WINDOW; - if (params->target_contents->IsCrashed()) { - params->target_contents->GetController().Reload( + if (contents_to_navigate_or_insert->IsCrashed()) { + contents_to_navigate_or_insert->GetController().Reload( content::ReloadType::NORMAL, true); } else if (params->path_behavior == NavigateParams::IGNORE_AND_NAVIGATE && - params->target_contents->GetURL() != params->url) { - LoadURLInContents(params->target_contents, params->url, params); + contents_to_navigate_or_insert->GetURL() != params->url) { + LoadURLInContents(contents_to_navigate_or_insert, params->url, params); } // If the singleton tab isn't already selected, select it. - if (params->source_contents != params->target_contents) { + if (params->source_contents != contents_to_navigate_or_insert) { // Use the index before the potential close below, because it could // make the index refer to a different tab. params->browser->tab_strip_model()->ActivateTabAt(singleton_index, @@ -713,8 +695,10 @@ content::NotificationService::current()->Notify( chrome::NOTIFICATION_TAB_ADDED, content::Source<content::WebContentsDelegate>(params->browser), - content::Details<WebContents>(params->target_contents)); + content::Details<WebContents>(contents_to_navigate_or_insert)); } + + params->navigated_or_inserted_contents = contents_to_navigate_or_insert; } bool IsURLAllowedInIncognito(const GURL& url,
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc index 78119a11..4bcf9eb 100644 --- a/chrome/browser/ui/browser_navigator_browsertest.cc +++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -119,19 +119,19 @@ post_data.data(), post_data.size()); ui_test_utils::NavigateToURL(¶m); - if (!param.target_contents) + if (!param.navigated_or_inserted_contents) return false; // Navigate() should have opened the contents in new foreground tab in the // current Browser. EXPECT_EQ(browser(), param.browser); EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), - param.target_contents); + param.navigated_or_inserted_contents); // We should have one window, with one tab. EXPECT_EQ(1u, chrome::GetTotalBrowserCount()); EXPECT_EQ(2, browser()->tab_strip_model()->count()); - *title = param.target_contents->GetTitle(); + *title = param.navigated_or_inserted_contents->GetTitle(); return true; } @@ -149,7 +149,8 @@ return browser; } -WebContents* BrowserNavigatorTest::CreateWebContents(bool initialize_renderer) { +std::unique_ptr<WebContents> BrowserNavigatorTest::CreateWebContents( + bool initialize_renderer) { content::WebContents::CreateParams create_params(browser()->profile()); create_params.initialize_renderer = initialize_renderer; content::WebContents* base_web_contents = @@ -158,7 +159,7 @@ create_params.initial_size = base_web_contents->GetContainerBounds().size(); } - return WebContents::Create(create_params); + return base::WrapUnique(WebContents::Create(create_params)); } void BrowserNavigatorTest::RunSuppressTest(WindowOpenDisposition disposition) { @@ -365,7 +366,7 @@ EXPECT_NE(old_contents, browser()->tab_strip_model()->GetActiveWebContents()); EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), - params.target_contents); + params.navigated_or_inserted_contents); EXPECT_EQ(2, browser()->tab_strip_model()->count()); } @@ -1044,14 +1045,14 @@ IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, TargetContents_ForegroundTab) { NavigateParams params(MakeNavigateParams()); params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; - params.target_contents = CreateWebContents(false); + params.contents_to_insert = CreateWebContents(false); Navigate(¶ms); // Navigate() should have opened the contents in a new foreground tab in the // current Browser. EXPECT_EQ(browser(), params.browser); EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), - params.target_contents); + params.navigated_or_inserted_contents); // We should have one window, with two tabs. EXPECT_EQ(1u, chrome::GetTotalBrowserCount()); @@ -1063,7 +1064,7 @@ IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, DISABLED_TargetContents_Popup) { NavigateParams params(MakeNavigateParams()); params.disposition = WindowOpenDisposition::NEW_POPUP; - params.target_contents = CreateWebContents(false); + params.contents_to_insert = CreateWebContents(false); params.window_bounds = gfx::Rect(10, 10, 500, 500); Navigate(¶ms); @@ -1087,7 +1088,7 @@ // All platforms should respect size however provided width > 400 (Mac has a // minimum window width of 400). EXPECT_EQ(params.window_bounds.size(), - params.target_contents->GetContainerBounds().size()); + params.navigated_or_inserted_contents->GetContainerBounds().size()); // We should have two windows, the new popup and the browser() provided by the // framework. @@ -1103,25 +1104,25 @@ CreateWebContentsWithRendererProcess) { NavigateParams params(MakeNavigateParams()); params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; - params.target_contents = CreateWebContents(true); - ASSERT_TRUE(params.target_contents); + params.contents_to_insert = CreateWebContents(true); + ASSERT_TRUE(params.contents_to_insert); // There is no navigation (to about:blank or something like that). - EXPECT_FALSE(params.target_contents->IsLoading()); + EXPECT_FALSE(params.contents_to_insert->IsLoading()); - ASSERT_TRUE(params.target_contents->GetMainFrame()); - EXPECT_TRUE(params.target_contents->GetMainFrame()->IsRenderFrameLive()); + ASSERT_TRUE(params.contents_to_insert->GetMainFrame()); + EXPECT_TRUE(params.contents_to_insert->GetMainFrame()->IsRenderFrameLive()); EXPECT_TRUE( - params.target_contents->GetController().IsInitialBlankNavigation()); + params.contents_to_insert->GetController().IsInitialBlankNavigation()); int renderer_id = - params.target_contents->GetMainFrame()->GetProcess()->GetID(); + params.contents_to_insert->GetMainFrame()->GetProcess()->GetID(); // We should have one window, with one tab of WebContents differ from // params.target_contents. EXPECT_EQ(1u, chrome::GetTotalBrowserCount()); EXPECT_EQ(1, browser()->tab_strip_model()->count()); EXPECT_NE(browser()->tab_strip_model()->GetActiveWebContents(), - params.target_contents); + params.contents_to_insert.get()); Navigate(¶ms); @@ -1129,9 +1130,10 @@ // current Browser, without changing the renderer process of target_contents. EXPECT_EQ(browser(), params.browser); EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), - params.target_contents); - EXPECT_EQ(renderer_id, - params.target_contents->GetMainFrame()->GetProcess()->GetID()); + params.navigated_or_inserted_contents); + EXPECT_EQ(renderer_id, params.navigated_or_inserted_contents->GetMainFrame() + ->GetProcess() + ->GetID()); // We should have one window, with two tabs. EXPECT_EQ(1u, chrome::GetTotalBrowserCount()); @@ -1153,7 +1155,8 @@ // Navigate() should have inserted a new tab at slot 0 in the tabstrip. EXPECT_EQ(browser(), params.browser); EXPECT_EQ(0, browser()->tab_strip_model()->GetIndexOfWebContents( - static_cast<const WebContents*>(params.target_contents))); + static_cast<const WebContents*>( + params.navigated_or_inserted_contents))); // We should have one window - the browser() provided by the framework. EXPECT_EQ(1u, chrome::GetTotalBrowserCount()); @@ -1757,10 +1760,11 @@ ui_test_utils::NavigateToURL(¶ms); base::string16 expected_title(base::UTF8ToUTF16(unescaped_title)); - EXPECT_TRUE(params.target_contents); - EXPECT_EQ(expected_title, params.target_contents->GetTitle()); + EXPECT_TRUE(params.navigated_or_inserted_contents); + EXPECT_EQ(expected_title, params.navigated_or_inserted_contents->GetTitle()); // GURL always keeps non-ASCII characters escaped, but check them anyways. - EXPECT_EQ(GURL(expected_url).spec(), params.target_contents->GetURL().spec()); + EXPECT_EQ(GURL(expected_url).spec(), + params.navigated_or_inserted_contents->GetURL().spec()); // Check the omnibox text. It should have escaped RTL with unescaped text. LocationBar* location_bar = browser()->window()->GetLocationBar(); OmniboxView* omnibox_view = location_bar->GetOmniboxView();
diff --git a/chrome/browser/ui/browser_navigator_browsertest.h b/chrome/browser/ui/browser_navigator_browsertest.h index 4660b4c..79af5d6 100644 --- a/chrome/browser/ui/browser_navigator_browsertest.h +++ b/chrome/browser/ui/browser_navigator_browsertest.h
@@ -32,7 +32,8 @@ Browser* CreateEmptyBrowserForType(Browser::Type type, Profile* profile); Browser* CreateEmptyBrowserForApp(Profile* profile); - content::WebContents* CreateWebContents(bool initialize_renderer); + std::unique_ptr<content::WebContents> CreateWebContents( + bool initialize_renderer); void RunSuppressTest(WindowOpenDisposition disposition); void RunUseNonIncognitoWindowTest(const GURL& url);
diff --git a/chrome/browser/ui/browser_navigator_params.cc b/chrome/browser/ui/browser_navigator_params.cc index d8c68a70..0f5171b 100644 --- a/chrome/browser/ui/browser_navigator_params.cc +++ b/chrome/browser/ui/browser_navigator_params.cc
@@ -7,6 +7,7 @@ #include "build/build_config.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/page_navigator.h" +#include "content/public/browser/web_contents.h" #if !defined(OS_ANDROID) #include "chrome/browser/ui/browser.h" @@ -17,8 +18,8 @@ using content::WebContents; #if defined(OS_ANDROID) -NavigateParams::NavigateParams(WebContents* a_target_contents) - : target_contents(a_target_contents) {} +NavigateParams::NavigateParams(std::unique_ptr<WebContents> contents_to_insert) + : contents_to_insert(std::move(contents_to_insert)) {} #else NavigateParams::NavigateParams(Browser* a_browser, const GURL& a_url, @@ -26,8 +27,8 @@ : url(a_url), transition(a_transition), browser(a_browser) {} NavigateParams::NavigateParams(Browser* a_browser, - WebContents* a_target_contents) - : target_contents(a_target_contents), browser(a_browser) {} + std::unique_ptr<WebContents> contents_to_insert) + : contents_to_insert(std::move(contents_to_insert)), browser(a_browser) {} #endif // !defined(OS_ANDROID) NavigateParams::NavigateParams(Profile* a_profile, @@ -39,7 +40,7 @@ window_action(SHOW_WINDOW), initiating_profile(a_profile) {} -NavigateParams::NavigateParams(const NavigateParams& other) = default; +NavigateParams::NavigateParams(NavigateParams&&) = default; NavigateParams::~NavigateParams() {}
diff --git a/chrome/browser/ui/browser_navigator_params.h b/chrome/browser/ui/browser_navigator_params.h index f5220e9..bfb2276 100644 --- a/chrome/browser/ui/browser_navigator_params.h +++ b/chrome/browser/ui/browser_navigator_params.h
@@ -56,25 +56,26 @@ // TODO(thestig): Split or ifdef out more fields that are not used on Android. struct NavigateParams { #if defined(OS_ANDROID) - explicit NavigateParams(content::WebContents* a_target_contents); + explicit NavigateParams( + std::unique_ptr<content::WebContents> contents_to_insert); #else NavigateParams(Browser* browser, const GURL& a_url, ui::PageTransition a_transition); NavigateParams(Browser* browser, - content::WebContents* a_target_contents); + std::unique_ptr<content::WebContents> contents_to_insert); #endif NavigateParams(Profile* profile, const GURL& a_url, ui::PageTransition a_transition); - NavigateParams(const NavigateParams& other); + NavigateParams(NavigateParams&& params); ~NavigateParams(); // Copies fields from |params| struct to |nav_params| struct. void FillNavigateParamsFromOpenURLParams( const content::OpenURLParams& params); - // The URL/referrer to be loaded. Ignored if |target_contents| is non-NULL. + // The URL/referrer to be loaded. Ignored if |contents_to_insert| is non-NULL. GURL url; content::Referrer referrer; @@ -100,19 +101,26 @@ // is terminated by \r\n. May be empty if no extra headers are needed. std::string extra_headers; - // [in] A WebContents to be navigated or inserted into the target - // Browser's tabstrip. If NULL, |url| or the homepage will be used - // instead. When non-NULL, Navigate() assumes it has already been - // navigated to its intended destination and will not load any URL in it - // (i.e. |url| is ignored). - // Default is NULL. - // [out] The WebContents in which the navigation occurred or that was - // inserted. Guaranteed non-NULL except for note below: - // Note: If this field is set to NULL by the caller and Navigate() creates - // a new WebContents, this field will remain NULL and the - // WebContents deleted if the WebContents it created is - // not added to a TabStripModel before Navigate() returns. - content::WebContents* target_contents = nullptr; + // Input parameter. + // WebContents to be inserted into the target Browser's tabstrip. If NULL, + // |url| or the homepage will be used instead. When non-NULL, Navigate() + // assumes it has already been navigated to its intended destination and will + // not load any URL in it (i.e. |url| is ignored). Default is NULL. + std::unique_ptr<content::WebContents> contents_to_insert; + + // Input parameter. + // Only used by Singleton tabs. Causes a tab-switch in addition to navigation. + content::WebContents* switch_to_singleton_tab = nullptr; + + // Output parameter. + // The WebContents in which the navigation occurred or that was inserted. + // Guaranteed non-NULL except for note below: + // + // Note: If this field is set to NULL by the caller and Navigate() creates a + // new WebContents, this field will remain NULL and the WebContents deleted if + // the WebContents it created is not added to a TabStripModel before + // Navigate() returns. + content::WebContents* navigated_or_inserted_contents = nullptr; // [in] The WebContents that initiated the Navigate() request if such // context is necessary. Default is NULL, i.e. no context. @@ -224,7 +232,8 @@ // navigation entry. bool should_replace_current_entry = false; - // Indicates whether |target_contents| is being created with a window.opener. + // Indicates whether |contents_to_insert| is being created with a + // window.opener. bool created_with_opener = false; // Whether or not the related navigation was started in the context menu. @@ -248,6 +257,7 @@ private: NavigateParams(); + DISALLOW_COPY_AND_ASSIGN(NavigateParams); }; #endif // CHROME_BROWSER_UI_BROWSER_NAVIGATOR_PARAMS_H_
diff --git a/chrome/browser/ui/browser_tabstrip.cc b/chrome/browser/ui/browser_tabstrip.cc index 4db66846..5d3c9a44 100644 --- a/chrome/browser/ui/browser_tabstrip.cc +++ b/chrome/browser/ui/browser_tabstrip.cc
@@ -32,7 +32,7 @@ params.tabstrip_index = idx; Navigate(¶ms); CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(params.target_contents); + CoreTabHelper::FromWebContents(params.navigated_or_inserted_contents); core_tab_helper->set_new_tab_start_time(new_tab_start_time); } @@ -43,7 +43,7 @@ NavigateParams params(browser, url, transition); params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms); - return params.target_contents; + return params.navigated_or_inserted_contents; } void AddWebContents(Browser* browser, @@ -57,7 +57,8 @@ // Can't create a new contents for the current tab - invalid case. DCHECK(disposition != WindowOpenDisposition::CURRENT_TAB); - NavigateParams params(browser, new_contents); + // TODO(erikchen): Fix ownership semantics. https://crbug.com/832879. + NavigateParams params(browser, base::WrapUnique(new_contents)); params.source_contents = source_contents; params.disposition = disposition; params.window_bounds = initial_rect;
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc index ef75973..6c629b3 100644 --- a/chrome/browser/ui/chrome_pages.cc +++ b/chrome/browser/ui/chrome_pages.cc
@@ -74,13 +74,13 @@ base::Int64ToString(node_id).c_str())); NavigateParams params(GetSingletonTabNavigateParams(browser, url)); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void NavigateToSingletonTab(Browser* browser, const GURL& url) { NavigateParams params(GetSingletonTabNavigateParams(browser, url)); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } // Shows either the help app or the appropriate help page for |source|. If @@ -203,7 +203,7 @@ NavigateParams params( GetSingletonTabNavigateParams(browser, GURL(kChromeUIBookmarksURL))); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void ShowBookmarkManagerForNode(Browser* browser, int64_t node_id) { @@ -216,7 +216,7 @@ NavigateParams params( GetSingletonTabNavigateParams(browser, GURL(kChromeUIHistoryURL))); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void ShowDownloads(Browser* browser) { @@ -242,7 +242,7 @@ replacements.SetQueryStr(query); params.url = params.url.ReplaceComponents(replacements); } - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void ShowConflicts(Browser* browser) { @@ -328,7 +328,7 @@ GURL gurl = GetSettingsUrl(sub_page); NavigateParams params(GetSingletonTabNavigateParams(browser, gurl)); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void ShowContentSettingsExceptions(Browser* browser, @@ -385,7 +385,7 @@ NavigateParams params( GetSingletonTabNavigateParams(browser, GURL(kChromeUIHelpURL))); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); #endif }
diff --git a/chrome/browser/ui/cocoa/applescript/window_applescript.mm b/chrome/browser/ui/cocoa/applescript/window_applescript.mm index 9062948..443d3838 100644 --- a/chrome/browser/ui/cocoa/applescript/window_applescript.mm +++ b/chrome/browser/ui/cocoa/applescript/window_applescript.mm
@@ -204,10 +204,10 @@ params.tabstrip_index = index; Navigate(¶ms); CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(params.target_contents); + CoreTabHelper::FromWebContents(params.navigated_or_inserted_contents); core_tab_helper->set_new_tab_start_time(newTabStartTime); - [aTab setWebContents:params.target_contents]; + [aTab setWebContents:params.navigated_or_inserted_contents]; } - (void)removeFromTabsAtIndex:(int)index {
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm index b970adf..5f72100 100644 --- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm +++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
@@ -105,10 +105,13 @@ CreatePopupIfNeeded(); NSImage* answerImage = nil; - if (!model_->rich_suggestion_bitmap().isNull()) { - answerImage = - gfx::Image::CreateFrom1xBitmap(model_->rich_suggestion_bitmap()) - .CopyNSImage(); + const size_t result_size = model_->result().size(); + for (size_t i = 0; i < result_size; ++i) { + const SkBitmap* bitmap = model_->RichSuggestionBitmapAt(i); + if (result.match_at(i).answer && bitmap != nullptr) { + answerImage = gfx::Image::CreateFrom1xBitmap(*bitmap).CopyNSImage(); + break; + } } [matrix_ setController:[[[OmniboxPopupTableController alloc] initWithMatchResults:result
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc index c738003..cb831d9c 100644 --- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc +++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -1006,9 +1006,11 @@ blink::mojom::WindowFeatures(), false, true); constexpr size_t kItemCount = 3; for (size_t i = 1; i <= kItemCount; i++) { + NavigateParams navigate_params = + params.CreateNavigateParams(web_contents()); EXPECT_TRUE(PopupBlockerTabHelper::MaybeBlockPopup( - web_contents(), url, params.CreateNavigateParams(web_contents()), - nullptr /*=open_url_params*/, params.features())); + web_contents(), url, &navigate_params, nullptr /*=open_url_params*/, + params.features())); EXPECT_EQ(i, list_items.size()); } }
diff --git a/chrome/browser/ui/docs/window-resize.md b/chrome/browser/ui/docs/window-resize.md new file mode 100644 index 0000000..a654777 --- /dev/null +++ b/chrome/browser/ui/docs/window-resize.md
@@ -0,0 +1,72 @@ +# Metrics for Understanding Browser Resize Behaviour +## Motivation +Providing a smooth experience during an interactive resize of a browser window +is remarkably difficult. Each resize operation involves the browser process +(which receives the event), the renderer process (which needs to update the +page, relayout etc.), and the gpu process (which updates the content on +screen). There are various trade-offs made in this process to make the +user-experience better. Having some data to model the resize-behaviour of users +can help us improve the various heuristics we make, and fine-tune them to +improve the user experience. It also makes it easier to write tests to match +the user behaviour and watch for regressions/improvements etc. + +## Possible Improvements in Chrome +* The amount of gutter allowed can be adjusted. + * If user is doing quick-drags to resize, then perhaps the amount of gutter is + the wrong metric to optimize for. +* Maybe some different UX? For example, scale the content instead of +live-updates for small incremental changes if we can tell with high confidence +that more resize events are on the way, etc. +https://crbug.com/837247 + +--- +## BrowserWindow.Resize.Duration +* units: milliseconds +* owners: sadrul@chromium.org, mustash-team@google.com +* added: 2018-01-01 +* expires: 2018-08-31 +* os: windows +* tags: input, interactive + +Duration of an interactive resize from start to end. Measured only on Windows. + +--- +## BrowserWindow.Resize.StepBoundsChange +* units: pixels +* owners: sadrul@chromium.org +* added: 2018-01-01 +* expires: 2018-08-31 +* os: windows +* tags: input, interactive + +Size changed between two consecutive steps during browser-window resize. +Measured only on Windows. + +--- +## BrowserWindow.Resize.StepCount +* units: steps +* owners: sadrul@chromium.org +* added: 2018-01-01 +* expires: 2018-08-31 +* os: windows +* tags: input, interactive + +Number of intermediate resize-steps taken to complete the resize from start to +end. Measured only on Windows. + +--- +## BrowserWindow.Resize.StepInterval +* units: milliseconds +* owners: sadrul@chromium.org +* added: 2018-01-01 +* expires: 2018-08-31 +* os: windows +* tags: input, interactive + +Time-interval between two consecutive steps during browser-window resize. An +interactive resize can have many number of small steps. This measures the +interval between two steps. 'Duration' measures the interval between the first +and last steps. Note that a high interval is not necessarily bad (e.g. a user +could pause in the middle of the resize). Measured only on Windows. + +---
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc index 02a673e..e4739a3 100644 --- a/chrome/browser/ui/extensions/application_launch.cc +++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -244,7 +244,7 @@ contents = existing_tab; } else { Navigate(¶ms); - contents = params.target_contents; + contents = params.navigated_or_inserted_contents; } #if defined(OS_CHROMEOS) @@ -376,7 +376,7 @@ nav_params.opener = params.opener; Navigate(&nav_params); - WebContents* web_contents = nav_params.target_contents; + WebContents* web_contents = nav_params.navigated_or_inserted_contents; extensions::HostedAppBrowserController::SetAppPrefsForWebContents( browser->hosted_app_controller(), web_contents);
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.cc b/chrome/browser/ui/extensions/extension_install_ui_default.cc index f24f7d2c..2307b0c 100644 --- a/chrome/browser/ui/extensions/extension_install_ui_default.cc +++ b/chrome/browser/ui/extensions/extension_install_ui_default.cc
@@ -144,7 +144,7 @@ content::NotificationService::current()->Notify( chrome::NOTIFICATION_APP_INSTALLED_TO_NTP, - content::Source<WebContents>(params.target_contents), + content::Source<WebContents>(params.navigated_or_inserted_contents), content::Details<const std::string>(&app_id)); } #endif
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc index 1d942e7..1ddbd4b6 100644 --- a/chrome/browser/ui/layout_constants.cc +++ b/chrome/browser/ui/layout_constants.cc
@@ -60,13 +60,9 @@ case LOCATION_BAR_ICON_INTERIOR_PADDING: return touch_optimized_material ? 8 : 4; case TABSTRIP_NEW_TAB_BUTTON_SPACING: { - // In non-touch optimized UI, we make the new tab button overlap with the - // last tab in the tabstrip (i.e negative spacing). However, in - // touch-optimized UI, we actually want to push the new tab button - // further away from the tab. The distance is 8 DIP from the point at - // which the last tab's endcap intersects with the tabstrip separator, - // which is actually 6 DIP from the last tab's right point. - constexpr int kSpacing[] = {-5, -6, 6, -5}; + // In older material UI, we make the new tab button overlap with the last + // tab in the tabstrip. + constexpr int kSpacing[] = {-5, -6, 6, 0}; return kSpacing[mode]; } case TAB_AFTER_TITLE_PADDING: @@ -76,7 +72,7 @@ case TAB_ALERT_INDICATOR_ICON_WIDTH: return touch_optimized_material ? 12 : 16; case TAB_HEIGHT: { - constexpr int kTabHeight[] = {29, 33, 41, 29}; + constexpr int kTabHeight[] = {29, 33, 41, 36}; return kTabHeight[mode]; } case TAB_PRE_TITLE_PADDING:
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc index 7008f368..041d5954 100644 --- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc +++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -74,6 +74,9 @@ namespace { +typedef base::RepeatingCallback<void(const SkBitmap& bitmap)> + RichSuggestionImageCallback; + // Calls the specified callback when the requested image is downloaded. This // is a separate class instead of being implemented on ChromeOmniboxClient // because BitmapFetcherService currently takes ownership of this object. @@ -82,14 +85,15 @@ // call directly. class RichSuggestionImageObserver : public BitmapFetcherService::Observer { public: - explicit RichSuggestionImageObserver(const BitmapFetchedCallback& callback) + explicit RichSuggestionImageObserver( + const RichSuggestionImageCallback& callback) : callback_(callback) {} void OnImageChanged(BitmapFetcherService::RequestId request_id, const SkBitmap& image) override; private: - const BitmapFetchedCallback callback_; + const RichSuggestionImageCallback callback_; DISALLOW_COPY_AND_ASSIGN(RichSuggestionImageObserver); }; @@ -108,7 +112,6 @@ : controller_(static_cast<ChromeOmniboxEditController*>(controller)), profile_(profile), scheme_classifier_(profile), - request_id_(BitmapFetcherService::REQUEST_ID_INVALID), favicon_cache_(FaviconServiceFactory::GetForProfile( profile, ServiceAccessType::EXPLICIT_ACCESS), @@ -119,8 +122,11 @@ ChromeOmniboxClient::~ChromeOmniboxClient() { BitmapFetcherService* image_service = BitmapFetcherServiceFactory::GetForBrowserContext(profile_); - if (image_service) - image_service->CancelRequest(request_id_); + if (image_service) { + for (auto request_id : request_ids_) { + image_service->CancelRequest(request_id); + } + } } std::unique_ptr<AutocompleteProviderClient> @@ -277,16 +283,23 @@ if (!image_service) { return; } - image_service->CancelRequest(request_id_); - const auto match = std::find_if( - result.begin(), result.end(), - [](const AutocompleteMatch& current) { return !!current.answer; }); - if (match != result.end()) { + // Clear out the old requests. + for (auto request_id : request_ids_) { + image_service->CancelRequest(request_id); + } + request_ids_.clear(); + // Create new requests. + int result_index = -1; + for (const auto& match : result) { + ++result_index; + if (match.ImageUrl().is_empty()) { + continue; + } // TODO(jdonnelly, rhalavati): Create a helper function with Callback to // create annotation and pass it to image_service, merging this annotation // and the one in // chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc - net::NetworkTrafficAnnotationTag traffic_annotation = + constexpr net::NetworkTrafficAnnotationTag traffic_annotation = net::DefineNetworkTrafficAnnotation("omnibox_result_change", R"( semantics { sender: "Omnibox" @@ -322,12 +335,12 @@ } })"); - request_id_ = image_service->RequestImage( - match->answer->second_line().image_url(), - new RichSuggestionImageObserver( - base::BindRepeating(&ChromeOmniboxClient::OnBitmapFetched, - base::Unretained(this), on_bitmap_fetched)), - traffic_annotation); + request_ids_.push_back(image_service->RequestImage( + match.ImageUrl(), + new RichSuggestionImageObserver(base::BindRepeating( + &ChromeOmniboxClient::OnBitmapFetched, base::Unretained(this), + on_bitmap_fetched, result_index)), + traffic_annotation)); } } @@ -482,9 +495,9 @@ } void ChromeOmniboxClient::OnBitmapFetched(const BitmapFetchedCallback& callback, + int result_index, const SkBitmap& bitmap) { - request_id_ = BitmapFetcherService::REQUEST_ID_INVALID; - callback.Run(bitmap); + callback.Run(result_index, bitmap); } void ChromeOmniboxClient::OnDefaultSearchProviderFaviconFetched(
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.h b/chrome/browser/ui/omnibox/chrome_omnibox_client.h index d80c2f5..5c42a15 100644 --- a/chrome/browser/ui/omnibox/chrome_omnibox_client.h +++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
@@ -5,6 +5,9 @@ #ifndef CHROME_BROWSER_UI_OMNIBOX_CHROME_OMNIBOX_CLIENT_H_ #define CHROME_BROWSER_UI_OMNIBOX_CHROME_OMNIBOX_CLIENT_H_ +#include <memory> +#include <vector> + #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -87,6 +90,7 @@ void DoPreconnect(const AutocompleteMatch& match); void OnBitmapFetched(const BitmapFetchedCallback& callback, + int result_index, const SkBitmap& bitmap); void OnDefaultSearchProviderFaviconFetched( @@ -95,7 +99,7 @@ ChromeOmniboxEditController* controller_; Profile* profile_; ChromeAutocompleteSchemeClassifier scheme_classifier_; - BitmapFetcherService::RequestId request_id_; + std::vector<BitmapFetcherService::RequestId> request_ids_; FaviconCache favicon_cache_; base::CancelableTaskTracker default_search_provider_favicon_task_tracker_;
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc index bc2a8ba..bbee1d94 100644 --- a/chrome/browser/ui/signin_view_controller.cc +++ b/chrome/browser/ui/signin_view_controller.cc
@@ -162,7 +162,7 @@ active_contents->OpenURL(params); } else { NavigateParams params = GetSingletonTabNavigateParams(browser, signin_url); - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); active_contents = browser->tab_strip_model()->GetActiveWebContents(); } DCHECK(active_contents);
diff --git a/chrome/browser/ui/singleton_tabs.cc b/chrome/browser/ui/singleton_tabs.cc index aefcb095..18b45d1 100644 --- a/chrome/browser/ui/singleton_tabs.cc +++ b/chrome/browser/ui/singleton_tabs.cc
@@ -39,27 +39,26 @@ Navigate(¶ms); } -void ShowSingletonTabOverwritingNTP(Browser* browser, - const NavigateParams& params) { +void ShowSingletonTabOverwritingNTP(Browser* browser, NavigateParams params) { DCHECK(browser); - NavigateParams local_params(params); + DCHECK_EQ(params.disposition, WindowOpenDisposition::SINGLETON_TAB); content::WebContents* contents = browser->tab_strip_model()->GetActiveWebContents(); if (contents) { const GURL& contents_url = contents->GetVisibleURL(); if (contents_url == chrome::kChromeUINewTabURL || search::IsInstantNTP(contents) || contents_url == url::kAboutBlankURL) { - int tab_index = GetIndexOfExistingTab(browser, local_params); + int tab_index = GetIndexOfExistingTab(browser, params); if (tab_index < 0) { - local_params.disposition = WindowOpenDisposition::CURRENT_TAB; + params.disposition = WindowOpenDisposition::CURRENT_TAB; } else { - local_params.target_contents = + params.switch_to_singleton_tab = browser->tab_strip_model()->GetWebContentsAt(tab_index); } } } - Navigate(&local_params); + Navigate(¶ms); } NavigateParams GetSingletonTabNavigateParams(Browser* browser,
diff --git a/chrome/browser/ui/singleton_tabs.h b/chrome/browser/ui/singleton_tabs.h index f9115b98..3dd4eb4 100644 --- a/chrome/browser/ui/singleton_tabs.h +++ b/chrome/browser/ui/singleton_tabs.h
@@ -25,8 +25,7 @@ // As ShowSingletonTab, but if the current tab is the new tab page or // about:blank, then overwrite it with the passed contents. -void ShowSingletonTabOverwritingNTP(Browser* browser, - const NavigateParams& params); +void ShowSingletonTabOverwritingNTP(Browser* browser, NavigateParams params); // Creates a NavigateParams struct for a singleton tab navigation. NavigateParams GetSingletonTabNavigateParams(Browser* browser, const GURL& url);
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc index 47af9a3..6eea3a1 100644 --- a/chrome/browser/ui/startup/startup_browser_creator.cc +++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -755,16 +755,26 @@ chrome::startup::IS_NOT_PROCESS_STARTUP; chrome::startup::IsFirstRun is_first_run = first_run::IsChromeFirstRun() ? chrome::startup::IS_FIRST_RUN : chrome::startup::IS_NOT_FIRST_RUN; + + // On Windows, when chrome is launched by notification activation where the + // kNotificationLaunchId switch is used, always use |last_used_profile| which + // contains the profile id extracted from the notification launch id. + // TODO(chengx): Investigate adding a test for this startup behavior. + bool was_windows_notification_launch = false; +#if defined(OS_WIN) + was_windows_notification_launch = + command_line.HasSwitch(switches::kNotificationLaunchId); +#endif // defined(OS_WIN) + // |last_opened_profiles| will be empty in the following circumstances: // - This is the first launch. |last_used_profile| is the initial profile. - // - The user exited the browser by closing all windows for all - // profiles. |last_used_profile| is the profile which owned the last open - // window. + // - The user exited the browser by closing all windows for all profiles. + // |last_used_profile| is the profile which owned the last open window. // - Only incognito windows were open when the browser exited. - // |last_used_profile| is the last used incognito profile. Restoring it will - // create a browser window for the corresponding original profile. + // |last_used_profile| is the last used incognito profile. Restoring it will + // create a browser window for the corresponding original profile. // - All of the last opened profiles fail to initialize. - if (last_opened_profiles.empty()) { + if (last_opened_profiles.empty() || was_windows_notification_launch) { if (CanOpenProfileOnStartup(last_used_profile)) { Profile* profile_to_open = last_used_profile->IsGuestSession() @@ -854,12 +864,14 @@ SessionStartupPref startup_pref = GetSessionStartupPref(command_line, *it); if (*it != last_used_profile && startup_pref.type == SessionStartupPref::DEFAULT && - !HasPendingUncleanExit(*it)) + !HasPendingUncleanExit(*it)) { continue; + } if (!LaunchBrowser((*it == last_used_profile) ? command_line : command_line_without_urls, - *it, cur_dir, is_process_startup, is_first_run)) + *it, cur_dir, is_process_startup, is_first_run)) { return false; + } // We've launched at least one browser. is_process_startup = chrome::startup::IS_NOT_PROCESS_STARTUP; }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc index cb12a154..70e9b88 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc
@@ -81,7 +81,7 @@ NavigateParams params( GetSingletonTabNavigateParams(browser, GURL("chrome:version"))); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void BookmarkBubbleSignInDelegateTest::SignInBrowser(Browser* browser) {
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc index 44b31717..6fa05a0 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -25,7 +25,6 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/test/test_browser_dialog.h" -#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension_test_util.h" #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc index 8f1a4576..8cfcf7d 100644 --- a/chrome/browser/ui/views/frame/browser_frame.cc +++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -180,7 +180,7 @@ } const ui::NativeTheme* BrowserFrame::GetNativeTheme() const { -#if defined(OS_WIN) || defined(OS_CHROMEOS) +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) if (browser_view_->browser()->profile()->GetProfileType() == Profile::INCOGNITO_PROFILE && ThemeServiceFactory::GetForProfile(browser_view_->browser()->profile())
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc index 7008f40..33e941f 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -63,7 +63,7 @@ // static int BrowserNonClientFrameView::GetAvatarIconPadding() { - return MD::IsTouchOptimizedUiEnabled() ? 8 : 4; + return MD::IsNewerMaterialUi() ? 8 : 4; } void BrowserNonClientFrameView::OnBrowserViewInitViewsComplete() { @@ -103,9 +103,9 @@ void BrowserNonClientFrameView::UpdateMinimumSize() {} int BrowserNonClientFrameView::GetTabStripLeftInset() const { - return profile_indicator_icon() - ? 2 * GetAvatarIconPadding() + GetIncognitoAvatarIcon().width() - : 4; + if (profile_indicator_icon()) + return 2 * GetAvatarIconPadding() + GetIncognitoAvatarIcon().width(); + return (MD::GetMode() == MD::MATERIAL_REFRESH) ? 8 : 4; } void BrowserNonClientFrameView::ChildPreferredSizeChanged(views::View* child) {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc index 2667380f..33459ae 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -64,19 +64,6 @@ namespace { -// Space between right edge of tabstrip and maximize button. -constexpr int kTabstripRightSpacing = 10; -// Height of the shadow in the tab image, used to ensure clicks in the shadow -// area still drag restored windows. This keeps the clickable area large enough -// to hit easily. -constexpr int kTabShadowHeight = 4; - -constexpr SkColor kMdWebUIFrameColor = SkColorSetARGB(0xff, 0x25, 0x4f, 0xae); - -// How long to wait before starting the titlebar animation. -constexpr base::TimeDelta kTitlebarAnimationDelay = - base::TimeDelta::FromMilliseconds(750); - bool IsV1AppBackButtonEnabled() { return base::CommandLine::ForCurrentProcess()->HasSwitch( ash::switches::kAshEnableV1AppBackButton); @@ -308,6 +295,7 @@ gfx::Point client_point(point); View::ConvertPointToTarget(this, frame()->client_view(), &client_point); gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); + constexpr int kTabShadowHeight = 4; if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight) return HTCAPTION; } @@ -565,6 +553,8 @@ // BrowserNonClientFrameViewAsh, private: int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const { + // Space between right edge of tabstrip and maximize button. + constexpr int kTabstripRightSpacing = 10; return kTabstripRightSpacing + caption_button_container_->GetPreferredSize().width(); } @@ -657,6 +647,8 @@ AddChildView(frame_header_origin_text_); // Schedule the title bar animation. + constexpr base::TimeDelta kTitlebarAnimationDelay = + base::TimeDelta::FromMilliseconds(750); base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::BindOnce(&BrowserNonClientFrameViewAsh::StartHostedAppAnimation, @@ -664,6 +656,8 @@ kTitlebarAnimationDelay); } else if (!browser->is_app()) { // For non app (i.e. WebUI) windows (e.g. Settings) use MD frame color. + constexpr SkColor kMdWebUIFrameColor = + SkColorSetARGB(0xff, 0x25, 0x4f, 0xae); default_frame_header->SetFrameColors(kMdWebUIFrameColor, kMdWebUIFrameColor); }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm index 1ab86238..13f3430 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -16,12 +16,7 @@ namespace { -// How far to inset the tabstrip from the sides of the window. -const int kTabstripTopInset = 8; -const int kTabstripLeftInset = 70; // Make room for window control buttons. -constexpr int kTabstripRightInset = 4; // Margin for profile switcher. -constexpr const gfx::Size kMinTabbedWindowSize(400, 272); -constexpr const gfx::Size kMinPopupWindowSize(100, 122); +constexpr int kTabstripTopInset = 8; } // namespace @@ -57,6 +52,7 @@ } int BrowserNonClientFrameViewMac::GetTabStripRightInset() const { + constexpr int kTabstripRightInset = 4; // Margin for profile switcher. int inset = kTabstripRightInset; views::View* profile_switcher_view = GetProfileSwitcherButton(); if (profile_switcher_view) { @@ -76,6 +72,7 @@ } int BrowserNonClientFrameViewMac::GetTabStripLeftInset() const { + constexpr int kTabstripLeftInset = 70; // Make room for caption buttons. return kTabstripLeftInset; } @@ -133,6 +130,8 @@ gfx::Size BrowserNonClientFrameViewMac::GetMinimumSize() const { gfx::Size size = browser_view()->GetMinimumSize(); + constexpr gfx::Size kMinTabbedWindowSize(400, 272); + constexpr gfx::Size kMinPopupWindowSize(100, 122); size.SetToMax(browser_view()->browser()->is_type_tabbed() ? kMinTabbedWindowSize : kMinPopupWindowSize);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc index 07ea1c83..71c0dff 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
@@ -49,14 +49,8 @@ #if defined(FRAME_AVATAR_BUTTON) // Space between the new avatar button and the minimize button. -const int kAvatarButtonOffset = 5; +constexpr int kAvatarButtonOffset = 5; #endif -// Space between right edge of tabstrip and maximize button. -const int kTabstripRightSpacing = 10; -// Height of the shadow in the tab image, used to ensure clicks in the shadow -// area still drag restored windows. This keeps the clickable area large enough -// to hit easily. -const int kTabShadowHeight = 4; #if defined(FRAME_AVATAR_BUTTON) // Combines View::ConvertPointToTarget() and View::HitTest() for a given @@ -269,6 +263,7 @@ View::ConvertPointToTarget(this, frame()->client_view(), &client_point); // Report hits in shadow at top of tabstrip as caption. gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); + constexpr int kTabShadowHeight = 4; if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight) hit_test = HTCAPTION; } @@ -386,6 +381,8 @@ } int BrowserNonClientFrameViewMus::GetTabStripRightInset() const { + // Space between right edge of tabstrip and maximize button. + constexpr int kTabstripRightSpacing = 10; const int frame_right_insets = frame_values().normal_insets.right() + frame_values().max_title_bar_button_width; int right_inset = kTabstripRightSpacing + frame_right_insets;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index b775c5e..3c72a8f 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -682,7 +682,14 @@ } void BrowserView::UpdateTitleBar() { +#if !defined(OS_CHROMEOS) + if (ShouldShowWindowTitle()) + frame_->UpdateWindowTitle(); +#else + // ChromeOS needs this to be called even on a tabbed browser to + // set the accessible title. frame_->UpdateWindowTitle(); +#endif if (ShouldShowWindowIcon() && !loading_animation_timer_.IsRunning()) frame_->UpdateWindowIcon(); }
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc index 64e3d22f..f529cb0 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -42,29 +42,9 @@ GlassBrowserFrameView::kThrobberIconCount]; namespace { -// Thickness of the frame edge between the non-client area and the web content. -const int kClientBorderThickness = 3; -// Besides the frame border, there's empty space atop the window in restored -// mode, to use to drag the window around. -const int kNonClientRestoredExtraThickness = 11; -// At the window corners the resize area is not actually bigger, but the 16 -// pixels at the end of the top and bottom edges trigger diagonal resizing. -const int kResizeCornerWidth = 16; + // How far the profile switcher button is from the left of the minimize button. -const int kProfileSwitcherButtonOffset = 1; -// The content edge images have a shadow built into them. -const int kContentEdgeShadowThickness = 2; -// In restored mode, the New Tab button isn't at the same height as the caption -// buttons, but the space will look cluttered if it actually slides under them, -// so we stop it when the gap between the two is down to 5 px. -const int kNewTabCaptionRestoredSpacing = 5; -// In maximized mode, where the New Tab button and the caption buttons are at -// similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid -// looking too cluttered. -const int kNewTabCaptionMaximizedSpacing = 16; -// There is a small one-pixel strip right above the caption buttons in which the -// resize border "peeks" through. -const int kCaptionButtonTopInset = 1; +constexpr int kProfileSwitcherButtonOffset = 1; // Converts the |image| to a Windows icon and returns the corresponding HICON // handle. |image| is resized to desired |width| and |height| if needed. @@ -154,6 +134,14 @@ const int x = GetTabStripLeftInset(); int end_x = width() - ClientBorderThickness(false); if (!CaptionButtonsOnLeadingEdge()) { + // In restored mode, the New Tab button isn't at the same height as the + // caption buttons, but the space will look cluttered if it actually slides + // under them, so we stop it when the gap between the two is down to 5 px. + constexpr int kNewTabCaptionRestoredSpacing = 5; + // In maximized mode, where the New Tab button and the caption buttons are + // at similar vertical coordinates, we need to reserve a larger, 16 px gap + // to avoid looking too cluttered. + constexpr int kNewTabCaptionMaximizedSpacing = 16; end_x = std::min(MinimizeButtonX(), end_x) - (IsMaximized() ? kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing); @@ -214,7 +202,9 @@ // Account for the client area insets. gfx::Insets insets = GetClientAreaInsets(false); min_size.Enlarge(insets.width(), insets.height()); - // Client area insets do not include the shadow thickness. + // The content edge images have a shadow built into them. Client area insets + // do not include this shadow thickness. + constexpr int kContentEdgeShadowThickness = 2; min_size.Enlarge(2 * kContentEdgeShadowThickness, 0); // Ensure that the minimum width is enough to hold a tab strip with minimum @@ -338,6 +328,10 @@ sizeof(button_bounds)))) { gfx::Rect buttons = gfx::ConvertRectToDIP(display::win::GetDPIScale(), gfx::Rect(button_bounds)); + + // There is a small one-pixel strip right above the caption buttons in + // which the resize border "peeks" through. + constexpr int kCaptionButtonTopInset = 1; // The sizing region at the window edge above the caption buttons is // 1 px regardless of scale factor. If we inset by 1 before converting // to DIPs, the precision loss might eliminate this region entirely. The @@ -352,6 +346,9 @@ } int top_border_thickness = FrameTopBorderThickness(false); + // At the window corners the resize area is not actually bigger, but the 16 + // pixels at the end of the top and bottom edges trigger diagonal resizing. + constexpr int kResizeCornerWidth = 16; // We want the resize corner behavior to apply to the kResizeCornerWidth // pixels at each end of the top and bottom edges. Because |point|'s x // coordinate is based on the DWM-inset portion of the window (so, it's 0 at @@ -510,6 +507,8 @@ if ((IsMaximized() || frame()->IsFullscreen()) && !restored) return 0; + // Thickness of the frame edge between the non-client area and web content. + constexpr int kClientBorderThickness = 3; return kClientBorderThickness; } @@ -550,9 +549,13 @@ // The tab top inset is equal to the height of any shadow region above the // tabs, plus a 1 px top stroke. In maximized mode, we want to push the // shadow region off the top of the screen but leave the top stroke. - return (IsMaximized() && !restored) - ? (top - GetLayoutInsets(TAB).top() + 1) - : (top + kNonClientRestoredExtraThickness); + if (IsMaximized() && !restored) + return top - GetLayoutInsets(TAB).top() + 1; + + // Besides the frame border, there's empty space atop the window in restored + // mode, to use to drag the window around. + constexpr int kNonClientRestoredExtraThickness = 11; + return top + kNonClientRestoredExtraThickness; } int GlassBrowserFrameView::TitlebarMaximizedVisualHeight() const {
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc index bffbbb9c..a4d9e6b 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
@@ -18,18 +18,13 @@ namespace { -const int kCaptionButtonHeight = 18; +constexpr int kCaptionButtonHeight = 18; #if defined(OS_LINUX) && !defined(OS_CHROMEOS) -// Default extra space between the top of the frame and the top of the window -// caption buttons. -const int kExtraCaption = 2; - -// Default extra spacing between individual window caption buttons. -const int kCaptionButtonSpacing = 2; +// Default spacing around window caption buttons. +constexpr int kCaptionButtonSpacing = 2; #else -const int kExtraCaption = 0; -const int kCaptionButtonSpacing = 0; +constexpr int kCaptionButtonSpacing = 0; #endif } // namespace @@ -79,7 +74,7 @@ minimum_size_for_buttons_(0), has_leading_buttons_(false), has_trailing_buttons_(false), - extra_caption_y_(kExtraCaption), + extra_caption_y_(kCaptionButtonSpacing), forced_window_caption_spacing_(-1), minimize_button_(nullptr), maximize_button_(nullptr),
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc index f9cb3655..757dfc24 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -333,9 +333,9 @@ const AutocompleteMatch& match = GetMatchAtIndex(i); view->SetMatch(match); view->SetVisible(true); - if (match.answer && !model_->rich_suggestion_bitmap().isNull()) { - view->SetRichSuggestionImage( - gfx::ImageSkia::CreateFrom1xBitmap(model_->rich_suggestion_bitmap())); + const SkBitmap* bitmap = model_->RichSuggestionBitmapAt(i); + if (bitmap != nullptr) { + view->SetRichSuggestionImage(gfx::ImageSkia::CreateFrom1xBitmap(*bitmap)); } }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc index e84fcc0..fd7d620 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -33,6 +33,7 @@ #include "components/omnibox/browser/omnibox_field_trial.h" #include "components/omnibox/browser/omnibox_popup_model.h" #include "components/omnibox/browser/vector_icons.h" +#include "extensions/common/image_util.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" @@ -43,6 +44,7 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" +#include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/image.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/range/range.h" @@ -132,6 +134,37 @@ GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING); } +class PlaceholderImageSource : public gfx::CanvasImageSource { + public: + PlaceholderImageSource(const gfx::Size& canvas_size, SkColor color); + ~PlaceholderImageSource() override; + + // CanvasImageSource override: + void Draw(gfx::Canvas* canvas) override; + + private: + SkColor color_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(PlaceholderImageSource); +}; + +PlaceholderImageSource::PlaceholderImageSource(const gfx::Size& canvas_size, + SkColor color) + : gfx::CanvasImageSource(canvas_size, false), + color_(color), + size_(canvas_size) {} + +PlaceholderImageSource::~PlaceholderImageSource() = default; + +void PlaceholderImageSource::Draw(gfx::Canvas* canvas) { + cc::PaintFlags flags; + flags.setAntiAlias(true); + flags.setStyle(cc::PaintFlags::kStrokeAndFill_Style); + flags.setColor(color_); + canvas->sk_canvas()->drawOval(gfx::RectToSkRect(gfx::Rect(size_)), flags); +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -180,6 +213,7 @@ void LayoutSplit(); bool is_answer_; + bool is_entity_; bool is_search_type_; int text_height_; @@ -197,7 +231,10 @@ OmniboxSuggestionView::OmniboxSuggestionView(OmniboxResultView* result_view, const gfx::FontList& font_list, int text_height) - : is_answer_(false), is_search_type_(false), text_height_(text_height) { + : is_answer_(false), + is_entity_(false), + is_search_type_(false), + text_height_(text_height) { AddChildView(icon_view_ = new OmniboxImageView()); AddChildView(image_view_ = new OmniboxImageView()); AddChildView(content_view_ = new OmniboxTextView(result_view, font_list)); @@ -214,7 +251,7 @@ gfx::Size OmniboxSuggestionView::CalculatePreferredSize() const { int height = text_height_ + GetVerticalInsets(text_height_, is_answer_).height(); - if (is_answer_) + if (is_answer_ || is_entity_) height += GetAnswerHeight(); return gfx::Size(0, height); } @@ -235,12 +272,14 @@ void OmniboxSuggestionView::OnMatchUpdate(const AutocompleteMatch& match) { is_answer_ = !!match.answer; + if (base::FeatureList::IsEnabled(omnibox::kOmniboxRichEntitySuggestions)) { + is_entity_ = !match.image_url.empty(); + } is_search_type_ = AutocompleteMatch::IsSearchType(match.type); - if (is_answer_) { + if (is_answer_ || is_entity_) { separator_view_->SetSize(gfx::Size()); } - if (is_answer_ && - base::FeatureList::IsEnabled(omnibox::kOmniboxRichEntitySuggestions)) { + if (is_entity_) { icon_view_->SetSize(gfx::Size()); } } @@ -252,11 +291,9 @@ void OmniboxSuggestionView::Layout() { views::View::Layout(); if (is_answer_) { - if (base::FeatureList::IsEnabled(omnibox::kOmniboxRichEntitySuggestions)) { - LayoutEntity(); - } else { - LayoutAnswer(); - } + LayoutAnswer(); + } else if (is_entity_) { + LayoutEntity(); } else { LayoutSplit(); } @@ -368,10 +405,19 @@ suggestion_view_->OnMatchUpdate(match_); keyword_view_->OnMatchUpdate(match_); - suggestion_view_->image()->SetVisible( - false); // Until SetRichSuggestionImage is called. + // Set up default (placeholder) image. + SkColor color = GetColor(OmniboxPart::RESULTS_BACKGROUND); + extensions::image_util::ParseHexColorString(match_.image_dominant_color, + &color); + color = SkColorSetA(color, 0x40); // 25% transparency (arbitrary). + int image_edge_length = suggestion_view_->description()->GetLineHeight(); + suggestion_view_->image()->SetImage( + gfx::CanvasImageSource::MakeImageSkia<PlaceholderImageSource>( + gfx::Size(image_edge_length, image_edge_length), color)); + keyword_view_->icon()->SetVisible(match_.associated_keyword.get()); + // Set up 'switch to tab' button. if (match.has_tab_match && !keyword_view_->icon()->visible()) { suggestion_tab_switch_button_ = std::make_unique<OmniboxTabSwitchButton>(this, GetTextHeight()); @@ -380,6 +426,7 @@ } else { suggestion_tab_switch_button_.reset(); } + Invalidate(); if (GetWidget()) Layout();
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc index 5aa8f64e..69a5286 100644 --- a/chrome/browser/ui/views/overlay/overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -208,12 +208,16 @@ return views::Widget::IsActive(); } +void OverlayWindowViews::Close() { + views::Widget::Close(); +} + void OverlayWindowViews::Show() { views::Widget::Show(); } -void OverlayWindowViews::Close() { - views::Widget::Close(); +void OverlayWindowViews::Hide() { + views::Widget::Hide(); } bool OverlayWindowViews::IsVisible() const {
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h index e418ce4..d753df0 100644 --- a/chrome/browser/ui/views/overlay/overlay_window_views.h +++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -25,8 +25,9 @@ // OverlayWindow: bool IsActive() const override; - void Show() override; void Close() override; + void Show() override; + void Hide() override; bool IsVisible() const override; bool IsAlwaysOnTop() const override; ui::Layer* GetLayer() override;
diff --git a/chrome/browser/ui/views/simple_message_box_views.cc b/chrome/browser/ui/views/simple_message_box_views.cc index 3b71a78..69d6bf72 100644 --- a/chrome/browser/ui/views/simple_message_box_views.cc +++ b/chrome/browser/ui/views/simple_message_box_views.cc
@@ -32,6 +32,10 @@ #include "ui/views/win/hwnd_util.h" #endif +#if defined(OS_MACOSX) +#include "chrome/browser/ui/cocoa/simple_message_box_cocoa.h" +#endif + namespace { #if defined(OS_WIN) UINT GetMessageBoxFlagsFromType(chrome::MessageBoxType type) { @@ -114,6 +118,17 @@ : chrome::MESSAGE_BOX_RESULT_NO); return chrome::MESSAGE_BOX_RESULT_DEFERRED; } +#elif defined(OS_MACOSX) + if (!base::MessageLoopForUI::IsCurrent() || + !base::RunLoop::IsRunningOnCurrentThread() || + !ui::ResourceBundle::HasSharedInstance()) { + // Even though this function could return a value synchronously here in + // principle, in practice call sites do not expect any behavior other than a + // return of DEFERRED and an invocation of the callback. + std::move(callback).Run( + chrome::ShowMessageBoxCocoa(message, type, checkbox_text)); + return chrome::MESSAGE_BOX_RESULT_DEFERRED; + } #else if (!base::MessageLoopForUI::IsCurrent() || !ui::ResourceBundle::HasSharedInstance()) {
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc index a390929..0ed7ee7 100644 --- a/chrome/browser/ui/views/tabs/new_tab_button.cc +++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -40,6 +40,8 @@ #include "chrome/browser/ui/views/feature_promos/new_tab_promo_bubble_view.h" #endif +using MD = ui::MaterialDesignController; + namespace { constexpr int kDistanceBetweenIcons = 6; @@ -72,7 +74,7 @@ ui::EF_MIDDLE_MOUSE_BUTTON); #endif - if (ui::MaterialDesignController::IsNewerMaterialUi()) { + if (MD::IsNewerMaterialUi()) { // Initialize the ink drop mode for a ripple highlight on button press. ink_drop_container_ = new views::InkDropContainerView(); AddChildView(ink_drop_container_); @@ -98,7 +100,7 @@ // In newer material UI, the button is placed vertically exactly in the // center of the tabstrip. - if (ui::MaterialDesignController::IsNewerMaterialUi()) + if (MD::IsNewerMaterialUi()) return extra_vertical_space / 2; // In the non-touch-optimized UI, the new tab button is placed at a fixed @@ -223,7 +225,6 @@ // Fill. SkPath fill, stroke; - using MD = ui::MaterialDesignController; const bool is_touch_ui = MD::IsTouchOptimizedUiEnabled(); if (MD::GetMode() != MD::MATERIAL_REFRESH) { fill = is_touch_ui ? GetTouchOptimizedButtonPath(0, scale, false, true) @@ -242,7 +243,7 @@ const int plus_icon_offset = GetCornerRadius() - (plus_icon_.width() / 2); canvas->DrawImageInt(plus_icon_, plus_icon_offset, plus_icon_offset, paint_flags); - if (is_incognito_) { + if (ShouldDrawIncognitoIcon()) { DCHECK(!incognito_icon_.isNull()); canvas->DrawImageInt( incognito_icon_, @@ -292,7 +293,7 @@ void NewTabButton::Layout() { ImageButton::Layout(); - if (ui::MaterialDesignController::IsNewerMaterialUi()) { + if (MD::IsNewerMaterialUi()) { // If icons are not initialized, initialize them now. Icons are always // initialized together so it's enough to check the |plus_icon_|. if (plus_icon_.isNull()) @@ -307,7 +308,7 @@ void NewTabButton::OnThemeChanged() { ImageButton::OnThemeChanged(); - if (!ui::MaterialDesignController::IsNewerMaterialUi()) + if (!MD::IsNewerMaterialUi()) return; InitButtonIcons(); @@ -344,6 +345,10 @@ SchedulePaint(); } +bool NewTabButton::ShouldDrawIncognitoIcon() const { + return is_incognito_ && MD::IsTouchOptimizedUiEnabled(); +} + gfx::Rect NewTabButton::GetVisibleBounds() const { // TODO(pkasting): This is correct but inefficient for newer material UI. SkPath border; @@ -365,10 +370,9 @@ const int button_height = GetLayoutSize(NEW_TAB_BUTTON, is_incognito_).height(); - using MD = ui::MaterialDesignController; if (MD::GetMode() == MD::MATERIAL_REFRESH) { path->addRect(0, extend_to_top ? 0 : button_y, width() * scale, - button_height * scale); + button_y + button_height * scale); return; } @@ -391,8 +395,7 @@ // For unpressed buttons, draw the fill and its shadow. // Note that for touch-optimized UI, we always draw the fill since the button // has a flat design with no hover highlight. - const bool is_touch_ui = - ui::MaterialDesignController::IsTouchOptimizedUiEnabled(); + const bool is_touch_ui = MD::IsTouchOptimizedUiEnabled(); if (is_touch_ui || !pressed) { // First we compute the background image coordinates and scale, in case we // need to draw a custom background image. @@ -431,12 +434,12 @@ shadow_flags.setLooper( CreateShadowDrawLooper(SkColorSetA(stroke_color, alpha))); canvas->DrawPath(fill, shadow_flags); - } - if (is_touch_ui) { - // We don't have hover/pressed states in the touch-optimized UI design. - // Instead we are using an ink drop effect. - return; + if (is_touch_ui) { + // We don't have hover/pressed states in the touch-optimized UI design. + // Instead we are using an ink drop effect. + return; + } } // Draw a white highlight on hover. @@ -469,14 +472,14 @@ } void NewTabButton::InitButtonIcons() { - DCHECK(ui::MaterialDesignController::IsNewerMaterialUi()); + DCHECK(MD::IsNewerMaterialUi()); const ui::ThemeProvider* theme_provider = GetThemeProvider(); DCHECK(theme_provider); const SkColor icon_color = theme_provider->GetColor(ThemeProperties::COLOR_TAB_TEXT); plus_icon_ = gfx::CreateVectorIcon(kNewTabButtonPlusIcon, icon_color); - if (is_incognito_) { + if (ShouldDrawIncognitoIcon()) { incognito_icon_ = gfx::CreateVectorIcon(kNewTabButtonIncognitoIcon, icon_color); } @@ -486,7 +489,7 @@ float scale, bool extend_to_top, bool for_fill) const { - DCHECK(ui::MaterialDesignController::IsTouchOptimizedUiEnabled()); + DCHECK(MD::IsTouchOptimizedUiEnabled()); const float radius = GetCornerRadius() * scale; const float rect_width = @@ -574,7 +577,7 @@ } void NewTabButton::UpdateInkDropBaseColor() { - DCHECK(ui::MaterialDesignController::IsNewerMaterialUi()); + DCHECK(MD::IsNewerMaterialUi()); set_ink_drop_base_color(color_utils::BlendTowardOppositeLuma( GetButtonFillColor(), SK_AlphaOPAQUE));
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h index 924f97d..e318f2b 100644 --- a/chrome/browser/ui/views/tabs/new_tab_button.h +++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -79,6 +79,9 @@ // views::WidgetObserver: void OnWidgetDestroying(views::Widget* widget) override; + // Returns whether this button should draw an incognito icon. + bool ShouldDrawIncognitoIcon() const; + // Returns the gfx::Rect around the visible portion of the New Tab Button. // Note: This is different than the rect around the entire New Tab Button as // it extends to the top of the tabstrip for Fitts' Law interaction in a
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index aadb3a47..6da53207 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -326,15 +326,6 @@ observers_.RemoveObserver(observer); } -TabStrip::NewTabButtonPosition TabStrip::GetNewTabButtonPosition() const { - if (MD::GetMode() != MD::MATERIAL_REFRESH) - return AFTER_TABS; - - const auto* frame_view = static_cast<const BrowserNonClientFrameView*>( - GetWidget()->non_client_view()->frame_view()); - return frame_view->CaptionButtonsOnLeadingEdge() ? TRAILING : LEADING; -} - int TabStrip::GetMaxX() const { // This function should not currently be called for TRAILING mode; if it is, // the API will need changing, since callers assume the tabstrip uses @@ -1465,6 +1456,15 @@ return in_tab_close_; } +TabStrip::NewTabButtonPosition TabStrip::GetNewTabButtonPosition() const { + if (MD::GetMode() != MD::MATERIAL_REFRESH) + return AFTER_TABS; + + const auto* frame_view = static_cast<const BrowserNonClientFrameView*>( + GetWidget()->non_client_view()->frame_view()); + return frame_view->CaptionButtonsOnLeadingEdge() ? TRAILING : LEADING; +} + bool TabStrip::MayHideNewTabButtonWhileDragging() const { return GetNewTabButtonPosition() == AFTER_TABS; }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h index d25126a69..0f782e93 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.h +++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -62,12 +62,6 @@ public views::ViewTargeterDelegate, public TabController { public: - enum NewTabButtonPosition { - LEADING, // Pinned to the leading edge of the tabstrip region. - AFTER_TABS, // After the last tab. - TRAILING, // Pinned to the trailing edge of the tabstrip region. - }; - explicit TabStrip(std::unique_ptr<TabStripController> controller); ~TabStrip() override; @@ -78,9 +72,6 @@ void AddObserver(TabStripObserver* observer); void RemoveObserver(TabStripObserver* observer); - // Returns the position of the new tab button within the strip. - NewTabButtonPosition GetNewTabButtonPosition() const; - // Max x-coordinate the tabstrip draws at, which is the right edge of the new // tab button. int GetMaxX() const; @@ -284,6 +275,12 @@ views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override; private: + enum NewTabButtonPosition { + LEADING, // Pinned to the leading edge of the tabstrip region. + AFTER_TABS, // After the last tab. + TRAILING, // Pinned to the trailing edge of the tabstrip region. + }; + using Tabs = std::vector<Tab*>; using TabsClosingMap = std::map<int, Tabs>; using FindClosingTabResult = @@ -360,9 +357,12 @@ // Returns whether the close button should be highlighted after a remove. bool ShouldHighlightCloseButtonAfterRemove(); + // Returns the position of the new tab button within the strip. + NewTabButtonPosition GetNewTabButtonPosition() const; + // Returns whether dragging tabs should ever result in the new tab button // being hidden. - bool MayHideNewTabButtonWhileDragging() const; + bool MayHideNewTabButtonWhileDragging() const; // Returns whether the window background behind the tabstrip is transparent. bool TitlebarBackgroundIsTransparent() const;
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc index 687c940..6de4d2c 100644 --- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc +++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -239,6 +239,8 @@ animation_target_size_ = target_width; resize_animation_->Show(); } else { + if (resize_animation_) + resize_animation_->Reset(); animation_target_size_ = target_width; AnimationEnded(resize_animation_.get()); } @@ -252,7 +254,13 @@ ? animation_target_size_ : width(); const int width_without_separator = target_width - GetSeparatorAreaWidth(); - return std::max(0, width_without_separator); + // This needs to be clamped to non-zero as ToolbarActionsBar::ResizeDelegate + // uses this value to distinguish between an empty bar without items and a bar + // that is showing no items. + // TODO(pbos): This is landed to fix to https://crbug.com/836182. Remove the + // need for this when ToolbarActionsBar and BrowserActionsContainer merges. + return std::max(toolbar_actions_bar_->GetMinimumWidth(), + width_without_separator); } bool BrowserActionsContainer::IsAnimating() const {
diff --git a/chrome/browser/ui/webui/chrome_web_contents_handler.cc b/chrome/browser/ui/webui/chrome_web_contents_handler.cc index 3b935948..8d2a5e6 100644 --- a/chrome/browser/ui/webui/chrome_web_contents_handler.cc +++ b/chrome/browser/ui/webui/chrome_web_contents_handler.cc
@@ -63,7 +63,7 @@ if (browser_created && (browser != nav_params.browser)) browser->window()->Close(); - return nav_params.target_contents; + return nav_params.navigated_or_inserted_contents; } // Creates a new tab with |new_contents|. |context| is the browser context that @@ -75,7 +75,7 @@ void ChromeWebContentsHandler::AddNewContents( content::BrowserContext* context, WebContents* source, - WebContents* new_contents, + std::unique_ptr<WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture) { @@ -90,7 +90,7 @@ browser = new Browser( Browser::CreateParams(Browser::TYPE_TABBED, profile, user_gesture)); } - NavigateParams params(browser, new_contents); + NavigateParams params(browser, std::move(new_contents)); params.source_contents = source; params.disposition = disposition; params.window_bounds = initial_rect;
diff --git a/chrome/browser/ui/webui/chrome_web_contents_handler.h b/chrome/browser/ui/webui/chrome_web_contents_handler.h index b88ea3e..7833ce6 100644 --- a/chrome/browser/ui/webui/chrome_web_contents_handler.h +++ b/chrome/browser/ui/webui/chrome_web_contents_handler.h
@@ -22,7 +22,7 @@ const content::OpenURLParams& params) override; void AddNewContents(content::BrowserContext* context, content::WebContents* source, - content::WebContents* new_contents, + std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc index 323811e..ba1daff 100644 --- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -194,6 +194,7 @@ &CoreOobeHandler::HandleSetOobeBootstrappingSlave); AddRawCallback("getPrimaryDisplayNameForTesting", &CoreOobeHandler::HandleGetPrimaryDisplayNameForTesting); + AddCallback("setupDemoMode", &CoreOobeHandler::HandleSetupDemoMode); } void CoreOobeHandler::ShowSignInError( @@ -575,6 +576,19 @@ ResolveJavascriptCallback(*callback_id, base::Value(display_name)); } +void CoreOobeHandler::HandleSetupDemoMode() { + const bool is_demo_mode_enabled = + base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kEnableDemoMode); + if (!is_demo_mode_enabled) + return; + + WizardController* wizard_controller = WizardController::default_controller(); + if (wizard_controller && !wizard_controller->login_screen_started()) { + wizard_controller->AdvanceToScreen(OobeScreen::SCREEN_OOBE_DEMO_SETUP); + } +} + void CoreOobeHandler::InitDemoModeDetection() { demo_mode_detector_.InitDetection(); }
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h index 2579735..795c016d 100644 --- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -130,6 +130,7 @@ void HandleHeaderBarVisible(); void HandleSetOobeBootstrappingSlave(); void HandleGetPrimaryDisplayNameForTesting(const base::ListValue* args); + void HandleSetupDemoMode(); // When keyboard_utils.js arrow key down event is reached, raise it // to tab/shift-tab event.
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc index 0689319..54fe4ad 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -47,6 +47,7 @@ #include "chrome/browser/chromeos/login/lock/screen_locker.h" #include "chrome/browser/chromeos/login/lock/webui_screen_locker.h" #include "chrome/browser/chromeos/login/lock_screen_utils.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/chromeos/login/reauth_stats.h" @@ -561,7 +562,6 @@ AddCallback("sendFeedback", &SigninScreenHandler::HandleSendFeedback); AddCallback("sendFeedbackAndResyncUserData", &SigninScreenHandler::HandleSendFeedbackAndResyncUserData); - AddCallback("setupDemoMode", &SigninScreenHandler::HandleSetupDemoMode); // This message is sent by the kiosk app menu, but is handled here // so we can tell the delegate to launch the app. @@ -664,14 +664,6 @@ params.SetBoolean("disableAddUser", AllWhitelistedUsersPresent()); UpdateUIState(UI_STATE_ACCOUNT_PICKER, ¶ms); } - - // Enable pin for any users who can use it. - if (user_manager::UserManager::IsInitialized()) { - for (user_manager::User* user : - user_manager::UserManager::Get()->GetLoggedInUsers()) { - UpdatePinKeyboardState(user->GetAccountId()); - } - } } void SigninScreenHandler::UpdateUIState(UIState ui_state, @@ -912,13 +904,10 @@ if (user_manager::UserManager::IsInitialized()) { for (user_manager::User* user : user_manager::UserManager::Get()->GetUnlockUsers()) { - chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage = - chromeos::quick_unlock::QuickUnlockFactory::GetForUser(user); - if (quick_unlock_storage && - quick_unlock_storage->IsPinAuthenticationAvailable()) { - CallJS("cr.ui.Oobe.preloadPinKeyboard"); - break; - } + quick_unlock::PinBackend::CanAuthenticate( + user->GetAccountId(), + base::BindOnce(&SigninScreenHandler::PreloadPinKeyboard, + weak_factory_.GetWeakPtr())); } } @@ -988,16 +977,22 @@ } void SigninScreenHandler::UpdatePinKeyboardState(const AccountId& account_id) { - chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage = - chromeos::quick_unlock::QuickUnlockFactory::GetForAccountId(account_id); - if (!quick_unlock_storage) - return; + quick_unlock::PinBackend::CanAuthenticate( + account_id, base::BindOnce(&SigninScreenHandler::SetPinEnabledForUser, + weak_factory_.GetWeakPtr(), account_id)); +} - bool is_enabled = quick_unlock_storage->IsPinAuthenticationAvailable(); +void SigninScreenHandler::SetPinEnabledForUser(const AccountId& account_id, + bool is_enabled) { CallJS("login.AccountPickerScreen.setPinEnabledForUser", account_id, is_enabled); } +void SigninScreenHandler::PreloadPinKeyboard(bool should_preload) { + if (should_preload) + CallJS("cr.ui.Oobe.preloadPinKeyboard"); +} + void SigninScreenHandler::OnUserRemoved(const AccountId& account_id, bool last_user_removed) { CallJS("login.AccountPickerScreen.removeUser", account_id); @@ -1216,12 +1211,6 @@ return; DCHECK_EQ(account_id.GetUserEmail(), gaia::SanitizeEmail(account_id.GetUserEmail())); - chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage = - chromeos::quick_unlock::QuickUnlockFactory::GetForAccountId(account_id); - // If pin storage is unavailable, authenticated by PIN must be false. - DCHECK(!quick_unlock_storage || - quick_unlock_storage->IsPinAuthenticationAvailable() || - !authenticated_by_pin); UserContext user_context(account_id); user_context.SetKey(Key(password)); @@ -1329,11 +1318,6 @@ delegate_->ShowEnableDebuggingScreen(); } -void SigninScreenHandler::HandleSetupDemoMode() { - if (delegate_) - delegate_->ShowDemoModeSetupScreen(); -} - void SigninScreenHandler::HandleToggleKioskEnableScreen() { policy::BrowserPolicyConnectorChromeOS* connector = g_browser_process->platform_part()->browser_policy_connector_chromeos(); @@ -1355,6 +1339,13 @@ const base::ListValue& users_list) { CallJSOrDefer("login.AccountPickerScreen.loadUsers", users_list, delegate_->IsShowGuest()); + + // Enable pin for any users who can use it. + // TODO(jdufault): Cache pin state in BrowserProcess::local_state() so we + // don't need to query cryptohome every time we show login. See + // https://crbug.com/721938. + for (user_manager::User* user : users) + UpdatePinKeyboardState(user->GetAccountId()); } void SigninScreenHandler::HandleAccountPickerReady() {
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h index d4938b73..c801908 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -163,9 +163,6 @@ // Shows Enable Developer Features screen. virtual void ShowEnableDebuggingScreen() = 0; - // Shows Demo Mode Setup screen. - virtual void ShowDemoModeSetupScreen() = 0; - // Shows Kiosk Enable screen. virtual void ShowKioskEnableScreen() = 0; @@ -376,6 +373,10 @@ // Enable or disable the pin keyboard for the given account. void UpdatePinKeyboardState(const AccountId& account_id); + void SetPinEnabledForUser(const AccountId& account_id, bool is_enabled); + // Callback run by PinBackend. If |should_preload| is true the PIN keyboard is + // preloaded. + void PreloadPinKeyboard(bool should_preload); // WebUI message handlers. void HandleGetUsers(); @@ -394,7 +395,6 @@ void HandleToggleEnrollmentScreen(); void HandleToggleEnrollmentAd(); void HandleToggleEnableDebuggingScreen(); - void HandleSetupDemoMode(); void HandleToggleKioskEnableScreen(); void HandleToggleResetScreen(); void HandleToggleKioskAutolaunchScreen();
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc index 2b653ec..e8e76b9 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc +++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
@@ -34,6 +34,15 @@ {"accept", IDS_MULTIDEVICE_SETUP_ACCEPT_LABEL}, {"cancel", IDS_CANCEL}, {"done", IDS_DONE}, + {"setupFailedPageHeader", IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_HEADER}, + {"setupFailedPageMessage", + IDS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_MESSAGE}, + {"setupSucceededPageHeader", + IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_HEADER}, + {"setupSucceededPageMessage", + IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_MESSAGE}, + {"startSetupPageHeader", IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_HEADER}, + {"startSetupPageMessage", IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE}, {"title", IDS_MULTIDEVICE_SETUP_DIALOG_TITLE}, {"tryAgain", IDS_MULTIDEVICE_SETUP_TRY_AGAIN_LABEL}, };
diff --git a/chrome/browser/ui/webui/extensions/extension_loader_handler.cc b/chrome/browser/ui/webui/extensions/extension_loader_handler.cc deleted file mode 100644 index 12c132d3..0000000 --- a/chrome/browser/ui/webui/extensions/extension_loader_handler.cc +++ /dev/null
@@ -1,225 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/webui/extensions/extension_loader_handler.h" - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/task_scheduler/post_task.h" -#include "base/values.h" -#include "chrome/browser/extensions/unpacked_installer.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/grit/generated_resources.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/web_contents.h" -#include "content/public/browser/web_ui.h" -#include "content/public/browser/web_ui_data_source.h" -#include "extensions/browser/extension_system.h" -#include "extensions/browser/file_highlighter.h" -#include "extensions/browser/path_util.h" -#include "extensions/common/constants.h" -#include "extensions/common/extension.h" -#include "extensions/common/manifest_constants.h" -#include "third_party/re2/src/re2/re2.h" -#include "ui/base/l10n/l10n_util.h" - -namespace extensions { - -namespace { - -// Read a file to a string and return. -std::string ReadFileToString(const base::FilePath& path) { - std::string data; - // This call can fail, but it doesn't matter for our purposes. If it fails, - // we simply return an empty string for the manifest, and ignore it. - base::ReadFileToString(path, &data); - return data; -} - -} // namespace - -ExtensionLoaderHandler::ExtensionLoaderHandler(Profile* profile) - : profile_(profile), - extension_error_reporter_observer_(this), - ui_ready_(false), - weak_ptr_factory_(this) { - DCHECK(profile_); - extension_error_reporter_observer_.Add( - extensions::LoadErrorReporter::GetInstance()); -} - -ExtensionLoaderHandler::~ExtensionLoaderHandler() { -} - -void ExtensionLoaderHandler::GetLocalizedValues( - content::WebUIDataSource* source) { - source->AddString( - "extensionLoadErrorHeading", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_HEADING)); - source->AddString( - "extensionLoadErrorMessage", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE)); - source->AddString( - "extensionLoadErrorRetry", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_RETRY)); - source->AddString( - "extensionLoadErrorGiveUp", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_GIVE_UP)); - source->AddString( - "extensionLoadCouldNotLoadManifest", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_COULD_NOT_LOAD_MANIFEST)); - source->AddString( - "extensionLoadAdditionalFailures", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ADDITIONAL_FAILURES)); -} - -void ExtensionLoaderHandler::RegisterMessages() { - // We observe WebContents in order to detect page refreshes, since notifying - // the frontend of load failures must be delayed until the page finishes - // loading. We never call Observe(NULL) because this object is constructed - // on page load and persists between refreshes. - content::WebContentsObserver::Observe(web_ui()->GetWebContents()); - - web_ui()->RegisterMessageCallback( - "extensionLoaderRetry", - base::BindRepeating(&ExtensionLoaderHandler::HandleRetry, - weak_ptr_factory_.GetWeakPtr())); - web_ui()->RegisterMessageCallback( - "extensionLoaderIgnoreFailure", - base::BindRepeating(&ExtensionLoaderHandler::HandleIgnoreFailure, - weak_ptr_factory_.GetWeakPtr())); - web_ui()->RegisterMessageCallback( - "extensionLoaderDisplayFailures", - base::BindRepeating(&ExtensionLoaderHandler::HandleDisplayFailures, - weak_ptr_factory_.GetWeakPtr())); -} - -// static -void ExtensionLoaderHandler::GetManifestError( - const std::string& error, - const base::FilePath& extension_path, - const GetManifestErrorCallback& callback) { - size_t line = 0u; - size_t column = 0u; - std::string regex = base::StringPrintf("%s Line: (\\d+), column: (\\d+), .*", - manifest_errors::kManifestParseError); - // If this was a JSON parse error, we can highlight the exact line with the - // error. Otherwise, we should still display the manifest (for consistency, - // reference, and so that if we ever make this really fancy and add an editor, - // it's ready). - // - // This regex call can fail, but if it does, we just don't highlight anything. - re2::RE2::FullMatch(error, regex, &line, &column); - - // This will read the manifest and call AddFailure with the read manifest - // contents. - base::PostTaskWithTraitsAndReplyWithResult( - FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, - base::Bind(&ReadFileToString, extension_path.Append(kManifestFilename)), - base::Bind(callback, extension_path, error, line)); -} - -void ExtensionLoaderHandler::HandleRetry(const base::ListValue* args) { - DCHECK(args->empty()); - const base::FilePath file_path = failed_paths_.back(); - failed_paths_.pop_back(); - LoadUnpackedExtension(file_path); -} - -void ExtensionLoaderHandler::HandleIgnoreFailure(const base::ListValue* args) { - DCHECK(args->empty()); - failed_paths_.pop_back(); -} - -void ExtensionLoaderHandler::HandleDisplayFailures( - const base::ListValue* args) { - DCHECK(args->empty()); - ui_ready_ = true; - - // Notify the frontend of any load failures that were triggered while the - // chrome://extensions page was loading. - if (!failures_.empty()) - NotifyFrontendOfFailure(); -} - -void ExtensionLoaderHandler::LoadUnpackedExtension( - const base::FilePath& file_path) { - scoped_refptr<UnpackedInstaller> installer = UnpackedInstaller::Create( - ExtensionSystem::Get(profile_)->extension_service()); - - // We do our own error handling, so we don't want a load failure to trigger - // a dialog. - installer->set_be_noisy_on_failure(false); - - installer->Load(file_path); -} - -void ExtensionLoaderHandler::OnLoadFailure( - content::BrowserContext* browser_context, - const base::FilePath& file_path, - const std::string& error) { - // Only show errors from our browser context. - if (web_ui()->GetWebContents()->GetBrowserContext() != browser_context) - return; - - GetManifestError(error, file_path, - base::Bind(&ExtensionLoaderHandler::AddFailure, - weak_ptr_factory_.GetWeakPtr())); -} - -void ExtensionLoaderHandler::DidStartNavigation( - content::NavigationHandle* navigation_handle) { - if (!navigation_handle->IsInMainFrame()) - return; - - // In the event of a page reload, we ensure that the frontend is not notified - // until the UI finishes loading, so we set |ui_ready_| to false. This is - // balanced in HandleDisplayFailures, which is called when the frontend is - // ready to receive failure notifications. - if (navigation_handle->GetReloadType() != content::ReloadType::NONE) - ui_ready_ = false; -} - -void ExtensionLoaderHandler::AddFailure( - const base::FilePath& file_path, - const std::string& error, - size_t line_number, - const std::string& manifest) { - failed_paths_.push_back(file_path); - base::FilePath prettified_path = path_util::PrettifyPath(file_path); - - std::unique_ptr<base::DictionaryValue> manifest_value( - new base::DictionaryValue()); - SourceHighlighter highlighter(manifest, line_number); - // If the line number is 0, this highlights no regions, but still adds the - // full manifest. - highlighter.SetHighlightedRegions(manifest_value.get()); - - std::unique_ptr<base::DictionaryValue> failure(new base::DictionaryValue()); - failure->SetString("path", prettified_path.LossyDisplayName()); - failure->SetString("error", error); - failure->Set("manifest", std::move(manifest_value)); - failures_.Append(std::move(failure)); - - // Only notify the frontend if the frontend UI is ready. - if (ui_ready_) - NotifyFrontendOfFailure(); -} - -void ExtensionLoaderHandler::NotifyFrontendOfFailure() { - web_ui()->CallJavascriptFunctionUnsafe( - "extensions.ExtensionLoader.notifyLoadFailed", failures_); - failures_.Clear(); -} - -} // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/extension_loader_handler.h b/chrome/browser/ui/webui/extensions/extension_loader_handler.h deleted file mode 100644 index a70ab5fb..0000000 --- a/chrome/browser/ui/webui/extensions/extension_loader_handler.h +++ /dev/null
@@ -1,121 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_LOADER_HANDLER_H_ -#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_LOADER_HANDLER_H_ - -#include <stddef.h> - -#include <memory> -#include <string> -#include <vector> - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/scoped_observer.h" -#include "base/values.h" -#include "chrome/browser/extensions/load_error_reporter.h" -#include "content/public/browser/reload_type.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_ui_message_handler.h" - -namespace content { -class WebUIDataSource; -} - -class Profile; - -namespace extensions { - -class Extension; - -// The handler page for the Extension Commands UI overlay. -class ExtensionLoaderHandler : public content::WebUIMessageHandler, - public extensions::LoadErrorReporter::Observer, - public content::WebContentsObserver { - public: - using GetManifestErrorCallback = - base::Callback<void(const base::FilePath& file_path, - const std::string& error, - size_t line_number, - const std::string& manifest)>; - - explicit ExtensionLoaderHandler(Profile* profile); - ~ExtensionLoaderHandler() override; - - // Fetches the localized values for the page and deposits them into |source|. - void GetLocalizedValues(content::WebUIDataSource* source); - - // WebUIMessageHandler implementation. - void RegisterMessages() override; - - // TODO(devlin): Move this to developerPrivate. - static void GetManifestError(const std::string& error, - const base::FilePath& extension_path, - const GetManifestErrorCallback& callback); - - private: - // Handle the 'extensionLoaderRetry' message. - void HandleRetry(const base::ListValue* args); - - // Handle the 'extensionLoaderIgnoreFailure' message. - void HandleIgnoreFailure(const base::ListValue* args); - - // Handle the 'extensionLoaderDisplayFailures' message. - void HandleDisplayFailures(const base::ListValue* args); - - // Try to load an unpacked extension from the given |file_path|. - void LoadUnpackedExtension(const base::FilePath& file_path); - - // extensions::LoadErrorReporter::Observer: - void OnLoadFailure(content::BrowserContext* browser_context, - const base::FilePath& file_path, - const std::string& error) override; - - // content::WebContentsObserver: - void DidStartNavigation( - content::NavigationHandle* navigation_handle) override; - - // Add a failure to |failures_|. If it was a manifest error, |manifest| will - // hold the manifest contents, and |line_number| will point to the line at - // which the error was found. - void AddFailure(const base::FilePath& file_path, - const std::string& error, - size_t line_number, - const std::string& manifest); - - // Notify the frontend of all failures. - void NotifyFrontendOfFailure(); - - // The profile with which this Handler is associated. - Profile* profile_; - - // Holds information about all unpacked extension install failures that - // were reported while the extensions page was loading. - base::ListValue failures_; - - // Holds failed paths for load retries. - std::vector<base::FilePath> failed_paths_; - - ScopedObserver<extensions::LoadErrorReporter, - extensions::LoadErrorReporter::Observer> - extension_error_reporter_observer_; - - // Set when the chrome://extensions page is fully loaded and the frontend is - // ready to receive failure notifications. We need this because the page - // fails to display failures if they are sent before the Javascript is loaded. - bool ui_ready_; - - // Weak pointer factory for posting background tasks. - base::WeakPtrFactory<ExtensionLoaderHandler> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionLoaderHandler); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_LOADER_HANDLER_H_
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc deleted file mode 100644 index 8e9efd24..0000000 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc +++ /dev/null
@@ -1,319 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" - -#include <vector> - -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/apps/app_info_dialog.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/common/buildflags.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/url_constants.h" -#include "chrome/grit/chromium_strings.h" -#include "chrome/grit/generated_resources.h" -#include "components/google/core/browser/google_util.h" -#include "components/pref_registry/pref_registry_syncable.h" -#include "components/prefs/pref_service.h" -#include "components/strings/grit/components_strings.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/web_contents.h" -#include "content/public/browser/web_ui.h" -#include "content/public/browser/web_ui_data_source.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" -#include "extensions/common/extension.h" -#include "extensions/common/extension_set.h" -#include "extensions/common/extension_urls.h" -#include "extensions/common/manifest.h" -#include "ui/base/l10n/l10n_util.h" -#include "url/gurl.h" - -#if BUILDFLAG(ENABLE_SUPERVISED_USERS) -#include "chrome/browser/supervised_user/supervised_user_service.h" -#include "chrome/browser/supervised_user/supervised_user_service_factory.h" -#endif - -namespace extensions { - -ExtensionSettingsHandler::ExtensionSettingsHandler() - : extension_service_(nullptr) { -} - -ExtensionSettingsHandler::~ExtensionSettingsHandler() { -} - -// static -void ExtensionSettingsHandler::RegisterProfilePrefs( - user_prefs::PrefRegistrySyncable* registry) { - registry->RegisterBooleanPref( - prefs::kExtensionsUIDeveloperMode, - false, - user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); - registry->RegisterBooleanPref( - prefs::kExtensionsUIDismissedADTPromo, - false, - user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); -} - -void ExtensionSettingsHandler::GetLocalizedValues( - content::WebUIDataSource* source) { - source->AddString("extensionSettings", - l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE)); - - source->AddString("extensionSettingsDeveloperMode", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK)); - source->AddString("extensionSettingsNoExtensions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED)); - source->AddString( - "extensionSettingsSuggestGallery", - l10n_util::GetStringFUTF16( - IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY, - base::ASCIIToUTF16( - google_util::AppendGoogleLocaleParam( - GURL(extension_urls::GetWebstoreExtensionsCategoryURL()), - g_browser_process->GetApplicationLocale()).spec()))); - source->AddString("extensionSettingsGetMoreExtensions", - l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS)); - source->AddString( - "extensionSettingsGetMoreExtensionsUrl", - base::ASCIIToUTF16( - google_util::AppendGoogleLocaleParam( - GURL(extension_urls::GetWebstoreExtensionsCategoryURL()), - g_browser_process->GetApplicationLocale()).spec())); - source->AddString("extensionSettingsExtensionId", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID)); - source->AddString("extensionSettingsExtensionPath", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH)); - source->AddString("extensionSettingsInspectViews", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS)); - source->AddString("extensionSettingsInstallWarnings", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_WARNINGS)); - source->AddString("viewIncognito", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO)); - source->AddString("viewInactive", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INACTIVE)); - source->AddString("viewIframe", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_IFRAME)); - source->AddString("backgroundPage", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_BACKGROUND_PAGE)); - source->AddString("extensionSettingsEnable", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE)); - source->AddString("extensionSettingsEnabled", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED)); - source->AddString("extensionSettingsRemove", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE)); - source->AddString("extensionSettingsEnableIncognito", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO)); - source->AddString("extensionSettingsEnableErrorCollection", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_ERROR_COLLECTION)); - source->AddString("extensionSettingsAllowFileAccess", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS)); - source->AddString("extensionSettingsAllowOnAllUrls", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_ON_ALL_URLS)); - source->AddString("extensionSettingsIncognitoWarning", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING)); - source->AddString("extensionSettingsReloadTerminated", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_TERMINATED)); - source->AddString("extensionSettingsRepairCorrupted", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_REPAIR_CORRUPTED)); - source->AddString("extensionSettingsLaunch", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LAUNCH)); - source->AddString("extensionSettingsReloadUnpacked", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_UNPACKED)); - source->AddString("extensionSettingsOptions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS_LINK)); - if (CanShowAppInfoDialog()) { - source->AddString("extensionSettingsPermissions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_INFO_LINK)); - } else { - source->AddString( - "extensionSettingsPermissions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_PERMISSIONS_LINK)); - } - source->AddString("extensionSettingsVisitWebsite", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE)); - source->AddString("extensionSettingsVisitWebStore", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSTORE)); - source->AddString("extensionSettingsPolicyControlled", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED)); - source->AddString("extensionSettingsPolicyRecommeneded", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_RECOMMENDED)); - source->AddString("extensionSettingsDependentExtensions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEPENDENT_EXTENSIONS)); -#if BUILDFLAG(ENABLE_SUPERVISED_USERS) - const SupervisedUserService* supervised_user_service = - SupervisedUserServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())); - source->AddString("extensionSettingsSupervisedUser", - supervised_user_service->GetExtensionsLockedMessage()); -#endif - source->AddString("loading", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOADING)); - source->AddString("extensionSettingsCorruptInstall", - l10n_util::GetStringUTF16( - IDS_EXTENSIONS_CORRUPTED_EXTENSION)); - source->AddString("extensionSettingsSuspiciousInstall", - l10n_util::GetStringFUTF16( - IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE, - l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE))); - source->AddString("extensionSettingsLearnMore", - l10n_util::GetStringUTF16(IDS_LEARN_MORE)); - source->AddString("extensionSettingsSuspiciousInstallHelpUrl", - base::ASCIIToUTF16( - google_util::AppendGoogleLocaleParam( - GURL(chrome::kRemoveNonCWSExtensionURL), - g_browser_process->GetApplicationLocale()).spec())); - source->AddString("extensionSettingsLoadUnpackedButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON)); - source->AddString("extensionSettingsPackButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON)); - source->AddString("extensionSettingsCommandsLink", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_COMMANDS_CONFIGURE)); - source->AddString("extensionSettingsUpdateButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON)); - source->AddString("extensionSettingsCrashMessage", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION)); - source->AddString("extensionSettingsInDevelopment", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT)); - source->AddString("extensionSettingsWarningsTitle", - l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE)); - source->AddString("extensionSettingsShowDetails", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS)); - source->AddString("extensionSettingsHideDetails", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS)); - source->AddString("extensionSettingsUpdateRequiredBePolicy", - l10n_util::GetStringUTF16( - IDS_EXTENSIONS_DISABLED_UPDATE_REQUIRED_BY_POLICY)); - - source->AddLocalizedString("extensionLogLevelInfo", - IDS_EXTENSIONS_LOG_LEVEL_INFO); - source->AddLocalizedString("extensionLogLevelWarn", - IDS_EXTENSIONS_LOG_LEVEL_WARN); - source->AddLocalizedString("extensionLogLevelError", - IDS_EXTENSIONS_LOG_LEVEL_ERROR); - - // TODO(estade): comb through the above strings to find ones no longer used in - // uber extensions. - source->AddString("extensionUninstall", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL)); - - // Pack Extension Overlay: - source->AddString("packExtensionOverlay", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE)); - source->AddString("packExtensionHeading", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING)); - source->AddString("packExtensionCommit", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_BUTTON)); - source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK)); - source->AddString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL)); - source->AddString("packExtensionRootDir", - l10n_util::GetStringUTF16( - IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL)); - source->AddString("packExtensionPrivateKey", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL)); - source->AddString("packExtensionBrowseButton", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE)); - source->AddString("packExtensionProceedAnyway", - l10n_util::GetStringUTF16(IDS_EXTENSION_PROCEED_ANYWAY)); - source->AddString("packExtensionWarningTitle", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_WARNING_TITLE)); - source->AddString("packExtensionErrorTitle", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_ERROR_TITLE)); - - // Extension Error and Extension Error Overlay: - source->AddString("extensionErrorHeading", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_HEADING)); - source->AddString("extensionErrorClearAll", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CLEAR_ALL)); - source->AddString("extensionErrorNoErrors", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_NO_ERRORS)); - source->AddString( - "extensionErrorNoErrorsCodeMessage", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE)); - source->AddString("extensionErrorOverlayDone", - l10n_util::GetStringUTF16(IDS_DONE)); - source->AddString( - "extensionErrorOverlayContextUrl", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CONTEXT)); - source->AddString( - "extensionErrorOverlayStackTrace", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_STACK_TRACE)); - source->AddString( - "extensionErrorOverlayAnonymousFunction", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION)); - source->AddString( - "extensionErrorOverlayLaunchDevtools", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_LAUNCH_DEVTOOLS)); - source->AddString( - "extensionErrorOverlayContextUnknown", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CONTEXT_UNKNOWN)); - source->AddString( - "extensionErrorOverlayNoCodeToDisplay", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_NO_CODE_TO_DISPLAY)); - - // Extension Commands Overlay: - source->AddString("extensionCommandsOverlay", - l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_DIALOG_TITLE)); - source->AddString("extensionCommandsEmpty", - l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_EMPTY)); - source->AddString("extensionCommandsInactive", - l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_INACTIVE)); - source->AddString("extensionCommandsStartTyping", - l10n_util::GetStringUTF16(IDS_EXTENSION_TYPE_SHORTCUT)); - source->AddString("extensionCommandsDelete", - l10n_util::GetStringUTF16(IDS_EXTENSION_DELETE_SHORTCUT)); - source->AddString("extensionCommandsGlobal", - l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GLOBAL)); - source->AddString("extensionCommandsRegular", - l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_NOT_GLOBAL)); - source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK)); - - // 'Bubble' text for the controlled-setting-indicator - source->AddString("extensionControlledSettingPolicy", - l10n_util::GetStringUTF16(IDS_CONTROLLED_SETTING_POLICY)); -} - -void ExtensionSettingsHandler::DidStartNavigation( - content::NavigationHandle* navigation_handle) { - if (!navigation_handle->IsInMainFrame()) - return; - - if (navigation_handle->GetReloadType() != content::ReloadType::NONE) - ReloadUnpackedExtensions(); -} - -void ExtensionSettingsHandler::RegisterMessages() { - Profile* profile = Profile::FromWebUI(web_ui())->GetOriginalProfile(); - extension_service_ = - extensions::ExtensionSystem::Get(profile)->extension_service(); - // Clear the preference for the ADT Promo before fully removing it. - // TODO(devlin): Take this out when everyone's been updated. - Profile::FromWebUI(web_ui())->GetPrefs()->ClearPref( - prefs::kExtensionsUIDismissedADTPromo); - - content::WebContentsObserver::Observe(web_ui()->GetWebContents()); -} - -void ExtensionSettingsHandler::ReloadUnpackedExtensions() { - ExtensionRegistry* registry = - ExtensionRegistry::Get(extension_service_->profile()); - std::vector<const Extension*> unpacked_extensions; - for (const scoped_refptr<const extensions::Extension>& extension : - registry->enabled_extensions()) { - if (Manifest::IsUnpackedLocation(extension->location())) - unpacked_extensions.push_back(extension.get()); - } - - for (std::vector<const Extension*>::iterator iter = - unpacked_extensions.begin(); iter != unpacked_extensions.end(); ++iter) { - extension_service_->ReloadExtensionWithQuietFailure((*iter)->id()); - } -} - -} // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chrome/browser/ui/webui/extensions/extension_settings_handler.h deleted file mode 100644 index 40f4982..0000000 --- a/chrome/browser/ui/webui/extensions/extension_settings_handler.h +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_ -#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_ - -#include "base/macros.h" -#include "content/public/browser/reload_type.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_ui_message_handler.h" - -class ExtensionService; - -namespace content { -class WebUIDataSource; -} - -namespace user_prefs { -class PrefRegistrySyncable; -} - -namespace extensions { - -// Extension Settings UI handler. -class ExtensionSettingsHandler : public content::WebUIMessageHandler, - public content::WebContentsObserver { - public: - ExtensionSettingsHandler(); - ~ExtensionSettingsHandler() override; - - static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - - // Note: This uses |web_ui()| from |WebUIMessageHandler|, so it must only be - // called after |web_ui->AddMessageHandler(this)| has been called. - void GetLocalizedValues(content::WebUIDataSource* source); - - private: - // WebContentsObserver implementation. - void DidStartNavigation( - content::NavigationHandle* navigation_handle) override; - - // WebUIMessageHandler implementation. - void RegisterMessages() override; - - // Helper method that reloads all unpacked extensions. - void ReloadUnpackedExtensions(); - - // Our model. Outlives us since it's owned by our containing profile. - ExtensionService* extension_service_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsHandler); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc index 0e6359f..0e3f99827 100644 --- a/chrome/browser/ui/webui/extensions/extensions_ui.cc +++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -14,8 +14,6 @@ #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/webui/extensions/extension_loader_handler.h" -#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" #include "chrome/browser/ui/webui/extensions/install_extension_handler.h" #include "chrome/browser/ui/webui/metrics_handler.h" #include "chrome/common/pref_names.h" @@ -27,6 +25,7 @@ #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "components/google/core/browser/google_util.h" +#include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" #include "content/public/browser/navigation_handle.h" @@ -430,6 +429,13 @@ return rb.LoadDataResourceBytesForScale(IDR_EXTENSIONS_FAVICON, scale_factor); } +void ExtensionsUI::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref( + prefs::kExtensionsUIDeveloperMode, false, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); +} + // Normally volatile data does not belong in loadTimeData, but in this case // prevents flickering on a very prominent surface (top of the landing page). void ExtensionsUI::OnDevModeChanged() {
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.h b/chrome/browser/ui/webui/extensions/extensions_ui.h index 8c2ff2a0..b16cc35 100644 --- a/chrome/browser/ui/webui/extensions/extensions_ui.h +++ b/chrome/browser/ui/webui/extensions/extensions_ui.h
@@ -14,6 +14,10 @@ class RefCountedMemory; } +namespace user_prefs { +class PrefRegistrySyncable; +} + namespace extensions { class ExtensionsUI : public content::WebUIController { @@ -24,6 +28,8 @@ static base::RefCountedMemory* GetFaviconResourceBytes( ui::ScaleFactor scale_factor); + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + private: // Called when developer mode is toggled. void OnDevModeChanged();
diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc index acad9c57e..42524631 100644 --- a/chrome/browser/ui/webui/inspect_ui.cc +++ b/chrome/browser/ui/webui/inspect_ui.cc
@@ -470,7 +470,7 @@ NavigateParams params(GetSingletonTabNavigateParams( browser, GURL(chrome::kChromeUIInspectURL))); params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; - ShowSingletonTabOverwritingNTP(browser, params); + ShowSingletonTabOverwritingNTP(browser, std::move(params)); } void InspectUI::Observe(int type,
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc index 5775be8..80fe9579 100644 --- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc +++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -9,9 +9,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/memory/ref_counted_memory.h" @@ -58,35 +55,15 @@ const char kProvisionalUsbLabel[] = "provisional-usb"; -// Updates |job| with raster file path, size and last modification time. -// Returns the updated print job. -std::unique_ptr<extensions::PrinterProviderPrintJob> -UpdateJobFileInfoOnWorkerThread( - const base::FilePath& raster_path, - std::unique_ptr<extensions::PrinterProviderPrintJob> job) { - if (base::GetFileInfo(raster_path, &job->file_info)) - job->document_path = raster_path; - return job; -} - -// Callback to PWG raster conversion. -// Posts a task to update print job with info about file containing converted -// PWG raster data. +// Updates |job| with raster data. Returns the updated print job. void UpdateJobFileInfo(std::unique_ptr<extensions::PrinterProviderPrintJob> job, ExtensionPrinterHandler::PrintJobCallback callback, - bool success, - const base::FilePath& pwg_file_path) { - if (!success) { - std::move(callback).Run(std::move(job)); - return; - } - - base::PostTaskWithTraitsAndReplyWithResult( - FROM_HERE, - {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, - base::BindOnce(&UpdateJobFileInfoOnWorkerThread, pwg_file_path, - std::move(job)), - std::move(callback)); + base::ReadOnlySharedMemoryRegion pwg_region) { + auto data = + base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(pwg_region); + if (data) + job->document_bytes = data; + std::move(callback).Run(std::move(job)); } bool HasUsbPrinterProviderPermissions(const Extension* extension) { @@ -281,7 +258,7 @@ void ExtensionPrinterHandler::DispatchPrintJob( PrintCallback callback, std::unique_ptr<extensions::PrinterProviderPrintJob> print_job) { - if (print_job->document_path.empty() && !print_job->document_bytes) { + if (!print_job->document_bytes) { WrapPrintCallback(std::move(callback), base::Value(kInvalidDataPrintError)); return; }
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc index 9e380aa..a8ff9102 100644 --- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc +++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -13,8 +13,6 @@ #include "base/bind.h" #include "base/containers/queue.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" #include "base/json/json_string_value_serializer.h" #include "base/macros.h" #include "base/memory/ref_counted_memory.h" @@ -278,47 +276,38 @@ // Fake PwgRasterConverter used in the tests. class FakePwgRasterConverter : public PwgRasterConverter { public: - FakePwgRasterConverter() : fail_conversion_(false), initialized_(false) {} + FakePwgRasterConverter() {} ~FakePwgRasterConverter() override = default; - // PwgRasterConverter implementation. It writes |data| to a temp file. + // PwgRasterConverter implementation. It writes |data| to shared memory. // Also, remembers conversion and bitmap settings passed into the method. void Start(base::RefCountedMemory* data, const printing::PdfRenderSettings& conversion_settings, const printing::PwgRasterSettings& bitmap_settings, ResultCallback callback) override { + base::ReadOnlySharedMemoryRegion invalid_pwg_region; if (fail_conversion_) { - std::move(callback).Run(false, base::FilePath()); + std::move(callback).Run(std::move(invalid_pwg_region)); return; } - if (!initialized_ && !temp_dir_.CreateUniqueTempDir()) { - ADD_FAILURE() << "Unable to create target dir for cenverter"; - std::move(callback).Run(false, base::FilePath()); + base::MappedReadOnlyRegion memory = + base::ReadOnlySharedMemoryRegion::Create(data->size()); + if (!memory.mapping.IsValid()) { + ADD_FAILURE() << "Failed to create pwg raster shared memory."; + std::move(callback).Run(std::move(invalid_pwg_region)); return; } - initialized_ = true; - - path_ = temp_dir_.GetPath().AppendASCII("output.pwg"); - std::string data_str(data->front_as<char>(), data->size()); - int written = WriteFile(path_, data_str.c_str(), data_str.size()); - if (written != static_cast<int>(data_str.size())) { - ADD_FAILURE() << "Failed to write pwg raster file."; - std::move(callback).Run(false, base::FilePath()); - return; - } - + memcpy(memory.mapping.memory(), data->front(), data->size()); conversion_settings_ = conversion_settings; bitmap_settings_ = bitmap_settings; - - std::move(callback).Run(true, path_); + std::move(callback).Run(std::move(memory.region)); } // Makes |Start| method always return an error. void FailConversion() { fail_conversion_ = true; } - const base::FilePath& path() { return path_; } const printing::PdfRenderSettings& conversion_settings() const { return conversion_settings_; } @@ -328,13 +317,9 @@ } private: - base::ScopedTempDir temp_dir_; - - base::FilePath path_; printing::PdfRenderSettings conversion_settings_; printing::PwgRasterSettings bitmap_settings_; - bool fail_conversion_; - bool initialized_; + bool fail_conversion_ = false; DISALLOW_COPY_AND_ASSIGN(FakePwgRasterConverter); }; @@ -696,7 +681,6 @@ EXPECT_EQ(title, print_job->job_title); EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json); EXPECT_EQ(kContentTypePDF, print_job->content_type); - EXPECT_TRUE(print_job->document_path.empty()); ASSERT_TRUE(print_job->document_bytes); EXPECT_EQ(RefCountedMemoryToString(print_data), RefCountedMemoryToString(print_job->document_bytes)); @@ -761,7 +745,6 @@ EXPECT_EQ(title, print_job->job_title); EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json); EXPECT_EQ(kContentTypePDF, print_job->content_type); - EXPECT_TRUE(print_job->document_path.empty()); ASSERT_TRUE(print_job->document_bytes); EXPECT_EQ(RefCountedMemoryToString(print_data), RefCountedMemoryToString(print_job->document_bytes)); @@ -816,11 +799,9 @@ EXPECT_EQ(title, print_job->job_title); EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json); EXPECT_EQ(kContentTypePWG, print_job->content_type); - EXPECT_FALSE(print_job->document_bytes); - EXPECT_FALSE(print_job->document_path.empty()); - EXPECT_EQ(pwg_raster_converter_->path(), print_job->document_path); - EXPECT_EQ(static_cast<int64_t>(print_data->size()), - print_job->file_info.size); + ASSERT_TRUE(print_job->document_bytes); + EXPECT_EQ(RefCountedMemoryToString(print_data), + RefCountedMemoryToString(print_job->document_bytes)); fake_api->TriggerNextPrintCallback(kPrintRequestSuccess); @@ -872,11 +853,9 @@ EXPECT_EQ(title, print_job->job_title); EXPECT_EQ(kPrintTicketWithDuplex, print_job->ticket_json); EXPECT_EQ(kContentTypePWG, print_job->content_type); - EXPECT_FALSE(print_job->document_bytes); - EXPECT_FALSE(print_job->document_path.empty()); - EXPECT_EQ(pwg_raster_converter_->path(), print_job->document_path); - EXPECT_EQ(static_cast<int64_t>(print_data->size()), - print_job->file_info.size); + ASSERT_TRUE(print_job->document_bytes); + EXPECT_EQ(RefCountedMemoryToString(print_data), + RefCountedMemoryToString(print_job->document_bytes)); fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
diff --git a/chrome/browser/vr/browser_ui_interface.h b/chrome/browser/vr/browser_ui_interface.h index e5ecee45..6eb1f76 100644 --- a/chrome/browser/vr/browser_ui_interface.h +++ b/chrome/browser/vr/browser_ui_interface.h
@@ -45,6 +45,7 @@ std::unique_ptr<Assets> assets, const base::Version& component_version) = 0; virtual void OnAssetsUnavailable() = 0; + virtual void SetRegularTabsOpen(bool open) = 0; virtual void SetIncognitoTabsOpen(bool open) = 0; virtual void SetOverlayTextureEmpty(bool empty) = 0;
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc index c3472c56..7366982 100644 --- a/chrome/browser/vr/elements/ui_element_name.cc +++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -63,8 +63,16 @@ "kOverflowMenuLayout", "kOverflowMenuForwardButton", "kOverflowMenuReloadButton", - "kOverflowMenuCloseAllIncognitoTabsItem", + "kOverflowMenuNewTabItem", "kOverflowMenuNewIncognitoTabItem", + "kOverflowMenuBookmarksItem", + "kOverflowMenuRecentTabsItem", + "kOverflowMenuHistoryItem", + "kOverflowMenuDownloadsItem", + "kOverflowMenuPreferencesItem", + "kOverflowMenuCloseAllTabsItem", + "kOverflowMenuCloseAllIncognitoTabsItem", + "kOverflowMenuSendFeedbackItem", "kOmniboxVisibiltyControlForVoice", "kOmniboxVisibilityControlForAudioPermissionPrompt", "kOmniboxDmmRoot",
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h index f28c6a2..73bb2e4 100644 --- a/chrome/browser/vr/elements/ui_element_name.h +++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -62,8 +62,16 @@ kOverflowMenuLayout, kOverflowMenuForwardButton, kOverflowMenuReloadButton, - kOverflowMenuCloseAllIncognitoTabsItem, + kOverflowMenuNewTabItem, kOverflowMenuNewIncognitoTabItem, + kOverflowMenuBookmarksItem, + kOverflowMenuRecentTabsItem, + kOverflowMenuHistoryItem, + kOverflowMenuDownloadsItem, + kOverflowMenuPreferencesItem, + kOverflowMenuCloseAllTabsItem, + kOverflowMenuCloseAllIncognitoTabsItem, + kOverflowMenuSendFeedbackItem, kOmniboxVisibiltyControlForVoice, kOmniboxVisibilityControlForAudioPermissionPrompt, kOmniboxDmmRoot,
diff --git a/chrome/browser/vr/model/model.h b/chrome/browser/vr/model/model.h index 6e47727..25227f7 100644 --- a/chrome/browser/vr/model/model.h +++ b/chrome/browser/vr/model/model.h
@@ -54,7 +54,9 @@ bool supports_selection = true; bool needs_keyboard_update = false; bool overflow_menu_enabled = false; + bool regular_tabs_open = false; bool incognito_tabs_open = false; + bool standalone_vr_device = false; // WebVR state. WebVrModel web_vr;
diff --git a/chrome/browser/vr/test/mock_browser_ui_interface.h b/chrome/browser/vr/test/mock_browser_ui_interface.h index e7a7584..4acef36 100644 --- a/chrome/browser/vr/test/mock_browser_ui_interface.h +++ b/chrome/browser/vr/test/mock_browser_ui_interface.h
@@ -38,6 +38,7 @@ std::unique_ptr<Assets> assets, const base::Version& component_version) {} MOCK_METHOD0(OnAssetsUnavailable, void()); + MOCK_METHOD1(SetRegularTabsOpen, void(bool)); MOCK_METHOD1(SetIncognitoTabsOpen, void(bool)); MOCK_METHOD1(SetOverlayTextureEmpty, void(bool));
diff --git a/chrome/browser/vr/test/mock_ui_browser_interface.h b/chrome/browser/vr/test/mock_ui_browser_interface.h index dc10831..0add6b9 100644 --- a/chrome/browser/vr/test/mock_ui_browser_interface.h +++ b/chrome/browser/vr/test/mock_ui_browser_interface.h
@@ -23,7 +23,14 @@ MOCK_METHOD0(NavigateForward, void()); MOCK_METHOD0(ReloadTab, void()); MOCK_METHOD1(OpenNewTab, void(bool)); + MOCK_METHOD0(OpenBookmarks, void()); + MOCK_METHOD0(OpenRecentTabs, void()); + MOCK_METHOD0(OpenHistory, void()); + MOCK_METHOD0(OpenDownloads, void()); + MOCK_METHOD0(OpenSettings, void()); + MOCK_METHOD0(CloseAllTabs, void()); MOCK_METHOD0(CloseAllIncognitoTabs, void()); + MOCK_METHOD0(OpenFeedback, void()); MOCK_METHOD0(ExitCct, void()); MOCK_METHOD0(CloseHostedDialog, void()); MOCK_METHOD1(OnUnsupportedMode, void(UiUnsupportedMode mode));
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc index 2a05622..a776b3408 100644 --- a/chrome/browser/vr/testapp/vr_test_context.cc +++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -516,18 +516,32 @@ } void VrTestContext::OpenNewTab(bool incognito) { - DCHECK(incognito); - incognito_ = true; - ui_->SetIncognito(true); - model_->incognito_tabs_open = true; + incognito_ = incognito; + ui_->SetIncognito(incognito); + model_->incognito_tabs_open = model_->incognito_tabs_open || incognito; +} + +void VrTestContext::OpenBookmarks() {} +void VrTestContext::OpenRecentTabs() {} +void VrTestContext::OpenHistory() {} +void VrTestContext::OpenDownloads() {} +void VrTestContext::OpenSettings() {} + +void VrTestContext::CloseAllTabs() { + incognito_ = false; + ui_->SetIncognito(false); + model_->incognito_tabs_open = false; + model_->regular_tabs_open = false; } void VrTestContext::CloseAllIncognitoTabs() { - incognito_ = true; + incognito_ = false; ui_->SetIncognito(false); model_->incognito_tabs_open = false; } +void VrTestContext::OpenFeedback() {} + void VrTestContext::ExitCct() {} void VrTestContext::OnUnsupportedMode(vr::UiUnsupportedMode mode) {
diff --git a/chrome/browser/vr/testapp/vr_test_context.h b/chrome/browser/vr/testapp/vr_test_context.h index bf762d06..678eb2c 100644 --- a/chrome/browser/vr/testapp/vr_test_context.h +++ b/chrome/browser/vr/testapp/vr_test_context.h
@@ -47,7 +47,14 @@ void NavigateForward() override; void ReloadTab() override; void OpenNewTab(bool incognito) override; + void OpenBookmarks() override; + void OpenRecentTabs() override; + void OpenHistory() override; + void OpenDownloads() override; + void OpenSettings() override; + void CloseAllTabs() override; void CloseAllIncognitoTabs() override; + void OpenFeedback() override; void ExitCct() override; void CloseHostedDialog() override; void OnUnsupportedMode(vr::UiUnsupportedMode mode) override;
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc index a63303c0..c9b71d5 100644 --- a/chrome/browser/vr/ui.cc +++ b/chrome/browser/vr/ui.cc
@@ -34,6 +34,9 @@ namespace vr { +UiInitialState::UiInitialState() = default; +UiInitialState::UiInitialState(const UiInitialState& other) = default; + Ui::Ui(UiBrowserInterface* browser, ContentInputForwarder* content_input_forwarder, KeyboardDelegate* keyboard_delegate, @@ -457,6 +460,10 @@ model_->waiting_for_background = false; } +void Ui::SetRegularTabsOpen(bool open) { + model_->regular_tabs_open = open; +} + void Ui::SetIncognitoTabsOpen(bool open) { model_->incognito_tabs_open = open; } @@ -495,6 +502,7 @@ model_->waiting_for_background = ui_initial_state.assets_supported; model_->supports_selection = ui_initial_state.supports_selection; model_->needs_keyboard_update = ui_initial_state.needs_keyboard_update; + model_->standalone_vr_device = ui_initial_state.is_standalone_vr_device; } void Ui::AcceptDoffPromptForTesting() {
diff --git a/chrome/browser/vr/ui.h b/chrome/browser/vr/ui.h index a76b20f..257b05c8 100644 --- a/chrome/browser/vr/ui.h +++ b/chrome/browser/vr/ui.h
@@ -38,6 +38,8 @@ struct ReticleModel; struct UiInitialState { + UiInitialState(); + UiInitialState(const UiInitialState& other); bool in_cct = false; bool in_web_vr = false; bool web_vr_autopresentation_expected = false; @@ -47,6 +49,7 @@ bool assets_supported = false; bool supports_selection = true; bool needs_keyboard_update = false; + bool is_standalone_vr_device = false; }; // This class manages all GLThread owned objects and GL rendering for VrShell. @@ -99,6 +102,7 @@ std::unique_ptr<Assets> assets, const base::Version& component_version) override; void OnAssetsUnavailable() override; + void SetRegularTabsOpen(bool open) override; void SetIncognitoTabsOpen(bool open) override; void SetOverlayTextureEmpty(bool empty) override;
diff --git a/chrome/browser/vr/ui_browser_interface.h b/chrome/browser/vr/ui_browser_interface.h index 1e58948..0ca8d36 100644 --- a/chrome/browser/vr/ui_browser_interface.h +++ b/chrome/browser/vr/ui_browser_interface.h
@@ -33,7 +33,14 @@ virtual void NavigateForward() = 0; virtual void ReloadTab() = 0; virtual void OpenNewTab(bool incognito) = 0; + virtual void OpenBookmarks() = 0; + virtual void OpenRecentTabs() = 0; + virtual void OpenHistory() = 0; + virtual void OpenDownloads() = 0; + virtual void OpenSettings() = 0; + virtual void CloseAllTabs() = 0; virtual void CloseAllIncognitoTabs() = 0; + virtual void OpenFeedback() = 0; virtual void ExitCct() = 0; virtual void CloseHostedDialog() = 0; virtual void OnUnsupportedMode(UiUnsupportedMode mode) = 0;
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc index 63caa50..947a3f41 100644 --- a/chrome/browser/vr/ui_scene_creator.cc +++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -2215,9 +2215,17 @@ overflow_layout->AddChild(std::move(button_spacer)); std::vector<std::tuple<UiElementName, int>> menu_items = { + {kOverflowMenuNewTabItem, IDS_VR_MENU_NEW_TAB}, + {kOverflowMenuNewIncognitoTabItem, IDS_VR_MENU_NEW_INCOGNITO_TAB}, + {kOverflowMenuBookmarksItem, IDS_VR_MENU_BOOKMARKS}, + {kOverflowMenuRecentTabsItem, IDS_VR_MENU_RECENT_TABS}, + {kOverflowMenuHistoryItem, IDS_VR_MENU_HISTORY}, + {kOverflowMenuDownloadsItem, IDS_VR_MENU_DOWNLOADS}, + {kOverflowMenuPreferencesItem, IDS_VR_MENU_PREFERENCES}, + {kOverflowMenuCloseAllTabsItem, IDS_VR_MENU_CLOSE_ALL_TABS}, {kOverflowMenuCloseAllIncognitoTabsItem, IDS_VR_MENU_CLOSE_INCOGNITO_TABS}, - {kOverflowMenuNewIncognitoTabItem, IDS_VR_MENU_NEW_INCOGNITO_TAB}, + {kOverflowMenuSendFeedbackItem, IDS_VR_MENU_SEND_FEEDBACK}, }; for (auto& item : menu_items) { @@ -2253,8 +2261,80 @@ &ColorScheme::url_bar_button, &Button::SetButtonColors); background->AddChild(std::move(layout)); - switch (std::get<0>(item)) { + case kOverflowMenuNewTabItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenNewTab(false); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); + break; + case kOverflowMenuNewIncognitoTabItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenNewTab(true); + }, + base::Unretained(model_), base::Unretained(browser_))); + break; + case kOverflowMenuBookmarksItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenBookmarks(); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); + break; + case kOverflowMenuRecentTabsItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenRecentTabs(); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); + break; + case kOverflowMenuHistoryItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenHistory(); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); + break; + case kOverflowMenuDownloadsItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenDownloads(); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); + break; + case kOverflowMenuPreferencesItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->OpenSettings(); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); + break; + case kOverflowMenuCloseAllTabsItem: + background->set_click_handler(base::BindRepeating( + [](Model* model, UiBrowserInterface* browser) { + model->overflow_menu_enabled = false; + browser->CloseAllTabs(); + }, + base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device && + (model->incognito_tabs_open || + model->regular_tabs_open)); + break; case kOverflowMenuCloseAllIncognitoTabsItem: background->set_click_handler(base::BindRepeating( [](Model* model, UiBrowserInterface* browser) { @@ -2264,13 +2344,14 @@ base::Unretained(model_), base::Unretained(browser_))); VR_BIND_VISIBILITY(background, model->incognito_tabs_open); break; - case kOverflowMenuNewIncognitoTabItem: + case kOverflowMenuSendFeedbackItem: background->set_click_handler(base::BindRepeating( [](Model* model, UiBrowserInterface* browser) { model->overflow_menu_enabled = false; - browser->OpenNewTab(true); + browser->OpenFeedback(); }, base::Unretained(model_), base::Unretained(browser_))); + VR_BIND_VISIBILITY(background, model->standalone_vr_device); break; default: break;
diff --git a/chrome/common/OWNERS b/chrome/common/OWNERS index 8db9fc7..d2864a7 100644 --- a/chrome/common/OWNERS +++ b/chrome/common/OWNERS
@@ -55,3 +55,6 @@ # WebUI. See also chrome/browser/ui/webui/OWNERS. per-file webui_url_constants.cc=file://ui/webui/PLATFORM_OWNERS per-file webui_url_constants.h=file://ui/webui/PLATFORM_OWNERS + +# Thread profiling +per-file thread_profiler.*=wittman@chromium.org
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc index d155995..df43d2b 100644 --- a/chrome/common/chrome_content_client.cc +++ b/chrome/common/chrome_content_client.cc
@@ -692,7 +692,7 @@ #endif } -content::OriginTrialPolicy* ChromeContentClient::GetOriginTrialPolicy() { +blink::OriginTrialPolicy* ChromeContentClient::GetOriginTrialPolicy() { // Prevent initialization race (see crbug.com/721144). There may be a // race when the policy is needed for worker startup (which happens on a // separate worker thread).
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h index f279016..3258d3a 100644 --- a/chrome/common/chrome_content_client.h +++ b/chrome/common/chrome_content_client.h
@@ -91,7 +91,7 @@ bool AllowScriptExtensionForServiceWorker(const GURL& script_url) override; - content::OriginTrialPolicy* GetOriginTrialPolicy() override; + blink::OriginTrialPolicy* GetOriginTrialPolicy() override; #if defined(OS_ANDROID) media::MediaDrmBridgeClient* GetMediaDrmBridgeClient() override;
diff --git a/chrome/common/chrome_content_client_unittest.cc b/chrome/common/chrome_content_client_unittest.cc index 8b84705f..be450bc2 100644 --- a/chrome/common/chrome_content_client_unittest.cc +++ b/chrome/common/chrome_content_client_unittest.cc
@@ -194,23 +194,22 @@ // Static helper which can also be called from the main thread. static void AccessPolicy( ChromeContentClient* content_client, - std::vector<content::OriginTrialPolicy*>* policy_objects) { + std::vector<blink::OriginTrialPolicy*>* policy_objects) { // Repeatedly access the lazily-created origin trial policy for (int i = 0; i < 20; i++) { - content::OriginTrialPolicy* policy = - content_client->GetOriginTrialPolicy(); + blink::OriginTrialPolicy* policy = content_client->GetOriginTrialPolicy(); policy_objects->push_back(policy); base::PlatformThread::YieldCurrentThread(); } } - const std::vector<content::OriginTrialPolicy*>* policy_objects() const { + const std::vector<blink::OriginTrialPolicy*>* policy_objects() const { return &policy_objects_; } private: ChromeContentClient* chrome_client_; - std::vector<content::OriginTrialPolicy*> policy_objects_; + std::vector<blink::OriginTrialPolicy*> policy_objects_; DISALLOW_COPY_AND_ASSIGN(OriginTrialInitializationTestThread); }; @@ -220,7 +219,7 @@ // race prevention is no longer sufficient. TEST(ChromeContentClientTest, OriginTrialPolicyConcurrentInitialization) { ChromeContentClient content_client; - std::vector<content::OriginTrialPolicy*> policy_objects; + std::vector<blink::OriginTrialPolicy*> policy_objects; OriginTrialInitializationTestThread thread(&content_client); base::PlatformThreadHandle handle; @@ -234,16 +233,16 @@ ASSERT_EQ(20UL, policy_objects.size()); - content::OriginTrialPolicy* first_policy = policy_objects[0]; + blink::OriginTrialPolicy* first_policy = policy_objects[0]; - const std::vector<content::OriginTrialPolicy*>* all_policy_objects[] = { + const std::vector<blink::OriginTrialPolicy*>* all_policy_objects[] = { &policy_objects, thread.policy_objects(), }; - for (const std::vector<content::OriginTrialPolicy*>* thread_policy_objects : + for (const std::vector<blink::OriginTrialPolicy*>* thread_policy_objects : all_policy_objects) { EXPECT_GE(20UL, thread_policy_objects->size()); - for (content::OriginTrialPolicy* policy : *(thread_policy_objects)) { + for (blink::OriginTrialPolicy* policy : *(thread_policy_objects)) { EXPECT_EQ(first_policy, policy); } }
diff --git a/chrome/common/extensions/docs/static/images/get_started/click_detials.png b/chrome/common/extensions/docs/static/images/get_started/click_details.png similarity index 100% rename from chrome/common/extensions/docs/static/images/get_started/click_detials.png rename to chrome/common/extensions/docs/static/images/get_started/click_details.png Binary files differ
diff --git a/chrome/common/extensions/docs/templates/articles/a11y.html b/chrome/common/extensions/docs/templates/articles/a11y.html index 6ca3013..d410b1fa 100644 --- a/chrome/common/extensions/docs/templates/articles/a11y.html +++ b/chrome/common/extensions/docs/templates/articles/a11y.html
@@ -76,7 +76,7 @@ These attributes provide clues to the screen reader about the function and current state of controls on a web page. ARIA is a -<a href=" http://www.w3.org/WAI/intro/aria">work in progress at the W3C</a>. +<a href="https://www.w3.org/WAI/intro/aria">work in progress at the W3C</a>. </p> <p> @@ -101,7 +101,7 @@ </p> <p> -The <a href="http://www.w3.org/WAI/PF/aria/roles">ARIA Role Specification</a> +The <a href="https://www.w3.org/WAI/PF/aria/roles">ARIA Role Specification</a> holds detailed information on how to pick the correct role. For example, if your extension includes a toolbar, set the <code>role</code> attribute of the toolbar's DOM element as follows: @@ -122,7 +122,7 @@ in that it expresses specific information about a control. For more information on ARIA states and properties, refer to the -<a href="http://www.w3.org/TR/wai-aria/states_and_properties">W3C States and Properties specification</a>. +<a href="https://www.w3.org/TR/wai-aria/#introstates">W3C States and Properties specification</a>. </p> @@ -144,7 +144,7 @@ <p> The -<a href="http://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant"><code>aria-activedescendant</code></a> +<a href="https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant"><code>aria-activedescendant</code></a> property specifies which child of the toolbar receives focus when the toolbar receives focus. In this example, the toolbar's first button @@ -174,12 +174,12 @@ Google Chrome might not raise an event for every ARIA property, and screen readers might not recognize all of the events being raised. You can find more information on ARIA support in Google Chrome in the -<a href="http://www.chromium.org/developers/design-documents/accessibility#TOC-WAI-ARIA-Support">Chromium Accessibility Design Document</a>. +<a href="https://www.chromium.org/developers/design-documents/accessibility#TOC-WAI-ARIA-Support">Chromium Accessibility Design Document</a>. </p> <p> For a quick tutorial on adding ARIA controls to custom controls, see -<a href="http://www.w3.org/2010/Talks/www2010-dsr-diy-aria/">Dave Raggett's presentation from WWW2010</a>. +<a href="https://www.w3.org/2010/Talks/www2010-dsr-diy-aria/">Dave Raggett's presentation from WWW2010</a>. <h3 id="focus">Focus in custom controls</h3> @@ -236,7 +236,7 @@ the different parts of your extension without using the mouse. Also check that any popups on page actions or browser actions -are keyboard navigable. +are keyboard navigable. </p> <p id="builtin"> @@ -264,7 +264,7 @@ Make sure that it's easy to see which part of the interface has keyboard focus. Usually a focus outline moves around the interface, -but if you’re using CSS heavily this outline might be suppressed +but if you’re using CSS heavily this outline might be suppressed or the contrast might be reduced. Two examples of focus outline follow. </p> @@ -318,11 +318,11 @@ <pre> <head> -<script> +<script> function optionKeyEvent(event) { var tb = event.target; - var buttonid; - + var buttonid; + ENTER_KEYCODE = 13; RIGHT_KEYCODE = 39; LEFT_KEYCODE = 37; @@ -333,29 +333,29 @@ ExecuteButtonAction(getCurrentButtonID()); <em>// getCurrentButtonID defined elsewhere </em> } else if (event.keyCode == event.RIGHT_KEYCODE) { - // Change the active toolbar button to the one to the right (circular). + // Change the active toolbar button to the one to the right (circular). var buttonid = getNextButtonID(); <em>// getNextButtonID defined elsewhere </em> - tb.setAttribute("aria-activedescendant", buttonid); + tb.setAttribute("aria-activedescendant", buttonid); } else if (event.keyCode == event.LEFT_KEYCODE) { - // Change the active toolbar button to the one to the left (circular). + // Change the active toolbar button to the one to the left (circular). var buttonid = getPrevButtonID(); <em>// getPrevButtonID defined elsewhere </em> - tb.setAttribute("aria-activedescendant", buttonid); + tb.setAttribute("aria-activedescendant", buttonid); } else { return true; } return false; } -} -</script> +} +</script> -<div role="toolbar" tabindex="0" aria-activedescendant="button1" id="tb1" +<div role="toolbar" tabindex="0" aria-activedescendant="button1" id="tb1" onkeydown="return optionKeyEvent(event);" onkeypress="return optionKeyEvent(event);"> - <img src="buttoncut" role="button" alt="cut" id="button1"> - <img src="buttoncopy" role="button" alt="copy" id="button1"> - <img src="buttonpaste" role="button" alt="paste" id="button1"> + <img src="buttoncut" role="button" alt="cut" id="button1"> + <img src="buttoncopy" role="button" alt="copy" id="button1"> + <img src="buttonpaste" role="button" alt="paste" id="button1"> </div> </pre> @@ -382,7 +382,7 @@ <p> As an indicator of the flexibility of your UI, -apply the <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-scale">200% test</a>. +apply the <a href="https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-scale">200% test</a>. If you increase the text size or page zoom 200%, is your extension still usable? </p> @@ -393,7 +393,7 @@ and screenreaders cannot interpret images. Consider using a web font instead, such as one of the fonts collected in the -<a href="http://code.google.com/apis/webfonts/">Google Font API</a>. +<a href="https://developers.google.com/fonts/">Google Font API</a>. Text styled in a web font is searchable, scales to different sizes, and is accessible to people using screen readers. @@ -404,7 +404,7 @@ <p> Check that there is sufficient contrast between background color and foreground/text color in your extension. -<a href="http://snook.ca/technical/colour_contrast/colour.html">This contrast checking tool</a> +<a href="https://snook.ca/technical/colour_contrast/colour.html">This contrast checking tool</a> checks whether your background and foreground colors provide appropriate contrast. If you’re developing in a Windows environment, @@ -421,7 +421,7 @@ <p> You might consider offering different color themes, or giving the user the ability to customize the color scheme -for better contrast. +for better contrast. </p> <h3 id="sound">Sound</h3> @@ -430,8 +430,8 @@ If your extension relies upon sound or video to convey information, ensure that captions or a transcript are available. See the -<a href="http://www.dcmp.org/ciy/">Described and Captioned Media Program guidelines</a> -for more information on captions. +<a href="https://dcmp.org/learn/213">Described and Captioned Media Program guidelines</a> +for more information on captions. </p> <h3 id="images">Images</h3> @@ -457,7 +457,7 @@ If you must use text in an image, include the image text in the alt text. A good resource to refer to is the -<a href="http://www.webaim.org/techniques/alttext/">WebAIM article on appropriate alt text</a>. +<a href="https://webaim.org/techniques/alttext/">WebAIM article on appropriate alt text</a>. <h2 id="examples">Examples</h2>
diff --git a/chrome/common/extensions/docs/templates/articles/about_apps.html b/chrome/common/extensions/docs/templates/articles/about_apps.html index fbf9a49..95776c4 100644 --- a/chrome/common/extensions/docs/templates/articles/about_apps.html +++ b/chrome/common/extensions/docs/templates/articles/about_apps.html
@@ -13,7 +13,7 @@ <p>By building a Chrome App, as opposed to going with a traditional web app or a native mobile app, you expand your potential audience and extend your -development capability. +development capability. </p> <h3 id="expand">Expand your potential audience</h3> @@ -38,7 +38,7 @@ <p>Writing a Chrome App is the ONLY way to have your app installed on a Chromebook. Chromebooks are appealing, inexpensive, low maintenance devices -that provide a full web experience.</p> +that provide a full web experience.</p> <h3 id="extend">Extend your development capability</h3> @@ -49,7 +49,7 @@ <ul> <li>Chrome Apps can integrate seamlessly into the desktop and look - more like desktop applications than traditional web apps.</li> + more like desktop applications than traditional web apps.</li> <li>Chrome Apps for Desktop have no omnibox (address bar) and tab strip like normal browser-based apps, because like native desktop apps, they don’t live in a browser.</li> @@ -62,7 +62,7 @@ interface devices).</li> </ul> -<p>The best way to see what Chrome Apps look like is to +<p>The best way to see what Chrome Apps look like is to <a href="https://chrome.google.com/webstore/category/apps?_feature=chromeapp"> install some</a>.</p> @@ -83,15 +83,15 @@ applications that run natively on Chromebooks. For example:</p> <ul> - <li><a href="http://www.google.com/edu/classroom">Google Classroom</a></li> - <li><a href="http://googleenterprise.blogspot.com/2014/08/more-teaching-less-tech-ing-google.html">More + <li><a href="https://classroom.google.com/">Google Classroom</a></li> + <li><a href="https://cloud.googleblog.com/2014/08/more-teaching-less-tech-ing-google.html">More Teaching, Less Tech-ing</a></li> </ul> <strong>Health care providers</strong> -<p>One pediatric service has so far -<a href="http://googleenterprise.blogspot.com/2013/12/pediatric-home-service-puts-patient.html">saved +<p>One pediatric service has so far +<a href="https://cloud.googleblog.com/2013/12/pediatric-home-service-puts-patient.html">saved tens of thousands of dollars</a> using Chrome Apps on Chromebooks.</p> <h2 id="more">Chrome Dev Editor</h2> @@ -102,7 +102,7 @@ <div class="video-container"> <iframe title="YouTube video player" width="610" height="380" - src="//www.youtube.com/embed/NNLnTz6yIc4" frameborder="0" + src="https://www.youtube.com/embed/NNLnTz6yIc4" frameborder="0" allowfullscreen></iframe> </div>
diff --git a/chrome/common/extensions/docs/templates/articles/getstarted.html b/chrome/common/extensions/docs/templates/articles/getstarted.html index 3e1f4b9..9962770f 100644 --- a/chrome/common/extensions/docs/templates/articles/getstarted.html +++ b/chrome/common/extensions/docs/templates/articles/getstarted.html
@@ -470,13 +470,13 @@ } </pre> <p> - Reload the extension and click <strong>DETIALS</strong>. + Reload the extension and click <strong>DETAILS</strong>. </p> -<img src="{{static}}/images/get_started/click_detials.png" +<img src="{{static}}/images/get_started/click_details.png" height="300" alt="Inspect Views" /> <p> - Scroll down the detials page and select <strong>Extension options</strong> + Scroll down the details page and select <strong>Extension options</strong> to view the options page, although it will currently appear blank. </p> @@ -492,7 +492,7 @@ download="options.js">here</a>. </p> <pre data-filename="options.js"> - const kButtonColors = ['#3aa757', '#e8453c', '#f9bb2d', '#4688f1'] + const kButtonColors = ['#3aa757', '#e8453c', '#f9bb2d', '#4688f1']; function constructOptions(kButtonColors) { for (let item of kButtonColors) { let button = document.createElement('button'); @@ -523,7 +523,7 @@ Congratulations! The directory now holds a fully-functional, albeit simplistic, - Chrome Extension. + Chrome extension. </p> <p> What's next? @@ -533,20 +533,20 @@ <li> <p> The <a href="/overview">Chrome Extension Overview</a> backs up a bit, - and fills in a lot of detail about extensions' architecture in general, + and fills in a lot of detail about the Extensions architecture in general, and some specific concepts - extension developers will want to be familiar with. + developers will want to be familiar with. </p> </li> <li> <p> - Learn about the options available for debugging extension in the + Learn about the options available for debugging Extensions in the <a href="/tut_debugging">debugging tutorial</a>. </p> </li> <li> <p> - Chrome extensions have access to powerful APIs above and beyond what's + Chrome Extensions have access to powerful APIs above and beyond what's available on the open web. The <a href="/api_index">chrome.* APIs documentation</a> will walk through each API.
diff --git a/chrome/common/extensions/docs/templates/articles/manifest.html b/chrome/common/extensions/docs/templates/articles/manifest.html index 1cd8ba6..0b5579c2 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest.html +++ b/chrome/common/extensions/docs/templates/articles/manifest.html
@@ -1,7 +1,7 @@ <h1>Manifest File Format</h1> <p> -Every {{platform}} has a <a href="http://www.json.org">JSON</a>-formatted +Every {{platform}} has a <a href="https://www.json.org">JSON</a>-formatted manifest file, named <code>manifest.json</code>, that provides important information. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/app.html b/chrome/common/extensions/docs/templates/articles/manifest/app.html index f5870704..d0a7e27 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/app.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/app.html
@@ -1,7 +1,7 @@ <h1 id="app">Manifest - App</h1> <p> -Used by <a href="http://developer.chrome.com/trunk/apps/app_lifecycle#eventpage">packaged apps</a> +Used by <a href="https://developer.chrome.com/trunk/apps/app_lifecycle#eventpage">packaged apps</a> to specify the app's background scripts. Also used by <a href="https://developers.google.com/chrome/apps/docs/developers_guide#live">hosted apps</a> to specify the URLs that the app uses.
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/default_locale.html b/chrome/common/extensions/docs/templates/articles/manifest/default_locale.html index af43183..14a43797 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/default_locale.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/default_locale.html
@@ -8,5 +8,5 @@ it <b>must be absent</b> in extensions that have no <code>_locales</code> directory. For details, see -<a href="http://developer.chrome.com/extensions/i18n">Internationalization</a>. +<a href="https://developer.chrome.com/extensions/i18n">Internationalization</a>. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/description.html b/chrome/common/extensions/docs/templates/articles/manifest/description.html index 4698fe89..51bde963 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/description.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/description.html
@@ -9,5 +9,5 @@ the browser's extension management UI and the <a href="https://chrome.google.com/webstore">Chrome Web Store</a>. You can specify locale-specific strings for this field; -see <a href="http://developer.chrome.com/extensions/i18n">Internationalization</a> for details. +see <a href="https://developer.chrome.com/extensions/i18n">Internationalization</a> for details. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/homepage_url.html b/chrome/common/extensions/docs/templates/articles/manifest/homepage_url.html index 371e388..79c908a1 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/homepage_url.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/homepage_url.html
@@ -3,7 +3,7 @@ <p> The URL of the homepage for this extension. The extensions management page (chrome://extensions) will contain a link to this URL. This field is particularly useful if you -<a href="http://developer.chrome.com/extensions/hosting">host the extension on your own site</a>. If you distribute your +<a href="https://developer.chrome.com/extensions/hosting">host the extension on your own site</a>. If you distribute your extension using the <a href="https://chrome.google.com/webstore">Chrome Web Store</a>, the homepage URL defaults to the extension's own page. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/icons.html b/chrome/common/extensions/docs/templates/articles/manifest/icons.html index 8fc03a9..8cafc5e 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/icons.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/icons.html
@@ -39,6 +39,6 @@ including at least one screenshot of your extension. For more information, see the -<a href="http://code.google.com/chrome/webstore/">Chrome Web Store +<a href="https://developer.chrome.com/webstore">Chrome Web Store developer documentation</a>. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/key.html b/chrome/common/extensions/docs/templates/articles/manifest/key.html index 1683a43..7ae4cf7 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/key.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/key.html
@@ -10,8 +10,8 @@ <b>Note:</b> You don't usually need to use this value. Instead, write your code so that the key value doesn't matter -by using <a href="http://developer.chrome.com/extensions/overview#relative-urls">relative paths</a> -and <a href="http://developer.chrome.com/extensions/extension#method-getURL">extension.getURL</a>. +by using <a href="https://developer.chrome.com/extensions/overview#relative-urls">relative paths</a> +and <a href="https://developer.chrome.com/extensions/extension#method-getURL">extension.getURL</a>. </p> <p> @@ -19,9 +19,9 @@ install your extension from a <code>.crx</code> file (you may need to <a href="https://chrome.google.com/webstore/developer/dashboard">upload your extension</a> -or <a href="http://developer.chrome.com/extensions/packaging">package it manually</a>). +or <a href="https://developer.chrome.com/extensions/packaging">package it manually</a>). Then, in your -<a href="http://www.chromium.org/user-experience/user-data-directory">user +<a href="https://www.chromium.org/user-experience/user-data-directory">user data directory</a>, look in the file <code>Default/Extensions/<em><extensionId></em>/<em><versionString></em>/manifest.json</code>. You will see the key value filled in there.
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/manifest_version.html b/chrome/common/extensions/docs/templates/articles/manifest/manifest_version.html index 6599501..d8662aa 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/manifest_version.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/manifest_version.html
@@ -21,7 +21,7 @@ <p> The changes between version 1 and version 2 of the manifest file format are -described in detail in <a href="http://developer.chrome.com/extensions/manifestVersion">the +described in detail in <a href="https://developer.chrome.com/extensions/manifestVersion">the <code>manifest_version</code> documentation.</a> </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/minimum_chrome_version.html b/chrome/common/extensions/docs/templates/articles/manifest/minimum_chrome_version.html index c6d28ba..4c1e8c4 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/minimum_chrome_version.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/minimum_chrome_version.html
@@ -3,4 +3,4 @@ <p> The version of Chrome that your extension, app, or theme requires, if any. The format for this string is the same as for the -<a href="http://developer.chrome.com/extensions/manifest#version">version</a> field. +<a href="https://developer.chrome.com/extensions/manifest#version">version</a> field.
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/nacl_modules.html b/chrome/common/extensions/docs/templates/articles/manifest/nacl_modules.html index 4382290..e089eae9 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/nacl_modules.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/nacl_modules.html
@@ -25,7 +25,7 @@ (a <code>.nmf</code> file) within the extension directory. For more information on Native Client and <code>.nmf</code> files, see the -<a href="http://code.google.com/chrome/nativeclient/docs/technical_overview.html">Native Client Technical Overview</a>. +<a href="https://developer.chrome.com/native-client/overview?csw=1">Native Client Technical Overview</a>. </p> <p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/name.html b/chrome/common/extensions/docs/templates/articles/manifest/name.html index f2781f30..49bd6d4 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/name.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/name.html
@@ -4,7 +4,7 @@ The <code>name</code> and <code>short_name</code> manifest properties are short, plain text strings that identify the {{platform}}. You can specify locale-specific strings for both fields; see -<a href="http://developer.chrome.com/extensions/i18n">Internationalization</a> +<a href="https://developer.chrome.com/extensions/i18n">Internationalization</a> for details. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/requirements.html b/chrome/common/extensions/docs/templates/articles/manifest/requirements.html index 89c3596d..41feeff 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/requirements.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/requirements.html
@@ -12,7 +12,7 @@ <p> The "3D" requirement denotes GPU hardware acceleration. The "webgl" requirement refers to the -<a href="http://www.khronos.org/webgl/">WebGL API</a>. +<a href="https://www.khronos.org/webgl/">WebGL API</a>. For more information on Chrome 3D graphics support, see the help article on <a href="https://support.google.com/chrome/answer/1220892">WebGL and 3D graphics</a>. @@ -29,7 +29,7 @@ </pre> <p class="warning"> -NPAPI Plugin support for extension has been <a href="http://blog.chromium.org/2013/09/saying-goodbye-to-our-old-friend-npapi.html">discontinued</a>. As part of this, the <b>"plugins"</b> requirement described below has been deprecated. +NPAPI Plugin support for extension has been <a href="https://blog.chromium.org/2013/09/saying-goodbye-to-our-old-friend-npapi.html">discontinued</a>. As part of this, the <b>"plugins"</b> requirement described below has been deprecated. </p> <p> @@ -37,7 +37,7 @@ if an app or extension requires NPAPI to run. This requirement is enabled by default when the manifest includes the -<a href="http://developer.chrome.com/extensions/npapi">"plugins" field</a>. +<a href="https://developer.chrome.com/extensions/npapi">"plugins" field</a>. For apps and extensions that still work when plugins aren't available, you can disable this requirement by setting NPAPI to false.
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/sandbox.html b/chrome/common/extensions/docs/templates/articles/manifest/sandbox.html index 09810bc..e15263a 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/sandbox.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/sandbox.html
@@ -19,7 +19,7 @@ <code>postMessage()</code>).</li> <li> <p>A sandboxed page is not subject to the - <a href="http://developer.chrome.com/extensions/contentSecurityPolicy">Content Security Policy + <a href="https://developer.chrome.com/extensions/contentSecurityPolicy">Content Security Policy (CSP)</a> used by the rest of the app or extension (it has its own separate CSP value). This means that, for example, it can use inline script and <code>eval</code>.</p> @@ -42,7 +42,7 @@ ... } </pre> - + <p> If not specified, the default <code>content_security_policy</code> value is <code>sandbox allow-scripts allow-forms allow-popups allow-modals; @@ -50,7 +50,7 @@ You can specify your CSP value to restrict the sandbox even further, but it must have the <code>sandbox</code> directive and may not have the <code>allow-same-origin</code> token (see - <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox">the + <a href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-sandbox">the HTML5 specification</a> for possible sandbox tokens). Also, the CSP you specify may not allow loading external web content inside sandboxed pages. </p> @@ -66,14 +66,14 @@ </p> <p> -<a href="http://developer.chrome.com/extensions/sandboxingEval">"Using eval in Chrome Extensions. Safely."</a> +<a href="https://developer.chrome.com/extensions/sandboxingEval">"Using eval in Chrome Extensions. Safely."</a> goes into more detail about implementing a sandboxing workflow that enables use of libraries that would otherwise have issues executing under extension's -<a href="http://developer.chrome.com/extensions/contentSecurityPolicy">default Content Security +<a href="https://developer.chrome.com/extensions/contentSecurityPolicy">default Content Security Policy</a>. </p> <p> Sandboxed page may only be specified when using -<a href="http://developer.chrome.com/extensions/manifest#manifest_version"><code>manifest_version</code></a> 2 or above. +<a href="https://developer.chrome.com/extensions/manifest#manifest_version"><code>manifest_version</code></a> 2 or above. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/storage.html b/chrome/common/extensions/docs/templates/articles/manifest/storage.html index d08e125..f8baec9a 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/storage.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/storage.html
@@ -3,7 +3,7 @@ <p> Unlike the <code>local</code> and <code>sync</code> storage areas, the <code>managed</code> storage area requires its structure to be declared as -<a href="http://tools.ietf.org/html/draft-zyp-json-schema-03">JSON Schema</a> +<a href="https://tools.ietf.org/html/draft-zyp-json-schema-03">JSON Schema</a> and is strictly validated by Chrome. This schema must be stored in a file indicated by the <code>"managed_schema"</code> property of the <code>"storage"</code> manifest key and declares the enterprise @@ -14,7 +14,7 @@ Policies are analogous to options but are configured by a system administrator instead of the user, allowing the {{platform}} to be preconfigured for all users of an organization. See -<a href="http://www.chromium.org/administrators/">how Chrome handles policies</a> +<a href="https://www.chromium.org/administrators/">how Chrome handles policies</a> for examples from Chrome itself. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/version.html b/chrome/common/extensions/docs/templates/articles/manifest/version.html index 65d5f52..51ddc14 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/version.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/version.html
@@ -44,7 +44,7 @@ <p> For more information, see -<a href="http://developer.chrome.com/extensions/autoupdate">Autoupdating</a>. +<a href="https://developer.chrome.com/extensions/autoupdate">Autoupdating</a>. </p> <h2 id="version_name">Version Name</h2>
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/web_accessible_resources.html b/chrome/common/extensions/docs/templates/articles/manifest/web_accessible_resources.html index 418d7b84..e28a3b8 100644 --- a/chrome/common/extensions/docs/templates/articles/manifest/web_accessible_resources.html +++ b/chrome/common/extensions/docs/templates/articles/manifest/web_accessible_resources.html
@@ -27,8 +27,8 @@ <p> These resources would then be available in a webpage via the URL <code>chrome-extension://[PACKAGE ID]/[PATH]</code>, which can be generated with -the <a href="http://developer.chrome.com/extensions/extension#method-getURL">extension.getURL</a> method. Whitelisted resources are served with appropriate -<a href="http://www.w3.org/TR/cors/">CORS</a> headers, so they're available via +the <a href="https://developer.chrome.com/extensions/extension#method-getURL">extension.getURL</a> method. Whitelisted resources are served with appropriate +<a href="https://www.w3.org/TR/cors/">CORS</a> headers, so they're available via mechanisms like XHR. </p> @@ -45,15 +45,15 @@ </ul> <p> -<a href="http://developer.chrome.com/extensions/content_scripts">Content scripts</a> themselves do not need to be whitelisted. +<a href="https://developer.chrome.com/extensions/content_scripts">Content scripts</a> themselves do not need to be whitelisted. </p> <p> Prior to manifest version 2 all resources within an extension could be accessed from any page on the web. This allowed a malicious website to -<a href="http://en.wikipedia.org/wiki/Device_fingerprint">fingerprint</a> the +<a href="https://en.wikipedia.org/wiki/Device_fingerprint">fingerprint</a> the extensions that a user has installed or exploit vulnerabilities (for example -<a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS bugs</a>) within +<a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS bugs</a>) within installed extensions. Limiting availability to only resources which are explicitly intended to be web accessible serves to both minimize the available attack surface and protect the privacy of users. @@ -62,7 +62,7 @@ <h2 id="availability">Default Availability</h2> <p> -Resources inside of packages using <a href="http://developer.chrome.com/extensions/manifest#manifest_version"><code>manifest_version</code></a> +Resources inside of packages using <a href="https://developer.chrome.com/extensions/manifest#manifest_version"><code>manifest_version</code></a> 2 or above are <strong>blocked by default</strong>, and must be whitelisted for use via this property. </p>
diff --git a/chrome/common/extensions/docs/templates/articles/migration.html b/chrome/common/extensions/docs/templates/articles/migration.html index 5e0257c8..de807da 100644 --- a/chrome/common/extensions/docs/templates/articles/migration.html +++ b/chrome/common/extensions/docs/templates/articles/migration.html
@@ -3,7 +3,7 @@ <p>Chrome packaged and hosted apps will be discontinued on Windows, Mac, and Linux over the course of now and early 2018. For more information, refer to the August 2016 -<a href="http://blog.chromium.org/2016/08/from-chrome-apps-to-web.html">Chromium blog post</a>. +<a href="https://blog.chromium.org/2016/08/from-chrome-apps-to-web.html">Chromium blog post</a>. This transition does not apply to Chrome OS, where Chrome packaged and hosted apps will remain supported and maintained for the foreseeable future.</p>
diff --git a/chrome/common/extensions/docs/templates/articles/nativeMessaging.html b/chrome/common/extensions/docs/templates/articles/nativeMessaging.html index 3a1f94d..127d37e2 100644 --- a/chrome/common/extensions/docs/templates/articles/nativeMessaging.html +++ b/chrome/common/extensions/docs/templates/articles/nativeMessaging.html
@@ -268,7 +268,7 @@ which corrupts the message format as line breaks (<code>\n</code> = <code>0A</code>) are replaced with Windows-style line endings (<code>\r\n</code> = <code>0D 0A</code>). The I/O mode can be set using - <a href="http://msdn.microsoft.com/en-us/library/tw4k6df8.aspx"><code>__setmode</code></a>. + <a href="https://msdn.microsoft.com/en-us/library/tw4k6df8.aspx"><code>__setmode</code></a>. </ul> </dl>
diff --git a/chrome/common/extensions/docs/templates/articles/npapi.html b/chrome/common/extensions/docs/templates/articles/npapi.html index 7fce0fb..b12e96e 100644 --- a/chrome/common/extensions/docs/templates/articles/npapi.html +++ b/chrome/common/extensions/docs/templates/articles/npapi.html
@@ -1,7 +1,7 @@ <h1>NPAPI Plugins</h1> <p class="warning"> - NPAPI Plugin support for extension has been <a href="http://blog.chromium.org/2013/09/saying-goodbye-to-our-old-friend-npapi.html">discontinued</a>. The documentation below is preserved for historical purposes only. + NPAPI Plugin support for extension has been <a href="https://blog.chromium.org/2013/09/saying-goodbye-to-our-old-friend-npapi.html">discontinued</a>. The documentation below is preserved for historical purposes only. </p> <p>
diff --git a/chrome/common/extensions/docs/templates/articles/offline_storage.html b/chrome/common/extensions/docs/templates/articles/offline_storage.html index de022a3..225f9a2 100644 --- a/chrome/common/extensions/docs/templates/articles/offline_storage.html +++ b/chrome/common/extensions/docs/templates/articles/offline_storage.html
@@ -13,14 +13,14 @@ <li><a href="#table">Comparing Storage Types</a></li> </ol> </li> - + <li><a href="#managing_quota">Managing your quota</a> <ol> <li><a href="#query">Query storage usage and availability</a></li> <li><a href="#asking_more">Ask for more storage</a></li> <li><a href="#reset">Reset quota for testing</a></li> </ol> - + </li> <li><a href="#reference">API reference</a> <ol> @@ -28,7 +28,7 @@ <li><a href="#method_overview">Method overview</a></li> <li><a href="#methods">Methods</a></li> </ol> - + </li> <li><a href="#future">Future development</a></li> </ol> @@ -57,8 +57,8 @@ <p>An application can have a larger quota for persistent storage than temporary storage, but you must request storage using the Quota Management API and the user must grant you permission to use more space. Chrome presents an info bar that prompts the user to grant the app more local storage space.</p> <h3 id="unlimited">Unlimited storage</h3> -<p>Unlimited storage is similar to persistent storage, but it is available only to Chrome apps and extensions (.crx files). The size of unlimited storage is limited only by the availability of space in the user's hard drive. You can ask for the <code>unlimitedStorage</code> permission in the manifest file for an <a href="/chrome/apps/docs/developers_guide#manifest">app</a> or <a href="http://code.google.com/chrome/extensions/manifest.html#permissions">extension</a>. At installation, the user is informed of permissions required by the app or extension. By proceeding with the installation, the user implicitly grants permission for all pages whose URLs are listed in the manifest.json file.</p> -<p>To learn more, see the respective developer guides for <a href="/chrome/apps/docs/developers_guide#manifest">apps</a> and <a href="http://code.google.com/chrome/extensions/manifest.html">extensions</a>. </p> +<p>Unlimited storage is similar to persistent storage, but it is available only to Chrome apps and extensions (.crx files). The size of unlimited storage is limited only by the availability of space in the user's hard drive. You can ask for the <code>unlimitedStorage</code> permission in the manifest file for an <a href="/chrome/apps/docs/developers_guide#manifest">app</a> or <a href="https://developer.chrome.com/extensions/manifest#permissions">extension</a>. At installation, the user is informed of permissions required by the app or extension. By proceeding with the installation, the user implicitly grants permission for all pages whose URLs are listed in the manifest.json file.</p> +<p>To learn more, see the respective developer guides for <a href="/chrome/apps/docs/developers_guide#manifest">apps</a> and <a href="https://developer.chrome.com/extensions/manifest">extensions</a>. </p> <p class="backtotop"><a href="#top">Back to top</a></p> @@ -83,22 +83,22 @@ <td><strong>Availability</strong></td> <td><p>All web apps.</p></td> <td>All web apps.</td> - <td>Unique to <a href="http://code.google.com/chrome/extensions/index.html">Chrome extensions</a> as well as hosted and installed <a href="http://code.google.com/chrome/apps/index.html">web apps</a>.</td> + <td>Unique to <a href="https://developer.chrome.com/extensions">Chrome extensions</a> as well as hosted and installed <a href="https://developer.chrome.com/apps/">web apps</a>.</td> </tr> <tr> <td><strong>Permission</strong></td> <td>None. You can use it without explicitly requesting it. </td> <td><p>You have to request more storage using the Quota Management API. </p></td> - <td>You can ask for the <code>unlimitedStorage</code> permission in the manifest file for the <a href="/chrome/apps/docs/developers_guide#manifest">app</a> or <a href="http://code.google.com/chrome/extensions/manifest.html#permissions">extension</a>. </td> + <td>You can ask for the <code>unlimitedStorage</code> permission in the manifest file for the <a href="/chrome/apps/docs/developers_guide#manifest">app</a> or <a href="https://developer.chrome.com/extensions/manifest#permissions">extension</a>. </td> </tr> <tr> <td><strong>User experience at first use</strong></td> <td>Invisible to the user. The app just runs.</td> <td><p>Chrome displays an info bar that prompts the user to either accept or decline the storage request. </p> <p>But if the amount of quota you request is actually less than the app's current allocation, no prompt is shown. The larger quota is kept. </p></td> - <td><p>At installation, the user is informed of permissions required by the app or extension. By proceeding with the installation, the user implicitly grants permission for all pages whose URLs are listed in the manifest.json file for <a href="/chrome/apps/docs/developers_guide#manifest">app</a> or <a href="http://code.google.com/chrome/extensions/manifest.html">extension</a>. </p></td> + <td><p>At installation, the user is informed of permissions required by the app or extension. By proceeding with the installation, the user implicitly grants permission for all pages whose URLs are listed in the manifest.json file for <a href="/chrome/apps/docs/developers_guide#manifest">app</a> or <a href="https://developer.chrome.com/extensions/manifest">extension</a>. </p></td> </tr> - + <tr> <td><strong>User experience at subsequent requests for increased storage </strong></td> <td>Not applicable. You cannot ask for more temporary storage.</td> @@ -141,7 +141,7 @@ <li>App Cache </li> <li> File System</li> <li> IndexedDB </li> - <li> WebSQL (<a href="http://www.w3.org/TR/webdatabase/">deprecated</a> since November 18, 2010) </li> + <li> WebSQL (<a href="https://www.w3.org/TR/webdatabase/">deprecated</a> since November 18, 2010) </li> </ul> {% comment %} <p class="note"><strong>Note:</strong> Web storage APIs like LocalStorage and SessionStorage remain fixed at 5 MB. </p> @@ -153,7 +153,7 @@ <li>App Cache </li> <li> File System</li> <li> IndexedDB </li> - <li> WebSQL (deprecated) </li> + <li> WebSQL (deprecated) </li> </ul> {% comment %} <p class="note"><strong>Note:</strong> Web storage APIs like LocalStorage and SessionStorage remain fixed at 5 MB.</p> @@ -185,10 +185,10 @@ <pre class="prettyprint"> // Request storage usage and capacity left // Choose either Temporary or Persistent -navigator.webkitTemporaryStorage.queryUsageAndQuota ( - function(usedBytes, grantedBytes) { +navigator.webkitTemporaryStorage.queryUsageAndQuota ( + function(usedBytes, grantedBytes) { console.log('we are using ', usedBytes, ' of ', grantedBytes, 'bytes'); - }, + }, function(e) { console.log('Error', e); } ); </pre> @@ -212,11 +212,11 @@ </ul> <p>The following shows how you can ask for more storage space:</p> <pre class="prettyprint"> -// Request Quota (only for File System API) </span> +// Request Quota (only for File System API) </span> var requestedBytes = 1024*1024*10; // 10MB navigator.webkitPersistentStorage.requestQuota ( - requestedBytes, function(grantedBytes) { + requestedBytes, function(grantedBytes) { window.requestFileSystem(PERSISTENT, grantedBytes, onInitFs, errorHandler); }, function(e) { console.log('Error', e); } @@ -307,7 +307,7 @@ <h5>Parameters</h5> <dl> - + <dt>newQuotaInBytes</dt> <dd>The amount of bytes you want in your storage quota. </dd> </dl>
diff --git a/chrome/common/extensions/docs/templates/articles/security.html b/chrome/common/extensions/docs/templates/articles/security.html index 5a106bbd..86d7a56 100644 --- a/chrome/common/extensions/docs/templates/articles/security.html +++ b/chrome/common/extensions/docs/templates/articles/security.html
@@ -193,7 +193,7 @@ </p> <pre data-filename="background.js"> var xhr = new XMLHttpRequest(); - xhr.open("GET", "http://api.example.com/data.json", true); + xhr.open("GET", "https://api.example.com/data.json", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // WARNING! Might be evaluating an evil script!
diff --git a/chrome/common/extensions/docs/templates/articles/tutorials.html b/chrome/common/extensions/docs/templates/articles/tutorials.html index 171e4ef..e12752d 100644 --- a/chrome/common/extensions/docs/templates/articles/tutorials.html +++ b/chrome/common/extensions/docs/templates/articles/tutorials.html
@@ -21,7 +21,7 @@ </li> <li> <a href="tut_oauth">OAuth</a> - Learn how your extension can use APIs - (such as <a href="http://code.google.com/apis/gdata/">Google Data APIs</a>) + (such as <a href="https://code.google.com/apis/gdata/">Google Data APIs</a>) that support OAuth. This tutorial features an OAuth library that you can reuse in your extension.
diff --git a/chrome/common/extensions/docs/templates/articles/user_privacy.html b/chrome/common/extensions/docs/templates/articles/user_privacy.html index 08b51ae16..59e69be 100644 --- a/chrome/common/extensions/docs/templates/articles/user_privacy.html +++ b/chrome/common/extensions/docs/templates/articles/user_privacy.html
@@ -98,7 +98,7 @@ // click handler. chrome.permissions.request({ permissions: ['tabs'], - origins: ['http://www.google.com/'] + origins: ['https://www.google.com/'] }, function(granted) { // The callback argument will be true if the user granted the permissions. if (granted) {
diff --git a/chrome/common/extensions/docs/templates/articles/xhr.html b/chrome/common/extensions/docs/templates/articles/xhr.html index f5270b5..d36b780 100644 --- a/chrome/common/extensions/docs/templates/articles/xhr.html +++ b/chrome/common/extensions/docs/templates/articles/xhr.html
@@ -3,10 +3,10 @@ <p id="classSummary"> Regular web pages can use the -<a href="http://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest</a> +<a href="https://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest</a> object to send and receive data from remote servers, but they're limited by the -<a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a>. +<a href="https://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a>. Extensions aren't so limited. An extension can talk to remote servers outside of its origin, as long as it first requests cross-origin permissions.</p> @@ -27,7 +27,7 @@ </pre> <p>If the extension attempts to use a security origin other than itself, -say http://www.google.com, +say https://www.google.com, the browser disallows it unless the extension has requested the appropriate cross-origin permissions. </p> @@ -44,7 +44,7 @@ "name": "My extension", ... <b>"permissions": [ - "http://www.google.com/" + "https://www.google.com/" ]</b>, ... } @@ -54,19 +54,19 @@ like these:</p> <ul> - <li> "http://www.google.com/" </li> - <li> "http://www.gmail.com/" </li> + <li> "https://www.google.com/" </li> + <li> "https://www.gmail.com/" </li> </ul> <p>Or they can be match patterns, like these:</p> <ul> - <li> "http://*.google.com/" </li> - <li> "http://*/" </li> + <li> "https://*.google.com/" </li> + <li> "https://*/" </li> </ul> <p> -A match pattern of "http://*/" allows HTTP access to all reachable domains. +A match pattern of "https://*/" allows HTTPS access to all reachable domains. Note that here, match patterns are similar to <a href="match_patterns">content script match patterns</a>, @@ -88,12 +88,12 @@ <p> When using resources retrieved via XMLHttpRequest, your background page should be careful not to fall victim to <a -href="http://en.wikipedia.org/wiki/Cross-site_scripting">cross-site +href="https://en.wikipedia.org/wiki/Cross-site_scripting">cross-site scripting</a>. Specifically, avoid using dangerous APIs such as the below: </p> <pre data-filename="background.js"> var xhr = new XMLHttpRequest(); -xhr.open("GET", "http://api.example.com/data.json", true); +xhr.open("GET", "https://api.example.com/data.json", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // WARNING! Might be evaluating an evil script! @@ -105,7 +105,7 @@ </pre> <pre data-filename="background.js"> var xhr = new XMLHttpRequest(); -xhr.open("GET", "http://api.example.com/data.json", true); +xhr.open("GET", "https://api.example.com/data.json", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // WARNING! Might be injecting a malicious script! @@ -120,7 +120,7 @@ </p> <pre data-filename="background.js"> var xhr = new XMLHttpRequest(); -xhr.open("GET", "http://api.example.com/data.json", true); +xhr.open("GET", "https://api.example.com/data.json", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // JSON.parse does not evaluate the attacker's scripts. @@ -131,7 +131,7 @@ </pre> <pre data-filename="background.js"> var xhr = new XMLHttpRequest(); -xhr.open("GET", "http://api.example.com/data.json", true); +xhr.open("GET", "https://api.example.com/data.json", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { // innerText does not let the attacker inject HTML elements. @@ -143,7 +143,7 @@ <p> Additionally, be especially careful of resources retrieved via HTTP. If your extension is used on a hostile network, an network attacker (aka a <a -href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack">"man-in-the-middle"</a>) +href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">"man-in-the-middle"</a>) could modify the response and, potentially, attack your extension. Instead, prefer HTTPS whenever possible. </p>
diff --git a/chrome/common/media_router/discovery/media_sink_service_base.h b/chrome/common/media_router/discovery/media_sink_service_base.h index f8e323cd..4332100 100644 --- a/chrome/common/media_router/discovery/media_sink_service_base.h +++ b/chrome/common/media_router/discovery/media_sink_service_base.h
@@ -44,7 +44,10 @@ // |StartTimer()| is called. void RestartTimer(); - // Sorted sinks from current round of discovery, keyed by sink ID. + // Sorted sinks from current round of discovery, keyed by sink ID. Subclasses + // may make modifications to this map while discovery is active. At the + // completion of the current round of discovery, |current_sinks_| will be + // copied over to |mrp_sinks_|, which will be used for |GetSink()| calls. base::flat_map<std::string, MediaSinkInternal> current_sinks_; private:
diff --git a/chrome/common/media_router/mojo/media_router.mojom b/chrome/common/media_router/mojo/media_router.mojom index 8bff4e9..d3f5c97 100644 --- a/chrome/common/media_router/mojo/media_router.mojom +++ b/chrome/common/media_router/mojo/media_router.mojom
@@ -413,7 +413,13 @@ // already a firewall rule for mDNS. EnableMdnsDiscovery(); - // Updates media sinks capable of displaying |media_source|. + // Requests the MediaRouteProvider to update the list of media sinks capable + // of displaying |media_source|. This call is made when a user gesture is + // detected, so MediaRouteProvider may treat this as a signal to take actions + // to become more responsive, e.g., initiate a one-off round of device + // discovery. + // TODO(https://crbug.com/698940): Rename this to OnUserGesture and remove the + // |media_source| parameter. UpdateMediaSinks(string media_source); // Indicates that the Media Router is interested in finding a sink that
diff --git a/chrome/common/origin_trials/OWNERS b/chrome/common/origin_trials/OWNERS index 0d67a41..73686a7 100644 --- a/chrome/common/origin_trials/OWNERS +++ b/chrome/common/origin_trials/OWNERS
@@ -1 +1 @@ -file://content/common/origin_trials/OWNERS +file://third_party/blink/common/origin_trials/OWNERS
diff --git a/chrome/common/origin_trials/chrome_origin_trial_policy.cc b/chrome/common/origin_trials/chrome_origin_trial_policy.cc index 3dcc096..2707d3fe 100644 --- a/chrome/common/origin_trials/chrome_origin_trial_policy.cc +++ b/chrome/common/origin_trials/chrome_origin_trial_policy.cc
@@ -10,8 +10,11 @@ #include "base/base64.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "base/strings/string_split.h" #include "chrome/common/chrome_switches.h" +#include "content/public/common/content_features.h" +#include "content/public/common/origin_util.h" // This is the default public key used for validating signatures. // TODO(iclelland): Provide a mechanism to allow for multiple signing keys. @@ -48,6 +51,10 @@ ChromeOriginTrialPolicy::~ChromeOriginTrialPolicy() {} +bool ChromeOriginTrialPolicy::IsOriginTrialsSupported() const { + return base::FeatureList::IsEnabled(features::kOriginTrials); +} + base::StringPiece ChromeOriginTrialPolicy::GetPublicKey() const { return base::StringPiece(public_key_); } @@ -62,6 +69,10 @@ return disabled_tokens_.count(token_signature.as_string()) > 0; } +bool ChromeOriginTrialPolicy::IsOriginSecure(const GURL& url) const { + return content::IsOriginSecure(url); +} + bool ChromeOriginTrialPolicy::SetPublicKeyFromASCIIString( const std::string& ascii_public_key) { // Base64-decode the incoming string. Set the key if it is correctly formatted
diff --git a/chrome/common/origin_trials/chrome_origin_trial_policy.h b/chrome/common/origin_trials/chrome_origin_trial_policy.h index 10bbd3f..ebb892e 100644 --- a/chrome/common/origin_trials/chrome_origin_trial_policy.h +++ b/chrome/common/origin_trials/chrome_origin_trial_policy.h
@@ -10,19 +10,21 @@ #include "base/macros.h" #include "base/strings/string_piece.h" -#include "content/public/common/origin_trial_policy.h" +#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h" // This class is instantiated on the main/ui thread, but its methods can be // accessed from any thread. -class ChromeOriginTrialPolicy : public content::OriginTrialPolicy { +class ChromeOriginTrialPolicy : public blink::OriginTrialPolicy { public: ChromeOriginTrialPolicy(); ~ChromeOriginTrialPolicy() override; - // OriginTrialPolicy interface + // blink::OriginTrialPolicy interface + bool IsOriginTrialsSupported() const override; base::StringPiece GetPublicKey() const override; bool IsFeatureDisabled(base::StringPiece feature) const override; bool IsTokenDisabled(base::StringPiece token_signature) const override; + bool IsOriginSecure(const GURL& url) const override; bool SetPublicKeyFromASCIIString(const std::string& ascii_public_key); bool SetDisabledFeatures(const std::string& disabled_feature_list);
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index d7d6eced..7dbcfbee 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -895,11 +895,6 @@ // (showing developer packing tools and extensions details) const char kExtensionsUIDeveloperMode[] = "extensions.ui.developer_mode"; -// Boolean pref which indicates whether the Chrome Apps & Extensions Developer -// Tool promotion has been dismissed by the user. -const char kExtensionsUIDismissedADTPromo[] = - "extensions.ui.dismissed_adt_promo"; - // Dictionary pref that tracks which command belongs to which // extension + named command pair. const char kExtensionCommands[] = "extensions.commands";
diff --git a/chrome/common/thread_profiler.cc b/chrome/common/thread_profiler.cc index 98e855f2..7f1f860 100644 --- a/chrome/common/thread_profiler.cc +++ b/chrome/common/thread_profiler.cc
@@ -184,10 +184,6 @@ metrics::CallStackProfileParams::Thread thread, scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner) : owning_thread_task_runner_(owning_thread_task_runner), - startup_profile_params_(GetProcess(), - thread, - metrics::CallStackProfileParams::PROCESS_STARTUP, - metrics::CallStackProfileParams::MAY_SHUFFLE), periodic_profile_params_( GetProcess(), thread, @@ -200,7 +196,10 @@ startup_profiler_ = std::make_unique<base::StackSamplingProfiler>( base::PlatformThread::CurrentId(), GetSamplingParams(), BindRepeating(&ThreadProfiler::ReceiveStartupProfiles, - GetReceiverCallback(&startup_profile_params_))); + GetReceiverCallback(metrics::CallStackProfileParams( + GetProcess(), thread, + metrics::CallStackProfileParams::PROCESS_STARTUP, + metrics::CallStackProfileParams::MAY_SHUFFLE)))); startup_profiler_->Start(); const base::StackSamplingProfiler::SamplingParams& sampling_params = @@ -224,8 +223,7 @@ base::StackSamplingProfiler::CompletedCallback ThreadProfiler::GetReceiverCallback( - metrics::CallStackProfileParams* profile_params) { - profile_params->start_timestamp = base::TimeTicks::Now(); + const metrics::CallStackProfileParams& profile_params) { // TODO(wittman): Simplify the approach to getting the profiler callback // across CallStackProfileMetricsProvider and // ChildCallStackProfileCollector. Ultimately both should expose functions @@ -238,26 +236,25 @@ // // and this function should bind the passed profile_params and // base::TimeTicks::Now() to those functions. + base::TimeTicks profile_start_time = base::TimeTicks::Now(); if (GetProcess() == metrics::CallStackProfileParams::BROWSER_PROCESS) { return metrics::CallStackProfileMetricsProvider:: GetProfilerCallbackForBrowserProcess(profile_params); } return g_child_call_stack_profile_collector.Get() - .ChildCallStackProfileCollector::GetProfilerCallback(*profile_params); + .ChildCallStackProfileCollector::GetProfilerCallback(profile_params, + profile_start_time); } // static -base::Optional<base::StackSamplingProfiler::SamplingParams> -ThreadProfiler::ReceiveStartupProfiles( +void ThreadProfiler::ReceiveStartupProfiles( const base::StackSamplingProfiler::CompletedCallback& receiver_callback, base::StackSamplingProfiler::CallStackProfiles profiles) { receiver_callback.Run(std::move(profiles)); - return base::Optional<base::StackSamplingProfiler::SamplingParams>(); } // static -base::Optional<base::StackSamplingProfiler::SamplingParams> -ThreadProfiler::ReceivePeriodicProfiles( +void ThreadProfiler::ReceivePeriodicProfiles( const base::StackSamplingProfiler::CompletedCallback& receiver_callback, scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner, base::WeakPtr<ThreadProfiler> thread_profiler, @@ -266,7 +263,6 @@ owning_thread_task_runner->PostTask( FROM_HERE, base::BindOnce(&ThreadProfiler::ScheduleNextPeriodicCollection, thread_profiler)); - return base::Optional<base::StackSamplingProfiler::SamplingParams>(); } void ThreadProfiler::ScheduleNextPeriodicCollection() { @@ -284,7 +280,7 @@ periodic_profiler_ = std::make_unique<base::StackSamplingProfiler>( base::PlatformThread::CurrentId(), GetSamplingParams(), BindRepeating(&ThreadProfiler::ReceivePeriodicProfiles, - GetReceiverCallback(&periodic_profile_params_), + GetReceiverCallback(periodic_profile_params_), owning_thread_task_runner_, weak_factory_.GetWeakPtr())); periodic_profiler_->Start(); }
diff --git a/chrome/common/thread_profiler.h b/chrome/common/thread_profiler.h index fcb1b97f..16cca7a 100644 --- a/chrome/common/thread_profiler.h +++ b/chrome/common/thread_profiler.h
@@ -93,19 +93,17 @@ // Gets the completed callback for the ultimate receiver of the profiles. base::StackSamplingProfiler::CompletedCallback GetReceiverCallback( - metrics::CallStackProfileParams* profile_params); + const metrics::CallStackProfileParams& profile_params); // Receives |profiles| from the StackSamplingProfiler and forwards them on to // the original |receiver_callback|. Note that we must obtain and bind the // original receiver callback prior to the start of collection because the // collection start time is currently recorded when obtaining the callback in // some collection scenarios. The implementation contains a TODO to fix this. - static base::Optional<base::StackSamplingProfiler::SamplingParams> - ReceiveStartupProfiles( + static void ReceiveStartupProfiles( const base::StackSamplingProfiler::CompletedCallback& receiver_callback, base::StackSamplingProfiler::CallStackProfiles profiles); - static base::Optional<base::StackSamplingProfiler::SamplingParams> - ReceivePeriodicProfiles( + static void ReceivePeriodicProfiles( const base::StackSamplingProfiler::CompletedCallback& receiver_callback, scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner, base::WeakPtr<ThreadProfiler> thread_profiler, @@ -121,7 +119,6 @@ // TODO(wittman): Make const after cleaning up the existing continuous // collection support. - metrics::CallStackProfileParams startup_profile_params_; metrics::CallStackProfileParams periodic_profile_params_; std::unique_ptr<base::StackSamplingProfiler> startup_profiler_;
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn index c0a42c1..6ea3100e 100644 --- a/chrome/installer/setup/BUILD.gn +++ b/chrome/installer/setup/BUILD.gn
@@ -39,6 +39,7 @@ "//components/crash/content/app:app", "//components/crash/content/app:crash_export_thunks", "//components/crash/content/app:run_as_crashpad_handler", + "//content/public/common:static_switches", ] libs = [ "netapi32.lib" ] @@ -89,7 +90,6 @@ "//components/crash/core/common", "//components/zucchini:zucchini_io", "//components/zucchini:zucchini_lib", - "//content/public/common:static_switches", "//courgette:courgette_lib", "//rlz:rlz_lib", "//third_party/bspatch",
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc index 1c75653..30a601f 100644 --- a/chrome/installer/setup/install_worker.cc +++ b/chrome/installer/setup/install_worker.cc
@@ -204,46 +204,6 @@ return true; } -// Adds work items to |list| to register a COM server with the OS after deleting -// the old ones, which is used to handle the toast notification activation. -void AddNativeNotificationWorkItems(const InstallerState& installer_state, - const base::FilePath& target_path, - const base::Version& new_version, - WorkItemList* list) { - base::string16 toast_activator_reg_path = - InstallUtil::GetToastActivatorRegistryPath(); - - if (toast_activator_reg_path.empty()) { - LOG(DFATAL) << "Cannot retrieve the toast activator registry path"; - return; - } - - HKEY root = installer_state.root_key(); - - // Delete the old registration before adding in the new key to ensure that the - // COM probe/flush below does its job. Delete both 64-bit and 32-bit keys to - // handle 32-bit -> 64-bit or 64-bit -> 32-bit migration. - list->AddDeleteRegKeyWorkItem(root, toast_activator_reg_path, - KEY_WOW64_32KEY); - - list->AddDeleteRegKeyWorkItem(root, toast_activator_reg_path, - KEY_WOW64_64KEY); - - // Force COM to flush its cache containing the path to the old handler. - list->AddCallbackWorkItem( - base::Bind(&ProbeNotificationActivatorCallback, - install_static::GetToastActivatorClsid())); - - // The path to the exe (in the version directory). - base::FilePath notification_helper = - target_path.AppendASCII(new_version.GetString()) - .Append(kNotificationHelperExe); - - AddNativeNotificationInstallWorkItems(installer_state.root_key(), - notification_helper, - toast_activator_reg_path, list); -} - // This is called when an MSI installation is run. It may be that a user is // attempting to install the MSI on top of a non-MSI managed installation. If // so, try and remove any existing "Add/Remove Programs" entry, as we want the @@ -886,8 +846,9 @@ // We don't have a version check for Win10+ here so that Windows upgrades // work. - AddNativeNotificationWorkItems(installer_state, target_path, new_version, - install_list); + AddNativeNotificationWorkItems( + installer_state.root_key(), + GetNotificationHelperPath(target_path, new_version), install_list); InstallUtil::AddUpdateDowngradeVersionItem(installer_state.system_install(), current_version, new_version, dist, @@ -904,17 +865,43 @@ install_list); } -void AddNativeNotificationInstallWorkItems( +void AddNativeNotificationWorkItems( HKEY root, - const base::FilePath& notification_helper, - const base::string16& toast_activator_reg_path, + const base::FilePath& notification_helper_path, WorkItemList* list) { + if (notification_helper_path.empty()) { + LOG(DFATAL) << "The path to notification_helper.exe is invalid."; + return; + } + + base::string16 toast_activator_reg_path = + InstallUtil::GetToastActivatorRegistryPath(); + + if (toast_activator_reg_path.empty()) { + LOG(DFATAL) << "Cannot retrieve the toast activator registry path"; + return; + } + + // Delete the old registration before adding in the new key to ensure that the + // COM probe/flush below does its job. Delete both 64-bit and 32-bit keys to + // handle 32-bit -> 64-bit or 64-bit -> 32-bit migration. + list->AddDeleteRegKeyWorkItem(root, toast_activator_reg_path, + KEY_WOW64_32KEY); + + list->AddDeleteRegKeyWorkItem(root, toast_activator_reg_path, + KEY_WOW64_64KEY); + + // Force COM to flush its cache containing the path to the old handler. + list->AddCallbackWorkItem( + base::BindRepeating(&ProbeNotificationActivatorCallback, + install_static::GetToastActivatorClsid())); + base::string16 toast_activator_server_path = toast_activator_reg_path + L"\\LocalServer32"; // Command-line featuring the quoted path to the exe. base::string16 command(1, L'"'); - command.append(notification_helper.value()).append(1, L'"'); + command.append(notification_helper_path.value()).append(1, L'"'); list->AddCreateRegKeyWorkItem(root, toast_activator_server_path, WorkItem::kWow64Default); @@ -924,7 +911,7 @@ list->AddSetRegValueWorkItem(root, toast_activator_server_path, WorkItem::kWow64Default, L"ServerExecutable", - notification_helper.value(), true); + notification_helper_path.value(), true); } void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
diff --git a/chrome/installer/setup/install_worker.h b/chrome/installer/setup/install_worker.h index 1b817df..aff102ec 100644 --- a/chrome/installer/setup/install_worker.h +++ b/chrome/installer/setup/install_worker.h
@@ -85,12 +85,11 @@ const base::Version& new_version, WorkItemList* install_list); -// Adds work items to |list| to register a COM server with the OS, which is used -// to handle the toast notification activation. -void AddNativeNotificationInstallWorkItems( +// Adds work items to |list| to register a COM server with the OS after deleting +// the old ones, which is used to handle the toast notification activation. +void AddNativeNotificationWorkItems( HKEY root, - const base::FilePath& notification_helper, - const base::string16& toast_activator_reg_path, + const base::FilePath& notification_helper_path, WorkItemList* list); void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc index b1fff084..72d4bc8 100644 --- a/chrome/installer/setup/setup_util.cc +++ b/chrome/installer/setup/setup_util.cc
@@ -935,4 +935,10 @@ return true; } +base::FilePath GetNotificationHelperPath(const base::FilePath& target_path, + const base::Version& version) { + return target_path.AppendASCII(version.GetString()) + .Append(kNotificationHelperExe); +} + } // namespace installer
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h index 1dde01a..bac3660 100644 --- a/chrome/installer/setup/setup_util.h +++ b/chrome/installer/setup/setup_util.h
@@ -173,6 +173,10 @@ // install modes of the browser (i.e., stable and all three side-by-side modes). bool StoreDMToken(const std::string& token); +// Returns the file path to notification_helper.exe (in |version| directory). +base::FilePath GetNotificationHelperPath(const base::FilePath& target_path, + const base::Version& version); + } // namespace installer #endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
diff --git a/chrome/notification_helper/notification_helper_process_unittest.cc b/chrome/notification_helper/notification_helper_process_unittest.cc index 7236f68..56722440 100644 --- a/chrome/notification_helper/notification_helper_process_unittest.cc +++ b/chrome/notification_helper/notification_helper_process_unittest.cc
@@ -8,21 +8,23 @@ // which additionally tests if chrome.exe can be successfully launched by // notification_helper.exe via the NotificationActivator::Activate function. +#include <memory> + #include <wrl/client.h> #include "base/base_paths.h" #include "base/files/file_path.h" +#include "base/memory/ptr_util.h" #include "base/path_service.h" #include "base/process/process.h" #include "base/process/process_iterator.h" #include "base/strings/string16.h" #include "base/test/test_timeouts.h" -#include "base/win/scoped_winrt_initializer.h" +#include "base/win/scoped_com_initializer.h" #include "base/win/windows_types.h" #include "chrome/install_static/install_util.h" #include "chrome/installer/setup/install_worker.h" #include "chrome/installer/util/install_util.h" -#include "chrome/installer/util/registry_key_backup.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/work_item.h" #include "chrome/installer/util/work_item_list.h" @@ -62,74 +64,56 @@ class NotificationHelperTest : public testing::Test { protected: - NotificationHelperTest() - : toast_activator_reg_path_(InstallUtil::GetToastActivatorRegistryPath()), - root_(HKEY_CURRENT_USER) {} + NotificationHelperTest() : root_(HKEY_CURRENT_USER) {} void SetUp() override { - // Back up the existing registration. - ASSERT_TRUE(backup_.Initialize(root_, toast_activator_reg_path_.c_str(), - WorkItem::kWow64Default)); - ASSERT_NO_FATAL_FAILURE(RegisterServer()); } void TearDown() override { ASSERT_NO_FATAL_FAILURE(UnregisterServer()); - - // Restore the registration. - ASSERT_TRUE(backup_.WriteTo(root_, toast_activator_reg_path_.c_str(), - WorkItem::kWow64Default)); } private: // Registers notification_helper.exe as the server. void RegisterServer() { - std::unique_ptr<WorkItemList> list(WorkItem::CreateWorkItemList()); - - // Delete the old registration to ensure a clean environment for server - // registration. This is okay because we have already backed up the existing - // registration in SetUp(), and will restore it in TearDown(). - list->AddDeleteRegKeyWorkItem(root_, toast_activator_reg_path_, - WorkItem::kWow64Default); + ASSERT_TRUE(scoped_com_initializer_.Succeeded()); // Notification_helper.exe is in the build output directory next to this // test executable, as the test build target has a data_deps dependency on // it. base::FilePath dir_exe; ASSERT_TRUE(PathService::Get(base::DIR_EXE, &dir_exe)); - base::FilePath notification_helper = + base::FilePath notification_helper_path = dir_exe.Append(installer::kNotificationHelperExe); - installer::AddNativeNotificationInstallWorkItems( - root_, notification_helper, toast_activator_reg_path_, list.get()); + work_item_list_ = + base::WrapUnique<WorkItemList>(WorkItem::CreateWorkItemList()); - ASSERT_TRUE(list->Do()); + installer::AddNativeNotificationWorkItems(root_, notification_helper_path, + work_item_list_.get()); + + ASSERT_TRUE(work_item_list_->Do()); } - // Unregisters the server by deleting the registry key installed during the - // test. + // Unregisters the server by rolling back the work item list. void UnregisterServer() { - ASSERT_TRUE(InstallUtil::DeleteRegistryKey(root_, toast_activator_reg_path_, - WorkItem::kWow64Default)); + if (work_item_list_) + work_item_list_->Rollback(); } - // Path to the toast activator registry. - const base::string16 toast_activator_reg_path_; - // Predefined handle to the registry. const HKEY root_; - // Backup of the deleted registry. - RegistryKeyBackup backup_; + // A list of work items on the registry. + std::unique_ptr<WorkItemList> work_item_list_; + + base::win::ScopedCOMInitializer scoped_com_initializer_; DISALLOW_COPY_AND_ASSIGN(NotificationHelperTest); }; TEST_F(NotificationHelperTest, NotificationHelperServerTest) { - base::win::ScopedWinrtInitializer winrt_initializer; - ASSERT_TRUE(winrt_initializer.Succeeded()); - // There isn't a way to directly correlate the notification_helper.exe server // to this test. So we need to hunt for the server.
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS index a402004f..fe4ecb78 100644 --- a/chrome/renderer/DEPS +++ b/chrome/renderer/DEPS
@@ -59,7 +59,6 @@ "+services/service_manager/public/cpp", "+skia", "+storage/common", - "+third_party/blink/public/common", "+third_party/blink/public/mojom", "+third_party/metrics_proto", "-components/spellcheck/browser", # Allow all spellcheck but browser.
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc index c27c0f3..26ecde90 100644 --- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc +++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -857,6 +857,20 @@ standard_actions.push_back( ToString(static_cast<api::automation::ActionType>(action))); } + + // The doDefault action is implied by having a default action verb. + int default_action_verb = + static_cast<int>(ax::mojom::DefaultActionVerb::kNone); + if (node->data().GetIntAttribute( + ax::mojom::IntAttribute::kDefaultActionVerb, + &default_action_verb) && + default_action_verb != + static_cast<int>(ax::mojom::DefaultActionVerb::kNone)) { + standard_actions.push_back( + ToString(static_cast<api::automation::ActionType>( + ax::mojom::Action::kDoDefault))); + } + auto actions_result = v8::Array::New(isolate, standard_actions.size()); for (size_t i = 0; i < standard_actions.size(); i++) { const v8::Maybe<bool>& did_set_value = actions_result->Set(
diff --git a/chrome/services/printing/pdf_to_pwg_raster_converter.cc b/chrome/services/printing/pdf_to_pwg_raster_converter.cc index 0334e588..1182b54 100644 --- a/chrome/services/printing/pdf_to_pwg_raster_converter.cc +++ b/chrome/services/printing/pdf_to_pwg_raster_converter.cc
@@ -19,32 +19,30 @@ namespace { -bool RenderPdfPagesToPwgRaster(base::File pdf_file, - const PdfRenderSettings& settings, - const PwgRasterSettings& bitmap_settings, - base::File bitmap_file) { - base::File::Info info; - if (!pdf_file.GetInfo(&info) || info.size <= 0 || - info.size > std::numeric_limits<int>::max()) - return false; - int data_size = static_cast<int>(info.size); +base::ReadOnlySharedMemoryRegion RenderPdfPagesToPwgRaster( + base::ReadOnlySharedMemoryRegion pdf_region, + const PdfRenderSettings& settings, + const PwgRasterSettings& bitmap_settings) { + base::ReadOnlySharedMemoryRegion invalid_pwg_region; + base::ReadOnlySharedMemoryMapping pdf_mapping = pdf_region.Map(); + if (!pdf_mapping.IsValid()) + return invalid_pwg_region; - std::string data(data_size, 0); - if (pdf_file.Read(0, &data[0], data_size) != data_size) - return false; - + // Get the page count and reserve 64 KB per page in |pwg_data| below. + static constexpr size_t kEstimatedSizePerPage = 64 * 1024; + static constexpr size_t kMaxPageCount = + std::numeric_limits<size_t>::max() / kEstimatedSizePerPage; int total_page_count = 0; - if (!chrome_pdf::GetPDFDocInfo(data.data(), data_size, &total_page_count, - nullptr)) { - return false; + if (!chrome_pdf::GetPDFDocInfo(pdf_mapping.memory(), pdf_mapping.size(), + &total_page_count, nullptr) || + total_page_count <= 0 || + static_cast<size_t>(total_page_count) >= kMaxPageCount) { + return invalid_pwg_region; } - std::string pwg_header = pwg_encoder::PwgEncoder::GetDocumentHeader(); - int bytes_written = - bitmap_file.WriteAtCurrentPos(pwg_header.data(), pwg_header.size()); - if (bytes_written != static_cast<int>(pwg_header.size())) - return false; - + std::string pwg_data; + pwg_data.reserve(total_page_count * kEstimatedSizePerPage); + pwg_data = pwg_encoder::PwgEncoder::GetDocumentHeader(); pwg_encoder::BitmapImage image(settings.area.size(), pwg_encoder::BitmapImage::BGRA); for (int i = 0; i < total_page_count; ++i) { @@ -54,10 +52,11 @@ page_number = total_page_count - 1 - page_number; if (!chrome_pdf::RenderPDFPageToBitmap( - data.data(), data_size, page_number, image.pixel_data(), - image.size().width(), image.size().height(), settings.dpi.width(), - settings.dpi.height(), settings.autorotate, settings.use_color)) { - return false; + pdf_mapping.memory(), pdf_mapping.size(), page_number, + image.pixel_data(), image.size().width(), image.size().height(), + settings.dpi.width(), settings.dpi.height(), settings.autorotate, + settings.use_color)) { + return invalid_pwg_region; } pwg_encoder::PwgHeaderInfo header_info; @@ -93,13 +92,26 @@ std::string pwg_page = pwg_encoder::PwgEncoder::EncodePage(image, header_info); if (pwg_page.empty()) - return false; - bytes_written = - bitmap_file.WriteAtCurrentPos(pwg_page.data(), pwg_page.size()); - if (bytes_written != static_cast<int>(pwg_page.size())) - return false; + return invalid_pwg_region; + pwg_data += pwg_page; } - return true; + + mojo::ScopedSharedBufferHandle pwg_handle = + mojo::SharedBufferHandle::Create(pwg_data.size()); + mojo::ScopedSharedBufferHandle readonly_handle; + if (pwg_handle.is_valid()) { + mojo::ScopedSharedBufferMapping pwg_mapping = + pwg_handle->Map(pwg_data.size()); + if (pwg_mapping) { + memcpy(pwg_mapping.get(), pwg_data.data(), pwg_data.size()); + readonly_handle = + pwg_handle->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY); + } + } + if (!readonly_handle.is_valid()) + return invalid_pwg_region; + + return mojo::UnwrapReadOnlySharedMemoryRegion(std::move(readonly_handle)); } } // namespace @@ -111,31 +123,12 @@ PdfToPwgRasterConverter::~PdfToPwgRasterConverter() {} void PdfToPwgRasterConverter::Convert( - mojo::ScopedHandle pdf_file_in, + base::ReadOnlySharedMemoryRegion pdf_region, const PdfRenderSettings& pdf_settings, const PwgRasterSettings& pwg_raster_settings, - mojo::ScopedHandle pwg_raster_file_out, ConvertCallback callback) { - base::PlatformFile pdf_file; - if (mojo::UnwrapPlatformFile(std::move(pdf_file_in), &pdf_file) != - MOJO_RESULT_OK) { - LOG(ERROR) << "Invalid PDF file passed to PdfToPwgRasterConverter."; - std::move(callback).Run(false); - return; - } - - base::PlatformFile pwg_raster_file; - if (mojo::UnwrapPlatformFile(std::move(pwg_raster_file_out), - &pwg_raster_file) != MOJO_RESULT_OK) { - LOG(ERROR) << "Invalid PWGRaster file passed to PdfToPwgRasterConverter."; - std::move(callback).Run(false); - return; - } - - bool result = RenderPdfPagesToPwgRaster(base::File(pdf_file), pdf_settings, - pwg_raster_settings, - base::File(pwg_raster_file)); - std::move(callback).Run(result); + std::move(callback).Run(RenderPdfPagesToPwgRaster( + std::move(pdf_region), pdf_settings, pwg_raster_settings)); } } // namespace printing
diff --git a/chrome/services/printing/pdf_to_pwg_raster_converter.h b/chrome/services/printing/pdf_to_pwg_raster_converter.h index 67b2d769..8d3849c 100644 --- a/chrome/services/printing/pdf_to_pwg_raster_converter.h +++ b/chrome/services/printing/pdf_to_pwg_raster_converter.h
@@ -25,10 +25,9 @@ private: // printing::mojom::PdfToPwgRasterConverter - void Convert(mojo::ScopedHandle pdf_file_in, + void Convert(base::ReadOnlySharedMemoryRegion pdf_region, const PdfRenderSettings& pdf_settings, const PwgRasterSettings& pwg_raster_settings, - mojo::ScopedHandle pwg_raster_file_out, ConvertCallback callback) override; const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
diff --git a/chrome/services/printing/public/mojom/BUILD.gn b/chrome/services/printing/public/mojom/BUILD.gn index ad55d24..ec5252f 100644 --- a/chrome/services/printing/public/mojom/BUILD.gn +++ b/chrome/services/printing/public/mojom/BUILD.gn
@@ -15,8 +15,11 @@ "//ui/gfx/geometry/mojo", ] + public_deps = [ + "//mojo/public/mojom/base", + ] + if (is_win) { sources += [ "pdf_to_emf_converter.mojom" ] - deps += [ "//mojo/public/mojom/base" ] } }
diff --git a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom index 2ed1a51..5f899308 100644 --- a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom +++ b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom
@@ -5,6 +5,7 @@ module printing.mojom; import "chrome/services/printing/public/mojom/pdf_render_settings.mojom"; +import "mojo/public/mojom/base/shared_memory.mojom"; struct PwgRasterSettings { enum TransformType { @@ -28,8 +29,8 @@ }; interface PdfToPwgRasterConverter { - Convert(handle pdf_file_in, + Convert(mojo_base.mojom.ReadOnlySharedMemoryRegion pdf_region, PdfRenderSettings pdf_settings, - PwgRasterSettings pwg_raster_settings, - handle pwg_raster_file_out) => (bool success); + PwgRasterSettings pwg_raster_settings) + => (mojo_base.mojom.ReadOnlySharedMemoryRegion? pwg_raster_region); };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index af5b7b03..a5215b7b 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2555,6 +2555,7 @@ "../browser/signin/signin_tracker_unittest.cc", "../browser/signin/test_signin_client_builder.cc", "../browser/signin/test_signin_client_builder.h", + "../browser/signin/unified_consent_helper_unittest.cc", "../browser/ssl/certificate_error_report_unittest.cc", "../browser/ssl/certificate_error_reporter_unittest.cc", "../browser/ssl/chrome_expect_ct_reporter_unittest.cc", @@ -3105,7 +3106,6 @@ "../browser/media/router/discovery/dial/dial_app_discovery_service_unittest.cc", "../browser/media/router/discovery/dial/dial_device_data_unittest.cc", "../browser/media/router/discovery/dial/dial_media_sink_service_impl_unittest.cc", - "../browser/media/router/discovery/dial/dial_media_sink_service_unittest.cc", "../browser/media/router/discovery/dial/dial_registry_unittest.cc", "../browser/media/router/discovery/dial/dial_service_unittest.cc", "../browser/media/router/discovery/dial/dial_url_fetcher_unittest.cc", @@ -3182,7 +3182,10 @@ "../browser/notifications/notification_platform_bridge_win_unittest.cc", ] - deps += [ "//chrome/installer/util:with_no_strings" ] + deps += [ + "//chrome/installer/setup:lib", + "//chrome/installer/util:with_no_strings", + ] data_deps += [ "//chrome/notification_helper" ] } @@ -3235,6 +3238,7 @@ "../browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc", "../browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc", "../browser/ui/ash/network/data_promo_notification_unittest.cc", + "../browser/ui/ash/network/network_portal_notification_controller_unittest.cc", "../browser/ui/ash/network/network_state_notifier_unittest.cc", "../browser/ui/ash/network/tether_notification_presenter_unittest.cc", "../browser/ui/ash/session_controller_client_unittest.cc",
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc index 7f1b0c7..2f8f0f5 100644 --- a/chrome/test/base/browser_with_test_window_test.cc +++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -139,7 +139,7 @@ params.tabstrip_index = 0; params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms); - CommitPendingLoad(¶ms.target_contents->GetController()); + CommitPendingLoad(¶ms.navigated_or_inserted_contents->GetController()); } void BrowserWithTestWindowTest::CommitPendingLoad(
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc index 6a596c5..be041aa0 100644 --- a/chrome/test/base/in_process_browser_test.cc +++ b/chrome/test/base/in_process_browser_test.cc
@@ -354,10 +354,12 @@ params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms); - if (check_navigation_success) - content::WaitForLoadStop(params.target_contents); - else - content::WaitForLoadStopWithoutSuccessCheck(params.target_contents); + if (check_navigation_success) { + content::WaitForLoadStop(params.navigated_or_inserted_contents); + } else { + content::WaitForLoadStopWithoutSuccessCheck( + params.navigated_or_inserted_contents); + } } void InProcessBrowserTest::AddTabAtIndex(
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc index a753dc0..0d134a3 100644 --- a/chrome/test/base/ui_test_utils.cc +++ b/chrome/test/base/ui_test_utils.cc
@@ -168,7 +168,7 @@ void NavigateToURL(NavigateParams* params) { Navigate(params); - content::WaitForLoadStop(params->target_contents); + content::WaitForLoadStop(params->navigated_or_inserted_contents); } void NavigateToURLWithPost(Browser* browser, const GURL& url) {
diff --git a/chrome/test/chromedriver/chrome/console_logger.cc b/chrome/test/chromedriver/chrome/console_logger.cc index 0fc78e3..578aa09 100644 --- a/chrome/test/chromedriver/chrome/console_logger.cc +++ b/chrome/test/chromedriver/chrome/console_logger.cc
@@ -21,7 +21,7 @@ // Translates DevTools log level strings into Log::Level. bool ConsoleLevelToLogLevel(const std::string& name, Log::Level *out_level) { - if (name == "verbose") + if (name == "verbose" || name == "debug" || name == "timeEnd") *out_level = Log::kDebug; else if (name == "log" || name == "info") *out_level = Log::kInfo;
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index 07a7ff9..ec90d0c 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -109,8 +109,6 @@ _OS_SPECIFIC_FILTER['linux'] = [ # Xvfb doesn't support maximization. 'ChromeDriverTest.testWindowMaximize', - # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2132 - 'MobileEmulationCapabilityTest.testDeviceMetricsWithDeviceWidth', ] _OS_SPECIFIC_FILTER['mac'] = [ # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1927
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations index 2ba650a..d34c84e 100644 --- a/chrome/test/chromedriver/test/test_expectations +++ b/chrome/test/chromedriver/test/test_expectations
@@ -285,7 +285,7 @@ # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2376 'ImplicitWaitTest.*', # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2401 - 'ObjectStateAssumptionsTest.testinitializedWebDriverDoesNotThrowNPE', + 'JavascriptEnabledDriverTest.testIssue80ClickShouldGenerateClickEvent', ] ) _OS_NEGATIVE_FILTER['android:chrome_beta'] = (
diff --git a/chrome/test/data/webui/multidevice_setup/integration_test.js b/chrome/test/data/webui/multidevice_setup/integration_test.js new file mode 100644 index 0000000..aef8b447 --- /dev/null +++ b/chrome/test/data/webui/multidevice_setup/integration_test.js
@@ -0,0 +1,75 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Suite of tests for button navigation functionality in + * MultiDevice setup WebUI. + */ +cr.define('multidevice_setup', () => { + function registerIntegrationTests() { + suite('MultiDeviceSetup', () => { + /** + * MultiDeviceSetup created before each test. Defined in setUp. + * @type {MultiDeviceSetup|undefined} + */ + let multiDeviceSetupElement; + + /** + * Forward navigation button. Defined in setUp. + * @type {PaperButton|undefined} + */ + let forwardButton; + + /** + * Backward navigation button. Defined in setUp. + * @type {PaperButton|undefined} + */ + let backwardButton; + + const FAILURE = 'setup-failed-page'; + const SUCCESS = 'setup-succeeded-page'; + const START = 'start-setup-page'; + + setup(() => { + multiDeviceSetupElement = document.createElement('multidevice-setup'); + document.body.appendChild(multiDeviceSetupElement); + forwardButton = + multiDeviceSetupElement.$$('button-bar /deep/ #forward'); + backwardButton = + multiDeviceSetupElement.$$('button-bar /deep/ #backward'); + }); + + test('SetupFailedPage forward button goes to start page', done => { + multiDeviceSetupElement.addEventListener( + 'visible-page_-changed', function() { + if (multiDeviceSetupElement.$$('iron-pages > .iron-selected') + .is == START) { + done(); + } + }); + multiDeviceSetupElement.visiblePageName_ = FAILURE; + MockInteractions.tap(forwardButton); + }); + + test('SetupFailedPage backward button closes UI', done => { + multiDeviceSetupElement.addEventListener('ui-closed', () => done()); + multiDeviceSetupElement.visiblePageName_ = FAILURE; + MockInteractions.tap(backwardButton); + }); + + test('SetupSucceededPage forward button closes UI', done => { + multiDeviceSetupElement.visiblePageName_ = SUCCESS; + multiDeviceSetupElement.addEventListener('ui-closed', () => done()); + MockInteractions.tap(forwardButton); + }); + + test('StartSetupPage backward button closes UI', done => { + multiDeviceSetupElement.visiblePageName_ = START; + multiDeviceSetupElement.addEventListener('ui-closed', () => done()); + MockInteractions.tap(backwardButton); + }); + }); + } + return {registerIntegrationTests: registerIntegrationTests}; +});
diff --git a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js index 4ec44bf..42ad8ef 100644 --- a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js +++ b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
@@ -26,12 +26,18 @@ browsePreload: 'chrome://multidevice-setup/', extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ - 'navigation_test.js', + 'integration_test.js', + 'setup_succeeded_page_test.js', ]), }; -TEST_F('MultiDeviceSetupBrowserTest', 'All', function() { - multidevice_setup.registerTests(); +TEST_F('MultiDeviceSetupBrowserTest', 'Integration', function() { + multidevice_setup.registerIntegrationTests(); + mocha.run(); +}); + +TEST_F('MultiDeviceSetupBrowserTest', 'SetupSucceededPage', function() { + multidevice_setup.registerSetupSucceededPageTests(); mocha.run(); });
diff --git a/chrome/test/data/webui/multidevice_setup/navigation_test.js b/chrome/test/data/webui/multidevice_setup/navigation_test.js deleted file mode 100644 index 438bb2b..0000000 --- a/chrome/test/data/webui/multidevice_setup/navigation_test.js +++ /dev/null
@@ -1,72 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Suite of tests for button navigation functionality in - * MultiDevice setup WebUI. - */ -cr.define('multidevice_setup', () => { - function registerTests() { - suite('MultiDeviceSetup', () => { - /** - * MultiDeviceSetup created before each test. - * @type {MultiDeviceSetup} - */ - let multiDeviceSetupElement; - - const FAILURE = 'setup-failed-page'; - const SUCCESS = 'setup-succeeded-page'; - const START = 'start-setup-page'; - - function tapForwardNavigation() { - MockInteractions.tap( - multiDeviceSetupElement.$$('button-bar').$.forward); - } - - function tapBackwardNavigation() { - MockInteractions.tap( - multiDeviceSetupElement.$$('button-bar').$.backward); - } - - setup(() => { - PolymerTest.clearBody(); - multiDeviceSetupElement = document.createElement('multidevice-setup'); - document.body.appendChild(multiDeviceSetupElement); - }); - - test( - 'Check SetupFailedPage forward button goes to start page', - done => { - multiDeviceSetupElement.visiblePageName_ = FAILURE; - multiDeviceSetupElement.addEventListener( - 'visible-page_-changed', function() { - if (multiDeviceSetupElement.visiblePage_ && - multiDeviceSetupElement.visiblePage_.is == START) { - done(); - } - }); - tapForwardNavigation(); - }); - - test('Check SetupFailedPage backward button closes UI', done => { - multiDeviceSetupElement.visiblePageName_ = FAILURE; - multiDeviceSetupElement.addEventListener('ui-closed', () => done()); - tapBackwardNavigation(); - }); - - test('Check SetupSucceededPage forward button closes UI', done => { - multiDeviceSetupElement.visiblePageName_ = SUCCESS; - multiDeviceSetupElement.addEventListener('ui-closed', () => done()); - tapForwardNavigation(); - }); - - test('Check StartSetupPage backward button closes UI', done => { - multiDeviceSetupElement.visiblePageName_ = START; - multiDeviceSetupElement.addEventListener('ui-closed', () => done()); - tapBackwardNavigation(); - }); - }); - } - return {registerTests: registerTests}; -});
diff --git a/chrome/test/data/webui/multidevice_setup/setup_succeeded_page_test.js b/chrome/test/data/webui/multidevice_setup/setup_succeeded_page_test.js new file mode 100644 index 0000000..6e811a5 --- /dev/null +++ b/chrome/test/data/webui/multidevice_setup/setup_succeeded_page_test.js
@@ -0,0 +1,35 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Suite of tests for button navigation functionality in + * MultiDevice setup WebUI. + */ +cr.define('multidevice_setup', () => { + function registerSetupSucceededPageTests() { + suite('MultiDeviceSetup', () => { + /** + * SetupSucceededPage created before each test. Defined in setUp. + * @type {SetupSucceededPage|undefined} + */ + let setupSucceededPageElement; + + const SUCCESS = 'setup-succeeded-page'; + + setup(() => { + setupSucceededPageElement = + document.createElement('setup-succeeded-page'); + document.body.appendChild(setupSucceededPageElement); + }); + + test('Settings link opens settings page', done => { + setupSucceededPageElement.addEventListener( + 'settings-opened', () => done()); + let settingsLink = setupSucceededPageElement.$$('#settings-link'); + MockInteractions.tap(settingsLink); + }); + }); + } + return {registerSetupSucceededPageTests: registerSetupSucceededPageTests}; +});
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js index adf0f424..6d93bd2 100644 --- a/chrome/test/data/webui/print_preview/settings_section_test.js +++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -106,7 +106,7 @@ function toggleMoreSettings() { const moreSettingsElement = page.$$('print-preview-more-settings'); - moreSettingsElement.$$('.label').click(); + moreSettingsElement.$.label.click(); } test(assert(TestNames.Copies), function() { @@ -346,13 +346,13 @@ assertFalse(scalingInputWrapper.hidden); // PDF to non-PDF destination -> checkbox and input shown. Check that if - // more settings is collapsed only the checkbox is shown. + // more settings is collapsed the section is hidden. initDocumentInfo(true, false); toggleMoreSettings(); + assertTrue(scalingElement.hidden); + toggleMoreSettings(); assertFalse(scalingElement.hidden); assertFalse(fitToPageContainer.hidden); - assertTrue(scalingInputWrapper.hidden); - toggleMoreSettings(); assertFalse(scalingInputWrapper.hidden); // PDF to PDF destination -> section disappears. @@ -362,25 +362,20 @@ test(assert(TestNames.Other), function() { const optionsElement = page.$$('print-preview-other-options-settings'); - const headerFooter = optionsElement.$.headerFooterContainer; - const duplex = optionsElement.$.duplexContainer; - const cssBackground = optionsElement.$.cssBackgroundContainer; - const rasterize = optionsElement.$.rasterizeContainer; - const selectionOnly = optionsElement.$.selectionOnlyContainer; + const headerFooter = optionsElement.$.headerFooter.parentElement; + const duplex = optionsElement.$.duplex.parentElement; + const cssBackground = optionsElement.$.cssBackground.parentElement; + const rasterize = optionsElement.$.rasterize.parentElement; + const selectionOnly = optionsElement.$.selectionOnly.parentElement; // Start with HTML + duplex capability. initDocumentInfo(false, false); let capabilities = print_preview_test_utils.getCddTemplate('FooPrinter').capabilities; page.set('destination_.capabilities', capabilities); - assertFalse(optionsElement.hidden); - assertTrue(headerFooter.hidden); - assertFalse(duplex.hidden); - assertTrue(cssBackground.hidden); - assertTrue(rasterize.hidden); - assertTrue(selectionOnly.hidden); + assertTrue(optionsElement.hidden); - // Expanding more settings will show header/footer. + // Expanding more settings will show the section. toggleMoreSettings(); assertFalse(headerFooter.hidden); assertFalse(duplex.hidden); @@ -390,10 +385,7 @@ // Add a selection - should show selection only. initDocumentInfo(false, true); - toggleMoreSettings(); assertFalse(optionsElement.hidden); - assertTrue(selectionOnly.hidden); - toggleMoreSettings(); assertFalse(selectionOnly.hidden); // Remove duplex capability. @@ -402,23 +394,17 @@ delete capabilities.printer.duplex; page.set('destination_.capabilities', capabilities); Polymer.dom.flush(); - toggleMoreSettings(); - assertTrue(optionsElement.hidden); - toggleMoreSettings(); + assertFalse(optionsElement.hidden); assertTrue(duplex.hidden); // PDF initDocumentInfo(true, false); Polymer.dom.flush(); - toggleMoreSettings(); - assertTrue(optionsElement.hidden); - - toggleMoreSettings(); if (cr.isWindows || cr.isMac) { // No options assertTrue(optionsElement.hidden); } else { - // All setions hidden except rasterize + // All sections hidden except rasterize assertTrue(headerFooter.hidden); assertTrue(duplex.hidden); assertTrue(cssBackground.hidden); @@ -442,16 +428,17 @@ test(assert(TestNames.HeaderFooter), function() { const optionsElement = page.$$('print-preview-other-options-settings'); - const headerFooter = optionsElement.$.headerFooterContainer; + const headerFooter = optionsElement.$.headerFooter.parentElement; // HTML page to show Header/Footer option. initDocumentInfo(false, false); let capabilities = print_preview_test_utils.getCddTemplate('FooPrinter').capabilities; page.set('destination_.capabilities', capabilities); - assertFalse(optionsElement.hidden); - assertTrue(headerFooter.hidden); + assertTrue(optionsElement.hidden); + toggleMoreSettings(); + assertFalse(optionsElement.hidden); assertFalse(headerFooter.hidden); // Set margins to NONE
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java index f4b1b9a..5cb2437 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -102,12 +102,6 @@ mAudioManagerState.set(CastAudioManager.getAudioManager(this)); })); - // Request audio focus when Activity is resumed. - mAudioManagerState.watch(ScopeFactories.onEnter((CastAudioManager audioManager) -> { - audioManager.requestAudioFocusWhen( - mResumedState, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); - })); - // Clean up stream mute state on pause events. mAudioManagerState.andThen(Observable.not(mResumedState)) .watch(ScopeFactories.onEnter((CastAudioManager audioManager, Unit u) -> {
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java index cceb66e..25f154a 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
@@ -7,16 +7,13 @@ import android.app.Notification; import android.app.Service; import android.content.Intent; -import android.media.AudioManager; import android.os.IBinder; import android.widget.Toast; import org.chromium.base.Log; import org.chromium.base.annotations.JNINamespace; -import org.chromium.chromecast.base.Both; import org.chromium.chromecast.base.Controller; -import org.chromium.chromecast.base.Observable; -import org.chromium.chromecast.base.Unit; +import org.chromium.content.browser.MediaSessionImpl; import org.chromium.content_public.browser.WebContents; /** @@ -31,19 +28,14 @@ private static final boolean DEBUG = true; private static final int CAST_NOTIFICATION_ID = 100; - private final Controller<Unit> mLifetimeController = new Controller<>(); private final Controller<WebContents> mWebContentsState = new Controller<>(); private String mInstanceId; private CastAudioManager mAudioManager; { - // Construct an Observable that is deactivated when either mWebContentsState or - // mLifetimeController is reset, and has the activation data of mWebContentsState. - Observable<WebContents> hasWebContentsState = - mWebContentsState.and(mLifetimeController).map(Both::getFirst); // React to web contents by presenting them in a headless view. - hasWebContentsState.watch(CastWebContentsView.withoutLayout(this)); - hasWebContentsState.watch(() -> { + mWebContentsState.watch(CastWebContentsView.withoutLayout(this)); + mWebContentsState.watch(() -> { if (DEBUG) Log.d(TAG, "show web contents"); // TODO(thoren): Notification.Builder(Context) is deprecated in O. Use the // (Context, String) constructor when CastWebContentsService starts supporting O. @@ -68,14 +60,14 @@ return; } + MediaSessionImpl.fromWebContents(webContents).requestSystemAudioFocus(); mWebContentsState.set(webContents); } @Override public void onDestroy() { if (DEBUG) Log.d(TAG, "onDestroy"); - - mLifetimeController.reset(); + mWebContentsState.reset(); super.onDestroy(); } @@ -87,9 +79,6 @@ .show(); stopSelf(); } - CastAudioManager.getAudioManager(this).requestAudioFocusWhen( - mLifetimeController, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); - mLifetimeController.set(Unit.unit()); } @Override
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java index 2b4d0203..79e4a9a4 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java
@@ -24,6 +24,7 @@ import org.chromium.chromecast.base.Unit; import org.chromium.content.browser.ActivityContentVideoViewEmbedder; import org.chromium.content.browser.ContentVideoViewEmbedder; +import org.chromium.content.browser.MediaSessionImpl; import org.chromium.content_public.browser.WebContents; /** @@ -56,6 +57,7 @@ private String mInstanceId; private ContentVideoViewEmbedderSetter mContentVideoViewEmbedderSetter; + private MediaSessionGetter mMediaSessionGetter; // TODO(vincentli) interrupt touch event from Fragment's root view when it's false. private boolean mTouchInputEnabled = false; @@ -117,6 +119,9 @@ (WebContents webContents, ContentVideoViewEmbedder embedder) -> nativeSetContentVideoViewEmbedder(webContents, embedder); + mMediaSessionGetter = + (WebContents webContents) -> MediaSessionImpl.fromWebContents(webContents); + // Receive broadcasts indicating the screen turned off while we have active WebContents. mHasUriState.watch((Uri uri) -> { IntentFilter filter = new IntentFilter(); @@ -162,6 +167,10 @@ // webContentsView is responsible for displaying each new WebContents. mWebContentsState.watch(webContentsView); + // Take audio focus when receiving new WebContents. + mWebContentsState.map(webContents -> mMediaSessionGetter.get(webContents)) + .watch(ScopeFactories.onEnter(MediaSessionImpl::requestSystemAudioFocus)); + // Miscellaneous actions responding to WebContents lifecycle. mWebContentsState.watch((WebContents webContents) -> { // Set ContentVideoViewEmbedder to allow video playback. @@ -221,6 +230,15 @@ } @RemovableInRelease + void setMediaSessionGetterForTesting(MediaSessionGetter mediaSessionGetter) { + mMediaSessionGetter = mediaSessionGetter; + } + + interface MediaSessionGetter { + MediaSessionImpl get(WebContents webContents); + } + + @RemovableInRelease void setContentVideoViewEmbedderSetterForTesting(ContentVideoViewEmbedderSetter cvves) { mContentVideoViewEmbedderSetter = cvves; }
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java index 6c97a71..45bb7d0 100644 --- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java +++ b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java
@@ -5,7 +5,6 @@ package org.chromium.chromecast.shell; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.mock; @@ -27,7 +26,6 @@ import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; -import org.robolectric.shadows.ShadowAudioManager; import org.chromium.content_public.browser.WebContents; import org.chromium.testing.local.LocalRobolectricTestRunner; @@ -80,37 +78,6 @@ } @Test - public void testDoesNotRequestAudioFocusBeforeResume() { - ShadowAudioManager shadowAudioManager = Shadows.shadowOf( - CastAudioManager.getAudioManager(RuntimeEnvironment.application).getInternal()); - ShadowAudioManager.AudioFocusRequest originalRequest = - shadowAudioManager.getLastAudioFocusRequest(); - mActivityLifecycle.create().start(); - assertEquals(shadowAudioManager.getLastAudioFocusRequest(), originalRequest); - } - - @Test - public void testRequestsAudioFocusOnResume() { - ShadowAudioManager shadowAudioManager = Shadows.shadowOf( - CastAudioManager.getAudioManager(RuntimeEnvironment.application).getInternal()); - ShadowAudioManager.AudioFocusRequest originalRequest = - shadowAudioManager.getLastAudioFocusRequest(); - mActivityLifecycle.create().start().resume(); - assertNotEquals(shadowAudioManager.getLastAudioFocusRequest(), originalRequest); - } - - @Test - public void testAbandonsAudioFocusOnPause() { - ShadowAudioManager shadowAudioManager = Shadows.shadowOf( - CastAudioManager.getAudioManager(RuntimeEnvironment.application).getInternal()); - mActivityLifecycle.create().start().resume().pause(); - ShadowAudioManager.AudioFocusRequest originalRequest = - shadowAudioManager.getLastAudioFocusRequest(); - assertEquals( - shadowAudioManager.getLastAbandonedAudioFocusListener(), originalRequest.listener); - } - - @Test public void testReleasesStreamMuteIfNecessaryOnPause() { CastAudioManager mockAudioManager = mock(CastAudioManager.class); mActivity.setAudioManagerForTesting(mockAudioManager);
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelperTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelperTest.java index 52ebd9d..55801e1 100644 --- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelperTest.java +++ b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelperTest.java
@@ -35,8 +35,10 @@ import org.chromium.chromecast.base.Scope; import org.chromium.chromecast.base.ScopeFactory; import org.chromium.chromecast.shell.CastWebContentsSurfaceHelper.ContentVideoViewEmbedderSetter; +import org.chromium.chromecast.shell.CastWebContentsSurfaceHelper.MediaSessionGetter; import org.chromium.chromecast.shell.CastWebContentsSurfaceHelper.StartParams; import org.chromium.content.browser.ContentVideoViewEmbedder; +import org.chromium.content.browser.MediaSessionImpl; import org.chromium.content_public.browser.WebContents; import java.util.ArrayList; @@ -53,6 +55,8 @@ private @Mock Consumer<Uri> mFinishCallback; private CastWebContentsSurfaceHelper mSurfaceHelper; private @Mock ContentVideoViewEmbedderSetter mContentVideoViewEmbedderSetter; + private @Mock MediaSessionGetter mMediaSessionGetter; + private @Mock MediaSessionImpl mMediaSessionImpl; private static class StartParamsBuilder { private String mId = "0"; @@ -112,9 +116,12 @@ @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mMediaSessionGetter.get(any())).thenReturn(mMediaSessionImpl); + mSurfaceHelper = new CastWebContentsSurfaceHelper(mActivity, mWebContentsView, mFinishCallback); mSurfaceHelper.setContentVideoViewEmbedderSetterForTesting(mContentVideoViewEmbedderSetter); + mSurfaceHelper.setMediaSessionGetterForTesting(mMediaSessionGetter); } @Test @@ -126,6 +133,14 @@ } @Test + public void testRequestsAudioFocusOnNewStartParams() { + WebContents webContents = mock(WebContents.class); + StartParams params = new StartParamsBuilder().withWebContents(webContents).build(); + mSurfaceHelper.onNewStartParams(params); + verify(mMediaSessionImpl).requestSystemAudioFocus(); + } + + @Test public void testDeactivatesOldWebContentsViewOnNewStartParams() { WebContents webContents1 = mock(WebContents.class); StartParams params1 =
diff --git a/chromecast/browser/extensions/cast_extension_host_delegate.cc b/chromecast/browser/extensions/cast_extension_host_delegate.cc index 16bc7f69..9e20efa 100644 --- a/chromecast/browser/extensions/cast_extension_host_delegate.cc +++ b/chromecast/browser/extensions/cast_extension_host_delegate.cc
@@ -30,11 +30,12 @@ return nullptr; } -void CastExtensionHostDelegate::CreateTab(content::WebContents* web_contents, - const std::string& extension_id, - WindowOpenDisposition disposition, - const gfx::Rect& initial_rect, - bool user_gesture) { +void CastExtensionHostDelegate::CreateTab( + std::unique_ptr<content::WebContents> web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) { NOTREACHED(); }
diff --git a/chromecast/browser/extensions/cast_extension_host_delegate.h b/chromecast/browser/extensions/cast_extension_host_delegate.h index d2b38782..f3488b9 100644 --- a/chromecast/browser/extensions/cast_extension_host_delegate.h +++ b/chromecast/browser/extensions/cast_extension_host_delegate.h
@@ -20,7 +20,7 @@ void OnExtensionHostCreated(content::WebContents* web_contents) override; void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) override; content::JavaScriptDialogManager* GetJavaScriptDialogManager() override; - void CreateTab(content::WebContents* web_contents, + void CreateTab(std::unique_ptr<content::WebContents> web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect,
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc b/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc index c0e6c81e..5a10fcf7 100644 --- a/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc +++ b/chromecast/device/bluetooth/le/gatt_client_manager_impl.cc
@@ -124,8 +124,13 @@ connected_devices_.erase(addr); } - observers_->Notify(FROM_HERE, &Observer::OnConnectChanged, it->second, - connected); + // We won't declare the device connected until service discovery completes. + // Only report disconnect callback if the connect callback was called ( + // service discovery completed). + if (!connected && it->second->GetServicesDiscovered()) { + observers_->Notify(FROM_HERE, &Observer::OnConnectChanged, it->second, + false); + } } void GattClientManagerImpl::OnNotification(const bluetooth_v2_shlib::Addr& addr, @@ -246,6 +251,12 @@ CHECK_DEVICE_EXISTS_IT(it); it->second->OnGetServices(services); + if (!it->second->GetServicesDiscovered()) { + observers_->Notify(FROM_HERE, &Observer::OnConnectChanged, it->second, + true); + it->second->SetServicesDiscovered(); + } + observers_->Notify(FROM_HERE, &Observer::OnServicesUpdated, it->second, it->second->GetServicesSync()); }
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc b/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc index dcabe4c0..18632cf 100644 --- a/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc +++ b/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc
@@ -165,7 +165,9 @@ device->Connect(cb_.Get()); bluetooth_v2_shlib::Gatt::Client::Delegate* delegate = gatt_client_->delegate(); + EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true)); delegate->OnConnectChanged(addr, true /* status */, true /* connected */); + delegate->OnGetServices(addr, {}); ASSERT_TRUE(device->IsConnected()); } @@ -200,16 +202,15 @@ EXPECT_CALL(cb_, Run(false)); device->ConnectionParameterUpdate(10, 10, 50, 100, cb_.Get()); - base::MockCallback<RemoteDevice::DiscoverServicesCb> discover_cb; - EXPECT_CALL(discover_cb, Run(false, _)); - device->DiscoverServices(discover_cb.Get()); - EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true)); EXPECT_CALL(cb_, Run(true)); device->Connect(cb_.Get()); + EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true)); delegate->OnConnectChanged(kTestAddr1, true /* status */, true /* connected */); + EXPECT_CALL(*observer_, OnConnectChanged(device, true)); + delegate->OnGetServices(kTestAddr1, {}); EXPECT_TRUE(device->IsConnected()); base::RunLoop().RunUntilIdle(); @@ -282,25 +283,20 @@ const auto kServices = GenerateServices(); Connect(kTestAddr1); scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1); + std::vector<scoped_refptr<RemoteService>> services; EXPECT_EQ(0ul, GetServices(device.get()).size()); - EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true)); - - bool status = false; - std::vector<scoped_refptr<RemoteService>> services; - device->DiscoverServices(base::BindOnce( - [](bool* pstatus, std::vector<scoped_refptr<RemoteService>>* pservices, - bool status, std::vector<scoped_refptr<RemoteService>> services) { - *pstatus = status; - *pservices = services; - }, - &status, &services)); - EXPECT_CALL(*observer_, OnServicesUpdated(device, _)); bluetooth_v2_shlib::Gatt::Client::Delegate* delegate = gatt_client_->delegate(); delegate->OnServicesAdded(kTestAddr1, kServices); + + device->GetServices(base::BindOnce( + [](std::vector<scoped_refptr<RemoteService>>* pservices, + std::vector<scoped_refptr<RemoteService>> services) { + *pservices = services; + }, + &services)); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(status); EXPECT_EQ(kServices.size(), GetServices(device.get()).size()); for (const auto& service : kServices) {
diff --git a/chromecast/device/bluetooth/le/mock_remote_device.h b/chromecast/device/bluetooth/le/mock_remote_device.h index 034074a..775e334 100644 --- a/chromecast/device/bluetooth/le/mock_remote_device.h +++ b/chromecast/device/bluetooth/le/mock_remote_device.h
@@ -29,7 +29,6 @@ int latency, int timeout, StatusCallback cb) override {} - void DiscoverServices(DiscoverServicesCb cb) override {} MOCK_METHOD0(IsConnected, bool()); MOCK_METHOD0(GetMtu, int()); void GetServices(
diff --git a/chromecast/device/bluetooth/le/remote_device.h b/chromecast/device/bluetooth/le/remote_device.h index 10cfe10..48be79a 100644 --- a/chromecast/device/bluetooth/le/remote_device.h +++ b/chromecast/device/bluetooth/le/remote_device.h
@@ -58,12 +58,6 @@ int timeout, StatusCallback cb) = 0; - // Initiate service discovery on this device. If it fails, the result will be - // empty. - using DiscoverServicesCb = base::OnceCallback< - void(bool success, std::vector<scoped_refptr<RemoteService>> services)>; - virtual void DiscoverServices(DiscoverServicesCb cb) = 0; - // Returns true if this device is connected. virtual bool IsConnected() = 0;
diff --git a/chromecast/device/bluetooth/le/remote_device_impl.cc b/chromecast/device/bluetooth/le/remote_device_impl.cc index 3af99d6..3b370c2 100644 --- a/chromecast/device/bluetooth/le/remote_device_impl.cc +++ b/chromecast/device/bluetooth/le/remote_device_impl.cc
@@ -181,32 +181,6 @@ LOG_EXEC_CB_AND_RET(cb, ret); } -void RemoteDeviceImpl::DiscoverServices(DiscoverServicesCb cb) { - MAKE_SURE_IO_THREAD(DiscoverServices, BindToCurrentSequence(std::move(cb))); - if (!gatt_client_manager_) { - LOG(ERROR) << __func__ << " failed: Destroyed"; - EXEC_CB_AND_RET(cb, false, {}); - } - - if (!connected_) { - LOG(ERROR) << __func__ << " failed: Not connected"; - EXEC_CB_AND_RET(cb, false, {}); - } - - if (discover_services_pending_) { - LOG(ERROR) << __func__ << " failed: Already discovering services"; - EXEC_CB_AND_RET(cb, false, {}); - } - - if (!gatt_client_manager_->gatt_client()->GetServices(addr_)) { - LOG(ERROR) << __func__ << " failed"; - EXEC_CB_AND_RET(cb, false, {}); - } - - discover_services_pending_ = true; - discover_services_cb_ = std::move(cb); -} - bool RemoteDeviceImpl::IsConnected() { return connected_; } @@ -257,6 +231,10 @@ void RemoteDeviceImpl::SetConnected(bool connected) { DCHECK(io_task_runner_->BelongsToCurrentThread()); + // We only set connected = true after services are discovered. + if (!connected) { + connected_ = false; + } if (connect_pending_) { connect_pending_ = false; if (connect_cb_) { @@ -271,7 +249,6 @@ } } - connected_ = connected; if (!connected && rssi_pending_) { LOG(ERROR) << "Read remote RSSI failed: disconnected"; if (rssi_cb_) { @@ -289,14 +266,6 @@ mtu_pending_ = false; } - if (!connected && discover_services_pending_) { - LOG(ERROR) << "Discover services failed: disconnected"; - if (discover_services_cb_) { - std::move(discover_services_cb_).Run(false, {}); - } - discover_services_pending_ = false; - } - for (const auto& characteristic : handle_to_characteristic_) { auto* char_impl = static_cast<RemoteCharacteristicImpl*>(characteristic.second.get()); @@ -308,6 +277,32 @@ static_cast<RemoteDescriptorImpl*>(descriptor.second.get()); desc_impl->OnConnectChanged(connected); } + + if (connected) { + if (!gatt_client_manager_) { + LOG(ERROR) << "Couldn't discover services: Destroyed"; + return; + } + + if (!gatt_client_manager_->gatt_client()->GetServices(addr_)) { + LOG(ERROR) << "Couldn't discover services, disconnecting"; + Disconnect({}); + } + } +} + +void RemoteDeviceImpl::SetServicesDiscovered() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + services_discovered_ = true; + connected_ = true; + if (connect_cb_) { + std::move(connect_cb_).Run(true); + } +} + +bool RemoteDeviceImpl::GetServicesDiscovered() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + return services_discovered_; } void RemoteDeviceImpl::SetMtu(int mtu) { @@ -347,11 +342,6 @@ handle_to_characteristic_.clear(); handle_to_descriptor_.clear(); OnServicesAdded(services); - - discover_services_pending_ = false; - if (discover_services_cb_) { - std::move(discover_services_cb_).Run(true, GetServicesSync()); - } } void RemoteDeviceImpl::OnServicesRemoved(uint16_t start_handle, @@ -371,11 +361,6 @@ ++it; } } - - discover_services_pending_ = false; - if (discover_services_cb_) { - std::move(discover_services_cb_).Run(true, GetServicesSync()); - } } void RemoteDeviceImpl::OnServicesAdded( @@ -395,11 +380,6 @@ } } } - - discover_services_pending_ = false; - if (discover_services_cb_) { - std::move(discover_services_cb_).Run(true, GetServicesSync()); - } } void RemoteDeviceImpl::OnReadRemoteRssiComplete(bool status, int rssi) {
diff --git a/chromecast/device/bluetooth/le/remote_device_impl.h b/chromecast/device/bluetooth/le/remote_device_impl.h index a69a89d..c220563 100644 --- a/chromecast/device/bluetooth/le/remote_device_impl.h +++ b/chromecast/device/bluetooth/le/remote_device_impl.h
@@ -35,7 +35,6 @@ int latency, int timeout, StatusCallback cb) override; - void DiscoverServices(DiscoverServicesCb cb) override; bool IsConnected() override; int GetMtu() override; void GetServices( @@ -59,6 +58,8 @@ // Friend methods for GattClientManagerImpl void SetConnected(bool connected); + void SetServicesDiscovered(); + bool GetServicesDiscovered(); void SetMtu(int mtu); scoped_refptr<RemoteCharacteristic> CharacteristicFromHandle(uint16_t handle); @@ -79,6 +80,8 @@ // should only be accessed on this task_runner. const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; + bool services_discovered_ = false; + bool connect_pending_ = false; StatusCallback connect_cb_; @@ -91,9 +94,6 @@ bool mtu_pending_ = false; StatusCallback mtu_cb_; - bool discover_services_pending_ = false; - DiscoverServicesCb discover_services_cb_; - std::atomic<bool> connected_{false}; std::atomic<int> mtu_{kDefaultMtu}; std::map<bluetooth_v2_shlib::Uuid, scoped_refptr<RemoteService>>
diff --git a/chromecast/graphics/BUILD.gn b/chromecast/graphics/BUILD.gn index 0264096..405a322 100644 --- a/chromecast/graphics/BUILD.gn +++ b/chromecast/graphics/BUILD.gn
@@ -8,6 +8,8 @@ cast_source_set("graphics") { sources = [ + "cast_side_swipe_gesture_handler.cc", + "cast_side_swipe_gesture_handler.h", "cast_window_manager.h", ] @@ -20,8 +22,6 @@ sources += [ "cast_focus_client_aura.cc", "cast_focus_client_aura.h", - "cast_side_swipe_gesture_handler.cc", - "cast_side_swipe_gesture_handler.h", "cast_system_gesture_event_handler.cc", "cast_system_gesture_event_handler.h", "cast_window_manager_aura.cc",
diff --git a/chromeos/account_manager/account_manager.cc b/chromeos/account_manager/account_manager.cc index 8414ca5dc..e8b9a48 100644 --- a/chromeos/account_manager/account_manager.cc +++ b/chromeos/account_manager/account_manager.cc
@@ -14,7 +14,6 @@ #include "base/sequenced_task_runner.h" #include "base/task_runner_util.h" #include "base/task_scheduler/post_task.h" -#include "chromeos/account_manager/tokens.pb.h" #include "third_party/protobuf/src/google/protobuf/message_lite.h" namespace chromeos { @@ -38,30 +37,44 @@ return tokens; } - chromeos::account_manager::Tokens tokens_proto; - success = tokens_proto.ParseFromString(token_file_data); + chromeos::account_manager::Accounts accounts_proto; + success = accounts_proto.ParseFromString(token_file_data); if (!success) { LOG(ERROR) << "Failed to parse tokens from file"; return tokens; } - tokens.insert(tokens_proto.login_scoped_tokens().begin(), - tokens_proto.login_scoped_tokens().end()); + for (const auto& account : accounts_proto.accounts()) { + AccountManager::AccountKey account_key{account.id(), + account.account_type()}; + + if (!account_key.IsValid()) { + LOG(WARNING) << "Ignoring invalid account_key load from disk: " + << account_key; + continue; + } + tokens[account_key] = account.token(); + } return tokens; } std::string GetSerializedTokens(const AccountManager::TokenMap& tokens) { - chromeos::account_manager::Tokens tokens_proto; - *tokens_proto.mutable_login_scoped_tokens() = - ::google::protobuf::Map<std::string, std::string>(tokens.begin(), - tokens.end()); - return tokens_proto.SerializeAsString(); + chromeos::account_manager::Accounts accounts_proto; + + for (const auto& token : tokens) { + account_manager::Account* account_proto = accounts_proto.add_accounts(); + account_proto->set_id(token.first.id); + account_proto->set_account_type(token.first.account_type); + account_proto->set_token(token.second); + } + + return accounts_proto.SerializeAsString(); } -std::vector<std::string> GetAccountIdKeys( +std::vector<AccountManager::AccountKey> GetAccountKeys( const AccountManager::TokenMap& tokens) { - std::vector<std::string> accounts; + std::vector<AccountManager::AccountKey> accounts; accounts.reserve(tokens.size()); for (const auto& key_val : tokens) { @@ -73,6 +86,23 @@ } // namespace +bool AccountManager::AccountKey::IsValid() const { + return !id.empty() && + account_type != account_manager::AccountType::ACCOUNT_TYPE_UNSPECIFIED; +} + +bool AccountManager::AccountKey::operator<(const AccountKey& other) const { + if (id != other.id) { + return id < other.id; + } + + return account_type < other.account_type; +} + +bool AccountManager::AccountKey::operator==(const AccountKey& other) const { + return id == other.id && account_type == other.account_type; +} + AccountManager::Observer::Observer() = default; AccountManager::Observer::~Observer() = default; @@ -154,30 +184,31 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_EQ(init_state_, InitializationState::kInitialized); - std::vector<std::string> accounts = GetAccountIdKeys(tokens_); + std::vector<AccountKey> accounts = GetAccountKeys(tokens_); std::move(callback).Run(std::move(accounts)); } -void AccountManager::UpsertToken(const std::string& account_id, - const std::string& login_scoped_token) { +void AccountManager::UpsertToken(const AccountKey& account_key, + const std::string& token) { DCHECK_NE(init_state_, InitializationState::kNotStarted); - base::OnceClosure closure = base::BindOnce( - &AccountManager::UpsertTokenInternal, weak_factory_.GetWeakPtr(), - account_id, login_scoped_token); + base::OnceClosure closure = + base::BindOnce(&AccountManager::UpsertTokenInternal, + weak_factory_.GetWeakPtr(), account_key, token); RunOnInitialization(std::move(closure)); } -void AccountManager::UpsertTokenInternal( - const std::string& account_id, - const std::string& login_scoped_token) { +void AccountManager::UpsertTokenInternal(const AccountKey& account_key, + const std::string& token) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_EQ(init_state_, InitializationState::kInitialized); - auto it = tokens_.find(account_id); + DCHECK(account_key.IsValid()) << "Invalid account_key: " << account_key; + + auto it = tokens_.find(account_key); const bool is_new_account = (it == tokens_.end()); - if (is_new_account || (it->second != login_scoped_token)) { - tokens_[account_id] = login_scoped_token; + if (is_new_account || (it->second != token)) { + tokens_[account_key] = token; PersistTokensAsync(); } @@ -195,7 +226,7 @@ void AccountManager::NotifyAccountListObservers() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::vector<std::string> accounts = GetAccountIdKeys(tokens_); + std::vector<AccountKey> accounts = GetAccountKeys(tokens_); for (auto& observer : observers_) { observer.OnAccountListUpdated(accounts); } @@ -211,4 +242,13 @@ observers_.RemoveObserver(observer); } +CHROMEOS_EXPORT std::ostream& operator<<( + std::ostream& os, + const AccountManager::AccountKey& account_key) { + os << "{ id: " << account_key.id + << ", account_type: " << account_key.account_type << " }"; + + return os; +} + } // namespace chromeos
diff --git a/chromeos/account_manager/account_manager.h b/chromeos/account_manager/account_manager.h index 0d29b14..4301795 100644 --- a/chromeos/account_manager/account_manager.h +++ b/chromeos/account_manager/account_manager.h
@@ -5,9 +5,11 @@ #ifndef CHROMEOS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_H_ #define CHROMEOS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_H_ +#include <map> #include <memory> +#include <ostream> #include <string> -#include <unordered_map> +#include <utility> #include <vector> #include "base/callback.h" @@ -18,6 +20,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/sequence_checker.h" +#include "chromeos/account_manager/tokens.pb.h" #include "chromeos/chromeos_export.h" namespace base { @@ -29,12 +32,21 @@ class CHROMEOS_EXPORT AccountManager { public: - // A map of account identifiers to login scoped tokens. - using TokenMap = std::unordered_map<std::string, std::string>; + struct AccountKey { + std::string id; + account_manager::AccountType account_type; - // A callback for list of Account Id Keys. - using AccountListCallback = - base::OnceCallback<void(std::vector<std::string>)>; + bool IsValid() const; + + bool operator<(const AccountKey& other) const; + bool operator==(const AccountKey& other) const; + }; + + // A map from |AccountKey| to a raw token. + using TokenMap = std::map<AccountKey, std::string>; + + // A callback for list of |AccountKey|s. + using AccountListCallback = base::OnceCallback<void(std::vector<AccountKey>)>; class Observer { public: @@ -43,8 +55,8 @@ // Called when the list of accounts known to |AccountManager| is updated. // Use |AccountManager::AddObserver| to add an |Observer|. - // Note: This is not called when the refresh token for an already known - // account is updated. + // Note: This is not called when the token for an already known account is + // updated. // Note: |Observer|s which register with |AccountManager| before its // initialization is complete will get notified when |AccountManager| is // fully initialized. @@ -52,7 +64,7 @@ // initialization is complete will not get an immediate // notification-on-registration. virtual void OnAccountListUpdated( - const std::vector<std::string>& accounts) = 0; + const std::vector<AccountKey>& accounts) = 0; private: DISALLOW_COPY_AND_ASSIGN(Observer); @@ -66,13 +78,12 @@ // user's cryptohome). This method MUST be called at least once. void Initialize(const base::FilePath& home_dir); - // Gets (async) a list of account identifiers known to |AccountManager|. + // Gets (async) a list of account keys known to |AccountManager|. void GetAccounts(AccountListCallback callback); - // Updates or inserts an LST (Login Scoped Token), for the account - // corresponding to the given account id. - void UpsertToken(const std::string& account_id, - const std::string& login_scoped_token); + // Updates or inserts a token, for the account corresponding to the given + // |account_key|. |account_key| must be valid (|AccountKey::IsValid|). + void UpsertToken(const AccountKey& account_key, const std::string& token); // Add a non owning pointer to an |AccountManager::Observer|. void AddObserver(Observer* observer); @@ -112,8 +123,8 @@ // Does the actual work of updating or inserting tokens. Assumes that // |AccountManager| initialization (|init_state_|) is complete. - void UpsertTokenInternal(const std::string& account_id, - const std::string& login_scoped_token); + void UpsertTokenInternal(const AccountKey& account_key, + const std::string& token); // Posts a task on |task_runner_|, which is usually a background thread, to // persist the current state of |tokens_|. @@ -129,7 +140,7 @@ scoped_refptr<base::SequencedTaskRunner> task_runner_; std::unique_ptr<base::ImportantFileWriter> writer_; - // A map of account ids to login scoped tokens. + // A map of account keys to tokens. TokenMap tokens_; // Callbacks waiting on class initialization (|init_state_|). @@ -145,6 +156,11 @@ DISALLOW_COPY_AND_ASSIGN(AccountManager); }; +// For logging. +CHROMEOS_EXPORT std::ostream& operator<<( + std::ostream& os, + const AccountManager::AccountKey& account_key); + } // namespace chromeos #endif // CHROMEOS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_H_
diff --git a/chromeos/account_manager/account_manager_unittest.cc b/chromeos/account_manager/account_manager_unittest.cc index 85e62f4..1b4f7cd 100644 --- a/chromeos/account_manager/account_manager_unittest.cc +++ b/chromeos/account_manager/account_manager_unittest.cc
@@ -37,6 +37,8 @@ base::test::ScopedTaskEnvironment scoped_task_environment_; base::ScopedTempDir tmp_dir_; std::unique_ptr<AccountManager> account_manager_; + const AccountManager::AccountKey kAccountKey_{ + "111", account_manager::AccountType::ACCOUNT_TYPE_GAIA}; private: DISALLOW_COPY_AND_ASSIGN(AccountManagerTest); @@ -47,18 +49,33 @@ AccountManagerObserver() = default; ~AccountManagerObserver() override = default; - void OnAccountListUpdated(const std::vector<std::string>& accounts) override { + void OnAccountListUpdated( + const std::vector<AccountManager::AccountKey>& accounts) override { is_callback_called_ = true; accounts_ = accounts; } bool is_callback_called_ = false; - std::vector<std::string> accounts_; + std::vector<AccountManager::AccountKey> accounts_; private: DISALLOW_COPY_AND_ASSIGN(AccountManagerObserver); }; +TEST(AccountManagerKeyTest, TestValidity) { + AccountManager::AccountKey key1{ + std::string(), account_manager::AccountType::ACCOUNT_TYPE_GAIA}; + EXPECT_FALSE(key1.IsValid()); + + AccountManager::AccountKey key2{ + "abc", account_manager::AccountType::ACCOUNT_TYPE_UNSPECIFIED}; + EXPECT_FALSE(key2.IsValid()); + + AccountManager::AccountKey key3{ + "abc", account_manager::AccountType::ACCOUNT_TYPE_GAIA}; + EXPECT_TRUE(key3.IsValid()); +} + TEST_F(AccountManagerTest, TestInitialization) { AccountManager account_manager; @@ -72,13 +89,14 @@ } TEST_F(AccountManagerTest, TestUpsert) { - account_manager_->UpsertToken("abc", "123"); + account_manager_->UpsertToken(kAccountKey_, "123"); - std::vector<std::string> accounts; + std::vector<AccountManager::AccountKey> accounts; base::RunLoop run_loop; account_manager_->GetAccounts(base::BindOnce( - [](std::vector<std::string>* accounts, base::OnceClosure quit_closure, - std::vector<std::string> stored_accounts) -> void { + [](std::vector<AccountManager::AccountKey>* accounts, + base::OnceClosure quit_closure, + std::vector<AccountManager::AccountKey> stored_accounts) -> void { *accounts = stored_accounts; std::move(quit_closure).Run(); }, @@ -86,22 +104,23 @@ run_loop.Run(); EXPECT_EQ(1UL, accounts.size()); - EXPECT_EQ("abc", accounts[0]); + EXPECT_EQ(kAccountKey_, accounts[0]); } TEST_F(AccountManagerTest, TestPersistence) { - account_manager_->UpsertToken("abc", "123"); + account_manager_->UpsertToken(kAccountKey_, "123"); scoped_task_environment_.RunUntilIdle(); account_manager_ = std::make_unique<AccountManager>(); account_manager_->Initialize(tmp_dir_.GetPath(), base::SequencedTaskRunnerHandle::Get()); - std::vector<std::string> accounts; + std::vector<AccountManager::AccountKey> accounts; base::RunLoop run_loop; account_manager_->GetAccounts(base::BindOnce( - [](std::vector<std::string>* accounts, base::OnceClosure quit_closure, - std::vector<std::string> stored_accounts) -> void { + [](std::vector<AccountManager::AccountKey>* accounts, + base::OnceClosure quit_closure, + std::vector<AccountManager::AccountKey> stored_accounts) -> void { *accounts = stored_accounts; std::move(quit_closure).Run(); }, @@ -109,7 +128,7 @@ run_loop.Run(); EXPECT_EQ(1UL, accounts.size()); - EXPECT_EQ("abc", accounts[0]); + EXPECT_EQ(kAccountKey_, accounts[0]); } TEST_F(AccountManagerTest, TestObserverAddAccount) { @@ -117,16 +136,16 @@ EXPECT_FALSE(observer->is_callback_called_); account_manager_->AddObserver(observer.get()); - account_manager_->UpsertToken("abc", "123"); + account_manager_->UpsertToken(kAccountKey_, "123"); scoped_task_environment_.RunUntilIdle(); EXPECT_TRUE(observer->is_callback_called_); EXPECT_EQ(1UL, observer->accounts_.size()); - EXPECT_EQ("abc", observer->accounts_[0]); + EXPECT_EQ(kAccountKey_, observer->accounts_[0]); // Observers should not be called if account list does not change. observer->is_callback_called_ = false; - account_manager_->UpsertToken("abc", "456"); + account_manager_->UpsertToken(kAccountKey_, "456"); scoped_task_environment_.RunUntilIdle(); EXPECT_FALSE(observer->is_callback_called_);
diff --git a/chromeos/account_manager/tokens.proto b/chromeos/account_manager/tokens.proto index 7787d5e21..da416ccc 100644 --- a/chromeos/account_manager/tokens.proto +++ b/chromeos/account_manager/tokens.proto
@@ -8,7 +8,22 @@ package chromeos.account_manager; -message Tokens { - // A mapping from GAIA id to a Login Scoped (Refresh) Token - map<string, string> login_scoped_tokens = 1; +enum AccountType { + ACCOUNT_TYPE_UNSPECIFIED = 0; // Proto best practice for enums. + ACCOUNT_TYPE_GAIA = 1; + ACCOUNT_TYPE_ACTIVE_DIRECTORY = 2; +} + +message Account { + // The tuple <id, account_type> uniquely identifies an Account. + // For |ACCOUNT_TYPE_GAIA|, |id| is the obfuscated GAIA id. + // For |ACCOUNT_TYPE_ACTIVE_DIRECTORY|, |id| is the Object GUID. + optional string id = 1; + optional AccountType account_type = 2; + + optional string token = 3; +} + +message Accounts { + repeated Account accounts = 1; }
diff --git a/chromeos/assistant/assistant.gni b/chromeos/assistant/assistant.gni index eae72a7..3aac971 100644 --- a/chromeos/assistant/assistant.gni +++ b/chromeos/assistant/assistant.gni
@@ -6,4 +6,8 @@ # Enable assistant implementation based on libassistant. This requires # enable_cros_assistant also enabled. enable_cros_libassistant = false + + # Enable building libassistant.so shared library from local checkout. This is + # for development purposes only. + enable_cros_libassistant_so = false }
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc index 0b1a748..ce7a13a 100644 --- a/chromeos/dbus/concierge_client.cc +++ b/chromeos/dbus/concierge_client.cc
@@ -166,6 +166,28 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } + void GetContainerAppIcons( + const vm_tools::concierge::ContainerAppIconRequest& request, + DBusMethodCallback<vm_tools::concierge::ContainerAppIconResponse> + callback) override { + dbus::MethodCall method_call( + vm_tools::concierge::kVmConciergeInterface, + vm_tools::concierge::kGetContainerAppIconMethod); + dbus::MessageWriter writer(&method_call); + + if (!writer.AppendProtoAsArrayOfBytes(request)) { + LOG(ERROR) << "Failed to encode ContainerAppIonRequest protobuf"; + std::move(callback).Run(base::nullopt); + return; + } + + concierge_proxy_->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, + base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse< + vm_tools::concierge::ContainerAppIconResponse>, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + } + void WaitForServiceToBeAvailable( dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) override {
diff --git a/chromeos/dbus/concierge_client.h b/chromeos/dbus/concierge_client.h index 38c5756..dc7a79d2 100644 --- a/chromeos/dbus/concierge_client.h +++ b/chromeos/dbus/concierge_client.h
@@ -89,6 +89,13 @@ vm_tools::concierge::LaunchContainerApplicationResponse> callback) = 0; + // Gets application icons from inside a Container. + // |callback| is called after the method call finishes. + virtual void GetContainerAppIcons( + const vm_tools::concierge::ContainerAppIconRequest& request, + DBusMethodCallback<vm_tools::concierge::ContainerAppIconResponse> + callback) = 0; + // Registers |callback| to run when the Concierge service becomes available. // If the service is already available, or if connecting to the name-owner- // changed signal fails, |callback| will be run once asynchronously.
diff --git a/chromeos/dbus/fake_concierge_client.cc b/chromeos/dbus/fake_concierge_client.cc index bde7e3f..26401df36 100644 --- a/chromeos/dbus/fake_concierge_client.cc +++ b/chromeos/dbus/fake_concierge_client.cc
@@ -93,6 +93,15 @@ FROM_HERE, base::BindOnce(std::move(callback), std::move(response))); } +void FakeConciergeClient::GetContainerAppIcons( + const vm_tools::concierge::ContainerAppIconRequest& request, + DBusMethodCallback<vm_tools::concierge::ContainerAppIconResponse> + callback) { + vm_tools::concierge::ContainerAppIconResponse response; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), std::move(response))); +} + void FakeConciergeClient::WaitForServiceToBeAvailable( dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) { base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chromeos/dbus/fake_concierge_client.h b/chromeos/dbus/fake_concierge_client.h index be76f92..87ac669 100644 --- a/chromeos/dbus/fake_concierge_client.h +++ b/chromeos/dbus/fake_concierge_client.h
@@ -75,6 +75,13 @@ vm_tools::concierge::LaunchContainerApplicationResponse> callback) override; + // Fake version of the method that gets application icons from inside a + // Container. |callback| is called after the method call finishes. + void GetContainerAppIcons( + const vm_tools::concierge::ContainerAppIconRequest& request, + DBusMethodCallback<vm_tools::concierge::ContainerAppIconResponse> + callback) override; + // Fake version of the method that waits for the Concierge service to be // availble. |callback| is called after the method call finishes. void WaitForServiceToBeAvailable(
diff --git a/chromeos/dbus/fake_shill_device_client.cc b/chromeos/dbus/fake_shill_device_client.cc index 04ca982..a60922b 100644 --- a/chromeos/dbus/fake_shill_device_client.cc +++ b/chromeos/dbus/fake_shill_device_client.cc
@@ -103,7 +103,8 @@ const ErrorCallback& error_callback) { if (IsReadOnlyProperty(name)) PostError(shill::kErrorResultInvalidArguments, error_callback); - SetPropertyInternal(device_path, name, value, callback, error_callback); + SetPropertyInternal(device_path, name, value, callback, error_callback, + /*notify_changed=*/true); } void FakeShillDeviceClient::SetPropertyInternal( @@ -111,7 +112,8 @@ const std::string& name, const base::Value& value, const base::Closure& callback, - const ErrorCallback& error_callback) { + const ErrorCallback& error_callback, + bool notify_changed) { base::DictionaryValue* device_properties = NULL; if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(), &device_properties)) { @@ -119,10 +121,12 @@ return; } device_properties->SetKey(name, value.Clone()); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&FakeShillDeviceClient::NotifyObserversPropertyChanged, - weak_ptr_factory_.GetWeakPtr(), device_path, name)); + if (notify_changed) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&FakeShillDeviceClient::NotifyObserversPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), device_path, name)); + } base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } @@ -265,7 +269,8 @@ const base::Closure& callback, const ErrorCallback& error_callback) { SetPropertyInternal(device_path, shill::kCarrierProperty, - base::Value(carrier), callback, error_callback); + base::Value(carrier), callback, error_callback, + /*notify_changed=*/true); } void FakeShillDeviceClient::Reset(const dbus::ObjectPath& device_path, @@ -417,12 +422,13 @@ void FakeShillDeviceClient::SetDeviceProperty(const std::string& device_path, const std::string& name, - const base::Value& value) { + const base::Value& value, + bool notify_changed) { VLOG(1) << "SetDeviceProperty: " << device_path << ": " << name << " = " << value; SetPropertyInternal(dbus::ObjectPath(device_path), name, value, base::DoNothing(), - base::Bind(&ErrorFunction, device_path)); + base::Bind(&ErrorFunction, device_path), notify_changed); } std::string FakeShillDeviceClient::GetDevicePathForType(
diff --git a/chromeos/dbus/fake_shill_device_client.h b/chromeos/dbus/fake_shill_device_client.h index 0355d7e..7fb0f81 100644 --- a/chromeos/dbus/fake_shill_device_client.h +++ b/chromeos/dbus/fake_shill_device_client.h
@@ -103,7 +103,8 @@ void ClearDevices() override; void SetDeviceProperty(const std::string& device_path, const std::string& name, - const base::Value& value) override; + const base::Value& value, + bool notify_changed) override; std::string GetDevicePathForType(const std::string& type) override; void SetTDLSBusyCount(int count) override; void SetTDLSState(const std::string& state) override; @@ -132,11 +133,14 @@ // Posts a task to run a void callback with status code |result|. void PostVoidCallback(VoidDBusMethodCallback callback, bool result); + // If |notify_changed| is true, NotifyObserversPropertyChanged is called, + // otherwise it is not (e.g. when setting up initial properties). void SetPropertyInternal(const dbus::ObjectPath& device_path, const std::string& name, const base::Value& value, const base::Closure& callback, - const ErrorCallback& error_callback); + const ErrorCallback& error_callback, + bool notify_changed); void NotifyObserversPropertyChanged(const dbus::ObjectPath& device_path, const std::string& property);
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc index f6139557..f4cd6bc3 100644 --- a/chromeos/dbus/fake_shill_manager_client.cc +++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -132,6 +132,15 @@ type == shill::kNetworkTechnologyLteAdvanced); } +void SetInitialDeviceProperty(const std::string& device_path, + const std::string& name, + const base::Value& value) { + DBusThreadManager::Get() + ->GetShillDeviceClient() + ->GetTestInterface() + ->SetDeviceProperty(device_path, name, value, /*notify_changed=*/false); +} + const char kTechnologyUnavailable[] = "unavailable"; const char kTechnologyInitializing[] = "initializing"; const char kNetworkActivated[] = "activated"; @@ -203,7 +212,8 @@ std::string device_path = device_client->GetDevicePathForType(device_type); if (!device_path.empty()) { device_client->SetDeviceProperty(device_path, shill::kScanningProperty, - base::Value(true)); + base::Value(true), + /*notify_changed=*/true); if (device_type == shill::kTypeCellular) device_client->AddCellularFoundNetwork(device_path); } @@ -653,22 +663,18 @@ state = GetInitialStateForType(shill::kTypeEthernet, &enabled); if (state == shill::kStateOnline || state == shill::kStateIdle) { AddTechnology(shill::kTypeEthernet, enabled); - devices->AddDevice( - "/device/eth1", shill::kTypeEthernet, "stub_eth_device1"); - devices->SetDeviceProperty("/device/eth1", shill::kAddressProperty, - base::Value("0123456789ab")); + devices->AddDevice("/device/eth1", shill::kTypeEthernet, + "stub_eth_device1"); + SetInitialDeviceProperty("/device/eth1", shill::kAddressProperty, + base::Value("0123456789ab")); base::ListValue eth_ip_configs; eth_ip_configs.AppendString("ipconfig_v4_path"); eth_ip_configs.AppendString("ipconfig_v6_path"); - devices->SetDeviceProperty("/device/eth1", - shill::kIPConfigsProperty, - eth_ip_configs); + SetInitialDeviceProperty("/device/eth1", shill::kIPConfigsProperty, + eth_ip_configs); const std::string kFakeEthernetNetworkPath = "/service/eth1"; - services->AddService(kFakeEthernetNetworkPath, - kFakeEthernetNetworkGuid, - "eth1" /* name */, - shill::kTypeEthernet, - state, + services->AddService(kFakeEthernetNetworkPath, kFakeEthernetNetworkGuid, + "eth1" /* name */, shill::kTypeEthernet, state, add_to_visible); profiles->AddService(shared_profile, kFakeEthernetNetworkPath); } @@ -690,22 +696,17 @@ } AddTechnology(shill::kTypeWifi, enabled); devices->AddDevice("/device/wifi1", shill::kTypeWifi, "stub_wifi_device1"); - devices->SetDeviceProperty("/device/wifi1", shill::kAddressProperty, - base::Value("23456789abcd")); + SetInitialDeviceProperty("/device/wifi1", shill::kAddressProperty, + base::Value("23456789abcd")); base::ListValue wifi_ip_configs; wifi_ip_configs.AppendString("ipconfig_v4_path"); wifi_ip_configs.AppendString("ipconfig_v6_path"); - devices->SetDeviceProperty("/device/wifi1", - shill::kIPConfigsProperty, - wifi_ip_configs); + SetInitialDeviceProperty("/device/wifi1", shill::kIPConfigsProperty, + wifi_ip_configs); const std::string kWifi1Path = "/service/wifi1"; - services->AddService(kWifi1Path, - "wifi1_guid", - "wifi1" /* name */, - shill::kTypeWifi, - state, - add_to_visible); + services->AddService(kWifi1Path, "wifi1_guid", "wifi1" /* name */, + shill::kTypeWifi, state, add_to_visible); services->SetServiceProperty(kWifi1Path, shill::kSecurityClassProperty, base::Value(shill::kSecurityWep)); services->SetServiceProperty(kWifi1Path, shill::kConnectableProperty, @@ -738,12 +739,9 @@ profiles->AddService(shared_profile, kWifi2Path); const std::string kWifi3Path = "/service/wifi3"; - services->AddService(kWifi3Path, - "", /* empty GUID */ - "wifi3" /* name */, - shill::kTypeWifi, - shill::kStateIdle, - add_to_visible); + services->AddService(kWifi3Path, "", /* empty GUID */ + "wifi3" /* name */, shill::kTypeWifi, + shill::kStateIdle, add_to_visible); services->SetServiceProperty(kWifi3Path, shill::kSignalStrengthProperty, base::Value(40)); @@ -778,8 +776,8 @@ state = GetInitialStateForType(shill::kTypeWimax, &enabled); if (state != kTechnologyUnavailable) { AddTechnology(shill::kTypeWimax, enabled); - devices->AddDevice( - "/device/wimax1", shill::kTypeWimax, "stub_wimax_device1"); + devices->AddDevice("/device/wimax1", shill::kTypeWimax, + "stub_wimax_device1"); services->AddService(kWimaxPath, "wimax1_guid", "wimax1" /* name */, shill::kTypeWimax, state, add_to_visible); @@ -804,24 +802,24 @@ AddTechnology(shill::kTypeCellular, enabled); devices->AddDevice("/device/cellular1", shill::kTypeCellular, "stub_cellular_device1"); - devices->SetDeviceProperty("/device/cellular1", shill::kCarrierProperty, - base::Value(shill::kCarrierSprint)); + SetInitialDeviceProperty("/device/cellular1", shill::kCarrierProperty, + base::Value(shill::kCarrierSprint)); base::ListValue carrier_list; carrier_list.AppendString(shill::kCarrierSprint); carrier_list.AppendString(shill::kCarrierGenericUMTS); - devices->SetDeviceProperty("/device/cellular1", - shill::kSupportedCarriersProperty, carrier_list); + SetInitialDeviceProperty("/device/cellular1", + shill::kSupportedCarriersProperty, carrier_list); if (roaming_state_ == kRoamingRequired) { - devices->SetDeviceProperty("/device/cellular1", - shill::kProviderRequiresRoamingProperty, - base::Value(true)); + SetInitialDeviceProperty("/device/cellular1", + shill::kProviderRequiresRoamingProperty, + base::Value(true)); } if (cellular_technology_ == shill::kNetworkTechnologyGsm) { - devices->SetDeviceProperty("/device/cellular1", - shill::kSupportNetworkScanProperty, - base::Value(true)); - devices->SetDeviceProperty("/device/cellular1", - shill::kSIMPresentProperty, base::Value(true)); + SetInitialDeviceProperty("/device/cellular1", + shill::kSupportNetworkScanProperty, + base::Value(true)); + SetInitialDeviceProperty("/device/cellular1", shill::kSIMPresentProperty, + base::Value(true)); devices->SetSimLocked("/device/cellular1", false); } @@ -830,9 +828,9 @@ "cellular1" /* name */, shill::kTypeCellular, state, add_to_visible); base::Value technology_value(cellular_technology_); - devices->SetDeviceProperty("/device/cellular1", - shill::kTechnologyFamilyProperty, - technology_value); + SetInitialDeviceProperty("/device/cellular1", + shill::kTechnologyFamilyProperty, + technology_value); services->SetServiceProperty(kCellularServicePath, shill::kNetworkTechnologyProperty, technology_value); @@ -880,8 +878,8 @@ base::ListValue apn_list; apn_list.Append(apn.CreateDeepCopy()); apn_list.Append(apn2.CreateDeepCopy()); - devices->SetDeviceProperty("/device/cellular1", - shill::kCellularApnListProperty, apn_list); + SetInitialDeviceProperty("/device/cellular1", + shill::kCellularApnListProperty, apn_list); profiles->AddService(shared_profile, kCellularServicePath); } @@ -899,14 +897,10 @@ shill::kProviderOpenVpn); provider_properties_openvpn.SetString(shill::kHostProperty, "vpn_host"); - services->AddService("/service/vpn1", - "vpn1_guid", - "vpn1" /* name */, - shill::kTypeVPN, - state, - add_to_visible); - services->SetServiceProperty( - "/service/vpn1", shill::kProviderProperty, provider_properties_openvpn); + services->AddService("/service/vpn1", "vpn1_guid", "vpn1" /* name */, + shill::kTypeVPN, state, add_to_visible); + services->SetServiceProperty("/service/vpn1", shill::kProviderProperty, + provider_properties_openvpn); profiles->AddService(shared_profile, "/service/vpn1"); base::DictionaryValue provider_properties_l2tp; @@ -914,14 +908,10 @@ shill::kProviderL2tpIpsec); provider_properties_l2tp.SetString(shill::kHostProperty, "vpn_host2"); - services->AddService("/service/vpn2", - "vpn2_guid", - "vpn2" /* name */, - shill::kTypeVPN, - shill::kStateIdle, - add_to_visible); - services->SetServiceProperty( - "/service/vpn2", shill::kProviderProperty, provider_properties_l2tp); + services->AddService("/service/vpn2", "vpn2_guid", "vpn2" /* name */, + shill::kTypeVPN, shill::kStateIdle, add_to_visible); + services->SetServiceProperty("/service/vpn2", shill::kProviderProperty, + provider_properties_l2tp); } // Additional device states @@ -931,7 +921,7 @@ std::string device_path = devices->GetDevicePathForType(device_type); for (ShillPropertyMap::iterator iter2 = iter1->second.begin(); iter2 != iter1->second.end(); ++iter2) { - devices->SetDeviceProperty(device_path, iter2->first, *(iter2->second)); + SetInitialDeviceProperty(device_path, iter2->first, *(iter2->second)); delete iter2->second; } } @@ -1064,7 +1054,7 @@ ->GetShillDeviceClient() ->GetTestInterface() ->SetDeviceProperty(device_path, shill::kScanningProperty, - base::Value(false)); + base::Value(false), /*notify_changed=*/true); } VLOG(1) << "ScanCompleted"; CallNotifyObserversPropertyChanged(shill::kServiceCompleteListProperty);
diff --git a/chromeos/dbus/fake_smb_provider_client.cc b/chromeos/dbus/fake_smb_provider_client.cc index 7cdb570..2f0e89b 100644 --- a/chromeos/dbus/fake_smb_provider_client.cc +++ b/chromeos/dbus/fake_smb_provider_client.cc
@@ -19,6 +19,9 @@ void FakeSmbProviderClient::Init(dbus::Bus* bus) {} void FakeSmbProviderClient::Mount(const base::FilePath& share_path, + const std::string& workgroup, + const std::string& username, + base::ScopedFD password_fd, MountCallback callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), smbprovider::ERROR_OK, 1));
diff --git a/chromeos/dbus/fake_smb_provider_client.h b/chromeos/dbus/fake_smb_provider_client.h index c3fc069c..537390a 100644 --- a/chromeos/dbus/fake_smb_provider_client.h +++ b/chromeos/dbus/fake_smb_provider_client.h
@@ -19,7 +19,12 @@ void Init(dbus::Bus* bus) override; // SmbProviderClient override. - void Mount(const base::FilePath& share_path, MountCallback callback) override; + void Mount(const base::FilePath& share_path, + const std::string& workgroup, + const std::string& username, + base::ScopedFD password_fd, + MountCallback callback) override; + void Remount(const base::FilePath& share_path, int32_t mount_id, StatusCallback callback) override;
diff --git a/chromeos/dbus/shill_device_client.h b/chromeos/dbus/shill_device_client.h index e30a0be..178e5ef 100644 --- a/chromeos/dbus/shill_device_client.h +++ b/chromeos/dbus/shill_device_client.h
@@ -56,7 +56,8 @@ virtual void ClearDevices() = 0; virtual void SetDeviceProperty(const std::string& device_path, const std::string& name, - const base::Value& value) = 0; + const base::Value& value, + bool notify_changed) = 0; virtual std::string GetDevicePathForType(const std::string& type) = 0; virtual void SetTDLSBusyCount(int count) = 0; virtual void SetTDLSState(const std::string& state) = 0;
diff --git a/chromeos/dbus/smb_provider_client.cc b/chromeos/dbus/smb_provider_client.cc index 0bb2ab0..f9a6cae 100644 --- a/chromeos/dbus/smb_provider_client.cc +++ b/chromeos/dbus/smb_provider_client.cc
@@ -63,11 +63,22 @@ ~SmbProviderClientImpl() override {} void Mount(const base::FilePath& share_path, + const std::string& workgroup, + const std::string& username, + base::ScopedFD password_fd, MountCallback callback) override { smbprovider::MountOptionsProto options; options.set_path(share_path.value()); - CallMethod(smbprovider::kMountMethod, options, - &SmbProviderClientImpl::HandleMountCallback, &callback); + options.set_workgroup(workgroup); + options.set_username(username); + + dbus::MethodCall method_call(smbprovider::kSmbProviderInterface, + smbprovider::kMountMethod); + dbus::MessageWriter writer(&method_call); + writer.AppendProtoAsArrayOfBytes(options); + writer.AppendFileDescriptor(password_fd.release()); + CallMethod(&method_call, &SmbProviderClientImpl::HandleMountCallback, + &callback); } void Remount(const base::FilePath& share_path,
diff --git a/chromeos/dbus/smb_provider_client.h b/chromeos/dbus/smb_provider_client.h index ca41e35..c5c88bb 100644 --- a/chromeos/dbus/smb_provider_client.h +++ b/chromeos/dbus/smb_provider_client.h
@@ -48,9 +48,13 @@ static SmbProviderClient* Create(); // Calls Mount. It runs OpenDirectory() on |share_path| to check that it is a - // valid share. |callback| is called after getting (or failing to get) D-BUS - // response. + // valid share. |workgroup|, |username|, and |password_fd| will be used as + // credentials to access the mount. |callback| is called after getting (or + // failing to get) D-BUS response. virtual void Mount(const base::FilePath& share_path, + const std::string& workgroup, + const std::string& username, + base::ScopedFD password_fd, MountCallback callback) = 0; // Calls Remount. This attempts to remount the share at |share_path| with its
diff --git a/chromeos/network/network_connect_unittest.cc b/chromeos/network/network_connect_unittest.cc index 91c62f9..c5451f4d 100644 --- a/chromeos/network/network_connect_unittest.cc +++ b/chromeos/network/network_connect_unittest.cc
@@ -127,9 +127,9 @@ "stub_wifi_device1"); device_test_->AddDevice(kCellular1DevicePath, shill::kTypeCellular, "stub_cellular_device1"); - device_test_->SetDeviceProperty(kCellular1DevicePath, - shill::kTechnologyFamilyProperty, - base::Value(shill::kNetworkTechnologyGsm)); + device_test_->SetDeviceProperty( + kCellular1DevicePath, shill::kTechnologyFamilyProperty, + base::Value(shill::kNetworkTechnologyGsm), /*notify_changed=*/true); service_test_ = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
diff --git a/chromeos/network/network_device_handler_unittest.cc b/chromeos/network/network_device_handler_unittest.cc index 291e7a7..bd5a1c6 100644 --- a/chromeos/network/network_device_handler_unittest.cc +++ b/chromeos/network/network_device_handler_unittest.cc
@@ -66,8 +66,9 @@ base::ListValue test_ip_configs; test_ip_configs.AppendString("ip_config1"); - device_test->SetDeviceProperty( - kDefaultWifiDevicePath, shill::kIPConfigsProperty, test_ip_configs); + device_test->SetDeviceProperty(kDefaultWifiDevicePath, + shill::kIPConfigsProperty, test_ip_configs, + /*notify_changed=*/true); base::RunLoop().RunUntilIdle(); } @@ -184,7 +185,7 @@ fake_device_client_->GetTestInterface(); device_test->SetDeviceProperty(kDefaultCellularDevicePath, shill::kCellularAllowRoamingProperty, - base::Value(false)); + base::Value(false), /*notify_changed=*/true); network_device_handler_->SetCellularAllowRoaming(true); base::RunLoop().RunUntilIdle();
diff --git a/chromeos/network/network_state_handler_unittest.cc b/chromeos/network/network_state_handler_unittest.cc index acfeeea..6b9fa5a 100644 --- a/chromeos/network/network_state_handler_unittest.cc +++ b/chromeos/network/network_state_handler_unittest.cc
@@ -1736,7 +1736,7 @@ EXPECT_EQ(0, test_observer_->PropertyUpdatesForDevice(wifi_device)); // Change a device property. device_test_->SetDeviceProperty(wifi_device, shill::kScanningProperty, - base::Value(true)); + base::Value(true), /*notify_changed=*/true); UpdateManagerProperties(); EXPECT_EQ(1, test_observer_->PropertyUpdatesForDevice(wifi_device)); } @@ -1756,9 +1756,9 @@ ip_config_test->AddIPConfig(kIPConfigPath, ip_config_properties); base::ListValue device_ip_configs; device_ip_configs.AppendString(kIPConfigPath); - device_test_->SetDeviceProperty( - kShillManagerClientStubWifiDevice, shill::kIPConfigsProperty, - device_ip_configs); + device_test_->SetDeviceProperty(kShillManagerClientStubWifiDevice, + shill::kIPConfigsProperty, device_ip_configs, + /*notify_changed=*/true); service_test_->SetServiceProperty(kShillManagerClientStubDefaultWifi, shill::kIPConfigProperty, base::Value(kIPConfigPath));
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn index 0b2ba3e..c9f23de7 100644 --- a/chromeos/services/assistant/BUILD.gn +++ b/chromeos/services/assistant/BUILD.gn
@@ -60,18 +60,20 @@ "//chromeos/assistant/internal/action", "//libassistant/contrib/core", "//libassistant/contrib/platform/audio", - "//libassistant/contrib/platform/auth", - "//libassistant/contrib/platform/file", "//libassistant/contrib/platform/net", "//libassistant/contrib/platform/resources", - "//libassistant/contrib/platform/system", - "//libassistant/internal/assistant/controller:libassistant", "//libassistant/shared/internal_api/c:api_wrappers_for_caller_no_chromium", "//libassistant/shared/proto:assistant_proto", "//libassistant/shared/public", "//libassistant/shared/public:export", ] + if (enable_cros_libassistant_so) { + deps += [ "//libassistant/internal/assistant/controller:libassistant" ] + } else { + libs = [ "assistant" ] + } + include_dirs = [ "//libassistant/contrib", "//libassistant/shared",
diff --git a/components/arc/bluetooth/bluetooth_type_converters.cc b/components/arc/bluetooth/bluetooth_type_converters.cc index dd4c613..9581d51 100644 --- a/components/arc/bluetooth/bluetooth_type_converters.cc +++ b/components/arc/bluetooth/bluetooth_type_converters.cc
@@ -77,44 +77,6 @@ } // static -arc::mojom::BluetoothGattStatus -TypeConverter<arc::mojom::BluetoothGattStatus, - device::BluetoothGattService::GattErrorCode>:: - Convert(const device::BluetoothGattService::GattErrorCode& error_code) { - arc::mojom::BluetoothGattStatus ret; - - switch (error_code) { - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_INVALID_LENGTH: - ret = arc::mojom::BluetoothGattStatus::GATT_INVALID_ATTRIBUTE_LENGTH; - break; - - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED: - ret = arc::mojom::BluetoothGattStatus::GATT_READ_NOT_PERMITTED; - break; - - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_AUTHORIZED: - ret = arc::mojom::BluetoothGattStatus::GATT_INSUFFICIENT_AUTHENTICATION; - break; - - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_SUPPORTED: - ret = arc::mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED; - break; - - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_UNKNOWN: - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED: - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_IN_PROGRESS: - case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PAIRED: - ret = arc::mojom::BluetoothGattStatus::GATT_FAILURE; - break; - - default: - ret = arc::mojom::BluetoothGattStatus::GATT_FAILURE; - break; - } - return ret; -} - -// static arc::mojom::BluetoothSdpAttributePtr TypeConverter<arc::mojom::BluetoothSdpAttributePtr, bluez::BluetoothServiceAttributeValueBlueZ>::
diff --git a/components/arc/bluetooth/bluetooth_type_converters.h b/components/arc/bluetooth/bluetooth_type_converters.h index 295e3c8e..67a43e74 100644 --- a/components/arc/bluetooth/bluetooth_type_converters.h +++ b/components/arc/bluetooth/bluetooth_type_converters.h
@@ -41,13 +41,6 @@ }; template <> -struct TypeConverter<arc::mojom::BluetoothGattStatus, - device::BluetoothGattService::GattErrorCode> { - static arc::mojom::BluetoothGattStatus Convert( - const device::BluetoothGattService::GattErrorCode& error_code); -}; - -template <> struct TypeConverter<arc::mojom::BluetoothSdpAttributePtr, bluez::BluetoothServiceAttributeValueBlueZ> { static arc::mojom::BluetoothSdpAttributePtr Convert(
diff --git a/components/autofill/core/browser/test_sync_service.cc b/components/autofill/core/browser/test_sync_service.cc index 56f510f..e2fcaf1 100644 --- a/components/autofill/core/browser/test_sync_service.cc +++ b/components/autofill/core/browser/test_sync_service.cc
@@ -55,7 +55,8 @@ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN, /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), - /*long_poll_interval=*/base::TimeDelta::FromMinutes(180)); + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false); } return syncer::SyncCycleSnapshot(); }
diff --git a/components/autofill/core/common/form_field_data.cc b/components/autofill/core/common/form_field_data.cc index fc86199..f84e8ac 100644 --- a/components/autofill/core/common/form_field_data.cc +++ b/components/autofill/core/common/form_field_data.cc
@@ -157,6 +157,8 @@ bool FormFieldData::SameFieldAs(const FormFieldData& field) const { // A FormFieldData stores a value, but the value is not part of the identity // of the field, so we don't want to compare the values. + // Similarly, flags like is_enabled, which are only used for parsing but are + // not stored persistently, are not used for comparison. return name == field.name && id == field.id && form_control_type == field.form_control_type && autocomplete_attribute == field.autocomplete_attribute && @@ -168,8 +170,7 @@ is_focusable == field.is_focusable && should_autocomplete == field.should_autocomplete && role == field.role && text_direction == field.text_direction && - is_enabled == field.is_enabled && is_readonly == field.is_readonly && - is_default == field.is_default && HaveSameLabel(*this, field); + HaveSameLabel(*this, field); // The option values/contents which are the list of items in the list // of a drop-down are currently not considered part of the identity of // a form element. This is debatable, since one might base heuristics @@ -263,19 +264,8 @@ return true; if (text_direction > field.text_direction) return false; - if (is_enabled < field.is_enabled) - return true; - if (is_enabled > field.is_enabled) - return false; - if (is_readonly < field.is_readonly) - return true; - if (is_readonly > field.is_readonly) - return false; - if (is_default < field.is_default) - return true; - if (is_default > field.is_default) - return false; - // See SameFieldAs above for why we don't check option_values/contents. + // See SameFieldAs above for why we don't check option_values/contents and + // flags like is_enabled. return false; }
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc index 7066aea..5487778c 100644 --- a/components/browser_sync/profile_sync_service.cc +++ b/components/browser_sync/profile_sync_service.cc
@@ -345,13 +345,13 @@ DCHECK(thread_checker_.CalledOnValidThread()); oauth2_token_service_->AddObserver(this); if (signin_) - signin_->GetIdentityManager()->AddObserver(this); + signin_->GetSigninManager()->AddObserver(this); } void ProfileSyncService::UnregisterAuthNotifications() { DCHECK(thread_checker_.CalledOnValidThread()); if (signin_) - signin_->GetIdentityManager()->RemoveObserver(this); + signin_->GetSigninManager()->RemoveObserver(this); if (oauth2_token_service_) oauth2_token_service_->RemoveObserver(this); } @@ -1720,13 +1720,12 @@ return last_snapshot_; } -bool ProfileSyncService::HasUnsyncedItemsForTest() const { +void ProfileSyncService::HasUnsyncedItemsForTest( + base::OnceCallback<void(bool)> cb) const { DCHECK(thread_checker_.CalledOnValidThread()); - if (HasSyncingEngine() && engine_initialized_) { - return engine_->HasUnsyncedItemsForTest(); - } - NOTREACHED(); - return false; + DCHECK(HasSyncingEngine()); + DCHECK(engine_initialized_); + engine_->HasUnsyncedItemsForTest(std::move(cb)); } BackendMigrator* ProfileSyncService::GetBackendMigratorForTest() { @@ -1921,8 +1920,8 @@ } } -void ProfileSyncService::OnPrimaryAccountSet( - const AccountInfo& primary_account_info) { +void ProfileSyncService::GoogleSigninSucceeded(const std::string& account_id, + const std::string& username) { DCHECK(thread_checker_.CalledOnValidThread()); if (!IsEngineInitialized() || GetAuthError().state() != GoogleServiceAuthError::NONE) { @@ -1930,14 +1929,12 @@ is_auth_in_progress_ = true; } - if (oauth2_token_service_->RefreshTokenIsAvailable( - primary_account_info.account_id)) { - OnRefreshTokenAvailable(primary_account_info.account_id); - } + if (oauth2_token_service_->RefreshTokenIsAvailable(account_id)) + OnRefreshTokenAvailable(account_id); } -void ProfileSyncService::OnPrimaryAccountCleared( - const AccountInfo& previous_primary_account_info) { +void ProfileSyncService::GoogleSignedOut(const std::string& account_id, + const std::string& username) { DCHECK(thread_checker_.CalledOnValidThread()); sync_disabled_by_admin_ = false; UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::SIGN_OUT,
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h index b2a972e..6790516 100644 --- a/components/browser_sync/profile_sync_service.h +++ b/components/browser_sync/profile_sync_service.h
@@ -20,6 +20,7 @@ #include "base/timer/timer.h" #include "build/build_config.h" #include "components/signin/core/browser/gaia_cookie_manager_service.h" +#include "components/signin/core/browser/signin_manager_base.h" #include "components/sync/base/experiments.h" #include "components/sync/base/model_type.h" #include "components/sync/base/sync_prefs.h" @@ -43,7 +44,6 @@ #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_token_service.h" #include "net/base/backoff_entry.h" -#include "services/identity/public/cpp/identity_manager.h" #include "url/gurl.h" class ProfileOAuth2TokenService; @@ -168,7 +168,7 @@ public syncer::DataTypeManagerObserver, public syncer::UnrecoverableErrorHandler, public OAuth2TokenService::Observer, - public identity::IdentityManager::Observer, + public SigninManagerBase::Observer, public GaiaCookieManagerService::Observer { public: using Status = syncer::SyncStatus; @@ -381,10 +381,11 @@ bool IsPassphraseRequired() const override; syncer::ModelTypeSet GetEncryptedDataTypes() const override; - // identity::IdentityManager::Observer implementation. - void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override; - void OnPrimaryAccountCleared( - const AccountInfo& previous_primary_account_info) override; + // SigninManagerBase::Observer implementation. + void GoogleSigninSucceeded(const std::string& account_id, + const std::string& username) override; + void GoogleSignedOut(const std::string& account_id, + const std::string& username) override; // GaiaCookieManagerService::Observer implementation. void OnGaiaAccountsInCookieUpdated( @@ -452,14 +453,10 @@ // The functions below (until ActivateDataType()) should only be // called if IsEngineInitialized() is true. - // TODO(akalin): These two functions are used only by - // ProfileSyncServiceHarness. Figure out a different way to expose - // this info to that class, and remove these functions. - // Returns whether or not the underlying sync engine has made any // local changes to items that have not yet been synced with the // server. - bool HasUnsyncedItemsForTest() const; + void HasUnsyncedItemsForTest(base::OnceCallback<void(bool)> cb) const; // Used by ProfileSyncServiceHarness. May return null. syncer::BackendMigrator* GetBackendMigratorForTest();
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc index 4cd533d4..fe7fb17 100644 --- a/components/browser_sync/profile_sync_service_unittest.cc +++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -560,8 +560,6 @@ signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST, signin_metrics::SignoutDelete::IGNORE_METRIC); - // Wait for PSS to be notified that the primary account has gone away. - base::RunLoop().RunUntilIdle(); EXPECT_FALSE(service()->IsSyncActive()); } #endif // !defined(OS_CHROMEOS) @@ -729,8 +727,8 @@ std::string primary_account_id = signin_manager()->GetAuthenticatedAccountId(); auth_service()->LoadCredentials(primary_account_id); - // Wait for ProfileSyncService to be notified of the loaded credentials and - // send an access token request. + // Wait for PSS to be notified of the loaded credentials and send an access + // token request. base::RunLoop().RunUntilIdle(); auth_service()->IssueAllTokensForAccount(primary_account_id, "access token", base::Time::Max()); @@ -740,7 +738,7 @@ // Emulate Chrome receiving a new, invalid LST. This happens when the user // signs out of the content area. auth_service()->UpdateCredentials(primary_account_id, "not a valid token"); - // Again, wait for ProfileSyncService to be notified. + // Again, wait for PSS to be notified. base::RunLoop().RunUntilIdle(); auth_service()->IssueErrorForAllPendingRequests( GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); @@ -774,8 +772,8 @@ std::string primary_account_id = signin_manager()->GetAuthenticatedAccountId(); auth_service()->LoadCredentials(primary_account_id); - // Wait for ProfileSyncService to be notified of the loaded credentials and - // send an access token request. + // Wait for PSS to be notified of the loaded credentials and send an access + // token request. base::RunLoop().RunUntilIdle(); auth_service()->IssueAllTokensForAccount(primary_account_id, "access token", base::Time::Max()); @@ -785,8 +783,8 @@ // Emulate Chrome receiving a new, invalid LST. This happens when the user // signs out of the content area. auth_service()->UpdateCredentials(primary_account_id, "not a valid token"); - // Wait for ProfileSyncService to be notified of the changed credentials and - // send a new access token request. + // Wait for PSS to be notified of the changed credentials and send a new + // access token request. base::RunLoop().RunUntilIdle(); auth_service()->IssueErrorForAllPendingRequests( GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); @@ -797,7 +795,7 @@ // Now emulate Chrome receiving a new, valid LST. auth_service()->UpdateCredentials(primary_account_id, "totally valid token"); - // Again, wait for ProfileSyncService to be notified. + // Again, wait for PSS to be notified. base::RunLoop().RunUntilIdle(); auth_service()->IssueTokenForAllPendingRequests( "this one works", base::Time::Now() + base::TimeDelta::FromDays(10)); @@ -1148,7 +1146,8 @@ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN, /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), - /*long_poll_interval=*/base::TimeDelta::FromMinutes(180))); + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false)); } // The OpenTabsUIDelegate should only be accessable when PROXY_TABS is enabled.
diff --git a/components/cast_channel/cast_message_util.cc b/components/cast_channel/cast_message_util.cc index 415556f5..0b3dc78 100644 --- a/components/cast_channel/cast_message_util.cc +++ b/components/cast_channel/cast_message_util.cc
@@ -277,6 +277,17 @@ return output; } +const char* GetAppAvailabilityResultToString(GetAppAvailabilityResult result) { + switch (result) { + case GetAppAvailabilityResult::kAvailable: + return "available"; + case GetAppAvailabilityResult::kUnavailable: + return "unavailable"; + case GetAppAvailabilityResult::kUnknown: + return "unknown"; + } +} + bool GetRequestIdFromResponse(const Value& payload, int* request_id) { DCHECK(request_id); DCHECK(payload.is_dict());
diff --git a/components/cast_channel/cast_message_util.h b/components/cast_channel/cast_message_util.h index c0882cc..4a64c59 100644 --- a/components/cast_channel/cast_message_util.h +++ b/components/cast_channel/cast_message_util.h
@@ -94,6 +94,8 @@ kUnknown, }; +const char* GetAppAvailabilityResultToString(GetAppAvailabilityResult result); + // Extracts request ID from |payload| corresponding to a Cast message response. // If request ID is available, assigns it to |request_id|. Return |true| if // request ID is found.
diff --git a/components/component_updater/component_updater_service.cc b/components/component_updater/component_updater_service.cc index c7ed4b0..b684882d 100644 --- a/components/component_updater/component_updater_service.cc +++ b/components/component_updater/component_updater_service.cc
@@ -291,7 +291,9 @@ UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL, UPDATE_TYPE_COUNT); update_client_->Install( - id, base::BindOnce(&CrxUpdateService::OnUpdate, base::Unretained(this)), + id, + base::BindOnce(&CrxUpdateService::GetCrxComponents, + base::Unretained(this)), base::BindOnce(&CrxUpdateService::OnUpdateComplete, base::Unretained(this), std::move(callback), base::TimeTicks::Now())); @@ -316,23 +318,23 @@ } if (!unsecure_ids.empty()) { - update_client_->Update( - unsecure_ids, - base::BindOnce(&CrxUpdateService::OnUpdate, base::Unretained(this)), - false, - base::BindOnce(&CrxUpdateService::OnUpdateComplete, - base::Unretained(this), Callback(), - base::TimeTicks::Now())); + update_client_->Update(unsecure_ids, + base::BindOnce(&CrxUpdateService::GetCrxComponents, + base::Unretained(this)), + false, + base::BindOnce(&CrxUpdateService::OnUpdateComplete, + base::Unretained(this), Callback(), + base::TimeTicks::Now())); } if (!secure_ids.empty()) { - update_client_->Update( - secure_ids, - base::BindOnce(&CrxUpdateService::OnUpdate, base::Unretained(this)), - false, - base::BindOnce(&CrxUpdateService::OnUpdateComplete, - base::Unretained(this), Callback(), - base::TimeTicks::Now())); + update_client_->Update(secure_ids, + base::BindOnce(&CrxUpdateService::GetCrxComponents, + base::Unretained(this)), + false, + base::BindOnce(&CrxUpdateService::OnUpdateComplete, + base::Unretained(this), Callback(), + base::TimeTicks::Now())); } return true; @@ -358,16 +360,17 @@ return false; } -void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { +std::vector<std::unique_ptr<CrxComponent>> CrxUpdateService::GetCrxComponents( + const std::vector<std::string>& ids) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(components->empty()); - + std::vector<std::unique_ptr<CrxComponent>> components; for (const auto& id : ids) { - const update_client::CrxComponent* registered_component(GetComponent(id)); - if (registered_component) - components->push_back(*registered_component); + const auto* registered_component = GetComponent(id); + components.push_back(registered_component ? std::make_unique<CrxComponent>( + *registered_component) + : nullptr); } + return components; } void CrxUpdateService::OnUpdateComplete(Callback callback,
diff --git a/components/component_updater/component_updater_service_internal.h b/components/component_updater/component_updater_service_internal.h index 7ad6243..1094ffa7 100644 --- a/components/component_updater/component_updater_service_internal.h +++ b/components/component_updater/component_updater_service_internal.h
@@ -76,8 +76,8 @@ const CrxUpdateItem* GetComponentState(const std::string& id) const; - void OnUpdate(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components); + std::vector<std::unique_ptr<CrxComponent>> GetCrxComponents( + const std::vector<std::string>& ids); void OnUpdateComplete(Callback callback, const base::TimeTicks& start_time, update_client::Error error);
diff --git a/components/cronet/tools/generators/cronet_c_generator.py b/components/cronet/tools/generators/cronet_c_generator.py index 03e0a26..7dacd42 100644 --- a/components/cronet/tools/generators/cronet_c_generator.py +++ b/components/cronet/tools/generators/cronet_c_generator.py
@@ -544,8 +544,7 @@ def _GetCppWrapperType(self, kind, add_same_module_namespaces=False): def _AddOptional(type_name): - pattern = "WTF::Optional<%s>" if self.for_blink else "base::Optional<%s>" - return pattern % type_name + return "base::Optional<%s>" % type_name if self._IsTypemappedKind(kind): type_name = self._GetNativeTypeName(kind)
diff --git a/components/exo/wayland/clients/info.cc b/components/exo/wayland/clients/info.cc index b2b9bba..1d79648 100644 --- a/components/exo/wayland/clients/info.cc +++ b/components/exo/wayland/clients/info.cc
@@ -232,6 +232,7 @@ case ZAURA_OUTPUT_SCALE_FACTOR_0900: case ZAURA_OUTPUT_SCALE_FACTOR_0950: case ZAURA_OUTPUT_SCALE_FACTOR_1000: + case ZAURA_OUTPUT_SCALE_FACTOR_1050: case ZAURA_OUTPUT_SCALE_FACTOR_1100: case ZAURA_OUTPUT_SCALE_FACTOR_1150: case ZAURA_OUTPUT_SCALE_FACTOR_1125: @@ -245,6 +246,7 @@ case ZAURA_OUTPUT_SCALE_FACTOR_1750: case ZAURA_OUTPUT_SCALE_FACTOR_1800: case ZAURA_OUTPUT_SCALE_FACTOR_2000: + case ZAURA_OUTPUT_SCALE_FACTOR_2200: case ZAURA_OUTPUT_SCALE_FACTOR_2250: case ZAURA_OUTPUT_SCALE_FACTOR_2500: case ZAURA_OUTPUT_SCALE_FACTOR_2750:
diff --git a/components/exo/wayland/protocol/aura-shell.xml b/components/exo/wayland/protocol/aura-shell.xml index a96d6be..443cf28 100644 --- a/components/exo/wayland/protocol/aura-shell.xml +++ b/components/exo/wayland/protocol/aura-shell.xml
@@ -164,6 +164,7 @@ <entry name="0900" value="900"/> <entry name="0950" value="950"/> <entry name="1000" value="1000"/> + <entry name="1050" value="1050"/> <entry name="1100" value="1100"/> <entry name="1150" value="1150"/> <entry name="1125" value="1125"/> @@ -177,6 +178,7 @@ <entry name="1750" value="1750"/> <entry name="1800" value="1800"/> <entry name="2000" value="2000"/> + <entry name="2200" value="2200"/> <entry name="2250" value="2250"/> <entry name="2500" value="2500"/> <entry name="2750" value="2750"/>
diff --git a/components/exo/wayland/public/aura-shell-client-protocol.h b/components/exo/wayland/public/aura-shell-client-protocol.h index 4272325..6ae7fe2 100644 --- a/components/exo/wayland/public/aura-shell-client-protocol.h +++ b/components/exo/wayland/public/aura-shell-client-protocol.h
@@ -368,6 +368,7 @@ ZAURA_OUTPUT_SCALE_FACTOR_0900 = 900, ZAURA_OUTPUT_SCALE_FACTOR_0950 = 950, ZAURA_OUTPUT_SCALE_FACTOR_1000 = 1000, + ZAURA_OUTPUT_SCALE_FACTOR_1050 = 1050, ZAURA_OUTPUT_SCALE_FACTOR_1100 = 1100, ZAURA_OUTPUT_SCALE_FACTOR_1150 = 1150, ZAURA_OUTPUT_SCALE_FACTOR_1125 = 1125, @@ -381,6 +382,7 @@ ZAURA_OUTPUT_SCALE_FACTOR_1750 = 1750, ZAURA_OUTPUT_SCALE_FACTOR_1800 = 1800, ZAURA_OUTPUT_SCALE_FACTOR_2000 = 2000, + ZAURA_OUTPUT_SCALE_FACTOR_2200 = 2200, ZAURA_OUTPUT_SCALE_FACTOR_2250 = 2250, ZAURA_OUTPUT_SCALE_FACTOR_2500 = 2500, ZAURA_OUTPUT_SCALE_FACTOR_2750 = 2750,
diff --git a/components/exo/wayland/public/aura-shell-server-protocol.h b/components/exo/wayland/public/aura-shell-server-protocol.h index b72ff33..6f5791c 100644 --- a/components/exo/wayland/public/aura-shell-server-protocol.h +++ b/components/exo/wayland/public/aura-shell-server-protocol.h
@@ -303,6 +303,7 @@ ZAURA_OUTPUT_SCALE_FACTOR_0900 = 900, ZAURA_OUTPUT_SCALE_FACTOR_0950 = 950, ZAURA_OUTPUT_SCALE_FACTOR_1000 = 1000, + ZAURA_OUTPUT_SCALE_FACTOR_1050 = 1050, ZAURA_OUTPUT_SCALE_FACTOR_1100 = 1100, ZAURA_OUTPUT_SCALE_FACTOR_1150 = 1150, ZAURA_OUTPUT_SCALE_FACTOR_1125 = 1125, @@ -316,6 +317,7 @@ ZAURA_OUTPUT_SCALE_FACTOR_1750 = 1750, ZAURA_OUTPUT_SCALE_FACTOR_1800 = 1800, ZAURA_OUTPUT_SCALE_FACTOR_2000 = 2000, + ZAURA_OUTPUT_SCALE_FACTOR_2200 = 2200, ZAURA_OUTPUT_SCALE_FACTOR_2250 = 2250, ZAURA_OUTPUT_SCALE_FACTOR_2500 = 2500, ZAURA_OUTPUT_SCALE_FACTOR_2750 = 2750,
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index 96991358..a74ac9e 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc
@@ -2913,13 +2913,16 @@ bool rv = display_manager->GetActiveModeForDisplayId(display.id(), &active_mode); DCHECK(rv); + const int32_t current_output_scale = + std::round(display_info.zoom_factor() * 1000.f); for (double zoom_factor : display::GetDisplayZoomFactors(active_mode)) { + const int32_t output_scale = std::round(zoom_factor * 1000.0); uint32_t flags = 0; - if (zoom_factor == 1.0) + if (output_scale == 1000) flags |= ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED; - if (display_info.zoom_factor() == zoom_factor) + if (current_output_scale == output_scale) flags |= ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT; - zaura_output_send_scale(resource_, flags, zoom_factor * 1000); + zaura_output_send_scale(resource_, flags, output_scale); } } else if (display_manager->GetDisplayIdForUIScaling() == display.id()) { display::ManagedDisplayMode active_mode;
diff --git a/components/metrics/call_stack_profile_collector.cc b/components/metrics/call_stack_profile_collector.cc index 3dfb1de..aa127fbf 100644 --- a/components/metrics/call_stack_profile_collector.cc +++ b/components/metrics/call_stack_profile_collector.cc
@@ -38,9 +38,8 @@ return; CallStackProfileParams params_copy = params; - params_copy.start_timestamp = start_timestamp; CallStackProfileMetricsProvider::ReceiveCompletedProfiles( - ¶ms_copy, std::move(profiles)); + params_copy, start_timestamp, std::move(profiles)); } } // namespace metrics
diff --git a/components/metrics/call_stack_profile_metrics_provider.cc b/components/metrics/call_stack_profile_metrics_provider.cc index e9429c84..bce2bbb 100644 --- a/components/metrics/call_stack_profile_metrics_provider.cc +++ b/components/metrics/call_stack_profile_metrics_provider.cc
@@ -58,6 +58,7 @@ // with them. struct ProfilesState { ProfilesState(const CallStackProfileParams& params, + base::TimeTicks start_timestamp, StackSamplingProfiler::CallStackProfiles profiles); ProfilesState(ProfilesState&&); ProfilesState& operator=(ProfilesState&&); @@ -66,6 +67,9 @@ // CallStackProfileMetricsProvider::GetProfilerCallback(). CallStackProfileParams params; + // The time at which the profile collection was started. + base::TimeTicks start_timestamp; + // The call stack profiles collected by the profiler. StackSamplingProfiler::CallStackProfiles profiles; @@ -74,8 +78,11 @@ }; ProfilesState::ProfilesState(const CallStackProfileParams& params, + base::TimeTicks start_timestamp, StackSamplingProfiler::CallStackProfiles profiles) - : params(params), profiles(std::move(profiles)) {} + : params(params), + start_timestamp(start_timestamp), + profiles(std::move(profiles)) {} ProfilesState::ProfilesState(ProfilesState&&) = default; @@ -171,7 +178,7 @@ // since the start of collection for this profile. if (!collection_enabled_ || (!last_collection_disable_time_.is_null() && - last_collection_disable_time_ >= profiles.params.start_timestamp)) { + last_collection_disable_time_ >= profiles.start_timestamp)) { return; } @@ -199,20 +206,17 @@ // Will be invoked on either the main thread or the profiler's thread. Provides // the profiles to PendingProfiles to append, if the collecting state allows. -base::Optional<StackSamplingProfiler::SamplingParams> -ReceiveCompletedProfilesImpl( - CallStackProfileParams* params, +void ReceiveCompletedProfilesImpl( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, StackSamplingProfiler::CallStackProfiles profiles) { PendingProfiles::GetInstance()->CollectProfilesIfCollectionEnabled( - ProfilesState(*params, std::move(profiles))); - return base::Optional<StackSamplingProfiler::SamplingParams>(); + ProfilesState(params, start_timestamp, std::move(profiles))); } // Invoked on an arbitrary thread. Ignores the provided profiles. -base::Optional<StackSamplingProfiler::SamplingParams> IgnoreCompletedProfiles( - StackSamplingProfiler::CallStackProfiles profiles) { - return base::Optional<StackSamplingProfiler::SamplingParams>(); -} +void IgnoreCompletedProfiles( + StackSamplingProfiler::CallStackProfiles profiles) {} // Functions to encode protobufs ---------------------------------------------- @@ -407,22 +411,23 @@ StackSamplingProfiler::CompletedCallback CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess( - CallStackProfileParams* params) { + const CallStackProfileParams& params) { // Ignore the profiles if the collection is disabled. If the collection state // changes while collecting, this will be detected by the callback and // profiles will be ignored at that point. if (!PendingProfiles::GetInstance()->IsCollectionEnabled()) return base::Bind(&IgnoreCompletedProfiles); - params->start_timestamp = base::TimeTicks::Now(); - return base::Bind(&ReceiveCompletedProfilesImpl, params); + return base::Bind(&ReceiveCompletedProfilesImpl, params, + base::TimeTicks::Now()); } // static void CallStackProfileMetricsProvider::ReceiveCompletedProfiles( - CallStackProfileParams* params, + const CallStackProfileParams& params, + base::TimeTicks profile_start_time, base::StackSamplingProfiler::CallStackProfiles profiles) { - ReceiveCompletedProfilesImpl(params, std::move(profiles)); + ReceiveCompletedProfilesImpl(params, profile_start_time, std::move(profiles)); } void CallStackProfileMetricsProvider::OnRecordingEnabled() {
diff --git a/components/metrics/call_stack_profile_metrics_provider.h b/components/metrics/call_stack_profile_metrics_provider.h index 7d97ba4..f995a9a4 100644 --- a/components/metrics/call_stack_profile_metrics_provider.h +++ b/components/metrics/call_stack_profile_metrics_provider.h
@@ -42,14 +42,15 @@ // parameters for general browser process sampling. The callback should be // immediately passed to the StackSamplingProfiler, and should not be reused. static base::StackSamplingProfiler::CompletedCallback - GetProfilerCallbackForBrowserProcess(CallStackProfileParams* params); + GetProfilerCallbackForBrowserProcess(const CallStackProfileParams& params); // Provides completed stack profiles to the metrics provider. Intended for use // when receiving profiles over IPC. In-process StackSamplingProfiler users // should instead use a variant of GetProfilerCallback*(). |profiles| is not // const& because it must be passed with std::move. static void ReceiveCompletedProfiles( - CallStackProfileParams* params, + const CallStackProfileParams& params, + base::TimeTicks profile_start_time, base::StackSamplingProfiler::CallStackProfiles profiles); // MetricsProvider:
diff --git a/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/components/metrics/call_stack_profile_metrics_provider_unittest.cc index ccee6cb0..c477711 100644 --- a/components/metrics/call_stack_profile_metrics_provider_unittest.cc +++ b/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -129,7 +129,7 @@ ~CallStackProfileMetricsProviderTest() override {} // Utility function to append profiles to the metrics provider. - void AppendProfiles(CallStackProfileParams* params, Profiles profiles) { + void AppendProfiles(const CallStackProfileParams& params, Profiles profiles) { CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess( params) .Run(std::move(profiles)); @@ -344,7 +344,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::MAY_SHUFFLE); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); @@ -428,7 +428,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::MAY_SHUFFLE); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); @@ -514,7 +514,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::PRESERVE_ORDER); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); @@ -558,7 +558,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::MAY_SHUFFLE); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); @@ -591,7 +591,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::MAY_SHUFFLE); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); @@ -621,7 +621,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::MAY_SHUFFLE); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); @@ -648,7 +648,7 @@ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::PROCESS_STARTUP, CallStackProfileParams::MAY_SHUFFLE); - AppendProfiles(¶ms, std::move(profiles)); + AppendProfiles(params, std::move(profiles)); ChromeUserMetricsExtension uma_proto; provider.ProvideCurrentSessionData(&uma_proto); @@ -667,7 +667,7 @@ CallStackProfileParams::MAY_SHUFFLE); base::StackSamplingProfiler::CompletedCallback callback = CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess( - ¶ms); + params); provider.OnRecordingDisabled(); Profiles profiles = ProfilesFactory() @@ -694,7 +694,7 @@ CallStackProfileParams::MAY_SHUFFLE); base::StackSamplingProfiler::CompletedCallback callback = CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess( - ¶ms); + params); provider.OnRecordingDisabled(); provider.OnRecordingEnabled(); @@ -722,7 +722,7 @@ CallStackProfileParams::MAY_SHUFFLE); base::StackSamplingProfiler::CompletedCallback callback = CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess( - ¶ms); + params); provider.OnRecordingEnabled(); Profiles profiles = ProfilesFactory()
diff --git a/components/metrics/call_stack_profile_params.h b/components/metrics/call_stack_profile_params.h index 5aecf65..05e4203 100644 --- a/components/metrics/call_stack_profile_params.h +++ b/components/metrics/call_stack_profile_params.h
@@ -85,12 +85,6 @@ // Whether to preserve sample ordering. SampleOrderingSpec ordering_spec; - - // The time at which the CallStackProfileMetricsProvider became aware of the - // request for profiling. In particular, this is when callback was requested - // via CallStackProfileMetricsProvider::GetProfilerCallback(). Used to - // determine if collection was disabled during the collection of the profile. - base::TimeTicks start_timestamp; }; } // namespace metrics
diff --git a/components/metrics/child_call_stack_profile_collector.cc b/components/metrics/child_call_stack_profile_collector.cc index 71ed56ca..978ecd73 100644 --- a/components/metrics/child_call_stack_profile_collector.cc +++ b/components/metrics/child_call_stack_profile_collector.cc
@@ -40,11 +40,11 @@ base::StackSamplingProfiler::CompletedCallback ChildCallStackProfileCollector::GetProfilerCallback( - const CallStackProfileParams& params) { + const CallStackProfileParams& params, + base::TimeTicks profile_start_time) { return base::Bind(&ChildCallStackProfileCollector::Collect, // This class has lazy instance lifetime. - base::Unretained(this), params, - base::TimeTicks::Now()); + base::Unretained(this), params, profile_start_time); } void ChildCallStackProfileCollector::SetParentProfileCollector( @@ -67,16 +67,13 @@ profiles_.clear(); } -base::Optional<base::StackSamplingProfiler::SamplingParams> -ChildCallStackProfileCollector::Collect( +void ChildCallStackProfileCollector::Collect( const CallStackProfileParams& params, base::TimeTicks start_timestamp, std::vector<CallStackProfile> profiles) { // Impl function is used as it needs to PostTask() to itself on a different // thread - which only works with a void return value. CollectImpl(params, start_timestamp, std::move(profiles)); - // Empty return value indicates that collection should not be re-started. - return base::Optional<base::StackSamplingProfiler::SamplingParams>(); } void ChildCallStackProfileCollector::CollectImpl(
diff --git a/components/metrics/child_call_stack_profile_collector.h b/components/metrics/child_call_stack_profile_collector.h index 5cf0879..a829f5191 100644 --- a/components/metrics/child_call_stack_profile_collector.h +++ b/components/metrics/child_call_stack_profile_collector.h
@@ -55,7 +55,8 @@ // StackSamplingProfiler, and should not be reused between // StackSamplingProfilers. This function may be called on any thread. base::StackSamplingProfiler::CompletedCallback GetProfilerCallback( - const CallStackProfileParams& params); + const CallStackProfileParams& params, + base::TimeTicks profile_start_time); // Sets the CallStackProfileCollector interface from |parent_collector|. This // function MUST be invoked exactly once, regardless of whether @@ -92,10 +93,9 @@ using CallStackProfile = base::StackSamplingProfiler::CallStackProfile; - base::Optional<base::StackSamplingProfiler::SamplingParams> Collect( - const CallStackProfileParams& params, - base::TimeTicks start_timestamp, - std::vector<CallStackProfile> profiles); + void Collect(const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + std::vector<CallStackProfile> profiles); void CollectImpl(const CallStackProfileParams& params, base::TimeTicks start_timestamp,
diff --git a/components/metrics/child_call_stack_profile_collector_unittest.cc b/components/metrics/child_call_stack_profile_collector_unittest.cc index 50408431..8bf1c5b 100644 --- a/components/metrics/child_call_stack_profile_collector_unittest.cc +++ b/components/metrics/child_call_stack_profile_collector_unittest.cc
@@ -60,7 +60,8 @@ base::StackSamplingProfiler::CallStackProfiles profiles; for (size_t i = 0; i < profile_count; ++i) profiles.push_back(base::StackSamplingProfiler::CallStackProfile()); - child_collector_.GetProfilerCallback(params).Run(std::move(profiles)); + child_collector_.GetProfilerCallback(params, base::TimeTicks::Now()) + .Run(std::move(profiles)); } const std::vector<ChildCallStackProfileCollector::ProfilesState>&
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc index 00fa3ba..d4c32f9 100644 --- a/components/omnibox/browser/autocomplete_match.cc +++ b/components/omnibox/browser/autocomplete_match.cc
@@ -658,6 +658,10 @@ destination_url.host() : std::string()); } +GURL AutocompleteMatch::ImageUrl() const { + return answer ? answer->second_line().image_url() : GURL(image_url); +} + void AutocompleteMatch::RecordAdditionalInfo(const std::string& property, const std::string& value) { DCHECK(!property.empty());
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h index e4bd3f7..c1df4008 100644 --- a/components/omnibox/browser/autocomplete_match.h +++ b/components/omnibox/browser/autocomplete_match.h
@@ -63,7 +63,7 @@ // This structure holds the classification information for each span. struct ACMatchClassification { // The values in here are not mutually exclusive -- use them like a - // bitfield. This also means we use "int" instead of this enum type when + // bit field. This also means we use "int" instead of this enum type when // passing the values around, so the compiler doesn't complain. // // A Java counterpart will be generated for this enum. @@ -294,6 +294,10 @@ TemplateURL* GetTemplateURL(TemplateURLService* template_url_service, bool allow_fallback_to_destination_host) const; + // Gets the URL for the match image (whether it be an answer or entity). If + // there isn't an image URL, returns an empty GURL (test with is_empty()). + GURL ImageUrl() const; + // Adds optional information to the |additional_info| dictionary. void RecordAdditionalInfo(const std::string& property, const std::string& value);
diff --git a/components/omnibox/browser/omnibox_client.h b/components/omnibox/browser/omnibox_client.h index 67f8c1a..2b6d15598 100644 --- a/components/omnibox/browser/omnibox_client.h +++ b/components/omnibox/browser/omnibox_client.h
@@ -29,7 +29,7 @@ } using BitmapFetchedCallback = - base::RepeatingCallback<void(const SkBitmap& bitmap)>; + base::RepeatingCallback<void(int result_index, const SkBitmap& bitmap)>; using FaviconFetchedCallback = base::OnceCallback<void(const gfx::Image& favicon)>;
diff --git a/components/omnibox/browser/omnibox_controller.cc b/components/omnibox/browser/omnibox_controller.cc index 156a791f..e2bc11ed 100644 --- a/components/omnibox/browser/omnibox_controller.cc +++ b/components/omnibox/browser/omnibox_controller.cc
@@ -81,6 +81,7 @@ popup_->SetSelectedLineState(OmniboxPopupModel::NORMAL); } -void OmniboxController::SetRichSuggestionBitmap(const SkBitmap& bitmap) { - popup_->SetRichSuggestionBitmap(bitmap); +void OmniboxController::SetRichSuggestionBitmap(int result_index, + const SkBitmap& bitmap) { + popup_->SetRichSuggestionBitmap(result_index, bitmap); }
diff --git a/components/omnibox/browser/omnibox_controller.h b/components/omnibox/browser/omnibox_controller.h index 76e5a67..8709244 100644 --- a/components/omnibox/browser/omnibox_controller.h +++ b/components/omnibox/browser/omnibox_controller.h
@@ -68,7 +68,7 @@ private: // Stores the bitmap in the OmniboxPopupModel. - void SetRichSuggestionBitmap(const SkBitmap& bitmap); + void SetRichSuggestionBitmap(int result_index, const SkBitmap& bitmap); // Weak, it owns us. // TODO(beaudoin): Consider defining a delegate to ease unit testing.
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc index a34a13c..004cd4b 100644 --- a/components/omnibox/browser/omnibox_popup_model.cc +++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -231,7 +231,7 @@ } void OmniboxPopupModel::OnResultChanged() { - rich_suggestion_bitmap_ = SkBitmap(); + rich_suggestion_bitmaps_.clear(); const AutocompleteResult& result = this->result(); selected_line_ = result.default_match() == result.end() ? kNoMatch : static_cast<size_t>(result.default_match() - result.begin()); @@ -246,8 +246,18 @@ edit_model_->controller()->OnPopupVisibilityChanged(); } -void OmniboxPopupModel::SetRichSuggestionBitmap(const SkBitmap& bitmap) { - rich_suggestion_bitmap_ = bitmap; +const SkBitmap* OmniboxPopupModel::RichSuggestionBitmapAt( + int result_index) const { + const auto iter = rich_suggestion_bitmaps_.find(result_index); + if (iter == rich_suggestion_bitmaps_.end()) { + return nullptr; + } + return &iter->second; +} + +void OmniboxPopupModel::SetRichSuggestionBitmap(int result_index, + const SkBitmap& bitmap) { + rich_suggestion_bitmaps_[result_index] = bitmap; view_->UpdatePopupAppearance(); }
diff --git a/components/omnibox/browser/omnibox_popup_model.h b/components/omnibox/browser/omnibox_popup_model.h index 75116bb..3830053 100644 --- a/components/omnibox/browser/omnibox_popup_model.h +++ b/components/omnibox/browser/omnibox_popup_model.h
@@ -6,6 +6,7 @@ #define COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_POPUP_MODEL_H_ #include <stddef.h> +#include <map> #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -128,11 +129,10 @@ void AddObserver(OmniboxPopupModelObserver* observer); void RemoveObserver(OmniboxPopupModelObserver* observer); + // Lookup the bitmap for |result_index|. Returns nullptr if not found. + const SkBitmap* RichSuggestionBitmapAt(int result_index) const; // Stores the image in a local data member and schedules a repaint. - void SetRichSuggestionBitmap(const SkBitmap& bitmap); - const SkBitmap& rich_suggestion_bitmap() const { - return rich_suggestion_bitmap_; - } + void SetRichSuggestionBitmap(int result_index, const SkBitmap& bitmap); #if !defined(OS_ANDROID) && !defined(OS_IOS) // Gets the icon for the match index. @@ -147,7 +147,7 @@ private: void OnFaviconFetched(const GURL& page_url, const gfx::Image& icon); - SkBitmap rich_suggestion_bitmap_; + std::map<int, SkBitmap> rich_suggestion_bitmaps_; OmniboxPopupView* view_;
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc index 4f4d241..14e1760 100644 --- a/components/omnibox/browser/zero_suggest_provider.cc +++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -153,9 +153,6 @@ done_ = false; - // TODO(jered): Consider adding locally-sourced zero-suggestions here too. - // These may be useful on the NTP or more relevant to the user than server - // suggestions, if based on local browsing history. MaybeUseCachedSuggestions(); if (result_type_running_ == MOST_VISITED) { @@ -538,10 +535,6 @@ ZeroSuggestProvider::ResultType ZeroSuggestProvider::TypeOfResultToRun( const GURL& current_url, const GURL& suggest_url) { - // TODO(jered): Consider adding locally-sourced zero-suggestions here too. - // These may be useful on the NTP or more relevant to the user than server - // suggestions, if based on local browsing history. - // Check if the URL can be sent in any suggest request. const TemplateURLService* template_url_service = client()->GetTemplateURLService();
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h index 88f2ddd..3e3fa8b 100644 --- a/components/omnibox/browser/zero_suggest_provider.h +++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -37,10 +37,9 @@ // Autocomplete provider for searches based on the current URL. // -// The controller will call Start() with |on_focus| set when the user focuses -// the omnibox. After construction, the autocomplete controller repeatedly calls -// Start() with some user input, each time expecting to receive an updated set -// of matches. +// The controller will call Start() when the user focuses the omnibox. After +// construction, the autocomplete controller repeatedly calls Start() with some +// user input, each time expecting to receive an updated set of matches. // // TODO(jered): Consider deleting this class and building this functionality // into SearchProvider after dogfood and after we break the association between
diff --git a/components/printing/browser/print_manager_utils.cc b/components/printing/browser/print_manager_utils.cc index 59b9ca8..a38d8ab6 100644 --- a/components/printing/browser/print_manager_utils.cc +++ b/components/printing/browser/print_manager_utils.cc
@@ -68,6 +68,7 @@ params->url = settings.url(); params->printed_doc_type = IsOopifEnabled() ? SkiaDocumentType::MSKP : SkiaDocumentType::PDF; + params->num_pages_per_sheet = settings.num_pages_per_sheet(); } } // namespace printing
diff --git a/components/printing/common/print_messages.cc b/components/printing/common/print_messages.cc index b308dc4..f4647d65 100644 --- a/components/printing/common/print_messages.cc +++ b/components/printing/common/print_messages.cc
@@ -96,7 +96,8 @@ footer_template(), should_print_backgrounds(false), printed_doc_type(printing::SkiaDocumentType::PDF), - prefer_css_page_size(false) {} + prefer_css_page_size(false), + num_pages_per_sheet(1) {} PrintMsg_Print_Params::PrintMsg_Print_Params( const PrintMsg_Print_Params& other) = default; @@ -128,6 +129,7 @@ should_print_backgrounds = false; printed_doc_type = printing::SkiaDocumentType::PDF; prefer_css_page_size = false; + num_pages_per_sheet = 1; } PrintMsg_PrintPages_Params::PrintMsg_PrintPages_Params()
diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h index b9e8aa76..cd3fe29 100644 --- a/components/printing/common/print_messages.h +++ b/components/printing/common/print_messages.h
@@ -63,6 +63,7 @@ bool should_print_backgrounds; printing::SkiaDocumentType printed_doc_type; bool prefer_css_page_size; + int num_pages_per_sheet; }; struct PrintMsg_PrintPages_Params { @@ -191,6 +192,11 @@ // True if page size defined by css should be preferred. IPC_STRUCT_TRAITS_MEMBER(prefer_css_page_size) + + // Number of pages per sheet. This parameter is for N-up mode. + // Defaults to 1 if the feature is disabled, and some number greater + // than 1 otherwise. See printing::NupParameters for supported values. + IPC_STRUCT_TRAITS_MEMBER(num_pages_per_sheet) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(printing::PageRange)
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc index d73427c..633afea 100644 --- a/components/printing/renderer/print_render_frame_helper.cc +++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -310,6 +310,9 @@ ConvertUnit(print_params.page_size.width(), dpi, kPointsPerInch); webkit_print_params->paper_size.height = ConvertUnit(print_params.page_size.height(), dpi, kPointsPerInch); + + // The following settings is for N-up mode. + webkit_print_params->num_pages_per_sheet = print_params.num_pages_per_sheet; } blink::WebPlugin* GetPlugin(const blink::WebLocalFrame* frame) {
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn index b59f917..8ef8074 100644 --- a/components/signin/core/browser/BUILD.gn +++ b/components/signin/core/browser/BUILD.gn
@@ -194,6 +194,8 @@ "fake_signin_manager.h", "scoped_account_consistency.cc", "scoped_account_consistency.h", + "scoped_unified_consent.cc", + "scoped_unified_consent.h", "test_signin_client.cc", "test_signin_client.h", ]
diff --git a/components/signin/core/browser/profile_management_switches.cc b/components/signin/core/browser/profile_management_switches.cc index a26592c..301da57 100644 --- a/components/signin/core/browser/profile_management_switches.cc +++ b/components/signin/core/browser/profile_management_switches.cc
@@ -76,6 +76,7 @@ const base::Feature kUnifiedConsent{"UnifiedConsent", base::FEATURE_DISABLED_BY_DEFAULT}; +const char kUnifiedConsentShowBumpParameter[] = "show_consent_bump"; bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a, AccountConsistencyMethod b) { @@ -212,4 +213,14 @@ *GetIsGaiaIsolatedCallback() = is_gaia_isolated; } +UnifiedConsentFeatureState GetUnifiedConsentFeatureState() { + if (!base::FeatureList::IsEnabled(signin::kUnifiedConsent)) + return UnifiedConsentFeatureState::kDisabled; + + std::string show_bump = base::GetFieldTrialParamValueByFeature( + kUnifiedConsent, kUnifiedConsentShowBumpParameter); + return show_bump.empty() ? UnifiedConsentFeatureState::kEnabledNoBump + : UnifiedConsentFeatureState::kEnabledWithBump; +} + } // namespace signin
diff --git a/components/signin/core/browser/profile_management_switches.h b/components/signin/core/browser/profile_management_switches.h index abc74d6..281effab 100644 --- a/components/signin/core/browser/profile_management_switches.h +++ b/components/signin/core/browser/profile_management_switches.h
@@ -39,6 +39,17 @@ // Improved and unified consent for privacy-related features. extern const base::Feature kUnifiedConsent; +extern const char kUnifiedConsentShowBumpParameter[]; + +// State of the "Unified Consent" feature. +enum class UnifiedConsentFeatureState { + // Unified consent is disabled. + kDisabled, + // Unified consent is enabled, but the bump is not shown. + kEnabledNoBump, + // Unified consent is enabled and the bump is shown. + kEnabledWithBump +}; // TODO(https://crbug.com/777774): Cleanup this enum and remove related // functions once Dice is fully rolled out, and/or Mirror code is removed on @@ -144,6 +155,9 @@ void SetGaiaOriginIsolatedCallback( const base::RepeatingCallback<bool()>& is_gaia_isolated); +// Returns the state of the "Unified Consent" feature. +UnifiedConsentFeatureState GetUnifiedConsentFeatureState(); + } // namespace signin #endif // COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
diff --git a/components/signin/core/browser/profile_management_switches_unittest.cc b/components/signin/core/browser/profile_management_switches_unittest.cc index c0f4046..5efa1cc 100644 --- a/components/signin/core/browser/profile_management_switches_unittest.cc +++ b/components/signin/core/browser/profile_management_switches_unittest.cc
@@ -9,8 +9,10 @@ #include "base/bind.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "build/buildflag.h" #include "components/prefs/pref_member.h" #include "components/signin/core/browser/scoped_account_consistency.h" +#include "components/signin/core/browser/scoped_unified_consent.h" #include "components/signin/core/browser/signin_buildflags.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" @@ -132,4 +134,18 @@ #endif // BUILDFLAG(ENABLE_MIRROR) +TEST(ProfileManagementSwitchesTest, UnifiedConsent) { + // Unified consent is disabled by default. + EXPECT_EQ(UnifiedConsentFeatureState::kDisabled, + GetUnifiedConsentFeatureState()); + + for (UnifiedConsentFeatureState state : + {UnifiedConsentFeatureState::kDisabled, + UnifiedConsentFeatureState::kEnabledNoBump, + UnifiedConsentFeatureState::kEnabledWithBump}) { + ScopedUnifiedConsent scoped_state(state); + EXPECT_EQ(state, GetUnifiedConsentFeatureState()); + } +} + } // namespace signin
diff --git a/components/signin/core/browser/scoped_unified_consent.cc b/components/signin/core/browser/scoped_unified_consent.cc new file mode 100644 index 0000000..8c19fa1 --- /dev/null +++ b/components/signin/core/browser/scoped_unified_consent.cc
@@ -0,0 +1,40 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/core/browser/scoped_unified_consent.h" + +#include <map> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/feature_list.h" +#include "base/logging.h" +#include "base/test/scoped_feature_list.h" + +namespace signin { + +ScopedUnifiedConsent::ScopedUnifiedConsent(UnifiedConsentFeatureState state) { + switch (state) { + case UnifiedConsentFeatureState::kDisabled: + scoped_feature_list_.InitAndDisableFeature(kUnifiedConsent); + break; + case UnifiedConsentFeatureState::kEnabledNoBump: + scoped_feature_list_.InitAndEnableFeature(kUnifiedConsent); + break; + case UnifiedConsentFeatureState::kEnabledWithBump: { + std::map<std::string, std::string> feature_params; + feature_params[kUnifiedConsentShowBumpParameter] = "true"; + scoped_feature_list_.InitAndEnableFeatureWithParameters(kUnifiedConsent, + feature_params); + break; + } + } + + DCHECK_EQ(state, GetUnifiedConsentFeatureState()); +} + +ScopedUnifiedConsent::~ScopedUnifiedConsent() {} + +} // namespace signin
diff --git a/components/signin/core/browser/scoped_unified_consent.h b/components/signin/core/browser/scoped_unified_consent.h new file mode 100644 index 0000000..69e55dc --- /dev/null +++ b/components/signin/core/browser/scoped_unified_consent.h
@@ -0,0 +1,31 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_UNIFIED_CONSENT_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_UNIFIED_CONSENT_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/test/scoped_feature_list.h" +#include "components/signin/core/browser/profile_management_switches.h" + +namespace signin { + +// Changes the unified consent feature state while it is in scope. Useful for +// tests. +class ScopedUnifiedConsent { + public: + explicit ScopedUnifiedConsent(UnifiedConsentFeatureState state); + ~ScopedUnifiedConsent(); + + private: + base::test::ScopedFeatureList scoped_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(ScopedUnifiedConsent); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_UNIFIED_CONSENT_H_
diff --git a/components/subresource_filter/tools/BUILD.gn b/components/subresource_filter/tools/BUILD.gn index 19a8274..f825e1a 100644 --- a/components/subresource_filter/tools/BUILD.gn +++ b/components/subresource_filter/tools/BUILD.gn
@@ -29,6 +29,7 @@ "../core/common", "../core/common:test_support", "rule_parser:unit_tests", + "ruleset_converter:unit_tests", "//base", "//base/test:test_support", "//components/url_pattern_index:test_support",
diff --git a/components/subresource_filter/tools/ruleset_converter/BUILD.gn b/components/subresource_filter/tools/ruleset_converter/BUILD.gn new file mode 100644 index 0000000..652c88d0 --- /dev/null +++ b/components/subresource_filter/tools/ruleset_converter/BUILD.gn
@@ -0,0 +1,37 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("ruleset_converter_support") { + sources = [ + "//third_party/protobuf:protobuf_lite", + "rule_stream.cc", + "rule_stream.h", + "ruleset_format.cc", + "ruleset_format.h", + ] + deps = [ + "../rule_parser", + "//base", + "//components/url_pattern_index", + "//components/url_pattern_index/proto:url_pattern_index", + "//third_party/protobuf:protobuf_lite", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "rule_stream_test.cc", + ] + deps = [ + ":ruleset_converter_support", + "../rule_parser", + "//base", + "//base/test:test_support", + "//components/url_pattern_index:test_support", + "//components/url_pattern_index/proto:url_pattern_index", + "//testing/gmock", + "//testing/gtest", + ] +}
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream.cc b/components/subresource_filter/tools/ruleset_converter/rule_stream.cc new file mode 100644 index 0000000..aa1aca3 --- /dev/null +++ b/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
@@ -0,0 +1,432 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/subresource_filter/tools/ruleset_converter/rule_stream.h" + +#include <utility> + +#include "base/logging.h" +#include "components/subresource_filter/tools/rule_parser/rule.h" +#include "components/subresource_filter/tools/rule_parser/rule_parser.h" +#include "components/url_pattern_index/unindexed_ruleset.h" +#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h" + +namespace subresource_filter { + +namespace { + +std::string ReadStreamToString(std::istream* input) { + return std::string(std::istreambuf_iterator<char>(*input), {}); +} + +bool IsTrivialParseError(const RuleParser::ParseError& error) { + return error.error_code == RuleParser::ParseError::NONE || + error.error_code == RuleParser::ParseError::EMPTY_RULE; +} + +// A helper class used by rule input streams for converting a FilteringRules +// message into a stream. +class ProtobufRuleInputStreamImpl { + public: + explicit ProtobufRuleInputStreamImpl( + const url_pattern_index::proto::FilteringRules& rules) + : rules_(rules) {} + + url_pattern_index::proto::RuleType FetchNextRule() { + if (not_first_rule_) + ++rule_index_; + not_first_rule_ = true; + + if (is_reading_url_rules_ && rule_index_ >= rules_.url_rules_size()) { + is_reading_url_rules_ = false; + rule_index_ = 0; + } + if (!is_reading_url_rules_ && rule_index_ >= rules_.css_rules_size()) + return url_pattern_index::proto::RULE_TYPE_UNSPECIFIED; + return is_reading_url_rules_ ? url_pattern_index::proto::RULE_TYPE_URL + : url_pattern_index::proto::RULE_TYPE_CSS; + } + + const url_pattern_index::proto::UrlRule& GetUrlRule() { + CHECK(is_reading_url_rules_ && rule_index_ < rules_.url_rules_size()); + return rules_.url_rules(rule_index_); + } + + const url_pattern_index::proto::CssRule& GetCssRule() { + CHECK(!is_reading_url_rules_ && rule_index_ < rules_.css_rules_size()); + return rules_.css_rules(rule_index_); + } + + private: + ProtobufRuleInputStreamImpl(const ProtobufRuleInputStreamImpl&) = delete; + void operator=(const ProtobufRuleInputStreamImpl&) = delete; + + const url_pattern_index::proto::FilteringRules& rules_; + bool not_first_rule_ = false; + bool is_reading_url_rules_ = true; + int rule_index_ = 0; +}; + +// FilterList streams --------------------------------------------------------- + +// Reads rules from a text |file| of the FilterList format. +class FilterListRuleInputStream : public RuleInputStream { + public: + explicit FilterListRuleInputStream(std::unique_ptr<std::istream> input) + : input_(std::move(input)) {} + + url_pattern_index::proto::RuleType FetchNextRule() override { + std::string line; + while (std::getline(*input_, line)) { + auto rule_type = parser_.Parse(line); + if (rule_type != url_pattern_index::proto::RULE_TYPE_UNSPECIFIED) + return rule_type; + if (!IsTrivialParseError(parser_.parse_error())) { + std::cerr << parser_.parse_error() << std::endl; + } + // TODO(pkalinnikov): Export the number of processed/skipped rules. + } + return url_pattern_index::proto::RULE_TYPE_UNSPECIFIED; + } + + url_pattern_index::proto::UrlRule GetUrlRule() override { + CHECK_EQ(url_pattern_index::proto::RULE_TYPE_URL, parser_.rule_type()); + return parser_.url_rule().ToProtobuf(); + } + + url_pattern_index::proto::CssRule GetCssRule() override { + CHECK_EQ(url_pattern_index::proto::RULE_TYPE_CSS, parser_.rule_type()); + return parser_.css_rule().ToProtobuf(); + } + + private: + FilterListRuleInputStream(const FilterListRuleInputStream&) = delete; + void operator=(const FilterListRuleInputStream&) = delete; + + RuleParser parser_; + std::unique_ptr<std::istream> input_; +}; + +// Writes rules to |output| in FilterList format. +class FilterListRuleOutputStream : public RuleOutputStream { + public: + explicit FilterListRuleOutputStream(std::unique_ptr<std::ostream> output) + : output_(std::move(output)) {} + + bool PutUrlRule(const url_pattern_index::proto::UrlRule& rule) override { + std::string line = ToString(rule) + '\n'; + output_->write(line.data(), line.size()); + return !output_->bad(); + } + + bool PutCssRule(const url_pattern_index::proto::CssRule& rule) override { + std::string line = ToString(rule) + '\n'; + output_->write(line.data(), line.size()); + return !output_->bad(); + } + + bool Finish() override { + output_->flush(); + return !output_->bad(); + } + + private: + FilterListRuleOutputStream(const FilterListRuleOutputStream&) = delete; + void operator=(const FilterListRuleOutputStream&) = delete; + + std::unique_ptr<std::ostream> output_; +}; + +// Protobuf streams ------------------------------------------------------------ + +// Reads rules from a |file| with a serialized FilteredRules message. +class ProtobufRuleInputStream : public RuleInputStream { + public: + explicit ProtobufRuleInputStream(std::unique_ptr<std::istream> input) { + std::string buffer = ReadStreamToString(input.get()); + CHECK(rules_.ParseFromString(buffer)); + impl_.reset(new ProtobufRuleInputStreamImpl(rules_)); + } + + url_pattern_index::proto::RuleType FetchNextRule() override { + return impl_->FetchNextRule(); + } + url_pattern_index::proto::UrlRule GetUrlRule() override { + return impl_->GetUrlRule(); + } + url_pattern_index::proto::CssRule GetCssRule() override { + return impl_->GetCssRule(); + } + + private: + ProtobufRuleInputStream(const ProtobufRuleInputStream&) = delete; + void operator=(const ProtobufRuleInputStream&) = delete; + + url_pattern_index::proto::FilteringRules rules_; + std::unique_ptr<ProtobufRuleInputStreamImpl> impl_; +}; + +// Writes rules to |output| as a single url_pattern_index::proto::FilteringRules +// message in binary format. +class ProtobufRuleOutputStream : public RuleOutputStream { + public: + explicit ProtobufRuleOutputStream(std::unique_ptr<std::ostream> output) + : output_(std::move(output)) {} + + bool PutUrlRule(const url_pattern_index::proto::UrlRule& rule) override { + *all_rules_.add_url_rules() = rule; + return true; + } + + bool PutCssRule(const url_pattern_index::proto::CssRule& rule) override { + *all_rules_.add_css_rules() = rule; + return true; + } + + bool Finish() override { + std::string buffer; + if (!all_rules_.SerializeToString(&buffer)) + return false; + output_->write(buffer.data(), buffer.size()); + output_->flush(); + return !output_->bad(); + } + + private: + ProtobufRuleOutputStream(const ProtobufRuleOutputStream&) = delete; + void operator=(const ProtobufRuleOutputStream&) = delete; + + url_pattern_index::proto::FilteringRules all_rules_; + std::unique_ptr<std::ostream> output_; +}; + +// UnindexedRuleset streams ---------------------------------------------------- + +// Reads rules stored in |input| in UnindexedRuleset format. +class UnindexedRulesetRuleInputStream : public RuleInputStream { + public: + explicit UnindexedRulesetRuleInputStream( + std::unique_ptr<std::istream> input) { + ruleset_ = ReadStreamToString(input.get()); + ruleset_input_.reset(new google::protobuf::io::ArrayInputStream( + ruleset_.data(), ruleset_.size())); + ruleset_reader_.reset( + new url_pattern_index::UnindexedRulesetReader(ruleset_input_.get())); + } + + url_pattern_index::proto::RuleType FetchNextRule() override { + if (!impl_ && !ReadNextChunk()) + return url_pattern_index::proto::RULE_TYPE_UNSPECIFIED; + url_pattern_index::proto::RuleType rule_type = impl_->FetchNextRule(); + while (rule_type == url_pattern_index::proto::RULE_TYPE_UNSPECIFIED && + ReadNextChunk()) { + rule_type = impl_->FetchNextRule(); + } + return rule_type; + } + + url_pattern_index::proto::UrlRule GetUrlRule() override { + return impl_->GetUrlRule(); + } + url_pattern_index::proto::CssRule GetCssRule() override { + return impl_->GetCssRule(); + } + + private: + UnindexedRulesetRuleInputStream(const UnindexedRulesetRuleInputStream&) = + delete; + void operator=(const UnindexedRulesetRuleInputStream&) = delete; + + bool ReadNextChunk() { + if (ruleset_reader_->ReadNextChunk(&rules_chunk_)) { + impl_.reset(new ProtobufRuleInputStreamImpl(rules_chunk_)); + return true; + } + impl_.reset(); + return false; + } + + std::string ruleset_; + std::unique_ptr<google::protobuf::io::ArrayInputStream> ruleset_input_; + std::unique_ptr<url_pattern_index::UnindexedRulesetReader> ruleset_reader_; + + url_pattern_index::proto::FilteringRules rules_chunk_; + std::unique_ptr<ProtobufRuleInputStreamImpl> impl_; +}; + +// Writes the rules to |output| in UnindexedRuleset format. Discards CSS rules. +class UnindexedRulesetRuleOutputStream : public RuleOutputStream { + public: + explicit UnindexedRulesetRuleOutputStream( + std::unique_ptr<std::ostream> output) + : ruleset_output_(&ruleset_), + ruleset_writer_(&ruleset_output_), + output_(std::move(output)) {} + + bool PutUrlRule(const url_pattern_index::proto::UrlRule& rule) override { + return ruleset_writer_.AddUrlRule(rule); + } + + bool PutCssRule(const url_pattern_index::proto::CssRule& rule) override { + return true; + } + + bool Finish() override { + if (!ruleset_writer_.Finish()) + return false; + output_->write(ruleset_.data(), ruleset_.size()); + output_->flush(); + return !output_->bad(); + } + + private: + UnindexedRulesetRuleOutputStream(const UnindexedRulesetRuleOutputStream&) = + delete; + void operator=(const UnindexedRulesetRuleOutputStream&) = delete; + + std::string ruleset_; + google::protobuf::io::StringOutputStream ruleset_output_; + url_pattern_index::UnindexedRulesetWriter ruleset_writer_; + std::unique_ptr<std::ostream> output_; +}; + +} // namespace + +// static +std::unique_ptr<RuleInputStream> RuleInputStream::Create( + std::unique_ptr<std::istream> input, + RulesetFormat format) { + CHECK(input); + std::unique_ptr<RuleInputStream> result; + switch (format) { + case RulesetFormat::kFilterList: + result = std::make_unique<FilterListRuleInputStream>(std::move(input)); + break; + case RulesetFormat::kProto: + result = std::make_unique<ProtobufRuleInputStream>(std::move(input)); + break; + case RulesetFormat::kUnindexedRuleset: + result = + std::make_unique<UnindexedRulesetRuleInputStream>(std::move(input)); + break; + default: + break; + } + return result; +} + +// static +std::unique_ptr<RuleOutputStream> RuleOutputStream::Create( + std::unique_ptr<std::ostream> output, + RulesetFormat format) { + CHECK(output); + std::unique_ptr<RuleOutputStream> result; + switch (format) { + case RulesetFormat::kFilterList: + result = std::make_unique<FilterListRuleOutputStream>(std::move(output)); + break; + case RulesetFormat::kProto: + result = std::make_unique<ProtobufRuleOutputStream>(std::move(output)); + break; + case RulesetFormat::kUnindexedRuleset: + result = + std::make_unique<UnindexedRulesetRuleOutputStream>(std::move(output)); + break; + default: + break; + } + return result; +} + +// TransferRules helpers and implementation. ----------------------------------- + +namespace { + +// Up to M58 subresource_filter supported only these types. +constexpr int kChrome54To58ElementTypes = 2047; + +static_assert(kChrome54To58ElementTypes & + url_pattern_index::proto::ELEMENT_TYPE_OTHER, + "Wrong M54 element types."); +static_assert(kChrome54To58ElementTypes & + url_pattern_index::proto::ELEMENT_TYPE_FONT, + "Wrong M54 element types."); +static_assert(!(kChrome54To58ElementTypes & + url_pattern_index::proto::ELEMENT_TYPE_WEBSOCKET), + "Wrong M54 element types."); +static_assert(!(kChrome54To58ElementTypes & + url_pattern_index::proto::ELEMENT_TYPE_POPUP), + "Wrong M54 element types."); + +} // namespace + +bool TransferRules(RuleInputStream* input, + RuleOutputStream* url_rules_output, + RuleOutputStream* css_rules_output, + int chrome_version) { + while (true) { + auto rule_type = input->FetchNextRule(); + if (rule_type == url_pattern_index::proto::RULE_TYPE_UNSPECIFIED) + break; + switch (rule_type) { + case url_pattern_index::proto::RULE_TYPE_URL: { + if (!url_rules_output) + break; + url_pattern_index::proto::UrlRule url_rule = input->GetUrlRule(); + if (!DeleteUrlRuleOrAmend(&url_rule, chrome_version)) + url_rules_output->PutUrlRule(url_rule); + break; + } + case url_pattern_index::proto::RULE_TYPE_CSS: + if (css_rules_output) + css_rules_output->PutCssRule(input->GetCssRule()); + break; + case url_pattern_index::proto::RULE_TYPE_COMMENT: + // Ignore comments. + break; + default: + return false; + } + } + return true; +} + +bool DeleteUrlRuleOrAmend(url_pattern_index::proto::UrlRule* rule, + int lowest_chrome_version) { + if (!lowest_chrome_version) + return false; + + CHECK(rule->has_element_types() || rule->element_types() == 0); + + // REGEXP rules are not supported in Chrome's subresource_filter. + if (rule->url_pattern_type() == + url_pattern_index::proto::URL_PATTERN_TYPE_REGEXP) + return true; + + // POPUP type is deprecated because popup blocking is activated by default + // in Chrome. + rule->set_element_types(rule->element_types() & + ~url_pattern_index::proto::ELEMENT_TYPE_POPUP); + + // Only the following activation types are supported in Chrome. + rule->set_activation_types( + rule->activation_types() & + (url_pattern_index::proto::ACTIVATION_TYPE_DOCUMENT | + url_pattern_index::proto::ACTIVATION_TYPE_GENERICBLOCK)); + if (!rule->activation_types()) + rule->clear_activation_types(); + + // Chrome 54-58 ignores rules with unknown element types (like websocket). + if (lowest_chrome_version == 54) { + // Remove unknown types to prevent the |rule| from being ignored. + rule->set_element_types(rule->element_types() & kChrome54To58ElementTypes); + } + if (!rule->element_types()) + rule->clear_element_types(); + + // The rule should have at least 1 type bit, otherwise it targets nothing. + return !rule->element_types() && !rule->activation_types(); +} + +} // namespace subresource_filter
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream.h b/components/subresource_filter/tools/ruleset_converter/rule_stream.h new file mode 100644 index 0000000..bab5871 --- /dev/null +++ b/components/subresource_filter/tools/ruleset_converter/rule_stream.h
@@ -0,0 +1,98 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SUBRESOURCE_FILTER_TOOLS_RULESET_CONVERTER_RULE_STREAM_H_ +#define COMPONENTS_SUBRESOURCE_FILTER_TOOLS_RULESET_CONVERTER_RULE_STREAM_H_ + +#include <istream> +#include <memory> +#include <ostream> +#include <string> + +#include "components/subresource_filter/tools/ruleset_converter/ruleset_format.h" +#include "components/url_pattern_index/proto/rules.pb.h" + +namespace subresource_filter { + +class RuleInputStream; +class RuleOutputStream; + +// Interface to read the rules one by one. +class RuleInputStream { + public: + virtual ~RuleInputStream() = default; + + // Fetches the next rule and returns its type. Returns RULE_TYPE_UNSPECIFIED + // if end of the stream is reached or an I/O error occurred. + // TODO(pkalinnikov): Distinguish between errors and end-of-stream. + virtual url_pattern_index::proto::RuleType FetchNextRule() = 0; + + // Returns the latest fetched UrlRule. If the last FetchNextRule() returned + // not url_pattern_index::proto::RULE_TYPE_URL, the resulting value is + // undefined. This method is idempotent and optional to call. + virtual url_pattern_index::proto::UrlRule GetUrlRule() = 0; + + // Same as above, but for CSS rules. + virtual url_pattern_index::proto::CssRule GetCssRule() = 0; + + // Factory method to produce a RuleInputStream reading rules from |input| in + // the specified |format|. If the |format| is not supported, then returns + // nullptr. + static std::unique_ptr<RuleInputStream> Create( + std::unique_ptr<std::istream> input, + RulesetFormat format); +}; + +// Interface to output rules one by one. +class RuleOutputStream { + public: + virtual ~RuleOutputStream() = default; + + // The following methods are used to write rules into the stream. Return false + // iff an error occurred. + virtual bool PutUrlRule(const url_pattern_index::proto::UrlRule& rule) = 0; + virtual bool PutCssRule(const url_pattern_index::proto::CssRule& rule) = 0; + + // Finalizes the serialization. Returns false on error. + virtual bool Finish() = 0; + + // Factory method to produce a RuleOutputStream writing to the |output| in the + // specified |format|. If the |format| is not supported, then returns nullptr. + static std::unique_ptr<RuleOutputStream> Create( + std::unique_ptr<std::ostream> output, + RulesetFormat format); +}; + +// Reads rules from the |input| stream, puts URL rules to |url_rules_output|, +// and CSS rules to |css_rules_output|. Returns false iff an error occurred +// either in the input or one of the output streams. +// +// If one of the output streams is nullptr, the corresponding rules are +// discarded. The same instance can be passed in for both output streams, but in +// this case the original order of rules may be not preserved. However, it is +// guaranteed that for a certain type of rules (e.g., URL) the relative order of +// such rules is preserved. +// +// If |chrome_version| is non-zero, some rules and element type options will be +// filtered out so that the output ruleset can be consumed by Chrome clients as +// early as the specified |chrome_version|. A value of 0 indicates that the +// ruleset should remain intact. +bool TransferRules(RuleInputStream* input, + RuleOutputStream* url_rules_output, + RuleOutputStream* css_rules_output, + int chrome_version = 0); + +// This function is used by TransferRules to amend a stream of UrlRules +// according to the given |lowest_chrome_version| which uses the produced +// ruleset. Exposed here only for testing purposes. +// +// Returns false if the |rule| should be deleted altogether, otherwise returns +// true and amends the |rule| if necessary for the given +// |lowest_chrome_version|. +bool DeleteUrlRuleOrAmend(url_pattern_index::proto::UrlRule* rule, + int lowest_chrome_version); + +} // namespace subresource_filter + +#endif // COMPONENTS_SUBRESOURCE_FILTER_TOOLS_RULESET_CONVERTER_RULE_STREAM_H_
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream_test.cc b/components/subresource_filter/tools/ruleset_converter/rule_stream_test.cc new file mode 100644 index 0000000..6ff3cbd --- /dev/null +++ b/components/subresource_filter/tools/ruleset_converter/rule_stream_test.cc
@@ -0,0 +1,555 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/subresource_filter/tools/ruleset_converter/rule_stream.h" + +#include <algorithm> +#include <fstream> +#include <ios> +#include <iostream> +#include <memory> +#include <string> +#include <vector> + +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "components/subresource_filter/tools/rule_parser/rule_parser.h" +#include "components/url_pattern_index/proto/rules.pb.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace subresource_filter { + +namespace { + +// Stores lists of the rules that a test ruleset consists of. +struct TestRulesetContents { + std::vector<url_pattern_index::proto::UrlRule> url_rules; + std::vector<url_pattern_index::proto::CssRule> css_rules; +}; + +// Stores identification information about a temporary File with a ruleset. +// Deletes the file automatically on destruction. +class ScopedTempRulesetFile { + public: + // Creates a temporary file of the specified |format|. + explicit ScopedTempRulesetFile(RulesetFormat format) : format_(format) { + // Cannot ASSERT due to returning in constructor. + CHECK(scoped_dir_.CreateUniqueTempDir()); + CHECK( + base::CreateTemporaryFileInDir(scoped_dir_.GetPath(), &ruleset_path_)); + } + + ~ScopedTempRulesetFile() {} + + const base::FilePath& ruleset_path() const { return ruleset_path_; } + RulesetFormat format() const { return format_; } + + private: + base::ScopedTempDir scoped_dir_; + base::FilePath ruleset_path_; + const RulesetFormat format_; // The format of the |file|. +}; + +bool IsEqualRules(const url_pattern_index::proto::UrlRule& first, + const url_pattern_index::proto::UrlRule& second) { + return first.SerializeAsString() == second.SerializeAsString(); +} + +bool IsEqualRules(const url_pattern_index::proto::CssRule& first, + const url_pattern_index::proto::CssRule& second) { + return first.SerializeAsString() == second.SerializeAsString(); +} + +void AssertEqualContents(const TestRulesetContents& lhs, + const TestRulesetContents& rhs) { + ASSERT_EQ(lhs.url_rules.size(), rhs.url_rules.size()); + for (size_t i = 0, size = lhs.url_rules.size(); i != size; ++i) { + ASSERT_TRUE(IsEqualRules(lhs.url_rules[i], rhs.url_rules[i])) + << i << ' ' << ToString(lhs.url_rules[i]) << ' ' + << ToString(rhs.url_rules[i]); + } + + ASSERT_EQ(lhs.css_rules.size(), rhs.css_rules.size()); + for (size_t i = 0, size = lhs.css_rules.size(); i != size; ++i) { + ASSERT_TRUE(IsEqualRules(lhs.css_rules[i], rhs.css_rules[i])) + << i << ' ' << ToString(lhs.css_rules[i]) << ' ' + << ToString(rhs.css_rules[i]); + } +} + +// Opens the |ruleset_file| and creates an empty rule output stream to this +// file. Returns the stream or nullptr if it failed to be created. +std::unique_ptr<RuleOutputStream> OpenNewRuleset( + const ScopedTempRulesetFile& ruleset_file) { + return RuleOutputStream::Create( + std::make_unique<std::ofstream>( + ruleset_file.ruleset_path().MaybeAsASCII(), + std::ios::binary | std::ios::out), + ruleset_file.format()); +} + +// Opens the |ruleset_file| with already existing ruleset and returns the +// corresponding input stream, or nullptr if it failed to be created. +std::unique_ptr<RuleInputStream> OpenRuleset( + const ScopedTempRulesetFile& ruleset_file) { + return RuleInputStream::Create(std::make_unique<std::ifstream>( + ruleset_file.ruleset_path().MaybeAsASCII(), + std::ios::binary | std::ios::in), + ruleset_file.format()); +} + +// Returns a small number of predefined rules in text format. +std::vector<std::string> GetSomeRules() { + return std::vector<std::string>{ + "example.com", + "||ex.com$image", + "|http://example.com/?key=value$~third-party,domain=ex.com", + "&key1=value1&key2=value2|$script,image,font", + "domain1.com,domain1.com###id", + "@@whitelisted.com$document,domain=example.com|~sub.example.com", + "###absolute_evil_id", + "@@whitelisted.com$match-case,document,domain=another.example.com", + "domain.com,~sub.domain.com,sub.sub.domain.com#@#id", + "#@#absolute_good_id", + "host$websocket", + }; +} + +// Returns some rules which Chrome doesn't support fully or partially, mixed +// with a couple of supported rules, or otherwise weird ones. +std::vector<std::string> GetSomeChromeUnfriendlyRules() { + return std::vector<std::string>{ + "/a[0-9].com/$image", + "a.com$image,popup" + "a.com$popup", + "a.com$~image", + "a.com$~popup", + "a.com$~image,~popup", + "@@a.com$subdocument,document", + "@@a.com$document,generichide", + "@@a.com$document", + "@@a.com$genericblock", + "@@a.com$elemhide", + "@@a.com$generichide", + "@@a.com$elemhide,generichide", + "@@a.com$image,elemhide,generichide", + "a.com$image,~image", + }; +} + +// Generates and returns many rules in text format. +std::vector<std::string> GetManyRules() { + constexpr size_t kNumberOfUrlRules = 10123; + constexpr size_t kNumberOfCssRules = 5321; + + std::vector<std::string> text_rules; + + for (size_t i = 0; i != kNumberOfUrlRules; ++i) { + std::string text_rule; + if (!(i & 3)) + text_rule += "@@"; + if (i & 1) + text_rule += "sub."; + text_rule += "example" + base::IntToString(i) + ".com"; + text_rule += '$'; + text_rule += (i & 7) ? "script" : "image"; + if (i & 1) + text_rule += ",domain=example.com|~but_not.example.com"; + text_rules.push_back(text_rule); + } + + for (size_t i = 0; i != kNumberOfCssRules; ++i) { + std::string text_rule = "domain.com"; + if (i & 1) + text_rule += ",~but_not.domain.com"; + text_rule += (i & 3) ? "##" : "#@#"; + text_rule += "#id" + base::IntToString(i); + text_rules.push_back(text_rule); + } + + return text_rules; +} + +// Parses |text_rules| and appends them to the |ruleset|. +void PrepareRuleset(TestRulesetContents* ruleset, + const std::vector<std::string>& text_rules, + bool allow_errors = false) { + RuleParser parser; + for (const std::string& text_rule : text_rules) { + url_pattern_index::proto::RuleType rule_type = parser.Parse(text_rule); + switch (rule_type) { + case url_pattern_index::proto::RULE_TYPE_URL: + ruleset->url_rules.push_back(parser.url_rule().ToProtobuf()); + break; + case url_pattern_index::proto::RULE_TYPE_CSS: + ruleset->css_rules.push_back(parser.css_rule().ToProtobuf()); + break; + case url_pattern_index::proto::RULE_TYPE_UNSPECIFIED: + ASSERT_TRUE(allow_errors); + break; + default: + ASSERT_TRUE(false); + } + } +} + +// Opens the |ruleset_file|, and writes the test ruleset |contents| to it in the +// corresponding format. +void WriteTestRuleset(const ScopedTempRulesetFile& ruleset_file, + const TestRulesetContents& contents) { + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(ruleset_file); + ASSERT_NE(nullptr, output); + + for (const auto& rule : contents.url_rules) + EXPECT_TRUE(output->PutUrlRule(rule)); + for (const auto& rule : contents.css_rules) + EXPECT_TRUE(output->PutCssRule(rule)); + EXPECT_TRUE(output->Finish()); +} + +// Reads the provided |ruleset_file| back, and EXPECTs that it contains exactly +// all the rules from |expected_contents| in the same order. +void ReadTestRulesetAndExpectContents( + const ScopedTempRulesetFile& ruleset_file, + const TestRulesetContents& expected_contents) { + std::unique_ptr<RuleInputStream> input = OpenRuleset(ruleset_file); + + TestRulesetContents contents; + url_pattern_index::proto::RuleType rule_type = + url_pattern_index::proto::RULE_TYPE_UNSPECIFIED; + while ((rule_type = input->FetchNextRule()) != + url_pattern_index::proto::RULE_TYPE_UNSPECIFIED) { + if (rule_type == url_pattern_index::proto::RULE_TYPE_URL) { + contents.url_rules.push_back(input->GetUrlRule()); + } else { + ASSERT_EQ(url_pattern_index::proto::RULE_TYPE_CSS, rule_type); + contents.css_rules.push_back(input->GetCssRule()); + } + } + + AssertEqualContents(contents, expected_contents); +} + +// Reads the provided |ruleset_file| skipping every second rule (independently +// for URL and CSS rules), and EXPECTs that it contains exactly all the rules +// from |expected_contents| in the same order. +void ReadHalfRulesOfTestRulesetAndExpectContents( + const ScopedTempRulesetFile& ruleset_file, + const TestRulesetContents& expected_contents) { + std::unique_ptr<RuleInputStream> input = OpenRuleset(ruleset_file); + + TestRulesetContents contents; + + bool take_url_rule = true; + bool take_css_rule = true; + url_pattern_index::proto::RuleType rule_type = + url_pattern_index::proto::RULE_TYPE_UNSPECIFIED; + while ((rule_type = input->FetchNextRule()) != + url_pattern_index::proto::RULE_TYPE_UNSPECIFIED) { + if (rule_type == url_pattern_index::proto::RULE_TYPE_URL) { + if (take_url_rule) + contents.url_rules.push_back(input->GetUrlRule()); + take_url_rule = !take_url_rule; + } else { + ASSERT_EQ(url_pattern_index::proto::RULE_TYPE_CSS, rule_type); + if (take_css_rule) + contents.css_rules.push_back(input->GetCssRule()); + take_css_rule = !take_css_rule; + } + } + + AssertEqualContents(contents, expected_contents); +} + +} // namespace + +TEST(RuleStreamTest, WriteAndReadRuleset) { + for (int small_or_big = 0; small_or_big < 2; ++small_or_big) { + TestRulesetContents contents; + if (small_or_big) + PrepareRuleset(&contents, GetManyRules()); + else + PrepareRuleset(&contents, GetSomeRules()); + + TestRulesetContents only_url_rules; + only_url_rules.url_rules = contents.url_rules; + + for (auto format : {RulesetFormat::kFilterList, RulesetFormat::kProto, + RulesetFormat::kUnindexedRuleset}) { + ScopedTempRulesetFile ruleset_file(format); + WriteTestRuleset(ruleset_file, contents); + // Note: kUnindexedRuleset discards CSS rules, test it differently. + ReadTestRulesetAndExpectContents( + ruleset_file, format == RulesetFormat::kUnindexedRuleset + ? only_url_rules + : contents); + } + } +} + +TEST(RuleStreamTest, WriteAndReadHalfRuleset) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetManyRules()); + + TestRulesetContents half_contents; + for (size_t i = 0, size = contents.url_rules.size(); i < size; i += 2) + half_contents.url_rules.push_back(contents.url_rules[i]); + for (size_t i = 0, size = contents.css_rules.size(); i < size; i += 2) + half_contents.css_rules.push_back(contents.css_rules[i]); + + TestRulesetContents half_url_rules; + half_url_rules.url_rules = half_contents.url_rules; + + for (auto format : {RulesetFormat::kFilterList, RulesetFormat::kProto, + RulesetFormat::kUnindexedRuleset}) { + ScopedTempRulesetFile ruleset_file(format); + WriteTestRuleset(ruleset_file, contents); + // Note: kUnindexedRuleset discards CSS rules, test it differently. + ReadHalfRulesOfTestRulesetAndExpectContents( + ruleset_file, format == RulesetFormat::kUnindexedRuleset + ? half_url_rules + : half_contents); + } +} + +TEST(RuleStreamTest, TransferAllRulesToSameStream) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetManyRules()); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset(RulesetFormat::kFilterList); + WriteTestRuleset(source_ruleset, contents); + + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(target_ruleset); + TransferRules(input.get(), output.get(), output.get()); + EXPECT_TRUE(output->Finish()); + input.reset(); + output.reset(); + + ReadTestRulesetAndExpectContents(target_ruleset, contents); +} + +TEST(RuleStreamTest, TransferUrlRulesToOneStream) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetManyRules()); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset(RulesetFormat::kFilterList); + WriteTestRuleset(source_ruleset, contents); + + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(target_ruleset); + TransferRules(input.get(), output.get(), nullptr); + EXPECT_TRUE(output->Finish()); + input.reset(); + output.reset(); + + contents.css_rules.clear(); + ReadTestRulesetAndExpectContents(target_ruleset, contents); +} + +TEST(RuleStreamTest, TransferCssRulesToOneStream) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetManyRules()); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset(RulesetFormat::kFilterList); + WriteTestRuleset(source_ruleset, contents); + + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(target_ruleset); + TransferRules(input.get(), nullptr, output.get()); + EXPECT_TRUE(output->Finish()); + input.reset(); + output.reset(); + + contents.url_rules.clear(); + ReadTestRulesetAndExpectContents(target_ruleset, contents); +} + +TEST(RuleStreamTest, TransferAllRulesToDifferentStreams) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetManyRules()); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset_url(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset_css(RulesetFormat::kFilterList); + WriteTestRuleset(source_ruleset, contents); + + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + std::unique_ptr<RuleOutputStream> output_url = + OpenNewRuleset(target_ruleset_url); + std::unique_ptr<RuleOutputStream> output_css = + OpenNewRuleset(target_ruleset_css); + TransferRules(input.get(), output_url.get(), output_css.get()); + EXPECT_TRUE(output_url->Finish()); + EXPECT_TRUE(output_css->Finish()); + input.reset(); + output_url.reset(); + output_css.reset(); + + TestRulesetContents only_url_rules; + only_url_rules.url_rules = contents.url_rules; + ReadTestRulesetAndExpectContents(target_ruleset_url, only_url_rules); + + contents.url_rules.clear(); + ReadTestRulesetAndExpectContents(target_ruleset_css, contents); +} + +TEST(RuleStreamTest, TransferRulesAndDiscardRegexpRules) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetManyRules()); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset(RulesetFormat::kFilterList); + WriteTestRuleset(source_ruleset, contents); + + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(target_ruleset); + TransferRules(input.get(), output.get(), nullptr, 54 /* chrome_version */); + EXPECT_TRUE(output->Finish()); + input.reset(); + output.reset(); + + contents.url_rules.erase( + std::remove_if(contents.url_rules.begin(), contents.url_rules.end(), + [](const url_pattern_index::proto::UrlRule& rule) { + return rule.url_pattern_type() == + url_pattern_index::proto::URL_PATTERN_TYPE_REGEXP; + }), + contents.url_rules.end()); + contents.css_rules.clear(); + ReadTestRulesetAndExpectContents(target_ruleset, contents); +} + +TEST(RuleStreamTest, TransferRulesChromeVersion) { + TestRulesetContents contents; + PrepareRuleset(&contents, GetSomeChromeUnfriendlyRules()); + PrepareRuleset(&contents, GetManyRules()); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + WriteTestRuleset(source_ruleset, contents); + + for (int chrome_version : {0, 54, 59}) { + TestRulesetContents expected_contents; + for (url_pattern_index::proto::UrlRule url_rule : contents.url_rules) { + if (DeleteUrlRuleOrAmend(&url_rule, chrome_version)) + continue; + expected_contents.url_rules.push_back(url_rule); + } + + ScopedTempRulesetFile target_ruleset(RulesetFormat::kFilterList); + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(target_ruleset); + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + TransferRules(input.get(), output.get(), nullptr, chrome_version); + EXPECT_TRUE(output->Finish()); + input.reset(); + output.reset(); + + ReadTestRulesetAndExpectContents(target_ruleset, expected_contents); + } +} + +TEST(RuleStreamTest, TransferRulesFromFilterListWithUnsupportedOptions) { + std::vector<std::string> text_rules = GetSomeRules(); + const size_t number_of_correct_rules = text_rules.size(); + + // Insert several rules with non-critical parse errors. + text_rules.insert(text_rules.begin(), "host1$donottrack"); + text_rules.push_back(""); + text_rules.insert(text_rules.begin() + text_rules.size() / 2, + "host3$collapse"); + + ScopedTempRulesetFile source_ruleset(RulesetFormat::kFilterList); + ScopedTempRulesetFile target_ruleset(RulesetFormat::kFilterList); + + // Output all the rules to the |source_ruleset| file. + std::string joined_rules = base::JoinString(text_rules, "\n"); + base::WriteFile(source_ruleset.ruleset_path(), joined_rules.data(), + joined_rules.size()); + + // Filter out the rules with parse errors, and save the rest to |contents|. + TestRulesetContents contents; + PrepareRuleset(&contents, text_rules, true /* allow_errors */); + + // Make sure all the rules with no errors were transferred. + { + std::unique_ptr<RuleInputStream> input = OpenRuleset(source_ruleset); + std::unique_ptr<RuleOutputStream> output = OpenNewRuleset(target_ruleset); + TransferRules(input.get(), output.get(), output.get()); + EXPECT_TRUE(output->Finish()); + } + + EXPECT_EQ(number_of_correct_rules, + contents.url_rules.size() + contents.css_rules.size()); + ReadTestRulesetAndExpectContents(target_ruleset, contents); +} + +TEST(RuleStreamTest, DeleteUrlRuleOrAmend) { + const struct TestCase { + const char* rule; + const char* chrome_54_rule; + const char* chrome_59_rule; + } kTestCases[] = { + {"/a[0-9].com/$image", nullptr, nullptr}, + {"a.com$image,popup", "a.com$image,~popup", "#54"}, + {"a.com$popup", nullptr, nullptr}, + {"a.com$~image", "a.com$~image,~popup,~websocket", "#0"}, + {"a.com$~popup", "a.com$~popup,~websocket", "a.com"}, + {"a.com$~image,~popup", "a.com$~image,~popup,~websocket", "#0"}, + {"@@a.com$subdocument,document", "#0", "#0"}, + {"@@a.com$document,generichide", "@@a.com$document", "#54"}, + {"@@a.com$document", "#0", "#0"}, + {"@@a.com$genericblock", "#0", "#0"}, + {"@@a.com$elemhide", nullptr, nullptr}, + {"@@a.com$generichide", nullptr, nullptr}, + {"@@a.com$elemhide,generichide", nullptr, nullptr}, + {"@@a.com$image,elemhide,generichide", "@@a.com$image", "#54"}, + {"a.com$image,~image", nullptr, nullptr}, + }; + + auto get_target_rule = [](const TestCase& test, std::string target_rule) { + RuleParser parser; + if (target_rule == "#0") + target_rule = test.rule; + else if (target_rule == "#54") + target_rule = test.chrome_54_rule; + EXPECT_EQ(url_pattern_index::proto::RULE_TYPE_URL, + parser.Parse(target_rule)); + return parser.url_rule().ToProtobuf(); + }; + + RuleParser parser; + for (const auto& test : kTestCases) { + SCOPED_TRACE(test.rule); + ASSERT_EQ(url_pattern_index::proto::RULE_TYPE_URL, parser.Parse(test.rule)); + const url_pattern_index::proto::UrlRule current_rule = + parser.url_rule().ToProtobuf(); + + url_pattern_index::proto::UrlRule modified_rule = current_rule; + EXPECT_FALSE(DeleteUrlRuleOrAmend(&modified_rule, 0)); + EXPECT_TRUE(IsEqualRules(modified_rule, current_rule)); + + modified_rule = current_rule; + EXPECT_EQ(!test.chrome_54_rule, DeleteUrlRuleOrAmend(&modified_rule, 54)); + if (test.chrome_54_rule) { + EXPECT_TRUE(IsEqualRules(modified_rule, + get_target_rule(test, test.chrome_54_rule))); + } + + modified_rule = current_rule; + EXPECT_EQ(!test.chrome_59_rule, DeleteUrlRuleOrAmend(&modified_rule, 59)); + if (test.chrome_59_rule) { + EXPECT_TRUE(IsEqualRules(modified_rule, + get_target_rule(test, test.chrome_59_rule))); + } + } +} + +} // namespace subresource_filter
diff --git a/components/subresource_filter/tools/ruleset_converter/ruleset_format.cc b/components/subresource_filter/tools/ruleset_converter/ruleset_format.cc new file mode 100644 index 0000000..743c5d74 --- /dev/null +++ b/components/subresource_filter/tools/ruleset_converter/ruleset_format.cc
@@ -0,0 +1,49 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/subresource_filter/tools/ruleset_converter/ruleset_format.h" + +#include "base/strings/string_number_conversions.h" + +namespace subresource_filter { + +bool ParseFlag(const std::string& text, + RulesetFormat* format, + std::string* error) { + if (text.empty()) { + *format = RulesetFormat::kUndefined; + return true; + } + if (text == "filter-list") { + *format = RulesetFormat::kFilterList; + return true; + } + if (text == "proto") { + *format = RulesetFormat::kProto; + return true; + } + if (text == "unindexed-ruleset") { + *format = RulesetFormat::kUnindexedRuleset; + return true; + } + *error = "unknown RulesetFormat"; + return false; +} + +std::string UnparseFlag(RulesetFormat format) { + switch (format) { + case RulesetFormat::kUndefined: + return ""; + case RulesetFormat::kFilterList: + return "filter-list"; + case RulesetFormat::kProto: + return "proto"; + case RulesetFormat::kUnindexedRuleset: + return "unindexed-ruleset"; + default: + return base::IntToString(static_cast<int>(format)); + } +} + +} // namespace subresource_filter
diff --git a/components/subresource_filter/tools/ruleset_converter/ruleset_format.h b/components/subresource_filter/tools/ruleset_converter/ruleset_format.h new file mode 100644 index 0000000..ca11b91d --- /dev/null +++ b/components/subresource_filter/tools/ruleset_converter/ruleset_format.h
@@ -0,0 +1,35 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SUBRESOURCE_FILTER_TOOLS_RULESET_CONVERTER_RULESET_FORMAT_H_ +#define COMPONENTS_SUBRESOURCE_FILTER_TOOLS_RULESET_CONVERTER_RULESET_FORMAT_H_ + +#include <string> + +namespace subresource_filter { + +// Enumerates supported ruleset serialization formats. +enum class RulesetFormat { + // The format is not defined. + kUndefined, + // Text representation of FilterList rules. See + // //components/subresource_filter/tools/rule_parser + // for details. + kFilterList, + // A serialized url_pattern_index::proto::FilteringRules message. + kProto, + // UnindexedRuleset format. See + // //components/url_pattern_index/unindexed_ruleset.* for details. + kUnindexedRuleset, +}; + +// Functions used for compatibility with "base/commandlineflags.h". +bool ParseFlag(const std::string& text, + RulesetFormat* format, + std::string* error); +std::string UnparseFlag(RulesetFormat format); + +} // namespace subresource_filter + +#endif // COMPONENTS_SUBRESOURCE_FILTER_TOOLS_RULESET_CONVERTER_RULESET_FORMAT_H_
diff --git a/components/suggestions/suggestions_service_impl_unittest.cc b/components/suggestions/suggestions_service_impl_unittest.cc index e6fc7cf..0052adfb 100644 --- a/components/suggestions/suggestions_service_impl_unittest.cc +++ b/components/suggestions/suggestions_service_impl_unittest.cc
@@ -185,7 +185,8 @@ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN, /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), - /*long_poll_interval=*/base::TimeDelta::FromMinutes(180)))); + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false))); // These objects are owned by the SuggestionsService, but we keep the // pointers around for testing. test_suggestions_store_ = new TestSuggestionsStore();
diff --git a/components/sync/driver/glue/sync_backend_host_core.cc b/components/sync/driver/glue/sync_backend_host_core.cc index 075f70c8..c701065a 100644 --- a/components/sync/driver/glue/sync_backend_host_core.cc +++ b/components/sync/driver/glue/sync_backend_host_core.cc
@@ -604,6 +604,12 @@ } } +bool SyncBackendHostCore::HasUnsyncedItemsForTest() const { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(sync_manager_); + return sync_manager_->HasUnsyncedItemsForTest(); +} + void SyncBackendHostCore::ClearServerDataDone( const base::Closure& frontend_callback) { DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/sync/driver/glue/sync_backend_host_core.h b/components/sync/driver/glue/sync_backend_host_core.h index b8a15132..7df3b6a 100644 --- a/components/sync/driver/glue/sync_backend_host_core.h +++ b/components/sync/driver/glue/sync_backend_host_core.h
@@ -173,6 +173,8 @@ bool empty_jar, const base::Closure& callback); + bool HasUnsyncedItemsForTest() const; + private: friend class base::RefCountedThreadSafe<SyncBackendHostCore>;
diff --git a/components/sync/driver/glue/sync_backend_host_impl.cc b/components/sync/driver/glue/sync_backend_host_impl.cc index c164b0c0..29f827a4 100644 --- a/components/sync/driver/glue/sync_backend_host_impl.cc +++ b/components/sync/driver/glue/sync_backend_host_impl.cc
@@ -11,6 +11,7 @@ #include "base/feature_list.h" #include "base/location.h" #include "base/logging.h" +#include "base/task_runner_util.h" #include "base/threading/thread_task_runner_handle.h" #include "components/invalidation/public/invalidation_service.h" #include "components/invalidation/public/object_id_invalidation_map.h" @@ -220,9 +221,13 @@ return core_->sync_manager()->GetDetailedStatus(); } -bool SyncBackendHostImpl::HasUnsyncedItemsForTest() const { +void SyncBackendHostImpl::HasUnsyncedItemsForTest( + base::OnceCallback<void(bool)> cb) const { DCHECK(initialized()); - return core_->sync_manager()->HasUnsyncedItemsForTest(); + base::PostTaskAndReplyWithResult( + sync_task_runner_.get(), FROM_HERE, + base::BindOnce(&SyncBackendHostCore::HasUnsyncedItemsForTest, core_), + std::move(cb)); } bool SyncBackendHostImpl::IsCryptographerReady(
diff --git a/components/sync/driver/glue/sync_backend_host_impl.h b/components/sync/driver/glue/sync_backend_host_impl.h index 908cd63..b1eabef 100644 --- a/components/sync/driver/glue/sync_backend_host_impl.h +++ b/components/sync/driver/glue/sync_backend_host_impl.h
@@ -81,7 +81,8 @@ void EnableEncryptEverything() override; UserShare* GetUserShare() const override; Status GetDetailedStatus() override; - bool HasUnsyncedItemsForTest() const override; + void HasUnsyncedItemsForTest( + base::OnceCallback<void(bool)> cb) const override; bool IsCryptographerReady(const BaseTransaction* trans) const override; void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) const override; void FlushDirectory() const override;
diff --git a/components/sync/driver/signin_manager_wrapper.cc b/components/sync/driver/signin_manager_wrapper.cc index 7862741..2f7e8f80 100644 --- a/components/sync/driver/signin_manager_wrapper.cc +++ b/components/sync/driver/signin_manager_wrapper.cc
@@ -4,7 +4,7 @@ #include "components/sync/driver/signin_manager_wrapper.h" -#include "services/identity/public/cpp/identity_manager.h" +#include "components/signin/core/browser/signin_manager_base.h" SigninManagerWrapper::SigninManagerWrapper( identity::IdentityManager* identity_manager, @@ -22,9 +22,9 @@ } std::string SigninManagerWrapper::GetEffectiveUsername() const { - return identity_manager_->GetPrimaryAccountInfo().email; + return signin_manager_->GetAuthenticatedAccountInfo().email; } std::string SigninManagerWrapper::GetAccountIdToUse() const { - return identity_manager_->GetPrimaryAccountInfo().account_id; + return signin_manager_->GetAuthenticatedAccountId(); }
diff --git a/components/sync/driver/sync_service_base.cc b/components/sync/driver/sync_service_base.cc index f31158d3..4636db18 100644 --- a/components/sync/driver/sync_service_base.cc +++ b/components/sync/driver/sync_service_base.cc
@@ -13,12 +13,12 @@ #include "base/syslog_logging.h" #include "components/invalidation/public/invalidation_service.h" #include "components/signin/core/browser/account_info.h" +#include "components/signin/core/browser/signin_manager_base.h" #include "components/sync/base/report_unrecoverable_error.h" #include "components/sync/device_info/local_device_info_provider.h" #include "components/sync/driver/sync_driver_switches.h" #include "components/sync/engine/engine_components_factory_impl.h" #include "components/sync/engine/polling_constants.h" -#include "services/identity/public/cpp/identity_manager.h" namespace syncer { @@ -86,7 +86,7 @@ AccountInfo SyncServiceBase::GetAuthenticatedAccountInfo() const { DCHECK(thread_checker_.CalledOnValidThread()); - return signin_ ? signin_->GetIdentityManager()->GetPrimaryAccountInfo() + return signin_ ? signin_->GetSigninManager()->GetAuthenticatedAccountInfo() : AccountInfo(); }
diff --git a/components/sync/driver/sync_service_utils_unittest.cc b/components/sync/driver/sync_service_utils_unittest.cc index 48928e3..3e500bf 100644 --- a/components/sync/driver/sync_service_utils_unittest.cc +++ b/components/sync/driver/sync_service_utils_unittest.cc
@@ -56,7 +56,8 @@ std::vector<int>(MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN, /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), - /*long_poll_interval=*/base::TimeDelta::FromMinutes(180)); + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false); } return SyncCycleSnapshot(); }
diff --git a/components/sync/engine/cycle/sync_cycle_snapshot.cc b/components/sync/engine/cycle/sync_cycle_snapshot.cc index 90a6fa4..69d99b45 100644 --- a/components/sync/engine/cycle/sync_cycle_snapshot.cc +++ b/components/sync/engine/cycle/sync_cycle_snapshot.cc
@@ -21,6 +21,7 @@ num_entries_(0), num_entries_by_type_(MODEL_TYPE_COUNT, 0), num_to_delete_entries_by_type_(MODEL_TYPE_COUNT, 0), + has_remaining_local_changes_(false), is_initialized_(false) {} SyncCycleSnapshot::SyncCycleSnapshot( @@ -38,7 +39,8 @@ const std::vector<int>& num_to_delete_entries_by_type, sync_pb::SyncEnums::GetUpdatesOrigin get_updates_origin, base::TimeDelta short_poll_interval, - base::TimeDelta long_poll_interval) + base::TimeDelta long_poll_interval, + bool has_remaining_local_changes) : model_neutral_state_(model_neutral_state), download_progress_markers_(download_progress_markers), is_silenced_(is_silenced), @@ -54,6 +56,7 @@ get_updates_origin_(get_updates_origin), short_poll_interval_(short_poll_interval), long_poll_interval_(long_poll_interval), + has_remaining_local_changes_(has_remaining_local_changes), is_initialized_(true) {} SyncCycleSnapshot::SyncCycleSnapshot(const SyncCycleSnapshot& other) = default; @@ -102,6 +105,7 @@ counter_entries->Set(model_type, std::move(type_entries)); } value->Set("counter_entries", std::move(counter_entries)); + value->SetBoolean("hasRemainingLocalChanges", has_remaining_local_changes_); return value; } @@ -148,6 +152,10 @@ return poll_finish_time_; } +bool SyncCycleSnapshot::has_remaining_local_changes() const { + return has_remaining_local_changes_; +} + bool SyncCycleSnapshot::is_initialized() const { return is_initialized_; }
diff --git a/components/sync/engine/cycle/sync_cycle_snapshot.h b/components/sync/engine/cycle/sync_cycle_snapshot.h index ac4fb8c..efda10f 100644 --- a/components/sync/engine/cycle/sync_cycle_snapshot.h +++ b/components/sync/engine/cycle/sync_cycle_snapshot.h
@@ -44,7 +44,8 @@ const std::vector<int>& num_to_delete_entries_by_type, sync_pb::SyncEnums::GetUpdatesOrigin get_updates_origin, base::TimeDelta short_poll_interval, - base::TimeDelta long_poll_interval); + base::TimeDelta long_poll_interval, + bool has_remaining_local_changes); SyncCycleSnapshot(const SyncCycleSnapshot& other); ~SyncCycleSnapshot(); @@ -67,6 +68,8 @@ sync_pb::SyncEnums::GetUpdatesOrigin get_updates_origin() const; base::TimeDelta short_poll_interval() const; base::TimeDelta long_poll_interval() const; + // Whether usynced items existed at the time the sync cycle completed. + bool has_remaining_local_changes() const; // Set iff this snapshot was not built using the default constructor. bool is_initialized() const; @@ -91,6 +94,8 @@ base::TimeDelta short_poll_interval_; base::TimeDelta long_poll_interval_; + bool has_remaining_local_changes_; + bool is_initialized_; };
diff --git a/components/sync/engine/cycle/sync_cycle_snapshot_unittest.cc b/components/sync/engine/cycle/sync_cycle_snapshot_unittest.cc index 4a86538..8d5b5f6 100644 --- a/components/sync/engine/cycle/sync_cycle_snapshot_unittest.cc +++ b/components/sync/engine/cycle/sync_cycle_snapshot_unittest.cc
@@ -48,9 +48,10 @@ std::vector<int>(MODEL_TYPE_COUNT, 0), std::vector<int>(MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN, /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), - /*long_poll_interval=*/base::TimeDelta::FromMinutes(180)); + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false); std::unique_ptr<base::DictionaryValue> value(snapshot.ToValue()); - EXPECT_EQ(16u, value->size()); + EXPECT_EQ(17u, value->size()); ExpectDictIntegerValue(model_neutral.num_successful_commits, *value, "numSuccessfulCommits"); ExpectDictIntegerValue(model_neutral.num_successful_bookmark_commits, *value, @@ -74,6 +75,7 @@ "numHierarchyConflicts"); ExpectDictIntegerValue(kNumServerConflicts, *value, "numServerConflicts"); ExpectDictBooleanValue(false, *value, "notificationsEnabled"); + ExpectDictBooleanValue(false, *value, "hasRemainingLocalChanges"); } } // namespace
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc index 1f3db85..a468060 100644 --- a/components/sync/engine/fake_sync_engine.cc +++ b/components/sync/engine/fake_sync_engine.cc
@@ -68,9 +68,8 @@ return SyncEngine::Status(); } -bool FakeSyncEngine::HasUnsyncedItemsForTest() const { - return false; -} +void FakeSyncEngine::HasUnsyncedItemsForTest( + base::OnceCallback<void(bool)> cb) const {} bool FakeSyncEngine::IsCryptographerReady(const BaseTransaction* trans) const { return false;
diff --git a/components/sync/engine/fake_sync_engine.h b/components/sync/engine/fake_sync_engine.h index 0a7066a9..f88c2b4 100644 --- a/components/sync/engine/fake_sync_engine.h +++ b/components/sync/engine/fake_sync_engine.h
@@ -68,7 +68,8 @@ Status GetDetailedStatus() override; - bool HasUnsyncedItemsForTest() const override; + void HasUnsyncedItemsForTest( + base::OnceCallback<void(bool)> cb) const override; bool IsCryptographerReady(const BaseTransaction* trans) const override;
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h index 8e11df6..e3e5e4a 100644 --- a/components/sync/engine/sync_engine.h +++ b/components/sync/engine/sync_engine.h
@@ -158,7 +158,8 @@ // Determines if the underlying sync engine has made any local changes to // items that have not yet been synced with the server. // ONLY CALL THIS IF OnInitializationComplete was called! - virtual bool HasUnsyncedItemsForTest() const = 0; + virtual void HasUnsyncedItemsForTest( + base::OnceCallback<void(bool)> cb) const = 0; // True if the cryptographer has any keys available to attempt decryption. // Could mean we've downloaded and loaded Nigori objects, or we bootstrapped
diff --git a/components/sync/engine_impl/cycle/sync_cycle.cc b/components/sync/engine_impl/cycle/sync_cycle.cc index 6f6e3c3..fdf8f923 100644 --- a/components/sync/engine_impl/cycle/sync_cycle.cc +++ b/components/sync/engine_impl/cycle/sync_cycle.cc
@@ -48,7 +48,8 @@ status_controller_->sync_start_time(), status_controller_->poll_finish_time(), num_entries_by_type, num_to_delete_entries_by_type, get_updates_origin, - context_->short_poll_interval(), context_->long_poll_interval()); + context_->short_poll_interval(), context_->long_poll_interval(), + context_->model_type_registry()->HasUnsyncedItems()); return snapshot; }
diff --git a/components/sync/engine_impl/js_sync_manager_observer_unittest.cc b/components/sync/engine_impl/js_sync_manager_observer_unittest.cc index af2493e..3d4a129 100644 --- a/components/sync/engine_impl/js_sync_manager_observer_unittest.cc +++ b/components/sync/engine_impl/js_sync_manager_observer_unittest.cc
@@ -67,7 +67,8 @@ std::vector<int>(MODEL_TYPE_COUNT, 0), std::vector<int>(MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN, /*short_poll_interval=*/base::TimeDelta::FromMinutes(30), - /*long_poll_interval=*/base::TimeDelta::FromMinutes(180)); + /*long_poll_interval=*/base::TimeDelta::FromMinutes(180), + /*has_remaining_local_changes=*/false); base::DictionaryValue expected_details; expected_details.Set("snapshot", snapshot.ToValue());
diff --git a/components/sync/engine_impl/model_type_registry.cc b/components/sync/engine_impl/model_type_registry.cc index 4c251ac7..1868357f 100644 --- a/components/sync/engine_impl/model_type_registry.cc +++ b/components/sync/engine_impl/model_type_registry.cc
@@ -287,7 +287,7 @@ } } -bool ModelTypeRegistry::HasUnsyncedItemsForTest() const { +bool ModelTypeRegistry::HasUnsyncedItems() const { // For model type workers, we ask them individually. for (const auto& worker : model_type_workers_) { if (worker->HasLocalChangesForTest()) {
diff --git a/components/sync/engine_impl/model_type_registry.h b/components/sync/engine_impl/model_type_registry.h index da8942f..c729e19 100644 --- a/components/sync/engine_impl/model_type_registry.h +++ b/components/sync/engine_impl/model_type_registry.h
@@ -105,7 +105,7 @@ const TypeDebugInfoObserver* observer) const; void RequestEmitDebugInfo(); - bool HasUnsyncedItemsForTest() const; + bool HasUnsyncedItems() const; base::WeakPtr<ModelTypeConnector> AsWeakPtr();
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc index fbc99f7..02e86301 100644 --- a/components/sync/engine_impl/sync_manager_impl.cc +++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -951,7 +951,7 @@ } bool SyncManagerImpl::HasUnsyncedItemsForTest() { - return model_type_registry_->HasUnsyncedItemsForTest(); + return model_type_registry_->HasUnsyncedItems(); } SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() {
diff --git a/components/update_client/action_runner.cc b/components/update_client/action_runner.cc index c91388a..8cfb13c 100644 --- a/components/update_client/action_runner.cc +++ b/components/update_client/action_runner.cc
@@ -44,7 +44,7 @@ void ActionRunner::Unpack( std::unique_ptr<service_manager::Connector> connector) { - const auto& installer = component_.crx_component().installer; + const auto& installer = component_.crx_component()->installer; base::FilePath file_path; installer->GetInstalledFile(component_.action_run(), &file_path);
diff --git a/components/update_client/component.cc b/components/update_client/component.cc index cbed9b7..6905698 100644 --- a/components/update_client/component.cc +++ b/components/update_client/component.cc
@@ -27,12 +27,12 @@ // The state machine representing how a CRX component changes during an update. // -// +------------------------> kNew <---------------------+--------+ -// | | | | -// | V | | -// | kChecking | | -// | | | | -// | error V no no | | +// +------------------------- kNew +// | | +// | V +// | kChecking +// | | +// V error V no no // kUpdateError <------------- [update?] -> [action?] -> kUpToDate kUpdated // ^ | | ^ ^ // | yes | | yes | | @@ -212,7 +212,8 @@ CrxUpdateItem crx_update_item; crx_update_item.state = state_->state(); crx_update_item.id = id_; - crx_update_item.component = crx_component_; + if (crx_component_) + crx_update_item.component = *crx_component_; crx_update_item.last_check = last_check_; crx_update_item.next_version = next_version_; crx_update_item.next_fp = next_fp_; @@ -256,6 +257,9 @@ DCHECK_EQ(ComponentState::kNew, state()); + crx_component_ = std::make_unique<CrxComponent>(); + crx_component_->version = version; + previous_version_ = version; next_version_ = base::Version("0"); extra_code1_ = reason; @@ -274,7 +278,8 @@ bool Component::CanDoBackgroundDownload() const { // Foreground component updates are always downloaded in foreground. - return !is_foreground() && crx_component_.allows_background_download && + return !is_foreground() && + (crx_component() && crx_component()->allows_background_download) && update_context_.config->EnabledBackgroundDownloader(); } @@ -361,6 +366,7 @@ DCHECK(thread_checker_.CalledOnValidThread()); auto& component = State::component(); + DCHECK(component.crx_component()); component.last_check_ = base::TimeTicks::Now(); component.update_check_complete_ = base::BindOnce( @@ -421,11 +427,13 @@ DCHECK(thread_checker_.CalledOnValidThread()); auto& component = State::component(); + DCHECK(component.crx_component()); component.is_update_available_ = true; component.NotifyObservers(Events::COMPONENT_UPDATE_FOUND); - if (component.crx_component_.supports_group_policy_enable_component_updates && + if (component.crx_component() + ->supports_group_policy_enable_component_updates && !component.update_context_.enabled_component_updates) { component.error_category_ = static_cast<int>(ErrorCategory::kServiceError); component.error_code_ = static_cast<int>(ServiceError::UPDATE_DISABLED); @@ -462,6 +470,7 @@ DCHECK(thread_checker_.CalledOnValidThread()); auto& component = State::component(); + DCHECK(component.crx_component()); component.NotifyObservers(Events::COMPONENT_NOT_UPDATED); EndState(); @@ -480,6 +489,8 @@ const auto& component = Component::State::component(); const auto& update_context = component.update_context_; + DCHECK(component.crx_component()); + crx_downloader_ = update_context.crx_downloader_factory( component.CanDoBackgroundDownload(), update_context.config->RequestContext()); @@ -548,6 +559,8 @@ const auto& component = Component::State::component(); const auto& update_context = component.update_context_; + DCHECK(component.crx_component()); + crx_downloader_ = update_context.crx_downloader_factory( component.CanDoBackgroundDownload(), update_context.config->RequestContext()); @@ -615,6 +628,8 @@ const auto& component = Component::State::component(); const auto& update_context = component.update_context_; + DCHECK(component.crx_component()); + component.NotifyObservers(Events::COMPONENT_UPDATE_READY); // Create a fresh connector that can be used on the other task runner. @@ -627,8 +642,8 @@ base::BindOnce( &update_client::StartInstallOnBlockingTaskRunner, base::ThreadTaskRunnerHandle::Get(), - component.crx_component_.pk_hash, component.crx_path_, - component.next_fp_, component.crx_component_.installer, + component.crx_component()->pk_hash, component.crx_path_, + component.next_fp_, component.crx_component()->installer, std::move(connector), base::BindOnce(&Component::StateUpdatingDiff::InstallComplete, base::Unretained(this)))); @@ -679,6 +694,8 @@ const auto& component = Component::State::component(); const auto& update_context = component.update_context_; + DCHECK(component.crx_component()); + component.NotifyObservers(Events::COMPONENT_UPDATE_READY); // Create a fresh connector that can be used on the other task runner. @@ -690,8 +707,8 @@ base::BindOnce( &update_client::StartInstallOnBlockingTaskRunner, base::ThreadTaskRunnerHandle::Get(), - component.crx_component_.pk_hash, component.crx_path_, - component.next_fp_, component.crx_component_.installer, + component.crx_component()->pk_hash, component.crx_path_, + component.next_fp_, component.crx_component()->installer, std::move(connector), base::BindOnce(&Component::StateUpdating::InstallComplete, base::Unretained(this)))); @@ -737,8 +754,10 @@ DCHECK(thread_checker_.CalledOnValidThread()); auto& component = State::component(); - component.crx_component_.version = component.next_version_; - component.crx_component_.fingerprint = component.next_fp_; + DCHECK(component.crx_component()); + + component.crx_component_->version = component.next_version_; + component.crx_component_->fingerprint = component.next_fp_; component.AppendEvent(BuildUpdateCompleteEventElement(component)); @@ -759,6 +778,8 @@ DCHECK(thread_checker_.CalledOnValidThread()); auto& component = State::component(); + DCHECK(component.crx_component()); + component.AppendEvent(BuildUninstalledEventElement(component)); EndState(); @@ -775,6 +796,8 @@ DCHECK(thread_checker_.CalledOnValidThread()); const auto& component = State::component(); + DCHECK(component.crx_component()); + action_runner_ = std::make_unique<ActionRunner>(component); action_runner_->Run( base::BindOnce(&StateRun::ActionRunComplete, base::Unretained(this)));
diff --git a/components/update_client/component.h b/components/update_client/component.h index ff52638..0e357fb15 100644 --- a/components/update_client/component.h +++ b/components/update_client/component.h
@@ -72,9 +72,9 @@ std::string id() const { return id_; } - const CrxComponent& crx_component() const { return crx_component_; } - void set_crx_component(const CrxComponent& crx_component) { - crx_component_ = crx_component; + const CrxComponent* crx_component() const { return crx_component_.get(); } + void set_crx_component(std::unique_ptr<CrxComponent> crx_component) { + crx_component_ = std::move(crx_component); } const base::Version& previous_version() const { return previous_version_; } @@ -369,7 +369,7 @@ base::ThreadChecker thread_checker_; const std::string id_; - CrxComponent crx_component_; + std::unique_ptr<CrxComponent> crx_component_; // The status of the updatecheck response. std::string status_;
diff --git a/components/update_client/ping_manager.cc b/components/update_client/ping_manager.cc index 7ec516eb..f9b885b4 100644 --- a/components/update_client/ping_manager.cc +++ b/components/update_client/ping_manager.cc
@@ -70,8 +70,10 @@ return; } + DCHECK(component.crx_component()); + auto urls(config_->PingUrl()); - if (component.crx_component().requires_network_encryption) + if (component.crx_component()->requires_network_encryption) RemoveUnsecureUrls(&urls); if (urls.empty()) {
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc index 7f945bc..e704457 100644 --- a/components/update_client/ping_manager_unittest.cc +++ b/components/update_client/ping_manager_unittest.cc
@@ -114,7 +114,7 @@ { Component component(*update_context, "abc"); - + component.crx_component_ = std::make_unique<CrxComponent>(); component.state_ = std::make_unique<Component::StateUpdated>(&component); component.previous_version_ = base::Version("1.0"); component.next_version_ = base::Version("2.0"); @@ -147,6 +147,7 @@ { // Test eventresult="0" is sent for failed updates. Component component(*update_context, "abc"); + component.crx_component_ = std::make_unique<CrxComponent>(); component.state_ = std::make_unique<Component::StateUpdateError>(&component); component.previous_version_ = base::Version("1.0"); @@ -170,6 +171,7 @@ { // Test the error values and the fingerprints. Component component(*update_context, "abc"); + component.crx_component_ = std::make_unique<CrxComponent>(); component.state_ = std::make_unique<Component::StateUpdateError>(&component); component.previous_version_ = base::Version("1.0"); @@ -206,6 +208,7 @@ { // Test an invalid |next_version| is not serialized. Component component(*update_context, "abc"); + component.crx_component_ = std::make_unique<CrxComponent>(); component.state_ = std::make_unique<Component::StateUpdateError>(&component); component.previous_version_ = base::Version("1.0"); @@ -229,6 +232,7 @@ // Test a valid |previouversion| and |next_version| = base::Version("0") // are serialized correctly under <event...> for uninstall. Component component(*update_context, "abc"); + component.crx_component_ = std::make_unique<CrxComponent>(); component.Uninstall(base::Version("1.2.3.4"), 0); component.AppendEvent(BuildUninstalledEventElement(component)); @@ -249,6 +253,7 @@ { // Test the download metrics. Component component(*update_context, "abc"); + component.crx_component_ = std::make_unique<CrxComponent>(); component.state_ = std::make_unique<Component::StateUpdated>(&component); component.previous_version_ = base::Version("1.0"); component.next_version_ = base::Version("2.0"); @@ -306,9 +311,10 @@ const auto update_context = MakeMockUpdateContext(); Component component(*update_context, "abc"); + component.crx_component_ = std::make_unique<CrxComponent>(); // The default value for |requires_network_encryption| is true. - EXPECT_TRUE(component.crx_component_.requires_network_encryption); + EXPECT_TRUE(component.crx_component_->requires_network_encryption); component.state_ = std::make_unique<Component::StateUpdated>(&component); component.previous_version_ = base::Version("1.0");
diff --git a/components/update_client/protocol_builder.cc b/components/update_client/protocol_builder.cc index e49737c8..3119038 100644 --- a/components/update_client/protocol_builder.cc +++ b/components/update_client/protocol_builder.cc
@@ -334,25 +334,27 @@ for (const auto& id : ids_checked) { DCHECK_EQ(1u, components.count(id)); const auto& component = *components.at(id); - const auto& crx_component = component.crx_component(); const auto& component_id = component.id(); + const auto* crx_component = component.crx_component(); + + DCHECK(crx_component); const update_client::InstallerAttributes installer_attributes( - SanitizeInstallerAttributes(crx_component.installer_attributes)); + SanitizeInstallerAttributes(crx_component->installer_attributes)); std::string app("<app "); base::StringAppendF(&app, "appid=\"%s\" version=\"%s\"", component_id.c_str(), - crx_component.version.GetString().c_str()); + crx_component->version.GetString().c_str()); if (!brand.empty()) base::StringAppendF(&app, " brand=\"%s\"", brand.c_str()); - if (!crx_component.install_source.empty()) + if (!crx_component->install_source.empty()) base::StringAppendF(&app, " installsource=\"%s\"", - crx_component.install_source.c_str()); + crx_component->install_source.c_str()); else if (component.is_foreground()) base::StringAppendF(&app, " installsource=\"ondemand\""); - if (!crx_component.install_location.empty()) + if (!crx_component->install_location.empty()) base::StringAppendF(&app, " installedby=\"%s\"", - crx_component.install_location.c_str()); + crx_component->install_location.c_str()); for (const auto& attr : installer_attributes) { base::StringAppendF(&app, " %s=\"%s\"", attr.first.c_str(), attr.second.c_str()); @@ -360,7 +362,7 @@ const auto& cohort = metadata->GetCohort(component_id); const auto& cohort_name = metadata->GetCohortName(component_id); const auto& cohort_hint = metadata->GetCohortHint(component_id); - const auto& disabled_reasons = crx_component.disabled_reasons; + const auto& disabled_reasons = crx_component->disabled_reasons; if (!cohort.empty()) base::StringAppendF(&app, " cohort=\"%s\"", cohort.c_str()); if (!cohort_name.empty()) @@ -374,7 +376,7 @@ base::StringAppendF(&app, "<disabled reason=\"%d\"/>", disabled_reason); base::StringAppendF(&app, "<updatecheck"); - if (crx_component.supports_group_policy_enable_component_updates && + if (crx_component->supports_group_policy_enable_component_updates && !enabled_component_updates) { base::StringAppendF(&app, " updatedisabled=\"true\""); } @@ -402,12 +404,12 @@ base::StringAppendF(&app, " ping_freshness=\"%s\"/>", metadata->GetPingFreshness(component_id).c_str()); - if (!crx_component.fingerprint.empty()) { + if (!crx_component->fingerprint.empty()) { base::StringAppendF(&app, "<packages>" "<package fp=\"%s\"/>" "</packages>", - crx_component.fingerprint.c_str()); + crx_component->fingerprint.c_str()); } base::StringAppendF(&app, "</app>"); app_elements.append(app);
diff --git a/components/update_client/update_checker.cc b/components/update_client/update_checker.cc index d63f0eb1..1fb0479 100644 --- a/components/update_client/update_checker.cc +++ b/components/update_client/update_checker.cc
@@ -41,7 +41,8 @@ bool IsEncryptionRequired(const IdToComponentPtrMap& components) { for (const auto& item : components) { const auto& component = item.second; - if (component->crx_component().requires_network_encryption) + if (component->crx_component() && + component->crx_component()->requires_network_encryption) return true; } return false;
diff --git a/components/update_client/update_checker_unittest.cc b/components/update_client/update_checker_unittest.cc index b7ddb0f..6c1eb25d 100644 --- a/components/update_client/update_checker_unittest.cc +++ b/components/update_client/update_checker_unittest.cc
@@ -212,16 +212,17 @@ } std::unique_ptr<Component> UpdateCheckerTest::MakeComponent() const { - CrxComponent crx_component; - crx_component.name = "test_jebg"; - crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx_component.installer = nullptr; - crx_component.version = base::Version("0.9"); - crx_component.fingerprint = "fp1"; + std::unique_ptr<CrxComponent> crx_component = + std::make_unique<CrxComponent>(); + crx_component->name = "test_jebg"; + crx_component->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx_component->installer = nullptr; + crx_component->version = base::Version("0.9"); + crx_component->fingerprint = "fp1"; auto component = std::make_unique<Component>(*update_context_, kUpdateItemId); component->state_ = std::make_unique<Component::StateNew>(component.get()); - component->crx_component_ = crx_component; + component->crx_component_ = std::move(crx_component); return component; } @@ -239,11 +240,11 @@ components[kUpdateItemId] = MakeComponent(); auto& component = components[kUpdateItemId]; - component->crx_component_.installer_attributes["ap"] = "some_ap"; + component->crx_component_->installer_attributes["ap"] = "some_ap"; update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -323,11 +324,10 @@ // Make "ap" too long. auto& component = components[kUpdateItemId]; - component->crx_component_.installer_attributes["ap"] = std::string(257, 'a'); + component->crx_component_->installer_attributes["ap"] = std::string(257, 'a'); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); @@ -355,8 +355,7 @@ components[kUpdateItemId] = MakeComponent(); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); @@ -386,8 +385,7 @@ auto& component = components[kUpdateItemId]; update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -414,8 +412,8 @@ components[kUpdateItemId] = MakeComponent(); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); @@ -443,8 +441,7 @@ const auto& component = components[kUpdateItemId]; update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); @@ -481,11 +478,10 @@ components[kUpdateItemId] = MakeComponent(); auto& component = components[kUpdateItemId]; - component->crx_component_.requires_network_encryption = true; + component->crx_component_->requires_network_encryption = true; update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -512,16 +508,16 @@ // Do two update-checks. activity_data_service_->SetDaysSinceLastRollCall(kUpdateItemId, 5); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); update_checker_ = UpdateChecker::Create(config_, metadata_.get()); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -555,8 +551,8 @@ activity_data_service_->SetActiveBit(kUpdateItemId, true); activity_data_service_->SetDaysSinceLastActive(kUpdateItemId, 10); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -567,8 +563,8 @@ activity_data_service_->SetActiveBit(kUpdateItemId, true); update_checker_ = UpdateChecker::Create(config_, metadata_.get()); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -578,8 +574,8 @@ update_checker_ = UpdateChecker::Create(config_, metadata_.get()); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "extra=\"params\"", true, + update_context_->session_id, {kUpdateItemId}, components, + "extra=\"params\"", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -606,14 +602,13 @@ components[kUpdateItemId] = MakeComponent(); auto& component = components[kUpdateItemId]; - auto& crx_component = const_cast<CrxComponent&>(component->crx_component()); + auto* crx_component = const_cast<CrxComponent*>(component->crx_component()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -626,8 +621,7 @@ std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -637,14 +631,13 @@ EXPECT_THAT(body1, testing::Not(testing::HasSubstr(R"(installedby=)"))); update_context_->is_foreground = false; - crx_component.install_source = "webstore"; - crx_component.install_location = "external"; + crx_component->install_source = "webstore"; + crx_component->install_location = "external"; EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -654,14 +647,13 @@ EXPECT_THAT(body2, testing::HasSubstr(R"(installedby="external")")); update_context_->is_foreground = true; - crx_component.install_source = "sideload"; - crx_component.install_location = "policy"; + crx_component->install_source = "sideload"; + crx_component->install_location = "policy"; EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -678,14 +670,13 @@ components[kUpdateItemId] = MakeComponent(); auto& component = components[kUpdateItemId]; - auto& crx_component = const_cast<CrxComponent&>(component->crx_component()); + auto* crx_component = const_cast<CrxComponent*>(component->crx_component()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -694,14 +685,13 @@ EXPECT_THAT(body0, testing::HasSubstr(R"(enabled="1")")); EXPECT_THAT(body0, testing::Not(testing::HasSubstr("<disabled"))); - crx_component.disabled_reasons = std::vector<int>(); + crx_component->disabled_reasons = {}; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -710,13 +700,12 @@ EXPECT_THAT(body1, testing::HasSubstr(R"(enabled="1")")); EXPECT_THAT(body1, testing::Not(testing::HasSubstr("<disabled"))); - crx_component.disabled_reasons = std::vector<int>({0}); + crx_component->disabled_reasons = {0}; EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -725,14 +714,13 @@ EXPECT_THAT(body2, testing::HasSubstr(R"(enabled="0")")); EXPECT_THAT(body2, testing::HasSubstr(R"(<disabled reason="0")")); - crx_component.disabled_reasons = std::vector<int>({1}); + crx_component->disabled_reasons = {1}; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -741,14 +729,13 @@ EXPECT_THAT(body3, testing::HasSubstr(R"(enabled="0")")); EXPECT_THAT(body3, testing::HasSubstr(R"(<disabled reason="1")")); - crx_component.disabled_reasons = std::vector<int>({4, 8, 16}); + crx_component->disabled_reasons = {4, 8, 16}; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -759,14 +746,13 @@ EXPECT_THAT(body4, testing::HasSubstr(R"(<disabled reason="8")")); EXPECT_THAT(body4, testing::HasSubstr(R"(<disabled reason="16")")); - crx_component.disabled_reasons = std::vector<int>({0, 4, 8, 16}); + crx_component->disabled_reasons = {0, 4, 8, 16}; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -793,15 +779,14 @@ // * the component updates are disabled. // Expects the group policy to be ignored and the update check to not // include the "updatedisabled" attribute. - EXPECT_FALSE( - component->crx_component_.supports_group_policy_enable_component_updates); + EXPECT_FALSE(component->crx_component_ + ->supports_group_policy_enable_component_updates); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -814,15 +799,14 @@ // * the component supports group policies. // * the component updates are disabled. // Expects the update check to include the "updatedisabled" attribute. - component->crx_component_.supports_group_policy_enable_component_updates = + component->crx_component_->supports_group_policy_enable_component_updates = true; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", false, + update_context_->session_id, {kUpdateItemId}, components, "", false, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -836,15 +820,14 @@ // * the component does not support group policies. // * the component updates are enabled. // Expects the update check to not include the "updatedisabled" attribute. - component->crx_component_.supports_group_policy_enable_component_updates = + component->crx_component_->supports_group_policy_enable_component_updates = false; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -857,15 +840,14 @@ // * the component supports group policies. // * the component updates are enabled. // Expects the update check to not include the "updatedisabled" attribute. - component->crx_component_.supports_group_policy_enable_component_updates = + component->crx_component_->supports_group_policy_enable_component_updates = true; update_checker_ = UpdateChecker::Create(config_, metadata_.get()); EXPECT_TRUE(post_interceptor_->ExpectRequest( std::make_unique<PartialMatch>("updatecheck"), test_file("updatecheck_reply_1.xml"))); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -888,8 +870,7 @@ auto& component = components[kUpdateItemId]; update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -920,8 +901,7 @@ components[kUpdateItemId] = MakeComponent(); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); RunThreads(); @@ -955,8 +935,7 @@ update_checker_ = UpdateChecker::Create(config_, metadata_.get()); update_checker_->CheckForUpdates( - update_context_->session_id, std::vector<std::string>{kUpdateItemId}, - components, "", true, + update_context_->session_id, {kUpdateItemId}, components, "", true, base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete, base::Unretained(this))); runloop.Run();
diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h index d2ec6e0e..d8d546a1 100644 --- a/components/update_client/update_client.h +++ b/components/update_client/update_client.h
@@ -275,8 +275,8 @@ class UpdateClient : public base::RefCounted<UpdateClient> { public: using CrxDataCallback = - base::OnceCallback<void(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components)>; + base::OnceCallback<std::vector<std::unique_ptr<CrxComponent>>( + const std::vector<std::string>& ids)>; // Defines an interface to observe the UpdateClient. It provides // notifications when state changes occur for the service itself or for the
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc index 4b9c0b1f..c051f43b 100644 --- a/components/update_client/update_client_unittest.cc +++ b/components/update_client/update_client_unittest.cc
@@ -213,14 +213,16 @@ TEST_F(UpdateClientTest, OneCrxNoUpdate) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.9"); - crx.installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.9"); + crx->installer = base::MakeRefCounted<TestInstaller>(); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -319,22 +321,24 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx1; - crx1.name = "test_jebg"; - crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx1.version = base::Version("0.9"); - crx1.installer = base::MakeRefCounted<TestInstaller>(); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx1 = std::make_unique<CrxComponent>(); + crx1->name = "test_jebg"; + crx1->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx1->version = base::Version("0.9"); + crx1->installer = base::MakeRefCounted<TestInstaller>(); - CrxComponent crx2; - crx2.name = "test_abag"; - crx2.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash)); - crx2.version = base::Version("2.2"); - crx2.installer = base::MakeRefCounted<TestInstaller>(); + std::unique_ptr<CrxComponent> crx2 = std::make_unique<CrxComponent>(); + crx2->name = "test_abag"; + crx2->pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash)); + crx2->version = base::Version("2.2"); + crx2->installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx1); - components->push_back(crx2); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx1)); + component.push_back(std::move(crx2)); + return component; } }; @@ -532,22 +536,24 @@ TEST_F(UpdateClientTest, TwoCrxUpdate) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx1; - crx1.name = "test_jebg"; - crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx1.version = base::Version("0.9"); - crx1.installer = base::MakeRefCounted<TestInstaller>(); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx1 = std::make_unique<CrxComponent>(); + crx1->name = "test_jebg"; + crx1->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx1->version = base::Version("0.9"); + crx1->installer = base::MakeRefCounted<TestInstaller>(); - CrxComponent crx2; - crx2.name = "test_ihfo"; - crx2.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); - crx2.version = base::Version("0.8"); - crx2.installer = base::MakeRefCounted<TestInstaller>(); + std::unique_ptr<CrxComponent> crx2 = std::make_unique<CrxComponent>(); + crx2->name = "test_ihfo"; + crx2->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); + crx2->version = base::Version("0.8"); + crx2->installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx1); - components->push_back(crx2); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx1)); + component.push_back(std::move(crx2)); + return component; } }; @@ -804,22 +810,24 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx1; - crx1.name = "test_jebg"; - crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx1.version = base::Version("0.9"); - crx1.installer = base::MakeRefCounted<TestInstaller>(); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx1 = std::make_unique<CrxComponent>(); + crx1->name = "test_jebg"; + crx1->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx1->version = base::Version("0.9"); + crx1->installer = base::MakeRefCounted<TestInstaller>(); - CrxComponent crx2; - crx2.name = "test_ihfo"; - crx2.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); - crx2.version = base::Version("0.8"); - crx2.installer = base::MakeRefCounted<TestInstaller>(); + std::unique_ptr<CrxComponent> crx2 = std::make_unique<CrxComponent>(); + crx2->name = "test_ihfo"; + crx2->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); + crx2->version = base::Version("0.8"); + crx2->installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx1); - components->push_back(crx2); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx1)); + component.push_back(std::move(crx2)); + return component; } }; @@ -1068,8 +1076,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { static int num_calls = 0; // Must use the same stateful installer object. @@ -1078,19 +1086,21 @@ ++num_calls; - CrxComponent crx; - crx.name = "test_ihfo"; - crx.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); - crx.installer = installer; + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_ihfo"; + crx->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); + crx->installer = installer; if (num_calls == 1) { - crx.version = base::Version("0.8"); + crx->version = base::Version("0.8"); } else if (num_calls == 2) { - crx.version = base::Version("1.0"); + crx->version = base::Version("1.0"); } else { NOTREACHED(); } - components->push_back(crx); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -1408,8 +1418,8 @@ class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { scoped_refptr<MockInstaller> installer = base::MakeRefCounted<MockInstaller>(); @@ -1418,12 +1428,15 @@ EXPECT_CALL(*installer, GetInstalledFile(_, _)).Times(0); EXPECT_CALL(*installer, Uninstall()).Times(0); - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.9"); - crx.installer = installer; - components->push_back(crx); + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.9"); + crx->installer = installer; + + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -1593,8 +1606,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { static int num_calls = 0; // Must use the same stateful installer object. @@ -1603,19 +1616,21 @@ ++num_calls; - CrxComponent crx; - crx.name = "test_ihfo"; - crx.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); - crx.installer = installer; + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_ihfo"; + crx->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); + crx->installer = installer; if (num_calls == 1) { - crx.version = base::Version("0.8"); + crx->version = base::Version("0.8"); } else if (num_calls == 2) { - crx.version = base::Version("1.0"); + crx->version = base::Version("1.0"); } else { NOTREACHED(); } - components->push_back(crx); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -1911,14 +1926,16 @@ TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.9"); - crx.installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.9"); + crx->installer = base::MakeRefCounted<TestInstaller>(); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -2028,15 +2045,17 @@ TEST_F(UpdateClientTest, OneCrxInstall) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.0"); - crx.installer = base::MakeRefCounted<TestInstaller>(); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.0"); + crx->installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -2212,15 +2231,16 @@ TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.0"); - crx.installer = base::MakeRefCounted<TestInstaller>(); - - components->push_back(crx); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.0"); + crx->installer = base::MakeRefCounted<TestInstaller>(); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -2337,8 +2357,10 @@ TEST_F(UpdateClientTest, EmptyIdList) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) {} + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + return {}; + } }; class CompletionCallbackMock { @@ -2474,14 +2496,16 @@ TEST_F(UpdateClientTest, RetryAfter) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.9"); - crx.installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.9"); + crx->installer = base::MakeRefCounted<TestInstaller>(); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -2666,23 +2690,25 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx1; - crx1.name = "test_jebg"; - crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx1.version = base::Version("0.9"); - crx1.installer = base::MakeRefCounted<TestInstaller>(); - crx1.supports_group_policy_enable_component_updates = true; + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx1 = std::make_unique<CrxComponent>(); + crx1->name = "test_jebg"; + crx1->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx1->version = base::Version("0.9"); + crx1->installer = base::MakeRefCounted<TestInstaller>(); + crx1->supports_group_policy_enable_component_updates = true; - CrxComponent crx2; - crx2.name = "test_ihfo"; - crx2.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); - crx2.version = base::Version("0.8"); - crx2.installer = base::MakeRefCounted<TestInstaller>(); + std::unique_ptr<CrxComponent> crx2 = std::make_unique<CrxComponent>(); + crx2->name = "test_ihfo"; + crx2->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); + crx2->version = base::Version("0.8"); + crx2->installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx1); - components->push_back(crx2); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx1)); + component.push_back(std::move(crx2)); + return component; } }; @@ -2924,14 +2950,16 @@ TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) { class DataCallbackMock { public: - static void Callback(const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_jebg"; - crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - crx.version = base::Version("0.9"); - crx.installer = base::MakeRefCounted<TestInstaller>(); - components->push_back(crx); + static std::vector<std::unique_ptr<CrxComponent>> Callback( + const std::vector<std::string>& ids) { + std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>(); + crx->name = "test_jebg"; + crx->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx->version = base::Version("0.9"); + crx->installer = base::MakeRefCounted<TestInstaller>(); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; } }; @@ -3170,14 +3198,15 @@ // The action is a program which returns 1877345072 as a hardcoded value. update_client->Install( std::string("gjpmebpgbhcamgdgjcmnjfhggjpgcimm"), - base::BindOnce([](const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_niea"; - crx.pk_hash.assign(gjpm_hash, gjpm_hash + arraysize(gjpm_hash)); - crx.version = base::Version("0.0"); - crx.installer = base::MakeRefCounted<VersionedTestInstaller>(); - components->push_back(crx); + base::BindOnce([](const std::vector<std::string>& ids) { + auto crx = std::make_unique<CrxComponent>(); + crx->name = "test_niea"; + crx->pk_hash.assign(gjpm_hash, gjpm_hash + arraysize(gjpm_hash)); + crx->version = base::Version("0.0"); + crx->installer = base::MakeRefCounted<VersionedTestInstaller>(); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; }), base::BindOnce( [](base::OnceClosure quit_closure, Error error) { @@ -3278,7 +3307,7 @@ base::OnceClosure quit_closure = runloop.QuitClosure(); auto config = base::MakeRefCounted<TestConfigurator>(); - scoped_refptr<ComponentUnpacker> component_unpacker = new ComponentUnpacker( + auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>( std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash)), TestFilePath("runaction_test_win.crx3"), nullptr, config->CreateServiceManagerConnector()); @@ -3317,15 +3346,16 @@ ids, base::BindOnce( [](const base::FilePath& unpack_path, - const std::vector<std::string>& ids, - std::vector<CrxComponent>* components) { - CrxComponent crx; - crx.name = "test_niea"; - crx.pk_hash.assign(gjpm_hash, gjpm_hash + arraysize(gjpm_hash)); - crx.version = base::Version("1.0"); - crx.installer = + const std::vector<std::string>& ids) { + auto crx = std::make_unique<CrxComponent>(); + crx->name = "test_niea"; + crx->pk_hash.assign(gjpm_hash, gjpm_hash + arraysize(gjpm_hash)); + crx->version = base::Version("1.0"); + crx->installer = base::MakeRefCounted<ReadOnlyTestInstaller>(unpack_path); - components->push_back(crx); + std::vector<std::unique_ptr<CrxComponent>> component; + component.push_back(std::move(crx)); + return component; }, unpack_path), false,
diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc index 1f33635..6ab9932 100644 --- a/components/update_client/update_engine.cc +++ b/components/update_client/update_engine.cc
@@ -99,23 +99,21 @@ // update context. DCHECK_EQ(ids.size(), update_context->ids.size()); DCHECK_EQ(update_context->ids.size(), update_context->components.size()); - std::vector<CrxComponent> crx_components; - std::move(update_context->crx_data_callback) - .Run(update_context->ids, &crx_components); + std::vector<std::unique_ptr<CrxComponent>> crx_components = + std::move(update_context->crx_data_callback).Run(update_context->ids); DCHECK_EQ(update_context->ids.size(), crx_components.size()); for (size_t i = 0; i != update_context->ids.size(); ++i) { const auto& id = update_context->ids[i]; - const auto& crx_component = crx_components[i]; - DCHECK_EQ(id, GetCrxComponentID(crx_component)); + DCHECK_EQ(id, GetCrxComponentID(*crx_components[i])); DCHECK_EQ(1u, update_context->components.count(id)); DCHECK(update_context->components.at(id)); auto& component = *update_context->components.at(id); - component.set_crx_component(crx_component); - component.set_previous_version(crx_component.version); - component.set_previous_fp(crx_component.fingerprint); + component.set_crx_component(std::move(crx_components[i])); + component.set_previous_version(component.crx_component()->version); + component.set_previous_fp(component.crx_component()->fingerprint); // Handle |kNew| state. This will transition the components to |kChecking|. component.Handle(
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn index e919b97b..f74e77e 100644 --- a/components/variations/BUILD.gn +++ b/components/variations/BUILD.gn
@@ -100,6 +100,15 @@ ] jni_package = "variations" } + + android_library("load_seed_result_enum_java") { + deps = [ "//base:base_java" ] + srcjar_deps = [ ":load_seed_result_enum_srcjar" ] + } + + java_cpp_enum("load_seed_result_enum_srcjar") { + sources = [ "metrics.h" ] + } } static_library("test_support") {
diff --git a/components/variations/metrics.h b/components/variations/metrics.h index 832f39b..5fe2b8c 100644 --- a/components/variations/metrics.h +++ b/components/variations/metrics.h
@@ -24,6 +24,7 @@ // The result of attempting to load a variations seed on startup. // Note: UMA histogram enum - don't re-order or remove entries. +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.variations enum class LoadSeedResult { SUCCESS, EMPTY, @@ -32,6 +33,9 @@ CORRUPT_BASE64, CORRUPT_PROTOBUF, CORRUPT_GZIP, + LOAD_TIMED_OUT, + LOAD_INTERRUPTED, + LOAD_OTHER_FAILURE, ENUM_SIZE };
diff --git a/components/viz/common/skia_helper.cc b/components/viz/common/skia_helper.cc index 151f552..65d12b5 100644 --- a/components/viz/common/skia_helper.cc +++ b/components/viz/common/skia_helper.cc
@@ -4,6 +4,7 @@ #include "components/viz/common/skia_helper.h" #include "base/trace_event/trace_event.h" #include "cc/base/math_util.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" #include "ui/gfx/skia_util.h" namespace viz { @@ -47,7 +48,7 @@ // Force a flush of the Skia pipeline before we switch back to the compositor // context. - image->getTextureHandle(true); + image->getBackendTexture(true); CHECK(image->isTextureBacked()); return image; }
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS index 6d13397..a54c1c58 100644 --- a/components/viz/host/DEPS +++ b/components/viz/host/DEPS
@@ -1,7 +1,6 @@ # Please consult components/viz/README.md about allowable dependencies. include_rules = [ - "+cc/ipc", "+components/viz/common/features.h", "-components/viz/common/switches.h", "+gpu/command_buffer/client",
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc index b99a8fd..995d59a1 100644 --- a/components/viz/service/display/gl_renderer.cc +++ b/components/viz/service/display/gl_renderer.cc
@@ -58,7 +58,6 @@ #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/common/gpu_memory_allocation.h" #include "media/base/media_switches.h" -#include "skia/ext/texture_handle.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColorFilter.h" @@ -1028,6 +1027,19 @@ return true; } +// Get a GL texture id from an SkImage. An optional origin pointer can be +// passed in which will be filled out with the origin for the texture +// backing the SkImage. +static GLuint GetGLTextureIDFromSkImage(const SkImage* image, + GrSurfaceOrigin* origin = nullptr) { + GrBackendTexture backend_texture = image->getBackendTexture(true, origin); + DCHECK(backend_texture.isValid()); + GrGLTextureInfo info; + bool result = backend_texture.getGLTextureInfo(&info); + DCHECK(result); + return info.fID; +} + void GLRenderer::UpdateRPDQShadersForBlending( DrawRenderPassDrawQuadParams* params) { const RenderPassDrawQuad* quad = params->quad; @@ -1067,9 +1079,7 @@ params->background_rect, unclipped_rect); if (params->background_image) { params->background_image_id = - skia::GrBackendObjectToGrGLTextureInfo( - params->background_image->getTextureHandle(true)) - ->fID; + GetGLTextureIDFromSkImage(params->background_image.get()); DCHECK(params->background_image_id); } } @@ -1211,9 +1221,7 @@ if (params->filter_image) { GrSurfaceOrigin origin; GLuint filter_image_id = - skia::GrBackendObjectToGrGLTextureInfo( - params->filter_image->getTextureHandle(true, &origin)) - ->fID; + GetGLTextureIDFromSkImage(params->filter_image.get(), &origin); DCHECK(filter_image_id); DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_)); gl_->BindTexture(GL_TEXTURE_2D, filter_image_id);
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS index b4441729..38bf218 100644 --- a/components/viz/service/display_embedder/DEPS +++ b/components/viz/service/display_embedder/DEPS
@@ -2,7 +2,6 @@ include_rules = [ "+cc/base", - "+cc/ipc", "+cc/output", "+cc/resources", "+cc/scheduler",
diff --git a/components/viz/service/display_embedder/gl_output_surface.cc b/components/viz/service/display_embedder/gl_output_surface.cc index 7cba62b7..8e8e063b 100644 --- a/components/viz/service/display_embedder/gl_output_surface.cc +++ b/components/viz/service/display_embedder/gl_output_surface.cc
@@ -24,7 +24,6 @@ SyntheticBeginFrameSource* synthetic_begin_frame_source) : OutputSurface(context_provider), synthetic_begin_frame_source_(synthetic_begin_frame_source), - latency_tracker_(true), latency_info_cache_(this), weak_ptr_factory_(this) { capabilities_.flipped_output_surface = @@ -150,9 +149,7 @@ void GLOutputSurface::LatencyInfoCompleted( const std::vector<ui::LatencyInfo>& latency_info) { - for (const auto& latency : latency_info) { - latency_tracker_.OnGpuSwapBuffersCompleted(latency); - } + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); client_->DidFinishLatencyInfo(latency_info); }
diff --git a/components/viz/service/display_embedder/software_output_device_mac.cc b/components/viz/service/display_embedder/software_output_device_mac.cc index b256bf2..e9f284c 100644 --- a/components/viz/service/display_embedder/software_output_device_mac.cc +++ b/components/viz/service/display_embedder/software_output_device_mac.cc
@@ -190,11 +190,6 @@ if (ca_layer_frame_sink) { ca_layer_frame_sink->SetSuspended(false); ca_layer_frame_sink->UpdateCALayerTree(ca_layer_params); - base::TimeTicks vsync_timebase; - base::TimeDelta vsync_interval; - ca_layer_frame_sink->GetVSyncParameters(&vsync_timebase, &vsync_interval); - if (!update_vsync_callback_.is_null()) - update_vsync_callback_.Run(vsync_timebase, vsync_interval); } } @@ -208,26 +203,7 @@ void SoftwareOutputDeviceMac::EnsureBackbuffer() {} gfx::VSyncProvider* SoftwareOutputDeviceMac::GetVSyncProvider() { - return this; -} - -void SoftwareOutputDeviceMac::GetVSyncParameters( - const gfx::VSyncProvider::UpdateVSyncCallback& callback) { - update_vsync_callback_ = callback; -} - -bool SoftwareOutputDeviceMac::GetVSyncParametersIfAvailable( - base::TimeTicks* timebase, - base::TimeDelta* interval) { - return false; -} - -bool SoftwareOutputDeviceMac::SupportGetVSyncParametersIfAvailable() const { - return false; -} - -bool SoftwareOutputDeviceMac::IsHWClock() const { - return false; + return nullptr; } } // namespace viz
diff --git a/components/viz/service/display_embedder/software_output_device_mac.h b/components/viz/service/display_embedder/software_output_device_mac.h index 4066ac5..7329763 100644 --- a/components/viz/service/display_embedder/software_output_device_mac.h +++ b/components/viz/service/display_embedder/software_output_device_mac.h
@@ -15,14 +15,12 @@ #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/vsync_provider.h" class SkCanvas; namespace viz { -class VIZ_SERVICE_EXPORT SoftwareOutputDeviceMac : public SoftwareOutputDevice, - public gfx::VSyncProvider { +class VIZ_SERVICE_EXPORT SoftwareOutputDeviceMac : public SoftwareOutputDevice { public: explicit SoftwareOutputDeviceMac(gfx::AcceleratedWidget widget); ~SoftwareOutputDeviceMac() override; @@ -35,14 +33,6 @@ void EnsureBackbuffer() override; gfx::VSyncProvider* GetVSyncProvider() override; - // gfx::VSyncProvider implementation. - void GetVSyncParameters( - const gfx::VSyncProvider::UpdateVSyncCallback& callback) override; - bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase, - base::TimeDelta* interval) override; - bool SupportGetVSyncParametersIfAvailable() const override; - bool IsHWClock() const override; - // Testing methods. SkRegion LastCopyRegionForTesting() const { return last_copy_region_for_testing_; @@ -83,8 +73,6 @@ // valid only between BeginPaint and EndPaint. std::unique_ptr<SkCanvas> current_paint_canvas_; - gfx::VSyncProvider::UpdateVSyncCallback update_vsync_callback_; - SkRegion last_copy_region_for_testing_; DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceMac);
diff --git a/components/viz/service/display_embedder/software_output_surface.cc b/components/viz/service/display_embedder/software_output_surface.cc index d56fdff..717b6524 100644 --- a/components/viz/service/display_embedder/software_output_surface.cc +++ b/components/viz/service/display_embedder/software_output_surface.cc
@@ -26,7 +26,6 @@ scoped_refptr<base::SequencedTaskRunner> task_runner) : OutputSurface(std::move(software_device)), task_runner_(std::move(task_runner)), - latency_tracker_(true /* metric_sampling */), weak_factory_(this) {} SoftwareOutputSurface::~SoftwareOutputSurface() = default; @@ -125,8 +124,7 @@ } void SoftwareOutputSurface::SwapBuffersCallback(uint64_t swap_id) { - for (const auto& latency : stored_latency_info_) - latency_tracker_.OnGpuSwapBuffersCompleted(latency); + latency_tracker_.OnGpuSwapBuffersCompleted(stored_latency_info_); client_->DidFinishLatencyInfo(stored_latency_info_); std::vector<ui::LatencyInfo>().swap(stored_latency_info_); client_->DidReceiveSwapBuffersAck(swap_id);
diff --git a/components/viz/service/frame_sinks/DEPS b/components/viz/service/frame_sinks/DEPS index 90500834..af649c13 100644 --- a/components/viz/service/frame_sinks/DEPS +++ b/components/viz/service/frame_sinks/DEPS
@@ -2,7 +2,6 @@ include_rules = [ "+cc/base", - "+cc/ipc", "+cc/scheduler", "+components/viz/service/display", "+components/viz/service/display_embedder",
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index 672e288..7f0fc3d69 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc
@@ -60,6 +60,7 @@ #include "services/service_manager/embedder/switches.h" #include "services/service_manager/sandbox/sandbox_type.h" #include "services/service_manager/sandbox/switches.h" +#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" #include "ui/display/display_switches.h" @@ -836,6 +837,13 @@ InitializeV8IfNeeded(command_line, process_type); + blink::TrialTokenValidator::SetOriginTrialPolicyGetter( + base::BindRepeating([]() -> blink::OriginTrialPolicy* { + if (auto* client = GetContentClient()) + return client->GetOriginTrialPolicy(); + return nullptr; + })); + #if !defined(OFFICIAL_BUILD) #if defined(OS_WIN) bool should_enable_stack_dump = !process_type.empty();
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 50c45c26..8f3b802 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -822,6 +822,8 @@ "frame_host/render_frame_proxy_host.h", "frame_host/render_widget_host_view_guest.cc", "frame_host/render_widget_host_view_guest.h", + "frame_host/webui_navigation_throttle.cc", + "frame_host/webui_navigation_throttle.h", "generic_sensor/sensor_provider_proxy_impl.cc", "generic_sensor/sensor_provider_proxy_impl.h", "geolocation/geolocation_service_impl.cc",
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.cc b/content/browser/blob_storage/chrome_blob_storage_context.cc index 0b5780d..89f91782 100644 --- a/content/browser/blob_storage/chrome_blob_storage_context.cc +++ b/content/browser/blob_storage/chrome_blob_storage_context.cc
@@ -174,27 +174,6 @@ return blob_handle; } -std::unique_ptr<BlobHandle> ChromeBlobStorageContext::CreateFileBackedBlob( - const FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - std::string uuid(base::GenerateGUID()); - auto blob_data_builder = std::make_unique<storage::BlobDataBuilder>(uuid); - blob_data_builder->AppendFile(path, offset, size, expected_modification_time); - - std::unique_ptr<storage::BlobDataHandle> blob_data_handle = - context_->AddFinishedBlob(std::move(blob_data_builder)); - if (!blob_data_handle) - return std::unique_ptr<BlobHandle>(); - - std::unique_ptr<BlobHandle> blob_handle( - new BlobHandleImpl(std::move(blob_data_handle))); - return blob_handle; -} - // static scoped_refptr<network::SharedURLLoaderFactory> ChromeBlobStorageContext::URLLoaderFactoryForToken(
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.h b/content/browser/blob_storage/chrome_blob_storage_context.h index 043d968c..812a6a5 100644 --- a/content/browser/blob_storage/chrome_blob_storage_context.h +++ b/content/browser/blob_storage/chrome_blob_storage_context.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_FILEAPI_CHROME_BLOB_STORAGE_CONTEXT_H_ -#define CONTENT_BROWSER_FILEAPI_CHROME_BLOB_STORAGE_CONTEXT_H_ +#ifndef CONTENT_BROWSER_BLOB_STORAGE_CHROME_BLOB_STORAGE_CONTEXT_H_ +#define CONTENT_BROWSER_BLOB_STORAGE_CHROME_BLOB_STORAGE_CONTEXT_H_ #include <stddef.h> #include <stdint.h> @@ -20,9 +20,7 @@ #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h" namespace base { -class FilePath; class TaskRunner; -class Time; } namespace network { @@ -67,13 +65,6 @@ size_t length, const std::string& content_type); - // Returns a NULL scoped_ptr on failure. - std::unique_ptr<BlobHandle> CreateFileBackedBlob( - const base::FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time); - // Must be called on the UI thread. static scoped_refptr<network::SharedURLLoaderFactory> URLLoaderFactoryForToken(BrowserContext* browser_context, @@ -117,4 +108,4 @@ } // namespace content -#endif // CONTENT_BROWSER_FILEAPI_CHROME_BLOB_STORAGE_CONTEXT_H_ +#endif // CONTENT_BROWSER_BLOB_STORAGE_CHROME_BLOB_STORAGE_CONTEXT_H_
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc index bb3d7a1f..af7cced 100644 --- a/content/browser/browser_context.cc +++ b/content/browser/browser_context.cc
@@ -339,26 +339,6 @@ } // static -void BrowserContext::CreateFileBackedBlob( - BrowserContext* browser_context, - const base::FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time, - BlobCallback callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - ChromeBlobStorageContext* blob_context = - ChromeBlobStorageContext::GetFor(browser_context); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&ChromeBlobStorageContext::CreateFileBackedBlob, - base::WrapRefCounted(blob_context), path, offset, size, - expected_modification_time), - std::move(callback)); -} - -// static BrowserContext::BlobContextGetter BrowserContext::GetBlobStorageContext( BrowserContext* browser_context) { DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_browser_compositor_output_surface.cc index 2694224..ca3da10 100644 --- a/content/browser/compositor/gpu_browser_compositor_output_surface.cc +++ b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -71,6 +71,7 @@ void GpuBrowserCompositorOutputSurface::LatencyInfoCompleted( const std::vector<ui::LatencyInfo>& latency_info) { RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); } void GpuBrowserCompositorOutputSurface::OnReflectorChanged() {
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.h b/content/browser/compositor/gpu_browser_compositor_output_surface.h index 95aa6d1..33cf8bc1 100644 --- a/content/browser/compositor/gpu_browser_compositor_output_surface.h +++ b/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -13,6 +13,7 @@ #include "content/browser/compositor/gpu_vsync_begin_frame_source.h" #include "gpu/vulkan/buildflags.h" #include "ui/gfx/swap_result.h" +#include "ui/latency/latency_tracker.h" namespace viz { class CompositorOverlayCandidateValidator; @@ -102,6 +103,7 @@ bool has_set_draw_rectangle_since_last_resize_ = false; gfx::Size size_; LatencyInfoCache latency_info_cache_; + ui::LatencyTracker latency_tracker_; private: DISALLOW_COPY_AND_ASSIGN(GpuBrowserCompositorOutputSurface);
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc index 05f9a5f..f4393a13 100644 --- a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc +++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
@@ -199,6 +199,7 @@ const std::vector<ui::LatencyInfo>& latency_info, uint64_t swap_id) { RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); client_->DidReceiveSwapBuffersAck(swap_id); client_->DidReceivePresentationFeedback(swap_id, gfx::PresentationFeedback()); }
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.h b/content/browser/compositor/offscreen_browser_compositor_output_surface.h index a1f4e6df..c3f9c992 100644 --- a/content/browser/compositor/offscreen_browser_compositor_output_surface.h +++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
@@ -16,6 +16,7 @@ #include "content/browser/compositor/browser_compositor_output_surface.h" #include "gpu/vulkan/buildflags.h" #include "ui/latency/latency_info.h" +#include "ui/latency/latency_tracker.h" namespace ui { class ContextProviderCommandBuffer; @@ -73,6 +74,7 @@ bool reflector_changed_ = false; std::unique_ptr<ReflectorTexture> reflector_texture_; uint64_t swap_id_ = 0; + ui::LatencyTracker latency_tracker_; base::WeakPtrFactory<OffscreenBrowserCompositorOutputSurface> weak_ptr_factory_;
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.cc b/content/browser/compositor/software_browser_compositor_output_surface.cc index d402e04ae..32fd593 100644 --- a/content/browser/compositor/software_browser_compositor_output_surface.cc +++ b/content/browser/compositor/software_browser_compositor_output_surface.cc
@@ -79,10 +79,6 @@ ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0, swap_time, 1); } - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&RenderWidgetHostImpl::OnGpuSwapBuffersCompleted, - frame.latency_info)); gfx::VSyncProvider* vsync_provider = software_device()->GetVSyncProvider(); if (vsync_provider) { @@ -96,11 +92,14 @@ FROM_HERE, base::BindOnce( &SoftwareBrowserCompositorOutputSurface::SwapBuffersCallback, - weak_factory_.GetWeakPtr(), swap_id_)); + weak_factory_.GetWeakPtr(), swap_id_, frame.latency_info)); } void SoftwareBrowserCompositorOutputSurface::SwapBuffersCallback( - uint64_t swap_id) { + uint64_t swap_id, + const std::vector<ui::LatencyInfo>& latency_info) { + RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); client_->DidReceiveSwapBuffersAck(swap_id); client_->DidReceivePresentationFeedback( swap_id,
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.h b/content/browser/compositor/software_browser_compositor_output_surface.h index 79bc9f5f..38ee1a57 100644 --- a/content/browser/compositor/software_browser_compositor_output_surface.h +++ b/content/browser/compositor/software_browser_compositor_output_surface.h
@@ -12,6 +12,7 @@ #include "content/browser/compositor/browser_compositor_output_surface.h" #include "content/common/content_export.h" #include "gpu/vulkan/buildflags.h" +#include "ui/latency/latency_tracker.h" namespace cc { class SoftwareOutputDevice; @@ -56,7 +57,8 @@ void SetSurfaceSuspendedForRecycle(bool suspended) override; #endif - void SwapBuffersCallback(uint64_t swap_id); + void SwapBuffersCallback(uint64_t swap_id, + const std::vector<ui::LatencyInfo>& latency_info); void UpdateVSyncCallback(const base::TimeTicks timebase, const base::TimeDelta interval); @@ -64,6 +66,7 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner_; uint64_t swap_id_ = 0; base::TimeDelta refresh_interval_; + ui::LatencyTracker latency_tracker_; base::WeakPtrFactory<SoftwareBrowserCompositorOutputSurface> weak_factory_; DISALLOW_COPY_AND_ASSIGN(SoftwareBrowserCompositorOutputSurface);
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc index 0c0c4cbb..8eb31c1 100644 --- a/content/browser/frame_host/navigation_handle_impl.cc +++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -22,6 +22,7 @@ #include "content/browser/frame_host/navigation_entry_impl.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/frame_host/navigator_delegate.h" +#include "content/browser/frame_host/webui_navigation_throttle.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_navigation_handle.h" @@ -1277,6 +1278,9 @@ throttles_ = GetDelegate()->CreateThrottlesForNavigation(this); + // Enforce rules for WebUI navigations. + AddThrottle(WebUINavigationThrottle::CreateThrottleForNavigation(this)); + // Check for renderer-inititated main frame navigations to data URLs. This is // done first as it may block the main frame navigation altogether. AddThrottle(DataUrlNavigationThrottle::CreateThrottleForNavigation(this));
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index f662f65..16814f52 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -84,6 +84,7 @@ #include "content/browser/scoped_active_url.h" #include "content/browser/shared_worker/shared_worker_connector_impl.h" #include "content/browser/shared_worker/shared_worker_service_impl.h" +#include "content/browser/speech/speech_recognition_dispatcher_host.h" #include "content/browser/storage_partition_impl.h" #include "content/browser/webauth/authenticator_impl.h" #include "content/browser/webauth/scoped_virtual_authenticator_environment.h" @@ -3437,6 +3438,15 @@ registry_->AddInterface(base::BindRepeating( &QuotaDispatcherHost::CreateForFrame, GetProcess(), routing_id_)); + registry_->AddInterface( + base::BindRepeating( + SpeechRecognitionDispatcherHost::Create, GetProcess()->GetID(), + routing_id_, + base::WrapRefCounted( + GetProcess()->GetStoragePartition()->GetURLRequestContext()), + weak_ptr_factory_.GetWeakPtr()), + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)); + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(BrowserContext::GetStoragePartition(
diff --git a/content/browser/frame_host/webui_navigation_browsertest.cc b/content/browser/frame_host/webui_navigation_browsertest.cc new file mode 100644 index 0000000..23f8a793 --- /dev/null +++ b/content/browser/frame_host/webui_navigation_browsertest.cc
@@ -0,0 +1,238 @@ +// Copyright 2018 The Chromium 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 "content/browser/child_process_security_policy_impl.h" +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/common/url_constants.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_navigation_observer.h" +#include "content/shell/browser/shell.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +namespace content { + +class WebUINavigationBrowserTest : public ContentBrowserTest { + public: + WebUINavigationBrowserTest() {} + + protected: + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(WebUINavigationBrowserTest); +}; + +// Verify that a chrome: scheme document cannot add iframes with web content. +// See https://crbug.com/683418. +IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, + WebFrameInChromeSchemeDisallowed) { + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + + // TODO(nasko): Replace this URL with one with a custom WebUI object that + // doesn't have restrictive CSP, so the test can successfully add an + // iframe and test the actual throttle blocking. Currently the CSP policy + // will just block the navigation prior to the throttle being even + // invoked. See http://crbug.com/776900. + GURL chrome_url = GURL(std::string(kChromeUIScheme) + "://" + + std::string(kChromeUIBlobInternalsHost)); + EXPECT_TRUE(NavigateToURL(shell(), chrome_url)); + EXPECT_EQ(chrome_url, root->current_frame_host()->GetLastCommittedURL()); + + { + GURL web_url(embedded_test_server()->GetURL("/title2.html")); + std::string script = base::StringPrintf( + "var frame = document.createElement('iframe');\n" + "frame.src = '%s';\n" + "document.body.appendChild(frame);\n", + web_url.spec().c_str()); + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(shell(), script)); + navigation_observer.Wait(); + + EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); + } + + // Verify data: URLs are also not allowed. + { + GURL data_url("data:text/html,foo"); + std::string script = base::StringPrintf( + "var frame = document.createElement('iframe');\n" + "frame.src = '%s';\n" + "document.body.appendChild(frame);\n", + data_url.spec().c_str()); + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(shell(), script)); + navigation_observer.Wait(); + + EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); + } + + // Verify that an iframe with "about:blank" URL is actually allowed. Not + // sure why this would be useful, but from a security perspective it can + // only host content coming from the parent document, so it effectively + // has the same security context. + { + GURL about_blank_url("about:blank"); + std::string script = base::StringPrintf( + "var frame = document.createElement('iframe');\n" + "frame.src = '%s';\n" + "document.body.appendChild(frame);\n", + about_blank_url.spec().c_str()); + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(shell(), script)); + navigation_observer.Wait(); + + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); + } +} + +// Verify that no web content can be loaded in a process that has WebUI +// bindings, regardless of what scheme the content was loaded from. +IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, + WebFrameInWebUIProcessDisallowed) { + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + GURL data_url("data:text/html,a data url document"); + EXPECT_TRUE(NavigateToURL(shell(), data_url)); + EXPECT_EQ(data_url, root->current_frame_host()->GetLastCommittedURL()); + EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + root->current_frame_host()->GetProcess()->GetID())); + + // Grant WebUI bindings to the process. This will ensure that if there is + // a mistake in the navigation logic and a process gets somehow WebUI + // bindings, it cannot include web content regardless of the scheme of the + // document. + ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings( + root->current_frame_host()->GetProcess()->GetID()); + EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + root->current_frame_host()->GetProcess()->GetID())); + { + GURL web_url(embedded_test_server()->GetURL("/title2.html")); + std::string script = base::StringPrintf( + "var frame = document.createElement('iframe');\n" + "frame.src = '%s';\n" + "document.body.appendChild(frame);\n", + web_url.spec().c_str()); + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(shell(), script)); + navigation_observer.Wait(); + + EXPECT_EQ(1U, root->child_count()); + EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); + } +} + +// Verify that a WebUI document in the main frame is allowed to navigate to +// web content and it properly does cross-process navigation. +IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, WebUIMainFrameToWebAllowed) { + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + GURL chrome_url = GURL(std::string(kChromeUIScheme) + "://" + + std::string(kChromeUIGpuHost)); + EXPECT_TRUE(NavigateToURL(shell(), chrome_url)); + RenderFrameHost* webui_rfh = root->current_frame_host(); + scoped_refptr<SiteInstance> webui_site_instance = + webui_rfh->GetSiteInstance(); + + EXPECT_EQ(chrome_url, webui_rfh->GetLastCommittedURL()); + EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + webui_rfh->GetProcess()->GetID())); + + GURL web_url(embedded_test_server()->GetURL("/title2.html")); + std::string script = + base::StringPrintf("location.href = '%s';", web_url.spec().c_str()); + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(shell(), script)); + navigation_observer.Wait(); + + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); + EXPECT_EQ(web_url, root->current_frame_host()->GetLastCommittedURL()); + EXPECT_NE(webui_site_instance, root->current_frame_host()->GetSiteInstance()); + EXPECT_FALSE(webui_site_instance->IsRelatedSiteInstance( + root->current_frame_host()->GetSiteInstance())); +} + +// Verify that a WebUI document in a subframe is allowed to target a new +// window and navigate it to web content. +IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, + WebUISubframeNewWindowToWebAllowed) { + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + + // TODO(nasko): Replace this URL with one with a custom WebUI object that + // doesn't have restrictive CSP, so the test can successfully add an + // iframe which gets WebUI bindings in the renderer process. + GURL chrome_url = GURL(std::string(kChromeUIScheme) + "://" + + std::string(kChromeUIBlobInternalsHost)); + EXPECT_TRUE(NavigateToURL(shell(), chrome_url)); + RenderFrameHost* webui_rfh = root->current_frame_host(); + scoped_refptr<SiteInstance> webui_site_instance = + webui_rfh->GetSiteInstance(); + + ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings( + webui_rfh->GetProcess()->GetID()); + + EXPECT_EQ(chrome_url, webui_rfh->GetLastCommittedURL()); + EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + webui_rfh->GetProcess()->GetID())); + + // Create a subframe with a WebUI document in it. + { + std::string script = base::StringPrintf( + "var frame = document.createElement('iframe');\n" + "frame.src = '%s';\n" + "document.body.appendChild(frame);\n", + chrome_url.spec().c_str()); + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(shell(), script)); + navigation_observer.Wait(); + + EXPECT_EQ(1U, root->child_count()); + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); + } + + // Add a link that targets a new window and click it. + GURL web_url(embedded_test_server()->GetURL("/title2.html")); + std::string script = base::StringPrintf( + "var a = document.createElement('a');" + "a.href = '%s'; a.target = '_blank'; a.click()", + web_url.spec().c_str()); + + ShellAddedObserver new_shell_observer; + EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(), script)); + Shell* new_shell = new_shell_observer.GetShell(); + WaitForLoadStop(new_shell->web_contents()); + + EXPECT_EQ(web_url, new_shell->web_contents()->GetLastCommittedURL()); + + // TODO(nasko): Verify the SiteInstance is different once + // https://crbug.com/776900 is fixed. + // Without a WebUI object which requires WebUI bindings, the RenderFrame is + // not notified that it has WebUI bindings. This in turn causes link clicks + // to use the BeginNavigation path, where otherwise the WebUI bindings will + // cause the OpenURL path to be taken. When using BeginNavigation, the + // navigation is committed same process, since it is renderer initiated. +} + +} // namespace content
diff --git a/content/browser/frame_host/webui_navigation_throttle.cc b/content/browser/frame_host/webui_navigation_throttle.cc new file mode 100644 index 0000000..625b1cb1 --- /dev/null +++ b/content/browser/frame_host/webui_navigation_throttle.cc
@@ -0,0 +1,59 @@ +// 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 "content/browser/frame_host/webui_navigation_throttle.h" + +#include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/frame_host/navigation_handle_impl.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/common/url_constants.h" + +namespace content { + +WebUINavigationThrottle::WebUINavigationThrottle( + NavigationHandle* navigation_handle) + : NavigationThrottle(navigation_handle) {} + +WebUINavigationThrottle::~WebUINavigationThrottle() {} + +NavigationThrottle::ThrottleCheckResult +WebUINavigationThrottle::WillStartRequest() { + // Allow only chrome: scheme documents to be navigated to. + if (navigation_handle()->GetURL().SchemeIs(kChromeUIScheme)) + return PROCEED; + + return BLOCK_REQUEST; +} + +const char* WebUINavigationThrottle::GetNameForLogging() { + return "WebUINavigationThrottle"; +} + +// static +std::unique_ptr<NavigationThrottle> +WebUINavigationThrottle::CreateThrottleForNavigation( + NavigationHandle* navigation_handle) { + // Create the throttle only for subframe navigations. + if (navigation_handle->IsInMainFrame()) + return nullptr; + + RenderFrameHostImpl* parent = + static_cast<NavigationHandleImpl*>(navigation_handle) + ->frame_tree_node() + ->parent() + ->current_frame_host(); + + // Create a throttle only for navigations where the parent frame is either + // at a chrome:// URL or is in a process with WebUI bindings. + if (ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + parent->GetProcess()->GetID()) || + parent->GetLastCommittedURL().SchemeIs(kChromeUIScheme)) { + return std::make_unique<WebUINavigationThrottle>(navigation_handle); + } + + return nullptr; +} + +} // namespace content
diff --git a/content/browser/frame_host/webui_navigation_throttle.h b/content/browser/frame_host/webui_navigation_throttle.h new file mode 100644 index 0000000..b47aa83 --- /dev/null +++ b/content/browser/frame_host/webui_navigation_throttle.h
@@ -0,0 +1,46 @@ +// Copyright 2018 The Chromium 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 CONTENT_BROWSER_FRAME_HOST_WEBUI_NAVIGATION_THROTTLE_H_ +#define CONTENT_BROWSER_FRAME_HOST_WEBUI_NAVIGATION_THROTTLE_H_ + +#include "content/public/browser/navigation_throttle.h" + +namespace content { + +// This NavigationThrottle class is used to check for subframe navigations to +// web content in WebUI processes and/or chrome:// documents. When the +// parent frame is at a chrome:// URL or is in a process with WebUI +// bindings, subframes are only allowed to navigate to chrome:// URLs. +// Note: There are WebUI documents that live on non-chrome: schemes and do +// not have WebUI bindings. Those are not covered by this restriction. +// +// This is an important security property to uphold, because by default +// WebUI documents have high privileges and if malicious web content is +// loaded in their process, it can be used as an easy step towards a sandbox +// escape. +// +// Note: Navigations in the main frame are allowed, as those will result in a +// process change with BrowsingInstance change and drop of privileges. +// Subframes are resticted because they must be in the same BrowsingInstance +// and would have the ability to communicate with the parent document. +class WebUINavigationThrottle : public NavigationThrottle { + public: + static std::unique_ptr<NavigationThrottle> CreateThrottleForNavigation( + NavigationHandle* navigation_handle); + + explicit WebUINavigationThrottle(NavigationHandle* navigation_handle); + ~WebUINavigationThrottle() override; + + // NavigationThrottle methods + ThrottleCheckResult WillStartRequest() override; + const char* GetNameForLogging() override; + + private: + DISALLOW_COPY_AND_ASSIGN(WebUINavigationThrottle); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_FRAME_HOST_WEBUI_NAVIGATION_THROTTLE_H_
diff --git a/content/browser/loader/cross_site_document_resource_handler.cc b/content/browser/loader/cross_site_document_resource_handler.cc index 703aa16..78b8882 100644 --- a/content/browser/loader/cross_site_document_resource_handler.cc +++ b/content/browser/loader/cross_site_document_resource_handler.cc
@@ -33,38 +33,9 @@ #include "services/metrics/public/cpp/ukm_recorder.h" using MimeType = network::CrossOriginReadBlocking::MimeType; -using SniffingResult = network::CrossOriginReadBlocking::SniffingResult; namespace content { -// An interface to enable incremental content sniffing. These are instantiated -// for each each request; thus they can be stateful. -class CrossSiteDocumentResourceHandler::ConfirmationSniffer { - public: - virtual ~ConfirmationSniffer() = default; - - // Called after data is read from the network. |sniffing_buffer| contains the - // entire response body delivered thus far. To support streaming, - // |new_data_offset| gives the offset into |sniffing_buffer| at which new data - // was appended since the last read. - virtual void OnDataAvailable(base::StringPiece sniffing_buffer, - size_t new_data_offset) = 0; - - // Returns true if the return value of IsConfirmedContentType() might change - // with the addition of more data. Returns false if a final decision is - // available. - virtual bool WantsMoreData() const = 0; - - // Returns true if the data has been confirmed to be of the CORB-protected - // content type that this sniffer is intended to detect. - virtual bool IsConfirmedContentType() const = 0; - - // Called when this sniffer's decision was used to block a response. This will - // only be invoked when an earlier call to IsConfirmedContentType() returned - // true. - virtual void LogBlockedResponse(ResourceType resource_type) const {} -}; - namespace { void LogCrossSiteDocumentAction( @@ -73,77 +44,6 @@ CrossSiteDocumentResourceHandler::Action::kCount); } -// A ConfirmationSniffer that wraps one of the sniffing functions from -// network::CrossOriginReadBlocking. -class SimpleConfirmationSniffer - : public CrossSiteDocumentResourceHandler::ConfirmationSniffer { - public: - // The function pointer type corresponding to one of the available sniffing - // functions from network::CrossOriginReadBlocking. - using SnifferFunction = - decltype(&network::CrossOriginReadBlocking::SniffForHTML); - - explicit SimpleConfirmationSniffer(SnifferFunction sniffer_function) - : sniffer_function_(sniffer_function) {} - ~SimpleConfirmationSniffer() override = default; - - void OnDataAvailable(base::StringPiece sniffing_buffer, - size_t new_data_offset) final { - DCHECK_LE(new_data_offset, sniffing_buffer.length()); - if (new_data_offset == sniffing_buffer.length()) { - // No new data -- do nothing. This happens at end-of-stream. - return; - } - // The sniffing functions don't support streaming, so with each new chunk of - // data, call the sniffer on the whole buffer. - last_sniff_result_ = (*sniffer_function_)(sniffing_buffer); - } - - bool WantsMoreData() const final { - // kNo and kYes results are final, meaning that sniffing can stop once they - // occur. A kMaybe result corresponds to an indeterminate state, that could - // change to kYes or kNo with more data. - return last_sniff_result_ == SniffingResult::kMaybe; - } - - bool IsConfirmedContentType() const final { - // Only confirm the mime type if an affirmative pattern (e.g. an HTML tag, - // if using the HTML sniffer) was detected. - // - // Note that if the stream ends (or net::kMaxBytesToSniff has been reached) - // and |last_sniff_result_| is kMaybe, the response is allowed to go - // through. - return last_sniff_result_ == SniffingResult::kYes; - } - - private: - // The function that actually knows how to sniff for a content type. - SnifferFunction sniffer_function_; - - // Result of sniffing the data available thus far. - SniffingResult last_sniff_result_ = SniffingResult::kMaybe; - - DISALLOW_COPY_AND_ASSIGN(SimpleConfirmationSniffer); -}; - -// A ConfirmationSniffer for parser breakers (fetch-only resources). This logs -// to an UMA histogram whenever it is the reason for a response being blocked. -class FetchOnlyResourceSniffer : public SimpleConfirmationSniffer { - public: - FetchOnlyResourceSniffer() - : SimpleConfirmationSniffer( - &network::CrossOriginReadBlocking::SniffForFetchOnlyResource) {} - - void LogBlockedResponse(ResourceType resource_type) const final { - UMA_HISTOGRAM_ENUMERATION( - "SiteIsolation.XSD.Browser.BlockedForParserBreaker", resource_type, - content::RESOURCE_TYPE_LAST_TYPE); - } - - private: - DISALLOW_COPY_AND_ASSIGN(FetchOnlyResourceSniffer); -}; - // An IOBuffer to enable writing into a existing IOBuffer at a given offset. class LocalIoBufferWithOffset : public net::WrappedIOBuffer { public: @@ -188,6 +88,7 @@ void CrossSiteDocumentResourceHandler::LogBlockedResponse( ResourceRequestInfoImpl* resource_request_info, bool needed_sniffing, + bool found_parser_breaker, MimeType canonical_mime_type, int http_response_code, int64_t content_length) { @@ -241,6 +142,11 @@ default: NOTREACHED(); } + if (found_parser_breaker) { + UMA_HISTOGRAM_ENUMERATION( + "SiteIsolation.XSD.Browser.BlockedForParserBreaker", resource_type, + content::RESOURCE_TYPE_LAST_TYPE); + } // The last committed URL is only available on the UI thread - we need to hop // onto the UI thread to log an UKM event. Note that this is racey - by the @@ -502,29 +408,13 @@ std::min(local_buffer_bytes_read_, net::kMaxBytesToSniff); base::StringPiece data(local_buffer_->data(), bytes_to_sniff); - for (size_t i = 0; i < sniffers_.size();) { - sniffers_[i]->OnDataAvailable(data, new_data_offset); + // If we have some new data, ask the |analyzer_| to sniff it. + if (new_data_offset < data.size()) + analyzer_->SniffResponseBody(data, new_data_offset); - if (!more_data_possible || !sniffers_[i]->WantsMoreData()) { - if (sniffers_[i]->IsConfirmedContentType()) { - // We're done sniffing; this response is CORB-protected. - confirmed_blockable = true; - sniffers_[i]->LogBlockedResponse(info->GetResourceType()); - break; - } - - // This response is CORB-exempt as far as this sniffer is concerned; - // remove it from the list. - sniffers_.erase(sniffers_.begin() + i); - } else { - i++; - } - } - - // When there are no sniffers left, the response is allowed. - const bool confirmed_allowed = sniffers_.empty(); + const bool confirmed_allowed = analyzer_->should_allow(); + confirmed_blockable = analyzer_->should_block(); DCHECK(!(confirmed_blockable && confirmed_allowed)); - DCHECK(confirmed_blockable || confirmed_allowed || more_data_possible); // If sniffing didn't yield a conclusive response, and we haven't read too // many bytes yet or hit the end of the stream, buffer up some more data. @@ -564,9 +454,9 @@ : "null", "url", request()->url().spec()); - LogBlockedResponse(info, analyzer_->needs_sniffing(), - analyzer_->canonical_mime_type(), http_response_code_, - content_length_); + LogBlockedResponse( + info, analyzer_->needs_sniffing(), analyzer_->found_parser_breaker(), + analyzer_->canonical_mime_type(), http_response_code_, content_length_); // Block the response and throw away the data. Report zero bytes read. blocked_read_completed_ = true; @@ -684,7 +574,7 @@ analyzer_ = std::make_unique<network::CrossOriginReadBlocking::ResponseAnalyzer>( *request(), response); - if (analyzer_->should_allow_based_on_headers()) + if (analyzer_->should_allow()) return false; // Check if the response's site needs to have its documents protected. By @@ -733,41 +623,6 @@ return false; } - // Create one or more |sniffers_| to confirm that the body is actually the - // MIME type advertised in the Content-Type header. - if (analyzer_->needs_sniffing()) { - // When the MIME type is "text/plain", create sniffers for HTML, XML and - // JSON. If any of these sniffers match, the response will be blocked. - const bool use_all = analyzer_->canonical_mime_type() == MimeType::kPlain; - - // HTML sniffer. - if (use_all || analyzer_->canonical_mime_type() == MimeType::kHtml) { - sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>( - &network::CrossOriginReadBlocking::SniffForHTML)); - } - - // XML sniffer. - if (use_all || analyzer_->canonical_mime_type() == MimeType::kXml) { - sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>( - &network::CrossOriginReadBlocking::SniffForXML)); - } - - // JSON sniffer. - if (use_all || analyzer_->canonical_mime_type() == MimeType::kJson) { - sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>( - &network::CrossOriginReadBlocking::SniffForJSON)); - } - - // Parser-breaker sniffer. - // - // Because these prefixes are an XSSI-defeating mechanism, CORB considers - // them distinctive enough to be worth blocking no matter the Content-Type - // header. So this sniffer is created unconditionally. - // - // For MimeType::kOthers, this will be the only sniffer that's active. - sniffers_.push_back(std::make_unique<FetchOnlyResourceSniffer>()); - } - return true; }
diff --git a/content/browser/loader/cross_site_document_resource_handler.h b/content/browser/loader/cross_site_document_resource_handler.h index e45d716a..3f2e504 100644 --- a/content/browser/loader/cross_site_document_resource_handler.h +++ b/content/browser/loader/cross_site_document_resource_handler.h
@@ -56,8 +56,6 @@ class CONTENT_EXPORT CrossSiteDocumentResourceHandler : public LayeredResourceHandler { public: - class ConfirmationSniffer; - // This enum backs a histogram, so do not change the order of entries or // remove entries. Put new entries before |kCount| and update enums.xml (see // the SiteIsolationResponseAction enum). @@ -136,6 +134,7 @@ static void LogBlockedResponse( ResourceRequestInfoImpl* resource_request_info, bool needed_sniffing, + bool found_parser_breaker, network::CrossOriginReadBlocking::MimeType canonical_mime_type, int http_response_code, int64_t content_length); @@ -164,8 +163,6 @@ // The helper class that encapsulates the logic for deciding whether to block // the response or not. - // TODO(lukasza): Also move the sniffing logic (e.g. |sniffers_|) into - // network::CrossOriginReadBlocking::ResponseAnalyzer. std::unique_ptr<network::CrossOriginReadBlocking::ResponseAnalyzer> analyzer_; // Indicates whether this request was made by a plugin and was not using CORS. @@ -201,9 +198,6 @@ // Content length if available. -1 if not available. int64_t content_length_ = -1; - // The sniffers to be used. - std::vector<std::unique_ptr<ConfirmationSniffer>> sniffers_; - base::WeakPtrFactory<CrossSiteDocumentResourceHandler> weak_this_; DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentResourceHandler);
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 75808002..126071c 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -1142,10 +1142,9 @@ request_data.resource_type == RESOURCE_TYPE_FAVICON) { do_not_prompt_for_login = true; } - if (request_data.resource_type == RESOURCE_TYPE_IMAGE && - HTTP_AUTH_RELATION_BLOCKED_CROSS == - HttpAuthRelationTypeOf(request_data.url, - request_data.site_for_cookies)) { + + if (DoNotPromptForLogin(static_cast<ResourceType>(request_data.resource_type), + request_data.url, request_data.site_for_cookies)) { // Prevent third-party image content from prompting for login, as this // is often a scam to extract credentials for another domain from the // user. Only block image loads, as the attack applies largely to the @@ -2150,6 +2149,18 @@ loader->CancelRequest(true); } +bool ResourceDispatcherHostImpl::DoNotPromptForLogin( + ResourceType resource_type, + const GURL& url, + const GURL& site_for_cookies) { + if (resource_type == RESOURCE_TYPE_IMAGE && + HTTP_AUTH_RELATION_BLOCKED_CROSS == + HttpAuthRelationTypeOf(url, site_for_cookies)) { + return true; + } + return false; +} + void ResourceDispatcherHostImpl::StartLoading( ResourceRequestInfoImpl* info, std::unique_ptr<ResourceLoader> loader) {
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h index 6bf3a5c1..ef311be 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.h +++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -339,6 +339,11 @@ return &keepalive_statistics_recorder_; } + // Checks if needs to prompt for login. + bool DoNotPromptForLogin(ResourceType resource_type, + const GURL& url, + const GURL& site_for_cookies); + private: class ScheduledResourceRequestAdapter; friend class ResourceDispatcherHostTest;
diff --git a/content/browser/media/session/media_session_android.cc b/content/browser/media/session/media_session_android.cc index b9733dd..cd30af1 100644 --- a/content/browser/media/session/media_session_android.cc +++ b/content/browser/media/session/media_session_android.cc
@@ -162,6 +162,14 @@ static_cast<blink::mojom::MediaSessionAction>(action)); } +void MediaSessionAndroid::RequestSystemAudioFocus( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& j_obj) { + DCHECK(media_session()); + static_cast<MediaSessionImpl*>(media_session()) + ->RequestSystemAudioFocus(AudioFocusManager::AudioFocusType::Gain); +} + WebContentsAndroid* MediaSessionAndroid::GetWebContentsAndroid() { MediaSessionImpl* session = static_cast<MediaSessionImpl*>(media_session()); if (!session)
diff --git a/content/browser/media/session/media_session_android.h b/content/browser/media/session/media_session_android.h index 3d3ea8e..3a95d72f 100644 --- a/content/browser/media/session/media_session_android.h +++ b/content/browser/media/session/media_session_android.h
@@ -53,6 +53,9 @@ void DidReceiveAction(JNIEnv* env, const base::android::JavaParamRef<jobject>& j_obj, jint action); + void RequestSystemAudioFocus( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& j_obj); private: WebContentsAndroid* GetWebContentsAndroid();
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h index 3a681b8b..637acb6 100644 --- a/content/browser/media/session/media_session_impl.h +++ b/content/browser/media/session/media_session_impl.h
@@ -199,6 +199,11 @@ // to blink::MediaSession corresponding to the current routed service. void DidReceiveAction(blink::mojom::MediaSessionAction action) override; + // Requests audio focus to the AudioFocusDelegate. + // Returns whether the request was granted. + CONTENT_EXPORT bool RequestSystemAudioFocus( + AudioFocusManager::AudioFocusType audio_focus_type); + private: friend class content::WebContentsUserData<MediaSessionImpl>; friend class ::MediaSessionImplBrowserTest; @@ -240,11 +245,6 @@ State new_state); CONTENT_EXPORT void OnResumeInternal(MediaSession::SuspendType suspend_type); - // Requests audio focus to the AudioFocusDelegate. - // Returns whether the request was granted. - CONTENT_EXPORT bool RequestSystemAudioFocus( - AudioFocusManager::AudioFocusType audio_focus_type); - // To be called after a call to AbandonAudioFocus() in order request the // delegate to abandon the audio focus. CONTENT_EXPORT void AbandonSystemAudioFocusIfNeeded();
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc index ad1a4b44..28fdceb 100644 --- a/content/browser/media/session/media_session_impl_browsertest.cc +++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -352,6 +352,18 @@ EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(0)); } +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, + CanRequestFocusBeforePlayerCreation) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + media_session_->RequestSystemAudioFocus( + content::AudioFocusManager::AudioFocusType::Gain); + EXPECT_TRUE(IsActive()); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); +} + IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, StartPlayerGivesFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>();
diff --git a/content/browser/network_service_client.cc b/content/browser/network_service_client.cc index 0869fd3..047398f29 100644 --- a/content/browser/network_service_client.cc +++ b/content/browser/network_service_client.cc
@@ -6,6 +6,7 @@ #include "base/optional.h" #include "content/browser/devtools/devtools_url_loader_interceptor.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/ssl/ssl_client_auth_handler.h" #include "content/browser/ssl/ssl_error_handler.h" #include "content/browser/ssl/ssl_manager.h" @@ -274,8 +275,10 @@ uint32_t routing_id, uint32_t request_id, const GURL& url, + const GURL& site_for_cookies, bool first_auth_attempt, const scoped_refptr<net::AuthChallengeInfo>& auth_info, + int32_t resource_type, network::mojom::AuthChallengeResponderPtr auth_challenge_responder) { base::Callback<WebContents*(void)> web_contents_getter = process_id ? base::Bind(WebContentsImpl::FromRenderFrameHostID, @@ -288,6 +291,12 @@ return; } + if (ResourceDispatcherHostImpl::Get()->DoNotPromptForLogin( + static_cast<ResourceType>(resource_type), url, site_for_cookies)) { + std::move(auth_challenge_responder)->OnAuthCredentials(base::nullopt); + return; + } + RenderFrameHost* rfh = process_id ? RenderFrameHost::FromID(process_id, routing_id)
diff --git a/content/browser/network_service_client.h b/content/browser/network_service_client.h index b8e6668..900cba8 100644 --- a/content/browser/network_service_client.h +++ b/content/browser/network_service_client.h
@@ -23,8 +23,10 @@ uint32_t routing_id, uint32_t request_id, const GURL& url, + const GURL& site_for_cookies, bool first_auth_attempt, const scoped_refptr<net::AuthChallengeInfo>& auth_info, + int32_t resource_type, network::mojom::AuthChallengeResponderPtr auth_challenge_responder) override; void OnCertificateRequested(
diff --git a/content/browser/payments/payment_app_content_unittest_base.cc b/content/browser/payments/payment_app_content_unittest_base.cc index 94b10e0fd..8480b59 100644 --- a/content/browser/payments/payment_app_content_unittest_base.cc +++ b/content/browser/payments/payment_app_content_unittest_base.cc
@@ -88,9 +88,15 @@ payments::mojom::PaymentHandlerResponseCallbackPtr response_callback, mojom::ServiceWorkerEventDispatcher::DispatchPaymentRequestEventCallback callback) override { - EmbeddedWorkerTestHelper::OnPaymentRequestEvent( - std::move(event_data), std::move(response_callback), - std::move(callback)); + if (respond_payment_request_immediately) { + EmbeddedWorkerTestHelper::OnPaymentRequestEvent( + std::move(event_data), std::move(response_callback), + std::move(callback)); + } else { + pending_response_callback_ = std::move(response_callback); + std::move(callback).Run(blink::mojom::ServiceWorkerEventStatus::COMPLETED, + base::Time::Now()); + } } void OnCanMakePaymentEvent( @@ -114,6 +120,10 @@ int64_t last_sw_registration_id_; GURL last_sw_scope_; + // Variables to delay payment request response. + bool respond_payment_request_immediately = true; + payments::mojom::PaymentHandlerResponseCallbackPtr pending_response_callback_; + private: DISALLOW_COPY_AND_ASSIGN(PaymentAppForWorkerTestHelper); }; @@ -210,6 +220,16 @@ EXPECT_TRUE(called); } +void PaymentAppContentUnitTestBase::SetNoPaymentRequestResponseImmediately() { + worker_helper_->respond_payment_request_immediately = false; +} + +void PaymentAppContentUnitTestBase::RespondPendingPaymentRequest() { + std::move(worker_helper_->pending_response_callback_) + ->OnResponseForPaymentRequest( + payments::mojom::PaymentHandlerResponse::New(), base::Time::Now()); +} + int64_t PaymentAppContentUnitTestBase::last_sw_registration_id() const { return worker_helper_->last_sw_registration_id_; }
diff --git a/content/browser/payments/payment_app_content_unittest_base.h b/content/browser/payments/payment_app_content_unittest_base.h index 7b3c482..12a82842 100644 --- a/content/browser/payments/payment_app_content_unittest_base.h +++ b/content/browser/payments/payment_app_content_unittest_base.h
@@ -37,6 +37,9 @@ int64_t last_sw_registration_id() const; const GURL& last_sw_scope_url() const; + void SetNoPaymentRequestResponseImmediately(); + void RespondPendingPaymentRequest(); + private: class PaymentAppForWorkerTestHelper;
diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc index 5d4aefd..dd0af04 100644 --- a/content/browser/payments/payment_app_provider_impl.cc +++ b/content/browser/payments/payment_app_provider_impl.cc
@@ -31,6 +31,50 @@ base::OnceCallback<void(scoped_refptr<ServiceWorkerVersion>, ServiceWorkerStatusCode)>; +class RespondWithCallbacks; + +// A repository to store invoking payment app callback. It is used to abort +// payment when the opened payment handler window is closed before payment +// response is received or timeout. +// Note that there is only one opened payment handler window per browser +// context. +class InvokePaymentAppCallbackRepository { + public: + static InvokePaymentAppCallbackRepository* GetInstance() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return base::Singleton<InvokePaymentAppCallbackRepository>::get(); + } + + RespondWithCallbacks* GetCallback(BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + auto it = invoke_callbacks_.find(browser_context); + if (it != invoke_callbacks_.end()) { + return it->second; + } + return nullptr; + } + + void SetCallback(BrowserContext* browser_context, + RespondWithCallbacks* callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + invoke_callbacks_[browser_context] = callback; + } + + void RemoveCallback(BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + invoke_callbacks_.erase(browser_context); + } + + private: + InvokePaymentAppCallbackRepository() {} + ~InvokePaymentAppCallbackRepository() {} + + friend struct base::DefaultSingletonTraits< + InvokePaymentAppCallbackRepository>; + + std::map<BrowserContext*, RespondWithCallbacks*> invoke_callbacks_; +}; + // Note that one and only one of the callbacks from this class must/should be // called. class RespondWithCallbacks @@ -42,6 +86,7 @@ scoped_refptr<ServiceWorkerVersion> service_worker_version, PaymentAppProvider::InvokePaymentAppCallback callback) : browser_context_(browser_context), + event_type_(event_type), service_worker_version_(service_worker_version), invoke_payment_app_callback_(std::move(callback)), binding_(this), @@ -49,13 +94,18 @@ request_id_ = service_worker_version->StartRequest( event_type, base::BindOnce(&RespondWithCallbacks::OnErrorStatus, weak_ptr_factory_.GetWeakPtr())); + InvokePaymentAppCallbackRepository::GetInstance()->SetCallback( + browser_context, this); } RespondWithCallbacks( + BrowserContext* browser_context, ServiceWorkerMetrics::EventType event_type, scoped_refptr<ServiceWorkerVersion> service_worker_version, PaymentAppProvider::PaymentEventResultCallback callback) - : service_worker_version_(service_worker_version), + : browser_context_(browser_context), + event_type_(event_type), + service_worker_version_(service_worker_version), payment_event_result_callback_(std::move(callback)), binding_(this), weak_ptr_factory_(this) { @@ -82,9 +132,7 @@ base::BindOnce(std::move(invoke_payment_app_callback_), std::move(response))); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&CloseClientWindowOnUIThread, browser_context_)); + ClearCallbackRepositoryAndCloseWindow(); delete this; } @@ -110,9 +158,7 @@ base::BindOnce(std::move(payment_event_result_callback_), payment_aborted)); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&CloseClientWindowOnUIThread, browser_context_)); + ClearCallbackRepositoryAndCloseWindow(); delete this; } @@ -135,18 +181,34 @@ if (event_type_ == ServiceWorkerMetrics::EventType::PAYMENT_REQUEST || event_type_ == ServiceWorkerMetrics::EventType::ABORT_PAYMENT) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&CloseClientWindowOnUIThread, browser_context_)); + ClearCallbackRepositoryAndCloseWindow(); } delete this; } int request_id() { return request_id_; } + void AbortPaymentSinceOpennedWindowClosing() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + service_worker_version_->FinishRequest(request_id_, false, + base::Time::Now()); + OnErrorStatus(SERVICE_WORKER_ERROR_ABORT); + } + private: ~RespondWithCallbacks() override {} + void ClearCallbackRepositoryAndCloseWindow() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + InvokePaymentAppCallbackRepository::GetInstance()->RemoveCallback( + browser_context_); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&CloseClientWindowOnUIThread, browser_context_)); + } + static void CloseClientWindowOnUIThread(BrowserContext* browser_context) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -183,6 +245,7 @@ } void DispatchAbortPaymentEvent( + BrowserContext* browser_context, PaymentAppProvider::PaymentEventResultCallback callback, scoped_refptr<ServiceWorkerVersion> active_version, ServiceWorkerStatusCode service_worker_status) { @@ -200,9 +263,9 @@ ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT, base::DoNothing()); // This object self-deletes after either success or error callback is invoked. - RespondWithCallbacks* invocation_callbacks = - new RespondWithCallbacks(ServiceWorkerMetrics::EventType::ABORT_PAYMENT, - active_version, std::move(callback)); + RespondWithCallbacks* invocation_callbacks = new RespondWithCallbacks( + browser_context, ServiceWorkerMetrics::EventType::ABORT_PAYMENT, + active_version, std::move(callback)); active_version->event_dispatcher()->DispatchAbortPaymentEvent( invocation_callbacks->request_id(), @@ -211,6 +274,7 @@ } void DispatchCanMakePaymentEvent( + BrowserContext* browser_context, payments::mojom::CanMakePaymentEventDataPtr event_data, PaymentAppProvider::PaymentEventResultCallback callback, scoped_refptr<ServiceWorkerVersion> active_version, @@ -230,8 +294,8 @@ // This object self-deletes after either success or error callback is invoked. RespondWithCallbacks* invocation_callbacks = new RespondWithCallbacks( - ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT, active_version, - std::move(callback)); + browser_context, ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT, + active_version, std::move(callback)); active_version->event_dispatcher()->DispatchCanMakePaymentEvent( invocation_callbacks->request_id(), std::move(event_data), @@ -359,6 +423,16 @@ std::move(callback).Run(std::move(permitted_apps)); } +void AbortInvokePaymentApp(BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + RespondWithCallbacks* callback = + InvokePaymentAppCallbackRepository::GetInstance()->GetCallback( + browser_context); + if (callback) + callback->AbortPaymentSinceOpennedWindowClosing(); +} + } // namespace // static @@ -452,8 +526,8 @@ StartServiceWorkerForDispatch( browser_context, registration_id, - base::BindOnce(&DispatchCanMakePaymentEvent, std::move(event_data), - std::move(callback))); + base::BindOnce(&DispatchCanMakePaymentEvent, browser_context, + std::move(event_data), std::move(callback))); } void PaymentAppProviderImpl::AbortPayment(BrowserContext* browser_context, @@ -463,7 +537,8 @@ StartServiceWorkerForDispatch( browser_context, registration_id, - base::BindOnce(&DispatchAbortPaymentEvent, std::move(callback))); + base::BindOnce(&DispatchAbortPaymentEvent, browser_context, + std::move(callback))); } void PaymentAppProviderImpl::SetOpenedWindow(WebContents* web_contents) { @@ -488,6 +563,15 @@ } } +void PaymentAppProviderImpl::OnClosingOpenedWindow( + BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&AbortInvokePaymentApp, browser_context)); +} + PaymentAppProviderImpl::PaymentAppProviderImpl() = default; PaymentAppProviderImpl::~PaymentAppProviderImpl() = default;
diff --git a/content/browser/payments/payment_app_provider_impl.h b/content/browser/payments/payment_app_provider_impl.h index 09cd78f..f1f4fa09 100644 --- a/content/browser/payments/payment_app_provider_impl.h +++ b/content/browser/payments/payment_app_provider_impl.h
@@ -44,6 +44,7 @@ PaymentEventResultCallback callback) override; void SetOpenedWindow(WebContents* web_contents) override; void CloseOpenedWindow(BrowserContext* browser_context) override; + void OnClosingOpenedWindow(BrowserContext* browser_context) override; private: PaymentAppProviderImpl();
diff --git a/content/browser/payments/payment_app_provider_impl_unittest.cc b/content/browser/payments/payment_app_provider_impl_unittest.cc index ef9b9a6..6143488 100644 --- a/content/browser/payments/payment_app_provider_impl_unittest.cc +++ b/content/browser/payments/payment_app_provider_impl_unittest.cc
@@ -110,6 +110,12 @@ browser_context(), registration_id, std::move(callback)); } + void OnClosingOpenedWindow() { + PaymentAppProviderImpl::GetInstance()->OnClosingOpenedWindow( + browser_context()); + base::RunLoop().RunUntilIdle(); + } + private: DISALLOW_COPY_AND_ASSIGN(PaymentAppProviderTest); }; @@ -266,4 +272,51 @@ ASSERT_EQ(2U, apps[bobpay_b_registration_id]->enabled_methods.size()); } +TEST_F(PaymentAppProviderTest, AbortPaymentWhenClosingOpenedWindow) { + PaymentManager* manager1 = CreatePaymentManager( + GURL("https://hellopay.com/a"), GURL("https://hellopay.com/a/script.js")); + PaymentManager* manager2 = CreatePaymentManager( + GURL("https://bobpay.com/b"), GURL("https://bobpay.com/b/script.js")); + + PaymentHandlerStatus status; + SetPaymentInstrument(manager1, "test_key1", + payments::mojom::PaymentInstrument::New(), + base::BindOnce(&SetPaymentInstrumentCallback, &status)); + SetPaymentInstrument(manager2, "test_key2", + payments::mojom::PaymentInstrument::New(), + base::BindOnce(&SetPaymentInstrumentCallback, &status)); + SetPaymentInstrument(manager2, "test_key3", + payments::mojom::PaymentInstrument::New(), + base::BindOnce(&SetPaymentInstrumentCallback, &status)); + + PaymentAppProvider::PaymentApps apps; + GetAllPaymentApps(base::BindOnce(&GetAllPaymentAppsCallback, &apps)); + ASSERT_EQ(2U, apps.size()); + + int64_t bobpay_registration_id = last_sw_registration_id(); + EXPECT_EQ(apps[bobpay_registration_id]->scope.spec(), "https://bobpay.com/b"); + + payments::mojom::PaymentRequestEventDataPtr event_data = + payments::mojom::PaymentRequestEventData::New(); + event_data->method_data.push_back(payments::mojom::PaymentMethodData::New()); + event_data->total = payments::mojom::PaymentCurrencyAmount::New(); + + SetNoPaymentRequestResponseImmediately(); + + bool called = false; + InvokePaymentApp(bobpay_registration_id, std::move(event_data), + base::BindOnce(&InvokePaymentAppCallback, &called)); + ASSERT_FALSE(called); + + // Abort payment request as closing opened window. + OnClosingOpenedWindow(); + ASSERT_TRUE(called); + + // Response after abort should not crash and take effect. + called = false; + RespondPendingPaymentRequest(); + base::RunLoop().RunUntilIdle(); + ASSERT_FALSE(called); +} + } // namespace content
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc index c2b58d8..76da83a 100644 --- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc +++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
@@ -56,12 +56,13 @@ void PictureInPictureWindowControllerImpl::Show() { DCHECK(window_); DCHECK(surface_id_.is_valid()); + window_->Show(); } void PictureInPictureWindowControllerImpl::Close() { - if (window_) - window_->Close(); + DCHECK(window_); + window_->Hide(); surface_id_ = viz::SurfaceId();
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc index d9dce56..05127ea 100644 --- a/content/browser/renderer_host/compositor_impl_android.cc +++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -81,6 +81,7 @@ #include "ui/gfx/ca_layer_params.h" #include "ui/gfx/swap_result.h" #include "ui/gl/gl_utils.h" +#include "ui/latency/latency_tracker.h" namespace content { @@ -285,6 +286,7 @@ void LatencyInfoCompleted( const std::vector<ui::LatencyInfo>& latency_info) override { RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); } void BindToClient(viz::OutputSurfaceClient* client) override { @@ -366,6 +368,7 @@ base::Closure swap_buffers_callback_; std::unique_ptr<viz::OverlayCandidateValidator> overlay_candidate_validator_; LatencyInfoCache latency_info_cache_; + ui::LatencyTracker latency_tracker_; base::WeakPtrFactory<AndroidOutputSurface> weak_ptr_factory_; };
diff --git a/content/browser/renderer_host/file_utilities_host_impl.cc b/content/browser/renderer_host/file_utilities_host_impl.cc index e3b08f3..3ac5de4 100644 --- a/content/browser/renderer_host/file_utilities_host_impl.cc +++ b/content/browser/renderer_host/file_utilities_host_impl.cc
@@ -20,7 +20,7 @@ void FileUtilitiesHostImpl::Create( int process_id, - content::mojom::FileUtilitiesHostRequest request) { + blink::mojom::FileUtilitiesHostRequest request) { mojo::MakeStrongBinding(std::make_unique<FileUtilitiesHostImpl>(process_id), std::move(request)); }
diff --git a/content/browser/renderer_host/file_utilities_host_impl.h b/content/browser/renderer_host/file_utilities_host_impl.h index 56fb38ae..23fe4807 100644 --- a/content/browser/renderer_host/file_utilities_host_impl.h +++ b/content/browser/renderer_host/file_utilities_host_impl.h
@@ -5,17 +5,17 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_FILE_UTILITIES_HOST_IMPL_H_ #define CONTENT_BROWSER_RENDERER_HOST_FILE_UTILITIES_HOST_IMPL_H_ -#include "content/common/file_utilities.mojom.h" +#include "third_party/blink/public/mojom/file/file_utilities.mojom.h" namespace content { -class FileUtilitiesHostImpl : public content::mojom::FileUtilitiesHost { +class FileUtilitiesHostImpl : public blink::mojom::FileUtilitiesHost { public: explicit FileUtilitiesHostImpl(int process_id); ~FileUtilitiesHostImpl() override; static void Create(int process_id, - content::mojom::FileUtilitiesHostRequest request); + blink::mojom::FileUtilitiesHostRequest request); private: // blink::mojom::FileUtilitiesHost implementation.
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc index 08a2ce0..f90fd14 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -97,9 +97,8 @@ } // namespace RenderWidgetHostLatencyTracker::RenderWidgetHostLatencyTracker( - bool metric_sampling, RenderWidgetHostDelegate* delegate) - : LatencyTracker(metric_sampling, GenerateUkmSourceId()), + : ukm_source_id_(GenerateUkmSourceId()), last_event_id_(0), latency_component_id_(0), has_seen_first_gesture_scroll_update_(false), @@ -196,9 +195,9 @@ OnEventStart(latency); if (!set_url_for_ukm_ && render_widget_host_delegate_ && - ukm_source_id() != ukm::kInvalidSourceId) { + ukm_source_id_ != ukm::kInvalidSourceId) { render_widget_host_delegate_->UpdateUrlForUkmSource(ukm::UkmRecorder::Get(), - ukm_source_id()); + ukm_source_id_); set_url_for_ukm_ = true; } @@ -303,4 +302,10 @@ *latency, ack_result); } +void RenderWidgetHostLatencyTracker::OnEventStart(ui::LatencyInfo* latency) { + static uint64_t global_trace_id = 0; + latency->set_trace_id(++global_trace_id); + latency->set_ukm_source_id(ukm_source_id_); +} + } // namespace content
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.h b/content/browser/renderer_host/input/render_widget_host_latency_tracker.h index 507eb4b..5f51b917 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.h +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.h
@@ -22,11 +22,9 @@ // Utility class for tracking the latency of events passing through // a given RenderWidgetHost. -class CONTENT_EXPORT RenderWidgetHostLatencyTracker - : public ui::LatencyTracker { +class CONTENT_EXPORT RenderWidgetHostLatencyTracker { public: - RenderWidgetHostLatencyTracker(bool metric_sampling, - RenderWidgetHostDelegate* delegate); + RenderWidgetHostLatencyTracker(RenderWidgetHostDelegate* delegate); virtual ~RenderWidgetHostLatencyTracker(); // Associates the latency tracker with a given route and process. @@ -59,6 +57,9 @@ int64_t latency_component_id() const { return latency_component_id_; } private: + void OnEventStart(ui::LatencyInfo* latency); + + ukm::SourceId ukm_source_id_; int64_t last_event_id_; int64_t latency_component_id_; bool has_seen_first_gesture_scroll_update_;
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc index c445a7ce..ab3b091 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -121,6 +121,8 @@ } RenderWidgetHostLatencyTracker* tracker() { return tracker_.get(); } + ui::LatencyTracker* viz_tracker() { return &viz_tracker_; } + void ResetHistograms() { histogram_tester_.reset(new base::HistogramTester()); } @@ -132,9 +134,9 @@ void SetUp() override { RenderViewHostImplTestHarness::SetUp(); old_browser_client_ = SetBrowserClientForTesting(&test_browser_client_); - tracker_ = - std::make_unique<RenderWidgetHostLatencyTracker>(false, contents()); + tracker_ = std::make_unique<RenderWidgetHostLatencyTracker>(contents()); tracker_->Initialize(kTestRoutingId, kTestProcessId); + viz_tracker_.DisableMetricSamplingForTesting(); } void TearDown() override { @@ -149,6 +151,7 @@ const int kTestProcessId = 1; std::unique_ptr<base::HistogramTester> histogram_tester_; std::unique_ptr<RenderWidgetHostLatencyTracker> tracker_; + ui::LatencyTracker viz_tracker_; RenderWidgetHostLatencyTrackerTestBrowserClient test_browser_client_; ContentBrowserClient* old_browser_client_; }; @@ -188,7 +191,7 @@ latency_info.AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0, now, 1); - tracker()->OnGpuSwapBuffersCompleted(latency_info); + viz_tracker()->OnGpuSwapBuffersCompleted(latency_info); // When last_event_time of the end_component is less than the first_event_time // of the start_component, zero is recorded instead of a negative value. @@ -235,7 +238,7 @@ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); tracker()->OnInputEventAck(wheel, &wheel_latency, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - tracker()->OnGpuSwapBuffersCompleted(wheel_latency); + viz_tracker()->OnGpuSwapBuffersCompleted(wheel_latency); // UKM metrics. total_ukm_entry_count++; @@ -331,7 +334,7 @@ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); tracker()->OnInputEventAck(wheel, &wheel_latency, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - tracker()->OnGpuSwapBuffersCompleted(wheel_latency); + viz_tracker()->OnGpuSwapBuffersCompleted(wheel_latency); // UKM metrics. total_ukm_entry_count++; @@ -449,7 +452,7 @@ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); tracker()->OnInputEventAck(touch, &touch_latency, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - tracker()->OnGpuSwapBuffersCompleted(touch_latency); + viz_tracker()->OnGpuSwapBuffersCompleted(touch_latency); } // UKM metrics. @@ -551,7 +554,7 @@ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); tracker()->OnInputEventAck(touch, &touch_latency, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - tracker()->OnGpuSwapBuffersCompleted(touch_latency); + viz_tracker()->OnGpuSwapBuffersCompleted(touch_latency); } // UKM metrics. @@ -1036,7 +1039,7 @@ base::TimeDelta::FromMicroseconds(event_timestamps_microseconds[1]), 1); - tracker()->OnGpuSwapBuffersCompleted(latency_info); + viz_tracker()->OnGpuSwapBuffersCompleted(latency_info); EXPECT_THAT( histogram_tester().GetAllSamples("Event.Latency.EndToEnd.KeyPress"),
diff --git a/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc b/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc index 0a3c9da3..21dd936 100644 --- a/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc +++ b/content/browser/renderer_host/pepper/pepper_print_settings_manager.cc
@@ -86,6 +86,7 @@ settings.paper_size = PrintSizeToPPPrintSize(page_setup.physical_size(), device_units_per_inch); settings.dpi = print_settings.dpi(); + settings.num_pages_per_sheet = print_settings.num_pages_per_sheet(); // The remainder of the attributes are hard-coded to the defaults as set // elsewhere.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index db1c09b8..ac31125 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -128,7 +128,6 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_dispatcher_host.h" #include "content/browser/site_instance_impl.h" -#include "content/browser/speech/speech_recognition_dispatcher_host.h" #include "content/browser/storage_partition_impl.h" #include "content/browser/streams/stream_context.h" #include "content/browser/tracing/trace_message_filter.h" @@ -1845,8 +1844,6 @@ #if BUILDFLAG(ENABLE_PLUGINS) AddFilter(new PepperRendererConnection(GetID())); #endif - AddFilter(new SpeechRecognitionDispatcherHost( - GetID(), storage_partition_impl_->GetURLRequestContext())); AddFilter(new FileAPIMessageFilter( GetID(), storage_partition_impl_->GetURLRequestContext(), storage_partition_impl_->GetFileSystemContext(),
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index faf2488..31854d0 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -362,7 +362,7 @@ is_last_unlocked_by_target_(false), has_touch_handler_(false), is_in_touchpad_gesture_fling_(false), - latency_tracker_(true, delegate_), + latency_tracker_(delegate_), next_browser_snapshot_id_(1), owned_by_render_frame_host_(false), is_focused_(false), @@ -2043,8 +2043,6 @@ WindowSnapshotReachedScreen(sequence_number); #endif } - - latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); } void RenderWidgetHostImpl::OnRenderProcessGone(int status, int exit_code) {
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc index 730d706..aeef5d5 100644 --- a/content/browser/renderer_host/render_widget_host_input_event_router.cc +++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -1159,9 +1159,10 @@ touchscreen_gesture_target_.target = target; touchscreen_gesture_target_in_map_ = IsViewInMap(target); base::debug::SetCrashKeyString(target_source_key, "touch_id=0"); - DCHECK(target_location.has_value()); touchscreen_gesture_target_.delta = - target_location.value() - gesture_event.PositionInWidget(); + target_location.has_value() + ? target_location.value() - gesture_event.PositionInWidget() + : gfx::Vector2dF(); } else if (no_matching_id && is_gesture_start) { // A long-standing Windows issues where occasionally a GestureStart is // encountered with no targets in the event queue. We never had a repro for
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 41b6ce659..239e5f3 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -358,8 +358,6 @@ // AcceleratedWidgetMacNSView implementation. NSView* AcceleratedWidgetGetNSView() const override; - void AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const override; void AcceleratedWidgetSwapCompleted() override; void SetShowingContextMenu(bool showing) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 44449284..3531fb2b 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -103,15 +103,6 @@ return cocoa_view(); } -void RenderWidgetHostViewMac::AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const { - if (display_link_ && - display_link_->GetVSyncParameters(timebase, interval)) - return; - *timebase = base::TimeTicks(); - *interval = base::TimeDelta(); -} - void RenderWidgetHostViewMac::AcceleratedWidgetSwapCompleted() { // Set the background color for the root layer from the frame that just // swapped. See RenderWidgetHostViewAura for more details. Note that this is
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc index 8ab6e038..ee6a526 100644 --- a/content/browser/service_worker/service_worker_storage_unittest.cc +++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -26,7 +26,7 @@ #include "content/common/service_worker/service_worker_status_code.h" #include "content/common/service_worker/service_worker_utils.h" #include "content/public/common/content_client.h" -#include "content/public/common/origin_trial_policy.h" +#include "content/public/common/origin_util.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "ipc/ipc_message.h" @@ -39,6 +39,7 @@ #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" @@ -1798,10 +1799,12 @@ : public ServiceWorkerStorageTest { public: ServiceWorkerStorageOriginTrialsDiskTest() { - SetContentClient(&test_content_client_); + blink::TrialTokenValidator::SetOriginTrialPolicyGetter(base::BindRepeating( + [](blink::OriginTrialPolicy* policy) { return policy; }, + base::Unretained(&origin_trial_policy_))); } ~ServiceWorkerStorageOriginTrialsDiskTest() override { - SetContentClient(nullptr); + blink::TrialTokenValidator::ResetOriginTrialPolicyGetter(); } void SetUp() override { ASSERT_TRUE(InitUserDataDirectory()); @@ -1809,24 +1812,18 @@ } private: - class TestOriginTrialPolicy : public OriginTrialPolicy { + class TestOriginTrialPolicy : public blink::OriginTrialPolicy { public: + bool IsOriginTrialsSupported() const override { return true; } base::StringPiece GetPublicKey() const override { return base::StringPiece(reinterpret_cast<const char*>(kTestPublicKey), arraysize(kTestPublicKey)); } - }; - class TestContentClient : public ContentClient { - public: - // ContentRendererClient methods - OriginTrialPolicy* GetOriginTrialPolicy() override { - return &origin_trial_policy_; + bool IsOriginSecure(const GURL& url) const override { + return content::IsOriginSecure(url); } - - private: - TestOriginTrialPolicy origin_trial_policy_; }; - TestContentClient test_content_client_; + TestOriginTrialPolicy origin_trial_policy_; }; TEST_F(ServiceWorkerStorageOriginTrialsDiskTest, FromMainScript) {
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index 03aefbe..42b1795 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc
@@ -31,7 +31,6 @@ #include "content/browser/service_worker/service_worker_installed_scripts_sender.h" #include "content/browser/service_worker/service_worker_registration.h" #include "content/browser/service_worker/service_worker_type_converters.h" -#include "content/common/origin_trials/trial_policy_impl.h" #include "content/common/service_worker/embedded_worker.mojom.h" #include "content/common/service_worker/service_worker_messages.h" #include "content/common/service_worker/service_worker_utils.h" @@ -330,7 +329,7 @@ tick_clock_(base::DefaultTickClock::GetInstance()), clock_(base::DefaultClock::GetInstance()), ping_controller_(new PingController(this)), - validator_(TrialPolicyImpl::CreateValidatorForPolicy()), + validator_(std::make_unique<blink::TrialTokenValidator>()), weak_factory_(this) { DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId, version_id); DCHECK(context_);
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index b1f2afaf..4f327766d 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -76,6 +76,7 @@ #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" +#include "content/public/common/use_zoom_for_dsf_policy.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/navigation_handle_observer.h" @@ -11513,4 +11514,202 @@ EXPECT_EQ(start_url, rfh->GetLastCommittedURL()); } +// Class to monitor incoming FrameHostMsg_UpdateViewportIntersection messages. +class UpdateViewportIntersectionMessageFilter + : public content::BrowserMessageFilter { + public: + UpdateViewportIntersectionMessageFilter() + : content::BrowserMessageFilter(FrameMsgStart), msg_received_(false) {} + + bool OnMessageReceived(const IPC::Message& message) override { + IPC_BEGIN_MESSAGE_MAP(UpdateViewportIntersectionMessageFilter, message) + IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection, + OnUpdateViewportIntersection) + IPC_END_MESSAGE_MAP() + return false; + } + + gfx::Rect GetCompositingRect() const { return compositing_rect_; } + + void Wait() { + DCHECK(!run_loop_); + if (msg_received_) { + msg_received_ = false; + return; + } + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); + run_loop_.reset(); + msg_received_ = false; + } + + private: + ~UpdateViewportIntersectionMessageFilter() override {} + + void OnUpdateViewportIntersection(const gfx::Rect& viewport_intersection, + const gfx::Rect& compositing_rect) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&UpdateViewportIntersectionMessageFilter:: + OnUpdateViewportIntersectionOnUI, + this, compositing_rect)); + } + void OnUpdateViewportIntersectionOnUI(const gfx::Rect& compositing_rect) { + compositing_rect_ = compositing_rect; + msg_received_ = true; + if (run_loop_) + run_loop_->Quit(); + } + std::unique_ptr<base::RunLoop> run_loop_; + bool msg_received_; + gfx::Rect compositing_rect_; + DISALLOW_COPY_AND_ASSIGN(UpdateViewportIntersectionMessageFilter); +}; + +// Tests that when a large OOPIF has been scaled, the compositor raster area +// sent from the embedder is correct. +#if defined(OS_ANDROID) +// Temporarily disabled on Android because this doesn't account for browser +// control height or page scale factor. +#define MAYBE_ScaledIframeRasterSize DISABLED_ScaledframeRasterSize +#else +#define MAYBE_ScaledIframeRasterSize ScaledIframeRasterSize +#endif +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + MAYBE_ScaledIframeRasterSize) { + GURL http_url(embedded_test_server()->GetURL( + "a.com", "/frame_tree/page_with_scaled_large_frame.html")); + EXPECT_TRUE(NavigateToURL(shell(), http_url)); + + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + + scoped_refptr<UpdateViewportIntersectionMessageFilter> filter = + new UpdateViewportIntersectionMessageFilter(); + root->current_frame_host()->GetProcess()->AddFilter(filter.get()); + + EXPECT_TRUE(ExecuteScript( + root, "document.getElementsByTagName('div')[0].scrollTo(0, 5000);")); + + gfx::Rect compositing_rect; + while (compositing_rect.y() == 0) { + // Ignore any messages that arrive before the compositing_rect scrolls + // away from the origin. + filter->Wait(); + compositing_rect = filter->GetCompositingRect(); + } + + float scale_factor = 1.0f; + if (IsUseZoomForDSFEnabled()) + scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents()); + + // The math below replicates the calculations in + // RemoteFrameView::GetCompositingRect(). That could be subject to tweaking, + // which would have to be reflected in these test expectations. Also, any + // changes to Blink that would affect the size of the frame rect or the + // visibile viewport would need to be accounted for. + // The multiplication by 5 accounts for the 0.2 scale factor in the test, + // which increases the area that has to be drawn in the OOPIF. + int view_height = root->current_frame_host() + ->GetRenderWidgetHost() + ->GetView() + ->GetViewBounds() + .height() * + 5 * scale_factor; + int expected_height = view_height * 13 / 10; + + // 185 is the height of the div in the main page after subtracting scroll + // bar height. + int expected_offset = + (5000 * scale_factor) - (expected_height - 185 * scale_factor) / 2; + + // Allow a small amount for rounding differences from applying page and + // device scale factors at different times. + EXPECT_GE(compositing_rect.height(), expected_height - 2); + EXPECT_LE(compositing_rect.height(), expected_height + 2); + EXPECT_GE(compositing_rect.y(), expected_offset - 2); + EXPECT_LE(compositing_rect.y(), expected_offset + 2); +} + +// Similar to ScaledIFrameRasterSize but with nested OOPIFs to ensure +// propagation works correctly. +#if defined(OS_ANDROID) +// Temporarily disabled on Android because this doesn't account for browser +// control height or page scale factor. +#define MAYBE_ScaledNestedIframeRasterSize DISABLED_ScaledNestedIframeRasterSize +#else +#define MAYBE_ScaledNestedIframeRasterSize ScaledNestedIframeRasterSize +#endif +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + MAYBE_ScaledNestedIframeRasterSize) { + GURL http_url(embedded_test_server()->GetURL( + "a.com", "/frame_tree/page_with_scaled_large_frames_nested.html")); + EXPECT_TRUE(NavigateToURL(shell(), http_url)); + + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + FrameTreeNode* child = root->child_at(0); + + NavigateFrameToURL( + child, + embedded_test_server()->GetURL( + "bar.com", "/frame_tree/page_with_large_scrollable_frame.html")); + + EXPECT_EQ( + " Site A ------------ proxies for B C\n" + " +--Site B ------- proxies for A C\n" + " +--Site C -- proxies for A B\n" + "Where A = http://a.com/\n" + " B = http://bar.com/\n" + " C = http://baz.com/", + DepictFrameTree(root)); + + // This adds the filter to the immediate child iframe. It verifies that the + // child sets the nested iframe's compositing rect correctly. + scoped_refptr<UpdateViewportIntersectionMessageFilter> filter = + new UpdateViewportIntersectionMessageFilter(); + child->current_frame_host()->GetProcess()->AddFilter(filter.get()); + + // This scrolls the div containing in the 'Site B' iframe that contains the + // 'Site C' iframe, and then we verify that the 'Site C' frame receives the + // correct compositor frame. + EXPECT_TRUE(ExecuteScript( + child, "document.getElementsByTagName('div')[0].scrollTo(0, 5000);")); + + gfx::Rect compositing_rect; + while (compositing_rect.y() == 0) { + // Ignore any messages that arrive before the compositing_rect scrolls + // away from the origin. + filter->Wait(); + compositing_rect = filter->GetCompositingRect(); + } + + float scale_factor = 1.0f; + if (IsUseZoomForDSFEnabled()) + scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents()); + + // See comment in ScaledIframeRasterSize for explanation of this. In this + // case, the raster area of the large iframe should be restricted to + // approximately the area of the smaller iframe in which it is embedded. + int view_height = child->current_frame_host() + ->GetRenderWidgetHost() + ->GetView() + ->GetViewBounds() + .height() * + scale_factor + + 5; + int expected_height = view_height * 13 / 10; + int expected_offset = + (5000 * scale_factor) - (expected_height - 185 * scale_factor) / 2; + + // Allow a small amount for rounding differences from applying page and + // device scale factors at different times. + EXPECT_GE(compositing_rect.height(), expected_height - 2); + EXPECT_LE(compositing_rect.height(), expected_height + 2); + EXPECT_GE(compositing_rect.y(), expected_offset - 2); + EXPECT_LE(compositing_rect.y(), expected_offset + 2); +} + } // namespace content
diff --git a/content/browser/speech/speech_recognition_dispatcher_host.cc b/content/browser/speech/speech_recognition_dispatcher_host.cc index 1e34625..ab80d7a 100644 --- a/content/browser/speech/speech_recognition_dispatcher_host.cc +++ b/content/browser/speech/speech_recognition_dispatcher_host.cc
@@ -22,164 +22,75 @@ #include "content/public/browser/speech_recognition_session_config.h" #include "content/public/browser/speech_recognition_session_context.h" #include "content/public/common/content_switches.h" +#include "mojo/public/cpp/bindings/strong_binding.h" namespace content { SpeechRecognitionDispatcherHost::SpeechRecognitionDispatcherHost( int render_process_id, - net::URLRequestContextGetter* context_getter) - : BrowserMessageFilter(SpeechRecognitionMsgStart), - render_process_id_(render_process_id), - context_getter_(context_getter), + int render_frame_id, + scoped_refptr<net::URLRequestContextGetter> context_getter, + base::WeakPtr<IPC::Sender> sender) + : render_process_id_(render_process_id), + render_frame_id_(render_frame_id), + context_getter_(std::move(context_getter)), + sender_(std::move(sender)), weak_factory_(this) { // Do not add any non-trivial initialization here, instead do it lazily when // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or // add an Init() method. } -SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() { +// static +void SpeechRecognitionDispatcherHost::Create( + int render_process_id, + int render_frame_id, + scoped_refptr<net::URLRequestContextGetter> context_getter, + base::WeakPtr<IPC::Sender> sender, + mojom::SpeechRecognizerRequest request) { + mojo::MakeStrongBinding(std::make_unique<SpeechRecognitionDispatcherHost>( + render_process_id, render_frame_id, + std::move(context_getter), std::move(sender)), + std::move(request)); } +SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() {} + base::WeakPtr<SpeechRecognitionDispatcherHost> SpeechRecognitionDispatcherHost::AsWeakPtr() { return weak_factory_.GetWeakPtr(); } -void SpeechRecognitionDispatcherHost::OnDestruct() const { - BrowserThread::DeleteOnIOThread::Destruct(this); -} +// -------- mojom::SpeechRecognizer interface implementation ------------------ -bool SpeechRecognitionDispatcherHost::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(SpeechRecognitionDispatcherHost, message) - IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StartRequest, - OnStartRequest) - IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortRequest, - OnAbortRequest) - IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StopCaptureRequest, - OnStopCaptureRequest) - IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortAllRequests, - OnAbortAllRequests) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void SpeechRecognitionDispatcherHost::OverrideThreadForMessage( - const IPC::Message& message, - BrowserThread::ID* thread) { - if (message.type() == SpeechRecognitionHostMsg_StartRequest::ID) - *thread = BrowserThread::UI; -} - -void SpeechRecognitionDispatcherHost::OnChannelClosing() { - weak_factory_.InvalidateWeakPtrs(); -} - -void SpeechRecognitionDispatcherHost::OnStartRequest( - const SpeechRecognitionHostMsg_StartRequest_Params& params) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); +// TODO(adithyas): Posting a task on the UI thread (and subsequently the IO +// thread) could result in a race. See https://crbug.com/836870 for details. +void SpeechRecognitionDispatcherHost::StartRequest( + mojom::StartSpeechRecognitionRequestParamsPtr params) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); // Check that the origin specified by the renderer process is one // that it is allowed to access. - if (params.origin_url != "null" && + if (params->origin_url != "null" && !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( - render_process_id_, GURL(params.origin_url))) { + render_process_id_, GURL(params->origin_url))) { LOG(ERROR) << "SRDH::OnStartRequest, disallowed origin: " - << params.origin_url; + << params->origin_url; return; } - int embedder_render_process_id = 0; - int embedder_render_frame_id = MSG_ROUTING_NONE; - - WebContentsImpl* web_contents = - static_cast<WebContentsImpl*>(WebContentsImpl::FromRenderFrameHostID( - render_process_id_, params.render_frame_id)); - if (!web_contents) { - // The render frame id is renderer-provided. If it's invalid, don't crash. - DLOG(ERROR) << "SRDH::OnStartRequest, invalid frame"; - return; - } - - // If the speech API request was from an inner WebContents or a guest, save - // the context of the outer WebContents or the embedder since we will use it - // to decide permission. - WebContents* outer_web_contents = web_contents->GetOuterWebContents(); - if (outer_web_contents) { - RenderFrameHost* embedder_frame = nullptr; - - FrameTreeNode* embedder_frame_node = web_contents->GetMainFrame() - ->frame_tree_node() - ->render_manager() - ->GetOuterDelegateNode(); - if (embedder_frame_node) { - embedder_frame = embedder_frame_node->current_frame_host(); - } else { - // The outer web contents is embedded using the browser plugin. Fall back - // to a simple lookup of the main frame. TODO(avi): When the browser - // plugin is retired, remove this code. - embedder_frame = outer_web_contents->GetMainFrame(); - } - - embedder_render_process_id = embedder_frame->GetProcess()->GetID(); - DCHECK_NE(embedder_render_process_id, 0); - embedder_render_frame_id = embedder_frame->GetRoutingID(); - DCHECK_NE(embedder_render_frame_id, MSG_ROUTING_NONE); - } - - bool filter_profanities = - SpeechRecognitionManagerImpl::GetInstance() && - SpeechRecognitionManagerImpl::GetInstance()->delegate() && - SpeechRecognitionManagerImpl::GetInstance()->delegate()-> - FilterProfanities(render_process_id_); - - SpeechRecognitionSessionContext context; - context.context_name = params.origin_url; - context.render_process_id = render_process_id_; - context.render_frame_id = params.render_frame_id; - context.embedder_render_process_id = embedder_render_process_id; - context.embedder_render_frame_id = embedder_render_frame_id; - context.request_id = params.request_id; - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&SpeechRecognitionDispatcherHost::StartSession, - base::Unretained(this), params, context, - filter_profanities)); + BrowserThread::UI, FROM_HERE, + base::BindOnce(&SpeechRecognitionDispatcherHost::StartRequestOnUI, + AsWeakPtr(), render_process_id_, render_frame_id_, + std::move(params))); } -void SpeechRecognitionDispatcherHost::StartSession( - const SpeechRecognitionHostMsg_StartRequest_Params& params, - const SpeechRecognitionSessionContext& context, - bool filter_profanities) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - SpeechRecognitionSessionConfig config; - config.language = params.language; - config.grammars = params.grammars; - config.max_hypotheses = params.max_hypotheses; - config.origin_url = params.origin_url; - config.initial_context = context; - config.url_request_context_getter = context_getter_.get(); - config.filter_profanities = filter_profanities; - config.continuous = params.continuous; - config.interim_results = params.interim_results; - config.event_listener = AsWeakPtr(); - - int session_id = SpeechRecognitionManager::GetInstance()->CreateSession( - config); - DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid); - SpeechRecognitionManager::GetInstance()->StartSession(session_id); -} - -void SpeechRecognitionDispatcherHost::OnAbortRequest(int render_frame_id, - int request_id) { +void SpeechRecognitionDispatcherHost::AbortRequest(int32_t request_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); int session_id = SpeechRecognitionManager::GetInstance()->GetSession( - render_process_id_, render_frame_id, request_id); + render_process_id_, render_frame_id_, request_id); // The renderer might provide an invalid |request_id| if the session was not // started as expected, e.g., due to unsatisfied security requirements. @@ -187,19 +98,18 @@ SpeechRecognitionManager::GetInstance()->AbortSession(session_id); } -void SpeechRecognitionDispatcherHost::OnAbortAllRequests(int render_frame_id) { +void SpeechRecognitionDispatcherHost::AbortAllRequests() { DCHECK_CURRENTLY_ON(BrowserThread::IO); SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderFrame( - render_process_id_, render_frame_id); + render_process_id_, render_frame_id_); } -void SpeechRecognitionDispatcherHost::OnStopCaptureRequest(int render_frame_id, - int request_id) { +void SpeechRecognitionDispatcherHost::StopCaptureRequest(int32_t request_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); int session_id = SpeechRecognitionManager::GetInstance()->GetSession( - render_process_id_, render_frame_id, request_id); + render_process_id_, render_frame_id_, request_id); // The renderer might provide an invalid |request_id| if the session was not // started as expected, e.g., due to unsatisfied security requirements. @@ -274,11 +184,111 @@ // The events below are currently not used by speech JS APIs implementation. void SpeechRecognitionDispatcherHost::OnAudioLevelsChange(int session_id, float volume, - float noise_volume) { -} + float noise_volume) {} void SpeechRecognitionDispatcherHost::OnEnvironmentEstimationComplete( - int session_id) { + int session_id) {} + +// static +void SpeechRecognitionDispatcherHost::StartRequestOnUI( + base::WeakPtr<SpeechRecognitionDispatcherHost> + speech_recognition_dispatcher_host, + int render_process_id, + int render_frame_id, + mojom::StartSpeechRecognitionRequestParamsPtr params) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + int embedder_render_process_id = 0; + int embedder_render_frame_id = MSG_ROUTING_NONE; + + WebContentsImpl* web_contents = + static_cast<WebContentsImpl*>(WebContentsImpl::FromRenderFrameHostID( + render_process_id, render_frame_id)); + if (!web_contents) { + // The render frame id is renderer-provided. If it's invalid, don't crash. + DLOG(ERROR) << "SRDH::OnStartRequest, invalid frame"; + return; + } + + // If the speech API request was from an inner WebContents or a guest, save + // the context of the outer WebContents or the embedder since we will use it + // to decide permission. + WebContents* outer_web_contents = web_contents->GetOuterWebContents(); + if (outer_web_contents) { + RenderFrameHost* embedder_frame = nullptr; + + FrameTreeNode* embedder_frame_node = web_contents->GetMainFrame() + ->frame_tree_node() + ->render_manager() + ->GetOuterDelegateNode(); + if (embedder_frame_node) { + embedder_frame = embedder_frame_node->current_frame_host(); + } else { + // The outer web contents is embedded using the browser plugin. Fall back + // to a simple lookup of the main frame. TODO(avi): When the browser + // plugin is retired, remove this code. + embedder_frame = outer_web_contents->GetMainFrame(); + } + + embedder_render_process_id = embedder_frame->GetProcess()->GetID(); + DCHECK_NE(embedder_render_process_id, 0); + embedder_render_frame_id = embedder_frame->GetRoutingID(); + DCHECK_NE(embedder_render_frame_id, MSG_ROUTING_NONE); + } + + bool filter_profanities = + SpeechRecognitionManagerImpl::GetInstance() && + SpeechRecognitionManagerImpl::GetInstance()->delegate() && + SpeechRecognitionManagerImpl::GetInstance() + ->delegate() + ->FilterProfanities(embedder_render_process_id); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&SpeechRecognitionDispatcherHost::StartSessionOnIO, + speech_recognition_dispatcher_host, std::move(params), + embedder_render_process_id, embedder_render_frame_id, + filter_profanities)); +} + +void SpeechRecognitionDispatcherHost::StartSessionOnIO( + mojom::StartSpeechRecognitionRequestParamsPtr params, + int embedder_render_process_id, + int embedder_render_frame_id, + bool filter_profanities) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + SpeechRecognitionSessionContext context; + context.context_name = params->origin_url; + context.render_process_id = render_process_id_; + context.render_frame_id = render_frame_id_; + context.embedder_render_process_id = embedder_render_process_id; + context.embedder_render_frame_id = embedder_render_frame_id; + context.request_id = params->request_id; + + SpeechRecognitionSessionConfig config; + config.language = params->language; + config.grammars = params->grammars; + config.max_hypotheses = params->max_hypotheses; + config.origin_url = params->origin_url; + config.initial_context = context; + config.url_request_context_getter = context_getter_.get(); + config.filter_profanities = filter_profanities; + config.continuous = params->continuous; + config.interim_results = params->interim_results; + config.event_listener = AsWeakPtr(); + + int session_id = + SpeechRecognitionManager::GetInstance()->CreateSession(config); + DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid); + SpeechRecognitionManager::GetInstance()->StartSession(session_id); +} + +void SpeechRecognitionDispatcherHost::Send(IPC::Message* message) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // The weak ptr must only be accessed on the UI thread. + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(base::IgnoreResult(&IPC::Sender::Send), sender_, message)); } } // namespace content
diff --git a/content/browser/speech/speech_recognition_dispatcher_host.h b/content/browser/speech/speech_recognition_dispatcher_host.h index babf35d..0ef24e0 100644 --- a/content/browser/speech/speech_recognition_dispatcher_host.h +++ b/content/browser/speech/speech_recognition_dispatcher_host.h
@@ -10,32 +10,48 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" +#include "content/common/speech_recognizer.mojom.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/speech_recognition_event_listener.h" #include "net/url_request/url_request_context_getter.h" -struct SpeechRecognitionHostMsg_StartRequest_Params; - namespace content { class SpeechRecognitionManager; -struct SpeechRecognitionSessionContext; // SpeechRecognitionDispatcherHost is a delegate for Speech API messages used by // RenderMessageFilter. Basically it acts as a proxy, relaying the events coming // from the SpeechRecognitionManager to IPC messages (and vice versa). // It's the complement of SpeechRecognitionDispatcher (owned by RenderFrame). class CONTENT_EXPORT SpeechRecognitionDispatcherHost - : public BrowserMessageFilter, + : public mojom::SpeechRecognizer, public SpeechRecognitionEventListener { public: SpeechRecognitionDispatcherHost( int render_process_id, - net::URLRequestContextGetter* context_getter); - + int render_frame_id, + scoped_refptr<net::URLRequestContextGetter> context_getter, + // WeakPtr can only be used on the UI thread. + base::WeakPtr<IPC::Sender> sender); + ~SpeechRecognitionDispatcherHost() override; + static void Create(int render_process_id, + int render_frame_id, + scoped_refptr<net::URLRequestContextGetter> context_getter, + base::WeakPtr<IPC::Sender>, + mojom::SpeechRecognizerRequest request); base::WeakPtr<SpeechRecognitionDispatcherHost> AsWeakPtr(); + // mojom::SpeechRecognizer implementation + void StartRequest( + mojom::StartSpeechRecognitionRequestParamsPtr params) override; + // TODO(adithyas): Once we convert browser -> renderer messages to mojo + // (https://crbug.com/781655) and replace |request_id| with a mojo + // InterfacePtr, we shouldn't need AbortRequest and AbortAllRequests. + void AbortRequest(int32_t request_id) override; + void AbortAllRequests() override; + void StopCaptureRequest(int32_t request_id) override; + // SpeechRecognitionEventListener methods. void OnRecognitionStart(int session_id) override; void OnAudioStart(int session_id) override; @@ -52,32 +68,31 @@ float volume, float noise_volume) override; - // BrowserMessageFilter implementation. - void OnDestruct() const override; - bool OnMessageReceived(const IPC::Message& message) override; - void OverrideThreadForMessage(const IPC::Message& message, - BrowserThread::ID* thread) override; - - void OnChannelClosing() override; - private: friend class base::DeleteHelper<SpeechRecognitionDispatcherHost>; friend class BrowserThread; - ~SpeechRecognitionDispatcherHost() override; - - void OnStartRequest( - const SpeechRecognitionHostMsg_StartRequest_Params& params); - void StartSession(const SpeechRecognitionHostMsg_StartRequest_Params& params, - const SpeechRecognitionSessionContext& context, - bool filter_profanities); - void OnAbortRequest(int render_frame_id, int request_id); - void OnStopCaptureRequest(int render_frame_id, int request_id); - void OnAbortAllRequests(int render_frame_id); + static void StartRequestOnUI( + base::WeakPtr<SpeechRecognitionDispatcherHost> + speech_recognition_dispatcher_host, + int render_process_id, + int render_frame_id, + mojom::StartSpeechRecognitionRequestParamsPtr params); + void StartSessionOnIO(mojom::StartSpeechRecognitionRequestParamsPtr params, + int embedder_render_process_id, + int embedder_render_frame_id, + bool filter_profanities); + // Sends the given message on the UI thread. Can be called from the IO thread. + void Send(IPC::Message* message); int render_process_id_; + int render_frame_id_; scoped_refptr<net::URLRequestContextGetter> context_getter_; + // sender_ can only be accessed on the UI thread. To send messages from the IO + // thread, use SpeechRecognitionDispatcherHost::Send. + base::WeakPtr<IPC::Sender> sender_; + // Used for posting asynchronous tasks (on the IO thread) without worrying // about this class being destroyed in the meanwhile (due to browser shutdown) // since tasks pending on a destroyed WeakPtr are automatically discarded.
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc index 11768f0..7b1ad2e9 100644 --- a/content/browser/storage_partition_impl_map.cc +++ b/content/browser/storage_partition_impl_map.cc
@@ -44,7 +44,6 @@ #include "content/public/browser/storage_partition.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" -#include "content/public/common/origin_trial_policy.h" #include "content/public/common/url_constants.h" #include "crypto/sha2.h" #include "net/url_request/url_request_context.h"
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc index 6fd8e9c..0778177b 100644 --- a/content/browser/tracing/background_tracing_manager_impl.cc +++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -25,7 +25,7 @@ #include "content/public/browser/tracing_delegate.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" namespace content { @@ -105,9 +105,11 @@ } void BackgroundTracingManagerImpl::AddMetadataGeneratorFunction() { - tracing::ChromeTraceEventAgent::GetInstance()->AddMetadataGeneratorFunction( - base::BindRepeating(&BackgroundTracingManagerImpl::GenerateMetadataDict, - base::Unretained(this))); + TracingControllerImpl::GetInstance() + ->GetTraceEventAgent() + ->AddMetadataGeneratorFunction(base::BindRepeating( + &BackgroundTracingManagerImpl::GenerateMetadataDict, + base::Unretained(this))); } void BackgroundTracingManagerImpl::WhenIdle(
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc index b9a550c8..680c548 100644 --- a/content/browser/tracing/tracing_controller_browsertest.cc +++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -23,7 +23,7 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "content/test/test_content_browser_client.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" using base::trace_event::RECORD_CONTINUOUSLY; using base::trace_event::RECORD_UNTIL_FULL; @@ -236,10 +236,9 @@ base::trace_event::TraceLog::GetInstance()->SetArgumentFilterPredicate( base::Bind(&IsTraceEventArgsWhitelisted)); - TracingController* controller = TracingController::GetInstance(); - tracing::ChromeTraceEventAgent::GetInstance()->AddMetadataGeneratorFunction( - base::Bind(&TracingControllerTest::GenerateMetadataDict, - base::Unretained(this))); + TracingControllerImpl* controller = TracingControllerImpl::GetInstance(); + controller->GetTraceEventAgent()->AddMetadataGeneratorFunction(base::Bind( + &TracingControllerTest::GenerateMetadataDict, base::Unretained(this))); { base::RunLoop run_loop;
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc index c1accd2..582ae77 100644 --- a/content/browser/tracing/tracing_controller_impl.cc +++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -34,7 +34,7 @@ #include "mojo/public/cpp/system/data_pipe.h" #include "net/base/network_change_notifier.h" #include "services/service_manager/public/cpp/connector.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "v8/include/v8-version-string.h" @@ -146,18 +146,24 @@ agents_.push_back(std::make_unique<EtwTracingAgent>(connector)); #endif - auto chrome_agent = std::make_unique<tracing::ChromeTraceEventAgent>( + auto trace_event_agent = tracing::TraceEventAgent::Create( connector, true /* request_clock_sync_marker_on_android */); + // For adding general CPU, network, OS, and other system information to the // metadata. - chrome_agent->AddMetadataGeneratorFunction(base::BindRepeating( + trace_event_agent->AddMetadataGeneratorFunction(base::BindRepeating( &TracingControllerImpl::GenerateMetadataDict, base::Unretained(this))); if (delegate_) { - chrome_agent->AddMetadataGeneratorFunction( + trace_event_agent->AddMetadataGeneratorFunction( base::BindRepeating(&TracingDelegate::GenerateMetadataDict, base::Unretained(delegate_.get()))); } - agents_.push_back(std::move(chrome_agent)); + trace_event_agent_ = std::move(trace_event_agent); +} + +tracing::TraceEventAgent* TracingControllerImpl::GetTraceEventAgent() const { + DCHECK(trace_event_agent_); + return trace_event_agent_.get(); } std::unique_ptr<base::DictionaryValue>
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h index 1d20517..356253a 100644 --- a/content/browser/tracing/tracing_controller_impl.h +++ b/content/browser/tracing/tracing_controller_impl.h
@@ -26,6 +26,10 @@ class RefCountedString; } // namespace base +namespace tracing { +class TraceEventAgent; +} // namespace tracing + namespace content { class TracingDelegate; @@ -43,7 +47,7 @@ CreateCompressedStringEndpoint(scoped_refptr<TraceDataEndpoint> endpoint, bool compress_with_background_priority); - static TracingControllerImpl* GetInstance(); + CONTENT_EXPORT static TracingControllerImpl* GetInstance(); // Should be called on the UI thread. TracingControllerImpl(); @@ -62,6 +66,8 @@ void RegisterTracingUI(TracingUI* tracing_ui); void UnregisterTracingUI(TracingUI* tracing_ui); + CONTENT_EXPORT tracing::TraceEventAgent* GetTraceEventAgent() const; + private: friend std::default_delete<TracingControllerImpl>; @@ -80,6 +86,7 @@ tracing::mojom::AgentRegistryPtr agent_registry_; tracing::mojom::CoordinatorPtr coordinator_; std::vector<std::unique_ptr<tracing::mojom::Agent>> agents_; + std::unique_ptr<tracing::TraceEventAgent> trace_event_agent_; std::unique_ptr<TracingDelegate> delegate_; std::unique_ptr<base::trace_event::TraceConfig> trace_config_; std::unique_ptr<mojo::DataPipeDrainer> drainer_;
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc index caa93917..200ea3c6 100644 --- a/content/browser/webauth/authenticator_impl.cc +++ b/content/browser/webauth/authenticator_impl.cc
@@ -427,8 +427,8 @@ return; } - if (GetContentClient()->browser()->ShouldEnforceFocusChecksForWebauthn() && - !render_frame_host_->GetView()->HasFocus()) { + if (!GetContentClient()->browser()->IsFocused( + WebContents::FromRenderFrameHost(render_frame_host_))) { std::move(callback).Run(webauth::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr); return; @@ -547,8 +547,8 @@ return; } - if (GetContentClient()->browser()->ShouldEnforceFocusChecksForWebauthn() && - !render_frame_host_->GetView()->HasFocus()) { + if (!GetContentClient()->browser()->IsFocused( + WebContents::FromRenderFrameHost(render_frame_host_))) { std::move(callback).Run(webauth::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr); return; @@ -850,8 +850,8 @@ MakeCredentialCallback callback, webauth::mojom::AuthenticatorStatus status, webauth::mojom::MakeCredentialAuthenticatorResponsePtr response) { - if (GetContentClient()->browser()->ShouldEnforceFocusChecksForWebauthn() && - !render_frame_host_->GetView()->HasFocus()) { + if (!GetContentClient()->browser()->IsFocused( + WebContents::FromRenderFrameHost(render_frame_host_))) { std::move(callback).Run(webauth::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr); } else { @@ -865,8 +865,8 @@ GetAssertionCallback callback, webauth::mojom::AuthenticatorStatus status, webauth::mojom::GetAssertionAuthenticatorResponsePtr response) { - if (GetContentClient()->browser()->ShouldEnforceFocusChecksForWebauthn() && - !render_frame_host_->GetView()->HasFocus()) { + if (!GetContentClient()->browser()->IsFocused( + WebContents::FromRenderFrameHost(render_frame_host_))) { std::move(callback).Run(webauth::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr); } else {
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc index fb4ec004..037709b 100644 --- a/content/browser/webauth/authenticator_impl_unittest.cc +++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -902,7 +902,7 @@ bool ShouldPermitIndividualAttestationForWebauthnRPID( content::BrowserContext* browser_context, const std::string& rp_id) override { - return individual_attestation == IndividualAttestation::REQUESTED; + return individual_attestation_ == IndividualAttestation::REQUESTED; } void ShouldReturnAttestationForWebauthnRPID( @@ -910,12 +910,29 @@ const std::string& rp_id, const url::Origin& origin, base::OnceCallback<void(bool)> callback) override { - std::move(callback).Run(attestation_consent == AttestationConsent::GRANTED); + std::move(callback).Run(attestation_consent_ == + AttestationConsent::GRANTED); } - IndividualAttestation individual_attestation = + bool IsFocused(content::WebContents* web_contents) override { + return focused_; + } + + void set_individual_attestation(IndividualAttestation value) { + individual_attestation_ = value; + } + + void set_attestation_consent(AttestationConsent value) { + attestation_consent_ = value; + } + + void set_focused(bool is_focused) { focused_ = is_focused; } + + private: + IndividualAttestation individual_attestation_ = IndividualAttestation::NOT_REQUESTED; - AttestationConsent attestation_consent = AttestationConsent::DENIED; + AttestationConsent attestation_consent_ = AttestationConsent::DENIED; + bool focused_ = true; }; // A test class that installs and removes an @@ -961,8 +978,8 @@ AttestationConveyancePreferenceToString(test.attestation_requested)); SCOPED_TRACE(i); - test_client_.individual_attestation = test.individual_attestation; - test_client_.attestation_consent = test.attestation_consent; + test_client_.set_individual_attestation(test.individual_attestation); + test_client_.set_attestation_consent(test.attestation_consent); PublicKeyCredentialCreationOptionsPtr options = GetTestPublicKeyCredentialCreationOptions(); @@ -1158,4 +1175,39 @@ RunTestCases(kTests); } +TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { + // When the |ContentBrowserClient| considers the tab to be unfocused, all + // requests should fail with a |NOT_FOCUSED| error. + test_client_.set_focused(false); + + NavigateAndCommit(GURL(kTestOrigin1)); + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + + { + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); + + TestMakeCredentialCallback cb; + authenticator->MakeCredential(std::move(options), cb.callback()); + cb.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::NOT_FOCUSED, cb.status()); + } + + { + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); + auto credential = PublicKeyCredentialDescriptor::New(); + credential->type = PublicKeyCredentialType::PUBLIC_KEY; + credential->id.resize(64); + options->allow_credentials.emplace_back(std::move(credential)); + + TestGetAssertionCallback cb; + authenticator->GetAssertion(std::move(options), cb.callback()); + cb.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::NOT_FOCUSED, cb.status()); + } +} + } // namespace content
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc index 9c982750..ceac82cb 100644 --- a/content/browser/webui/web_ui_impl.cc +++ b/content/browser/webui/web_ui_impl.cc
@@ -106,7 +106,6 @@ } void WebUIImpl::OnWebUISend(RenderFrameHost* sender, - const GURL& source_url, const std::string& message, const base::ListValue& args) { // Ignore IPCs from frames that are pending deletion. See also @@ -114,6 +113,7 @@ if (!sender->IsCurrent()) return; + const GURL& source_url = sender->GetLastCommittedURL(); if (!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( sender->GetProcess()->GetID()) || !WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
diff --git a/content/browser/webui/web_ui_impl.h b/content/browser/webui/web_ui_impl.h index 5561026..484f918 100644 --- a/content/browser/webui/web_ui_impl.h +++ b/content/browser/webui/web_ui_impl.h
@@ -86,7 +86,6 @@ // IPC message handling. void OnWebUISend(RenderFrameHost* sender, - const GURL& source_url, const std::string& message, const base::ListValue& args);
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc index 4125543..1325ce0 100644 --- a/content/child/child_thread_impl.cc +++ b/content/child/child_thread_impl.cc
@@ -596,7 +596,7 @@ channel_->AddFilter(new tracing::ChildTraceMessageFilter( ChildProcess::current()->io_task_runner())); - chrome_trace_event_agent_ = std::make_unique<tracing::ChromeTraceEventAgent>( + trace_event_agent_ = tracing::TraceEventAgent::Create( GetConnector(), false /* request_clock_sync_marker_on_android */); }
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h index cb101727..9f4155d 100644 --- a/content/child/child_thread_impl.h +++ b/content/child/child_thread_impl.h
@@ -30,7 +30,7 @@ #include "ipc/message_router.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/associated_binding_set.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" #if defined(OS_WIN) #include "content/public/common/font_cache_win.mojom.h" @@ -254,7 +254,7 @@ scoped_refptr<base::SingleThreadTaskRunner> browser_process_io_runner_; - std::unique_ptr<tracing::ChromeTraceEventAgent> chrome_trace_event_agent_; + std::unique_ptr<tracing::TraceEventAgent> trace_event_agent_; std::unique_ptr<variations::ChildProcessFieldTrialSyncer> field_trial_syncer_;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 8b1dfc5..98ac496 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -230,8 +230,6 @@ "net/url_request_user_data.h", "notifications/notification_struct_traits.cc", "notifications/notification_struct_traits.h", - "origin_trials/trial_policy_impl.cc", - "origin_trials/trial_policy_impl.h", "origin_util.cc", "page_message_enums.h", "page_messages.h", @@ -325,7 +323,6 @@ "//base", "//base/third_party/dynamic_annotations", "//build/util:webkit_version", - "//cc/ipc", "//components/discardable_memory/common", "//components/services/filesystem/public/interfaces", "//components/tracing", @@ -541,7 +538,6 @@ "child_control.mojom", "child_memory_coordinator.mojom", "field_trial_recorder.mojom", - "file_utilities.mojom", "frame.mojom", "frame_sink_provider.mojom", "histogram_fetcher.mojom", @@ -582,6 +578,7 @@ "shared_worker/shared_worker_factory.mojom", "shared_worker/shared_worker_host.mojom", "shared_worker/shared_worker_info.mojom", + "speech_recognizer.mojom", "storage_partition_service.mojom", "url_loader_factory_bundle.mojom", "widget.mojom",
diff --git a/content/common/browser_plugin/browser_plugin_messages.h b/content/common/browser_plugin/browser_plugin_messages.h index 9aa128f..5c72f2b 100644 --- a/content/common/browser_plugin/browser_plugin_messages.h +++ b/content/common/browser_plugin/browser_plugin_messages.h
@@ -9,7 +9,6 @@ #include "base/process/process.h" #include "base/unguessable_token.h" -#include "cc/ipc/cc_param_traits.h" #include "components/viz/common/surfaces/surface_info.h" #include "content/common/content_export.h" #include "content/common/content_param_traits.h"
diff --git a/content/common/common_param_traits_unittest.cc b/content/common/common_param_traits_unittest.cc index 71b30f65..4d4ee04 100644 --- a/content/common/common_param_traits_unittest.cc +++ b/content/common/common_param_traits_unittest.cc
@@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/values.h" +#include "components/viz/common/surfaces/surface_info.h" #include "content/common/resource_messages.h" #include "content/public/common/content_constants.h" #include "ipc/ipc_message.h" @@ -285,3 +286,24 @@ output.has_transparent_background); #endif } + +static constexpr viz::FrameSinkId kArbitraryFrameSinkId(1, 1); + +TEST(IPCMessageTest, SurfaceInfo) { + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + const viz::SurfaceId kArbitrarySurfaceId( + kArbitraryFrameSinkId, + viz::LocalSurfaceId(3, base::UnguessableToken::Create())); + constexpr float kArbitraryDeviceScaleFactor = 0.9f; + const gfx::Size kArbitrarySize(65, 321); + const viz::SurfaceInfo surface_info_in( + kArbitrarySurfaceId, kArbitraryDeviceScaleFactor, kArbitrarySize); + IPC::ParamTraits<viz::SurfaceInfo>::Write(&msg, surface_info_in); + + viz::SurfaceInfo surface_info_out; + base::PickleIterator iter(msg); + EXPECT_TRUE( + IPC::ParamTraits<viz::SurfaceInfo>::Read(&msg, &iter, &surface_info_out)); + + ASSERT_EQ(surface_info_in, surface_info_out); +}
diff --git a/content/common/content_param_traits.cc b/content/common/content_param_traits.cc index ed7e487e..310cd7f7 100644 --- a/content/common/content_param_traits.cc +++ b/content/common/content_param_traits.cc
@@ -7,6 +7,11 @@ #include <stddef.h> #include "base/strings/string_number_conversions.h" +#include "base/unguessable_token.h" +#include "components/viz/common/surfaces/frame_sink_id.h" +#include "components/viz/common/surfaces/local_surface_id.h" +#include "components/viz/common/surfaces/surface_id.h" +#include "components/viz/common/surfaces/surface_info.h" #include "content/common/frame_message_structs.h" #include "ipc/ipc_mojo_message_helper.h" #include "ipc/ipc_mojo_param_traits.h" @@ -17,6 +22,7 @@ #include "ui/accessibility/ax_modes.h" #include "ui/base/ui_base_features.h" #include "ui/events/blink/web_input_event_traits.h" +// #include "ui/gfx/ipc/geometry/gfx_param_traits.h" namespace IPC { @@ -240,6 +246,139 @@ l->append("<blink::TransferableMessage>"); } +void ParamTraits<viz::FrameSinkId>::Write(base::Pickle* m, + const param_type& p) { + DCHECK(p.is_valid()); + WriteParam(m, p.client_id()); + WriteParam(m, p.sink_id()); +} + +bool ParamTraits<viz::FrameSinkId>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + uint32_t client_id; + if (!ReadParam(m, iter, &client_id)) + return false; + + uint32_t sink_id; + if (!ReadParam(m, iter, &sink_id)) + return false; + + *p = viz::FrameSinkId(client_id, sink_id); + return p->is_valid(); +} + +void ParamTraits<viz::FrameSinkId>::Log(const param_type& p, std::string* l) { + l->append("viz::FrameSinkId("); + LogParam(p.client_id(), l); + l->append(", "); + LogParam(p.sink_id(), l); + l->append(")"); +} + +void ParamTraits<viz::LocalSurfaceId>::Write(base::Pickle* m, + const param_type& p) { + DCHECK(p.is_valid()); + WriteParam(m, p.parent_sequence_number()); + WriteParam(m, p.child_sequence_number()); + WriteParam(m, p.embed_token()); +} + +bool ParamTraits<viz::LocalSurfaceId>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + uint32_t parent_sequence_number; + if (!ReadParam(m, iter, &parent_sequence_number)) + return false; + + uint32_t child_sequence_number; + if (!ReadParam(m, iter, &child_sequence_number)) + return false; + + base::UnguessableToken embed_token; + if (!ReadParam(m, iter, &embed_token)) + return false; + + *p = viz::LocalSurfaceId(parent_sequence_number, child_sequence_number, + embed_token); + return p->is_valid(); +} + +void ParamTraits<viz::LocalSurfaceId>::Log(const param_type& p, + std::string* l) { + l->append("viz::LocalSurfaceId("); + LogParam(p.parent_sequence_number(), l); + l->append(", "); + LogParam(p.child_sequence_number(), l); + l->append(", "); + LogParam(p.embed_token(), l); + l->append(")"); +} + +void ParamTraits<viz::SurfaceId>::Write(base::Pickle* m, const param_type& p) { + WriteParam(m, p.frame_sink_id()); + WriteParam(m, p.local_surface_id()); +} + +bool ParamTraits<viz::SurfaceId>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + viz::FrameSinkId frame_sink_id; + if (!ReadParam(m, iter, &frame_sink_id)) + return false; + + viz::LocalSurfaceId local_surface_id; + if (!ReadParam(m, iter, &local_surface_id)) + return false; + + *p = viz::SurfaceId(frame_sink_id, local_surface_id); + return true; +} + +void ParamTraits<viz::SurfaceId>::Log(const param_type& p, std::string* l) { + l->append("viz::SurfaceId("); + LogParam(p.frame_sink_id(), l); + l->append(", "); + LogParam(p.local_surface_id(), l); + l->append(")"); +} + +void ParamTraits<viz::SurfaceInfo>::Write(base::Pickle* m, + const param_type& p) { + WriteParam(m, p.id()); + WriteParam(m, p.device_scale_factor()); + WriteParam(m, p.size_in_pixels()); +} + +bool ParamTraits<viz::SurfaceInfo>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + viz::SurfaceId surface_id; + if (!ReadParam(m, iter, &surface_id)) + return false; + + float device_scale_factor; + if (!ReadParam(m, iter, &device_scale_factor)) + return false; + + gfx::Size size_in_pixels; + if (!ReadParam(m, iter, &size_in_pixels)) + return false; + + *p = viz::SurfaceInfo(surface_id, device_scale_factor, size_in_pixels); + return p->is_valid(); +} + +void ParamTraits<viz::SurfaceInfo>::Log(const param_type& p, std::string* l) { + l->append("viz::SurfaceInfo("); + LogParam(p.id(), l); + l->append(", "); + LogParam(p.device_scale_factor(), l); + l->append(", "); + LogParam(p.size_in_pixels(), l); + l->append(")"); +} + } // namespace IPC // Generate param traits write methods.
diff --git a/content/common/content_param_traits.h b/content/common/content_param_traits.h index ffe7fc9..e9a29ff 100644 --- a/content/common/content_param_traits.h +++ b/content/common/content_param_traits.h
@@ -30,6 +30,13 @@ struct FrameMsg_ViewChanged_Params; } +namespace viz { +class FrameSinkId; +class LocalSurfaceId; +class SurfaceId; +class SurfaceInfo; +} // namespace viz + namespace IPC { template <> @@ -109,6 +116,46 @@ static void Log(const param_type& p, std::string* l); }; +template <> +struct CONTENT_EXPORT ParamTraits<viz::FrameSinkId> { + typedef viz::FrameSinkId param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct CONTENT_EXPORT ParamTraits<viz::LocalSurfaceId> { + typedef viz::LocalSurfaceId param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct CONTENT_EXPORT ParamTraits<viz::SurfaceId> { + typedef viz::SurfaceId param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct CONTENT_EXPORT ParamTraits<viz::SurfaceInfo> { + typedef viz::SurfaceInfo param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + } // namespace IPC #endif // CONTENT_COMMON_CONTENT_PARAM_TRAITS_H_
diff --git a/content/common/content_param_traits_macros.h b/content/common/content_param_traits_macros.h index 9dd58cf..65199c2f 100644 --- a/content/common/content_param_traits_macros.h +++ b/content/common/content_param_traits_macros.h
@@ -8,8 +8,8 @@ #ifndef CONTENT_COMMON_CONTENT_PARAM_TRAITS_MACROS_H_ #define CONTENT_COMMON_CONTENT_PARAM_TRAITS_MACROS_H_ -#include "cc/ipc/cc_param_traits.h" #include "content/common/content_export.h" +#include "content/common/content_param_traits.h" #include "content/common/download/mhtml_save_status.h" #include "content/common/render_widget_surface_properties.h" #include "content/public/common/input_event_ack_state.h" @@ -22,6 +22,7 @@ #include "third_party/blink/public/web/web_ime_text_span.h" #include "ui/gfx/gpu_memory_buffer.h" #include "ui/gfx/ipc/geometry/gfx_param_traits.h" +#include "ui/gfx/ipc/gfx_param_traits.h" #undef IPC_MESSAGE_EXPORT #define IPC_MESSAGE_EXPORT CONTENT_EXPORT @@ -48,6 +49,11 @@ IPC_ENUM_TRAITS_MAX_VALUE(ui::mojom::ImeTextSpanThickness, ui::mojom::ImeTextSpanThickness::kThick) +IPC_STRUCT_TRAITS_BEGIN(viz::Selection<gfx::SelectionBound>) + IPC_STRUCT_TRAITS_MEMBER(start) + IPC_STRUCT_TRAITS_MEMBER(end) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(blink::WebImeTextSpan) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_MEMBER(start_offset)
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h index fc12d2e..43d5461 100644 --- a/content/common/frame_messages.h +++ b/content/common/frame_messages.h
@@ -1741,8 +1741,7 @@ // A message from HTML-based UI. When (trusted) Javascript calls // send(message, args), this message is sent to the browser. -IPC_MESSAGE_ROUTED3(FrameHostMsg_WebUISend, - GURL /* source_url */, +IPC_MESSAGE_ROUTED2(FrameHostMsg_WebUISend, std::string /* message */, base::ListValue /* args */)
diff --git a/content/common/input/synchronous_compositor.typemap b/content/common/input/synchronous_compositor.typemap index 6adbf16..cb07fb3 100644 --- a/content/common/input/synchronous_compositor.typemap +++ b/content/common/input/synchronous_compositor.typemap
@@ -6,7 +6,6 @@ public_headers = [ "//content/common/input/sync_compositor_messages.h" ] traits_headers = [ "//content/common/input/sync_compositor_messages.h" ] deps = [ - "//cc/ipc", "//ui/gfx/ipc", ] type_mappings = [
diff --git a/content/common/native_types.typemap b/content/common/native_types.typemap index 025b5c4..35e7fbb 100644 --- a/content/common/native_types.typemap +++ b/content/common/native_types.typemap
@@ -46,7 +46,6 @@ # includes from //content/common and //content/public/common, this isn't a # transitive allowance, so those targets' own public_deps aren't included in # the set of implied dependencies. - "//cc/ipc", "//content/common:buildflags", "//media", "//media/base/ipc",
diff --git a/content/common/native_types_mac.typemap b/content/common/native_types_mac.typemap index c162fc1..c350855 100644 --- a/content/common/native_types_mac.typemap +++ b/content/common/native_types_mac.typemap
@@ -16,7 +16,6 @@ # includes from //content/common and //content/public/common, this isn't a # transitive allowance, so those targets' own public_deps aren't included in # the set of implied dependencies. - "//cc/ipc", "//media", "//media/base/ipc", "//net",
diff --git a/content/common/origin_trials/OWNERS b/content/common/origin_trials/OWNERS deleted file mode 100644 index 47b509b..0000000 --- a/content/common/origin_trials/OWNERS +++ /dev/null
@@ -1,13 +0,0 @@ -# This file also covers ownership of the following directories: -# //chrome/common/origin_trials/ -# //content/renderer/origin_trials/ -# //third_party/blink/common/origin_trials/ -# //third_party/blink/public/common/origin_trials/ -# //tools/origin_trials/ - -chasej@chromium.org -iclelland@chromium.org -mek@chromium.org - -# TEAM: experimentation-dev@chromium.org -# COMPONENT: Internals>OriginTrials
diff --git a/content/common/origin_trials/trial_policy_impl.cc b/content/common/origin_trials/trial_policy_impl.cc deleted file mode 100644 index e85ee25..0000000 --- a/content/common/origin_trials/trial_policy_impl.cc +++ /dev/null
@@ -1,44 +0,0 @@ -// 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 "content/common/origin_trials/trial_policy_impl.h" - -#include "base/feature_list.h" -#include "content/public/common/content_client.h" -#include "content/public/common/content_features.h" -#include "content/public/common/origin_trial_policy.h" -#include "content/public/common/origin_util.h" -#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" - -namespace content { - -bool TrialPolicyImpl::IsOriginTrialsSupported() const { - // In order for the validator to work these are all required. - return base::FeatureList::IsEnabled(features::kOriginTrials) && policy() && - !GetPublicKey().empty(); -} -base::StringPiece TrialPolicyImpl::GetPublicKey() const { - return policy()->GetPublicKey(); -} -bool TrialPolicyImpl::IsFeatureDisabled(base::StringPiece feature) const { - return policy()->IsFeatureDisabled(feature); -} -bool TrialPolicyImpl::IsTokenDisabled(base::StringPiece token_signature) const { - return policy()->IsTokenDisabled(token_signature); -} -bool TrialPolicyImpl::IsOriginSecure(const GURL& url) const { - return ::content::IsOriginSecure(url); -} - -const OriginTrialPolicy* TrialPolicyImpl::policy() const { - return GetContentClient()->GetOriginTrialPolicy(); -} - -std::unique_ptr<blink::TrialTokenValidator> -TrialPolicyImpl::CreateValidatorForPolicy() { - return std::make_unique<blink::TrialTokenValidator>( - std::make_unique<TrialPolicyImpl>()); -} - -} // namespace content
diff --git a/content/common/origin_trials/trial_policy_impl.h b/content/common/origin_trials/trial_policy_impl.h deleted file mode 100644 index 370c506..0000000 --- a/content/common/origin_trials/trial_policy_impl.h +++ /dev/null
@@ -1,42 +0,0 @@ -// 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 CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_IMPL_H_ -#define CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_IMPL_H_ - -#include "base/strings/string_piece.h" -#include "content/common/content_export.h" -#include "third_party/blink/public/common/origin_trials/trial_policy.h" - -namespace blink { -class TrialTokenValidator; -} // namespace blink - -namespace content { - -class OriginTrialPolicy; - -// TrialPolicyImpl is an adaptor to fit the policy provided by the content -// embedder via ContentClient to the interface allowed by the DEPS rules of -// third_party/WebKit/public/common -// TODO(avallee, kinuko): Plumb the the content embedder policy straight through -// to the users in third_party/WebKit/public/common/origin_trials. -class CONTENT_EXPORT TrialPolicyImpl : public blink::TrialPolicy { - public: - bool IsOriginTrialsSupported() const override; - - base::StringPiece GetPublicKey() const override; - bool IsFeatureDisabled(base::StringPiece feature) const override; - bool IsTokenDisabled(base::StringPiece token_signature) const override; - bool IsOriginSecure(const GURL& url) const override; - - static std::unique_ptr<blink::TrialTokenValidator> CreateValidatorForPolicy(); - - private: - const OriginTrialPolicy* policy() const; -}; - -} // namespace content - -#endif // CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_IMPL_H_
diff --git a/content/common/speech_recognition_messages.h b/content/common/speech_recognition_messages.h index a784338..1606bb549 100644 --- a/content/common/speech_recognition_messages.h +++ b/content/common/speech_recognition_messages.h
@@ -38,62 +38,6 @@ IPC_STRUCT_TRAITS_MEMBER(hypotheses) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionGrammar) - IPC_STRUCT_TRAITS_MEMBER(url) - IPC_STRUCT_TRAITS_MEMBER(weight) -IPC_STRUCT_TRAITS_END() - -// ------- Messages for Speech JS APIs (SpeechRecognitionDispatcher) ---------- - -// Renderer -> Browser messages. - -// Used to start a speech recognition session. -IPC_STRUCT_BEGIN(SpeechRecognitionHostMsg_StartRequest_Params) - // The render frame requesting speech recognition. - IPC_STRUCT_MEMBER(int, render_frame_id) - // Unique ID associated with the JS object making the calls. - IPC_STRUCT_MEMBER(int, request_id) - // Language to use for speech recognition. - IPC_STRUCT_MEMBER(std::string, language) - // Speech grammars to use. - IPC_STRUCT_MEMBER(content::SpeechRecognitionGrammarArray, grammars) - // URL of the page (or iframe if applicable). - IPC_STRUCT_MEMBER(std::string, origin_url) - // Maximum number of hypotheses allowed for each results. - IPC_STRUCT_MEMBER(uint32_t, max_hypotheses) - // Whether the user requested continuous recognition or not. - IPC_STRUCT_MEMBER(bool, continuous) - // Whether the user requested interim results or not. - IPC_STRUCT_MEMBER(bool, interim_results) -IPC_STRUCT_END() - - -// Requests the speech recognition service to start speech recognition. -IPC_MESSAGE_CONTROL1(SpeechRecognitionHostMsg_StartRequest, - SpeechRecognitionHostMsg_StartRequest_Params) - -// Requests the speech recognition service to abort speech recognition on -// behalf of the given |render_frame_id| and |request_id|. If there are no -// sessions associated with the |request_id| in the render frame, this call -// does nothing. -IPC_MESSAGE_CONTROL2(SpeechRecognitionHostMsg_AbortRequest, - int /* render_frame_id */, - int /* request_id */) - -// Requests the speech recognition service to abort all speech recognitions on -// behalf of the given |render_frame_id|. If speech recognition is not happening -// or is happening on behalf of some other render frame, this call does nothing. -IPC_MESSAGE_CONTROL1(SpeechRecognitionHostMsg_AbortAllRequests, - int /* render_frame_id */) - -// Requests the speech recognition service to stop audio capture on behalf of -// the given |render_frame_id|. Any audio recorded so far will be fed to the -// speech recognizer. If speech recognition is not happening nor or is -// happening on behalf of some other render frame, this call does nothing. -IPC_MESSAGE_CONTROL2(SpeechRecognitionHostMsg_StopCaptureRequest, - int /* render_frame_id */, - int /* request_id */) - // Browser -> Renderer messages. // The messages below follow exactly the same semantic of the corresponding
diff --git a/content/common/speech_recognizer.mojom b/content/common/speech_recognizer.mojom new file mode 100644 index 0000000..1cb8138 --- /dev/null +++ b/content/common/speech_recognizer.mojom
@@ -0,0 +1,56 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module content.mojom; + +import "content/public/common/speech_recognition_grammar.mojom"; + +// Used to start a speech recognition session. +struct StartSpeechRecognitionRequestParams { + // Unique ID associated with the JS object making the calls. + int32 request_id; + + // Language to use for speech recognition. + string language; + + // Speech grammars to use. + array<content.mojom.SpeechRecognitionGrammar> grammars; + + // URL of the page (or iframe if applicable). + // TODO(adithyas): Make this a mojom origin instead. + string origin_url; + + // Maximum number of hypotheses allowed for each results. + uint32 max_hypotheses; + + // Whether the user requested continuous recognition. + bool continuous; + + // Whether the user requested interim results. + bool interim_results; +}; + +// API for the renderer process to send speech recognition JS API messages to +// the browser process. +// TODO(adithyas): Remove |request_id|. +interface SpeechRecognizer { + // Requests the speech recognition service to start speech recognition. + StartRequest(StartSpeechRecognitionRequestParams params); + + // Requests the speech recognition service to abort speech recognition on + // behalf of the given |request_id|. If there are no sessions associated with + // the |request_id| in the render view, this call does nothing. + AbortRequest(int32 request_id); + + // Requests the speech recognition service to abort all speech recognitions on + // behalf of the current frame. If speech recognition is not happening or is + // happening on behalf of some other render view, this call does nothing. + AbortAllRequests(); + + // Requests the speech recognition service to stop audio capture on the + // current frame. Any audio recorded so far will be fed to the + // speech recognizer. If speech recognition is not happening or is happening + // on behalf of some other render view, this call does nothing. + StopCaptureRequest(int32 request_id); +};
diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 845d1d7..0a677eb 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h
@@ -19,7 +19,6 @@ #include "base/process/process.h" #include "base/strings/string16.h" #include "build/build_config.h" -#include "cc/ipc/cc_param_traits.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/shared_bitmap.h"
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.cc b/content/ppapi_plugin/ppapi_blink_platform_impl.cc index a6e5823..0128170c 100644 --- a/content/ppapi_plugin/ppapi_blink_platform_impl.cc +++ b/content/ppapi_plugin/ppapi_blink_platform_impl.cc
@@ -146,11 +146,6 @@ return nullptr; } -blink::WebFileUtilities* PpapiBlinkPlatformImpl::GetFileUtilities() { - NOTREACHED(); - return nullptr; -} - blink::WebSandboxSupport* PpapiBlinkPlatformImpl::GetSandboxSupport() { #if !defined(OS_ANDROID) && !defined(OS_WIN) return sandbox_support_.get();
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.h b/content/ppapi_plugin/ppapi_blink_platform_impl.h index 8a58a2a..676ce59 100644 --- a/content/ppapi_plugin/ppapi_blink_platform_impl.h +++ b/content/ppapi_plugin/ppapi_blink_platform_impl.h
@@ -26,7 +26,6 @@ // BlinkPlatformImpl methods: blink::WebThread* CurrentThread() override; blink::WebClipboard* Clipboard() override; - blink::WebFileUtilities* GetFileUtilities() override; blink::WebSandboxSupport* GetSandboxSupport() override; virtual bool sandboxEnabled(); unsigned long long VisitedLinkHash(const char* canonicalURL,
diff --git a/content/public/android/java/src/org/chromium/content/browser/MediaSessionImpl.java b/content/public/android/java/src/org/chromium/content/browser/MediaSessionImpl.java index 15332db0..a88cd28 100644 --- a/content/public/android/java/src/org/chromium/content/browser/MediaSessionImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/MediaSessionImpl.java
@@ -75,6 +75,11 @@ nativeDidReceiveAction(mNativeMediaSessionAndroid, action); } + @Override + public void requestSystemAudioFocus() { + nativeRequestSystemAudioFocus(mNativeMediaSessionAndroid); + } + @CalledByNative private boolean hasObservers() { return !mObservers.isEmpty(); @@ -133,6 +138,7 @@ private native void nativeSeekForward(long nativeMediaSessionAndroid, long millis); private native void nativeSeekBackward(long nativeMediaSessionAndroid, long millis); private native void nativeDidReceiveAction(long nativeMediaSessionAndroid, int action); + private native void nativeRequestSystemAudioFocus(long nativeMediaSessionAndroid); private static native MediaSessionImpl nativeGetMediaSessionFromWebContents( WebContents contents); }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/MediaSession.java b/content/public/android/java/src/org/chromium/content_public/browser/MediaSession.java index de2abaf..d1905004 100644 --- a/content/public/android/java/src/org/chromium/content_public/browser/MediaSession.java +++ b/content/public/android/java/src/org/chromium/content_public/browser/MediaSession.java
@@ -59,4 +59,9 @@ * Notify the media session that an action has been performed. */ public abstract void didReceiveAction(int action); + + /** + * Request audio focus from the system. + */ + public abstract void requestSystemAudioFocus(); }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java b/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java index 9b4d26c..6735a1f 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java
@@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo; import android.graphics.Rect; +import android.os.Build; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; @@ -16,6 +17,7 @@ import org.junit.runner.RunWith; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Restriction; import org.chromium.content.browser.test.ContentJUnit4ClassRunner; @@ -134,6 +136,8 @@ @Test @MediumTest + @DisableIf.Build(message = "crbug.com/837423", sdk_is_greater_than = Build.VERSION_CODES.KITKAT, + sdk_is_less_than = Build.VERSION_CODES.M) @Feature({"VideoFullscreenOrientationLock"}) @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) public void testEnterExitFullscreenWithControlsButton() throws Exception {
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json index 0170b95..f2e69020 100644 --- a/content/public/app/mojo/content_browser_manifest.json +++ b/content/public/app/mojo/content_browser_manifest.json
@@ -34,6 +34,7 @@ "blink::mojom::BlobRegistry", "blink::mojom::BroadcastChannelProvider", "blink::mojom::ClipboardHost", + "blink::mojom::FileUtilitiesHost", "blink::mojom::LockManager", "blink::mojom::Hyphenation", "blink::mojom::MimeRegistry", @@ -43,7 +44,6 @@ "content::mojom::AppCacheBackend", "content::mojom::ClipboardHost", "content::mojom::FieldTrialRecorder", - "content::mojom::FileUtilitiesHost", "content::mojom::FrameSinkProvider", "content::mojom::MediaStreamTrackMetricsHost", "content::mojom::MemoryCoordinatorHandle", @@ -165,6 +165,7 @@ "content::mojom::RendererAudioInputStreamFactory", "content::mojom::RendererAudioOutputStreamFactory", "content::mojom::SharedWorkerConnector", + "content::mojom::SpeechRecognizer", "device::mojom::Geolocation", "device::mojom::NFC", "device::mojom::SensorProvider",
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h index 4df24800..dffff04f 100644 --- a/content/public/browser/browser_context.h +++ b/content/public/browser/browser_context.h
@@ -29,7 +29,6 @@ namespace base { class FilePath; -class Time; } namespace service_manager { @@ -142,14 +141,6 @@ const std::string& content_type, BlobCallback callback); - // |callback| returns a nullptr scoped_ptr on failure. - static void CreateFileBackedBlob(BrowserContext* browser_context, - const base::FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time, - BlobCallback callback); - // Get a BlobStorageContext getter that needs to run on IO thread. static BlobContextGetter GetBlobStorageContext( BrowserContext* browser_context);
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index ae14e522..66d869f 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -684,8 +684,8 @@ std::move(callback).Run(true); } -bool ContentBrowserClient::ShouldEnforceFocusChecksForWebauthn() { - return false; +bool ContentBrowserClient::IsFocused(content::WebContents* web_contents) { + return true; } std::unique_ptr<net::ClientCertStore>
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index c8dcbf0..0a34762 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -1121,10 +1121,14 @@ const url::Origin& origin, base::OnceCallback<void(bool)> callback); - // Returns true if the Webauthn implementation should require that the - // |RenderFrameHost|'s View has focus before processing requests, and before - // sending replies. - virtual bool ShouldEnforceFocusChecksForWebauthn(); + // Returns whether |web_contents| is the active tab in the focused window. + // As an example, webauthn uses this because it doesn't want to allow + // authenticator operations to be triggered by background tabs. + // + // Note that the default implementation of this function, and the + // implementation in ChromeContentBrowserClient for Android, return |true| so + // that testing is possible. + virtual bool IsFocused(content::WebContents* web_contents); // Get platform ClientCertStore. May return nullptr. virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
diff --git a/content/public/browser/overlay_window.h b/content/public/browser/overlay_window.h index 00f7063b..96c468a 100644 --- a/content/public/browser/overlay_window.h +++ b/content/public/browser/overlay_window.h
@@ -36,8 +36,9 @@ PictureInPictureWindowController* controller); virtual bool IsActive() const = 0; - virtual void Show() = 0; virtual void Close() = 0; + virtual void Show() = 0; + virtual void Hide() = 0; virtual bool IsVisible() const = 0; virtual bool IsAlwaysOnTop() const = 0; virtual ui::Layer* GetLayer() = 0;
diff --git a/content/public/browser/payment_app_provider.h b/content/public/browser/payment_app_provider.h index 8e13ae5..8c1c0bb 100644 --- a/content/public/browser/payment_app_provider.h +++ b/content/public/browser/payment_app_provider.h
@@ -75,6 +75,10 @@ virtual void SetOpenedWindow(WebContents* web_contents) = 0; virtual void CloseOpenedWindow(BrowserContext* browser_context) = 0; + // Notify the opened payment handler window is closing or closed by user so as + // to abort payment request. + virtual void OnClosingOpenedWindow(BrowserContext* browser_context) = 0; + protected: virtual ~PaymentAppProvider() {} };
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn index 594efd4..c007644b 100644 --- a/content/public/common/BUILD.gn +++ b/content/public/common/BUILD.gn
@@ -176,8 +176,6 @@ "network_connection_tracker.h", "notification_resources.cc", "notification_resources.h", - "origin_trial_policy.cc", - "origin_trial_policy.h", "origin_util.h", "page_importance_signals.h", "page_state.cc", @@ -379,6 +377,7 @@ "push_messaging_status.mojom", "resource_load_info.mojom", "resource_usage_reporter.mojom", + "speech_recognition_grammar.mojom", "transferrable_url_loader.mojom", "webplugininfo.mojom", "window_container_type.mojom",
diff --git a/content/public/common/content_client.cc b/content/public/common/content_client.cc index a2a5a331..2493243 100644 --- a/content/public/common/content_client.cc +++ b/content/public/common/content_client.cc
@@ -102,7 +102,7 @@ return std::string(); } -OriginTrialPolicy* ContentClient::GetOriginTrialPolicy() { +blink::OriginTrialPolicy* ContentClient::GetOriginTrialPolicy() { return nullptr; }
diff --git a/content/public/common/content_client.h b/content/public/common/content_client.h index ea3c1ef6..caf91a3 100644 --- a/content/public/common/content_client.h +++ b/content/public/common/content_client.h
@@ -22,6 +22,10 @@ class RefCountedMemory; } +namespace blink { +class OriginTrialPolicy; +} + namespace IPC { class Message; } @@ -46,7 +50,6 @@ class ContentGpuClient; class ContentRendererClient; class ContentUtilityClient; -class OriginTrialPolicy; class ServiceManagerConnection; struct CdmInfo; struct PepperPluginInfo; @@ -173,7 +176,7 @@ // Returns the origin trial policy, or nullptr if origin trials are not // supported by the embedder. - virtual OriginTrialPolicy* GetOriginTrialPolicy(); + virtual blink::OriginTrialPolicy* GetOriginTrialPolicy(); #if defined(OS_ANDROID) // Returns true for clients like Android WebView that uses synchronous
diff --git a/content/public/common/origin_trial_policy.cc b/content/public/common/origin_trial_policy.cc deleted file mode 100644 index 76472c9..0000000 --- a/content/public/common/origin_trial_policy.cc +++ /dev/null
@@ -1,22 +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 "content/public/common/origin_trial_policy.h" - -namespace content { - -base::StringPiece OriginTrialPolicy::GetPublicKey() const { - return base::StringPiece(); -} - -bool OriginTrialPolicy::IsFeatureDisabled(base::StringPiece feature) const { - return false; -} - -bool OriginTrialPolicy::IsTokenDisabled( - base::StringPiece token_signature) const { - return false; -} - -} // namespace content
diff --git a/content/public/common/origin_trial_policy.h b/content/public/common/origin_trial_policy.h deleted file mode 100644 index 5368e6d..0000000 --- a/content/public/common/origin_trial_policy.h +++ /dev/null
@@ -1,25 +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 CONTENT_PUBLIC_COMMON_ORIGIN_TRIAL_POLICY_H_ -#define CONTENT_PUBLIC_COMMON_ORIGIN_TRIAL_POLICY_H_ - -#include "base/strings/string_piece.h" -#include "content/common/content_export.h" - -namespace content { - -class CONTENT_EXPORT OriginTrialPolicy { - public: - virtual ~OriginTrialPolicy() = default; - - virtual void Initialize() {} - virtual base::StringPiece GetPublicKey() const; - virtual bool IsFeatureDisabled(base::StringPiece feature) const; - virtual bool IsTokenDisabled(base::StringPiece token_signature) const; -}; - -} // namespace content - -#endif // CONTENT_PUBLIC_COMMON_ORIGIN_TRIAL_POLICY_H_
diff --git a/content/public/common/speech_recognition_grammar.h b/content/public/common/speech_recognition_grammar.h index 0621a56..0ed53a2 100644 --- a/content/public/common/speech_recognition_grammar.h +++ b/content/public/common/speech_recognition_grammar.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_GRAMMAR_H_ #define CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_GRAMMAR_H_ +#include <string> #include <vector> #include "content/common/content_export.h" @@ -12,17 +13,11 @@ namespace content { struct CONTENT_EXPORT SpeechRecognitionGrammar { - SpeechRecognitionGrammar() - : weight(0.0f) { - } + SpeechRecognitionGrammar() : weight(0.0f) {} explicit SpeechRecognitionGrammar(const std::string& url_value) - : url(url_value), - weight(0.0f) { - } + : url(url_value), weight(0.0f) {} SpeechRecognitionGrammar(const std::string& url_value, double weight_value) - : url(url_value), - weight(weight_value) { - } + : url(url_value), weight(weight_value) {} std::string url; double weight;
diff --git a/content/public/common/speech_recognition_grammar.mojom b/content/public/common/speech_recognition_grammar.mojom new file mode 100644 index 0000000..6ce7096 --- /dev/null +++ b/content/public/common/speech_recognition_grammar.mojom
@@ -0,0 +1,13 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module content.mojom; + +// TODO(adithyas): Update users of content::SpeechRecognitionGrammar to use this +// instead, then remove content::SpeechRecognitionGrammar. +struct SpeechRecognitionGrammar { + // TODO(adithyas): Make this a mojom URL instead. + string url; + double weight; +};
diff --git a/content/public/common/speech_recognition_grammar.typemap b/content/public/common/speech_recognition_grammar.typemap new file mode 100644 index 0000000..81c6be6 --- /dev/null +++ b/content/public/common/speech_recognition_grammar.typemap
@@ -0,0 +1,15 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +mojom = "//content/public/common/speech_recognition_grammar.mojom" +public_headers = [ "//content/public/common/speech_recognition_grammar.h" ] +traits_headers = + [ "//content/public/common/speech_recognition_grammar_struct_traits.h" ] +sources = [ + "//content/public/common/speech_recognition_grammar_struct_traits.cc", +] +deps = [] +type_mappings = [ + "content.mojom.SpeechRecognitionGrammar=content::SpeechRecognitionGrammar", +]
diff --git a/content/public/common/speech_recognition_grammar_struct_traits.cc b/content/public/common/speech_recognition_grammar_struct_traits.cc new file mode 100644 index 0000000..3e1e3bf --- /dev/null +++ b/content/public/common/speech_recognition_grammar_struct_traits.cc
@@ -0,0 +1,21 @@ +// Copyright 2018 The Chromium 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 "content/public/common/speech_recognition_grammar_struct_traits.h" +#include <string> + +namespace mojo { + +// static +bool StructTraits<content::mojom::SpeechRecognitionGrammarDataView, + content::SpeechRecognitionGrammar>:: + Read(content::mojom::SpeechRecognitionGrammarDataView data, + content::SpeechRecognitionGrammar* out) { + if (!data.ReadUrl(&out->url)) + return false; + out->weight = data.weight(); + return true; +} + +} // namespace mojo
diff --git a/content/public/common/speech_recognition_grammar_struct_traits.h b/content/public/common/speech_recognition_grammar_struct_traits.h new file mode 100644 index 0000000..2449699c --- /dev/null +++ b/content/public/common/speech_recognition_grammar_struct_traits.h
@@ -0,0 +1,29 @@ +// Copyright 2018 The Chromium 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 CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_GRAMMAR_STRUCT_TRAITS_H_ +#define CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_GRAMMAR_STRUCT_TRAITS_H_ + +#include "content/public/common/speech_recognition_grammar.h" +#include "content/public/common/speech_recognition_grammar.mojom-shared.h" +#include "mojo/public/cpp/bindings/struct_traits.h" + +namespace mojo { + +template <> +struct StructTraits<content::mojom::SpeechRecognitionGrammarDataView, + content::SpeechRecognitionGrammar> { + static const std::string& url(const content::SpeechRecognitionGrammar& r) { + return r.url; + } + static double weight(const content::SpeechRecognitionGrammar& r) { + return r.weight; + } + static bool Read(content::mojom::SpeechRecognitionGrammarDataView data, + content::SpeechRecognitionGrammar* out); +}; + +} // namespace mojo + +#endif // CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_GRAMMAR_STRUCT_TRAITS_H_
diff --git a/content/public/common/typemaps.gni b/content/public/common/typemaps.gni index 11d85046..fe6ebc0 100644 --- a/content/public/common/typemaps.gni +++ b/content/public/common/typemaps.gni
@@ -7,5 +7,6 @@ "//content/public/common/manifest.typemap", "//content/public/common/referrer.typemap", "//content/public/common/resource_type.typemap", + "//content/public/common/speech_recognition_grammar.typemap", "//content/public/common/webplugininfo.typemap", ]
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 46d4760..15ff76c 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -328,8 +328,6 @@ "notifications/notification_dispatcher.h", "notifications/notification_manager.cc", "notifications/notification_manager.h", - "origin_trials/web_trial_token_validator_impl.cc", - "origin_trials/web_trial_token_validator_impl.h", "p2p/network_list_manager.h", "pepper/fullscreen_container.h", "peripheral_content_heuristic.cc", @@ -456,8 +454,6 @@ "web_ui_extension.h", "web_ui_extension_data.cc", "web_ui_extension_data.h", - "webfileutilities_impl.cc", - "webfileutilities_impl.h", "webgraphicscontext3d_provider_impl.cc", "webgraphicscontext3d_provider_impl.h", "webpublicsuffixlist_impl.cc", @@ -497,7 +493,6 @@ "//cc", "//cc/animation", "//cc/blink", - "//cc/ipc", "//cc/paint", "//components/discardable_memory/client", "//components/metrics",
diff --git a/content/renderer/android/synchronous_compositor_proxy.cc b/content/renderer/android/synchronous_compositor_proxy.cc index f5424a9..441d8e86 100644 --- a/content/renderer/android/synchronous_compositor_proxy.cc +++ b/content/renderer/android/synchronous_compositor_proxy.cc
@@ -7,7 +7,6 @@ #include "base/auto_reset.h" #include "base/command_line.h" #include "base/memory/shared_memory.h" -#include "cc/ipc/cc_param_traits.h" #include "content/common/android/sync_compositor_statics.h" #include "content/common/input/sync_compositor_messages.h" #include "content/public/common/content_switches.h"
diff --git a/content/renderer/origin_trials/OWNERS b/content/renderer/origin_trials/OWNERS deleted file mode 100644 index 0d67a41..0000000 --- a/content/renderer/origin_trials/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -file://content/common/origin_trials/OWNERS
diff --git a/content/renderer/origin_trials/web_trial_token_validator_impl.cc b/content/renderer/origin_trials/web_trial_token_validator_impl.cc deleted file mode 100644 index b17913f..0000000 --- a/content/renderer/origin_trials/web_trial_token_validator_impl.cc +++ /dev/null
@@ -1,33 +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 "content/renderer/origin_trials/web_trial_token_validator_impl.h" - -#include "base/time/time.h" -#include "third_party/blink/public/common/origin_trials/trial_token.h" -#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" - -namespace content { - -WebTrialTokenValidatorImpl::WebTrialTokenValidatorImpl( - std::unique_ptr<blink::TrialTokenValidator> validator) - : validator_(std::move(validator)) { - DCHECK(validator_.get()) << "Should not pass null validator."; -} - -WebTrialTokenValidatorImpl::~WebTrialTokenValidatorImpl() {} - -blink::OriginTrialTokenStatus WebTrialTokenValidatorImpl::ValidateToken( - const blink::WebString& token, - const blink::WebSecurityOrigin& origin, - blink::WebString* feature_name) { - std::string feature; - blink::OriginTrialTokenStatus status = validator_->ValidateToken( - token.Utf8(), origin, &feature, base::Time::Now()); - if (status == blink::OriginTrialTokenStatus::kSuccess) - *feature_name = blink::WebString::FromUTF8(feature); - return status; -} - -} // namespace content
diff --git a/content/renderer/origin_trials/web_trial_token_validator_impl.h b/content/renderer/origin_trials/web_trial_token_validator_impl.h deleted file mode 100644 index 4f33737..0000000 --- a/content/renderer/origin_trials/web_trial_token_validator_impl.h +++ /dev/null
@@ -1,49 +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 CONTENT_RENDERER_ORIGIN_TRIALS_WEB_TRIAL_TOKEN_VALIDATOR_IMPL_H_ -#define CONTENT_RENDERER_ORIGIN_TRIALS_WEB_TRIAL_TOKEN_VALIDATOR_IMPL_H_ - -#include <memory> -#include <string> - -#include "base/compiler_specific.h" -#include "content/common/content_export.h" -#include "third_party/blink/public/platform/web_trial_token_validator.h" - -namespace blink { -class TrialTokenValidator; -} - -namespace content { - -// The TrialTokenValidator is called by the Origin Trials Framework code in -// Blink to validate tokens to enable experimental features. -// -// This class is thread-safe. -// TODO(crbug.com/738505) Move this to WebKit/core once conversion between -// blink::WebSecurityOrigin and url::Origin is allowed in blink. See -// https://crbug.com/490074 -class CONTENT_EXPORT WebTrialTokenValidatorImpl - : public blink::WebTrialTokenValidator { - public: - WebTrialTokenValidatorImpl( - std::unique_ptr<blink::TrialTokenValidator> validator); - ~WebTrialTokenValidatorImpl() override; - - // blink::WebTrialTokenValidator implementation - blink::OriginTrialTokenStatus ValidateToken( - const blink::WebString& token, - const blink::WebSecurityOrigin& origin, - blink::WebString* feature_name) override; - - private: - DISALLOW_COPY_AND_ASSIGN(WebTrialTokenValidatorImpl); - - std::unique_ptr<blink::TrialTokenValidator> validator_; -}; - -} // namespace content - -#endif // CONTENT_RENDERER_ORIGIN_TRIALS_WEB_TRIAL_TOKEN_VALIDATOR_IMPL_H_
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc index 0cee2cf..67cc02c 100644 --- a/content/renderer/pepper/pepper_plugin_instance_impl.cc +++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1974,6 +1974,9 @@ print_settings.print_scaling_option = static_cast<PP_PrintScalingOption_Dev>(print_params.print_scaling_option); print_settings.format = format; + + print_settings.num_pages_per_sheet = print_params.num_pages_per_sheet; + num_pages = plugin_print_interface_->Begin(pp_instance(), &print_settings); if (!num_pages) return 0;
diff --git a/content/renderer/pepper/pepper_webplugin_impl.cc b/content/renderer/pepper/pepper_webplugin_impl.cc index 91dd459..f1ab901 100644 --- a/content/renderer/pepper/pepper_webplugin_impl.cc +++ b/content/renderer/pepper/pepper_webplugin_impl.cc
@@ -11,6 +11,7 @@ #include "base/debug/crash_logging.h" #include "base/location.h" #include "base/single_thread_task_runner.h" +#include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "content/public/renderer/content_renderer_client.h" #include "content/renderer/pepper/message_channel.h" @@ -22,7 +23,8 @@ #include "content/renderer/renderer_blink_platform_impl.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var_tracker.h" -#include "third_party/blink/public/platform/web_clipboard.h" +#include "services/service_manager/public/cpp/connector.h" +#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_coalesced_input_event.h" #include "third_party/blink/public/platform/web_point.h" #include "third_party/blink/public/platform/web_rect.h" @@ -319,26 +321,40 @@ if (!HasSelection() || !CanEditText()) return false; - blink::Platform::Current()->Clipboard()->WriteHTML( - SelectionAsMarkup(), WebURL(), SelectionAsText(), false); + if (!clipboard_) { + blink::Platform::Current()->GetConnector()->BindInterface( + blink::Platform::Current()->GetBrowserServiceName(), &clipboard_); + } + base::string16 markup; + base::string16 text; + if (instance_) { + markup = instance_->GetSelectedText(true); + text = instance_->GetSelectedText(false); + } + clipboard_->WriteHtml(ui::CLIPBOARD_TYPE_COPY_PASTE, markup, GURL()); + clipboard_->WriteText(ui::CLIPBOARD_TYPE_COPY_PASTE, text); + clipboard_->CommitWrite(ui::CLIPBOARD_TYPE_COPY_PASTE); instance_->ReplaceSelection(""); return true; } // If the clipboard contains something other than text (e.g. an image), - // WebClipboard::ReadPlainText() returns an empty string. The empty string is + // ClipboardHost::ReadText() returns an empty string. The empty string is // then pasted, replacing any selected text. This behavior is consistent with // that of HTML text form fields. if (name == "Paste" || name == "PasteAndMatchStyle") { if (!CanEditText()) return false; - blink::WebString text = - blink::Platform::Current()->Clipboard()->ReadPlainText( - blink::mojom::ClipboardBuffer::kStandard); + if (!clipboard_) { + blink::Platform::Current()->GetConnector()->BindInterface( + blink::Platform::Current()->GetBrowserServiceName(), &clipboard_); + } + base::string16 text; + clipboard_->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text); - instance_->ReplaceSelection(text.Utf8()); + instance_->ReplaceSelection(base::UTF16ToUTF8(text)); return true; }
diff --git a/content/renderer/pepper/pepper_webplugin_impl.h b/content/renderer/pepper/pepper_webplugin_impl.h index e08c137..64e09efc 100644 --- a/content/renderer/pepper/pepper_webplugin_impl.h +++ b/content/renderer/pepper/pepper_webplugin_impl.h
@@ -13,6 +13,7 @@ #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner_helpers.h" #include "ppapi/c/pp_var.h" +#include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h" #include "third_party/blink/public/web/web_plugin.h" #include "ui/gfx/geometry/rect.h" @@ -104,6 +105,7 @@ gfx::Rect plugin_rect_; PP_Var instance_object_; blink::WebPluginContainer* container_; + blink::mojom::ClipboardHostPtr clipboard_; DISALLOW_COPY_AND_ASSIGN(PepperWebPluginImpl); };
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 3c94a2d..50c5f92 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -5546,13 +5546,17 @@ engagement_level_.first = url::Origin(); } - // Set the correct autoplay flags on the webview and wipe the cached origin so + // If we are a top frame navigation we should clear any existing autoplay + // flags on the Page. This is because flags are stored at the page level so + // subframes would only add to them. + if (!frame_->Parent()) + render_view_->webview()->ClearAutoplayFlags(); + + // Set the correct autoplay flags on the Page and wipe the cached origin so // this will not be used incorrectly. if (url::Origin(frame_->GetSecurityOrigin()) == autoplay_flags_.first) { render_view_->webview()->AddAutoplayFlags(autoplay_flags_.second); autoplay_flags_.first = url::Origin(); - } else { - render_view_->webview()->AddAutoplayFlags(blink::mojom::kAutoplayFlagNone); } }
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc index 5431516..311dd066 100644 --- a/content/renderer/render_frame_impl_browsertest.cc +++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -60,6 +60,10 @@ constexpr int32_t kFrameProxyRouteId = 22; constexpr int32_t kEmbeddedSubframeRouteId = 23; +const char kParentFrameHTML[] = "Parent frame <iframe name='frame'></iframe>"; + +const char kAutoplayTestOrigin[] = "https://www.google.com"; + } // namespace // RenderFrameImplTest creates a RenderFrameImpl that is a child of the @@ -75,14 +79,17 @@ RenderViewTest::SetUp(); EXPECT_TRUE(GetMainRenderFrame()->is_main_frame_); + IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); + + LoadHTML(kParentFrameHTML); + LoadChildFrame(); + } + + void LoadChildFrame() { mojom::CreateFrameWidgetParams widget_params; widget_params.routing_id = kSubframeWidgetRouteId; widget_params.hidden = false; - IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); - - LoadHTML("Parent frame <iframe name='frame'></iframe>"); - FrameReplicationState frame_replication_state; frame_replication_state.name = "frame"; frame_replication_state.unique_name = "frame-uniqueName"; @@ -133,6 +140,14 @@ return frame_->render_widget_.get(); } + static url::Origin GetOriginForFrame(TestRenderFrame* frame) { + return url::Origin(frame->GetWebFrame()->GetSecurityOrigin()); + } + + static int32_t AutoplayFlagsForFrame(TestRenderFrame* frame) { + return frame->render_view()->webview()->AutoplayFlagsForTest(); + } + #if defined(OS_ANDROID) void ReceiveOverlayRoutingToken(const base::UnguessableToken& token) { overlay_routing_token_ = token; @@ -560,6 +575,51 @@ EXPECT_DCHECK_DEATH(frame()->GetPreviewsStateForFrame()); } +TEST_F(RenderFrameImplTest, AutoplayFlags) { + // Add autoplay flags to the page. + GetMainRenderFrame()->AddAutoplayFlags( + url::Origin::Create(GURL(kAutoplayTestOrigin)), + blink::mojom::kAutoplayFlagHighMediaEngagement); + + // Navigate the top frame. + LoadHTMLWithUrlOverride(kParentFrameHTML, kAutoplayTestOrigin); + + // Check the flags have been set correctly. + EXPECT_EQ(blink::mojom::kAutoplayFlagHighMediaEngagement, + AutoplayFlagsForFrame(GetMainRenderFrame())); + + // Navigate the child frame. + LoadChildFrame(); + + // Check the flags are set on both frames. + EXPECT_EQ(blink::mojom::kAutoplayFlagHighMediaEngagement, + AutoplayFlagsForFrame(GetMainRenderFrame())); + EXPECT_EQ(blink::mojom::kAutoplayFlagHighMediaEngagement, + AutoplayFlagsForFrame(frame())); + + // Navigate the top frame. + LoadHTMLWithUrlOverride(kParentFrameHTML, "https://www.example.com"); + LoadChildFrame(); + + // Check the flags have been cleared. + EXPECT_EQ(blink::mojom::kAutoplayFlagNone, + AutoplayFlagsForFrame(GetMainRenderFrame())); + EXPECT_EQ(blink::mojom::kAutoplayFlagNone, AutoplayFlagsForFrame(frame())); +} + +TEST_F(RenderFrameImplTest, AutoplayFlags_WrongOrigin) { + // Add autoplay flags to the page. + GetMainRenderFrame()->AddAutoplayFlags( + url::Origin(), blink::mojom::kAutoplayFlagHighMediaEngagement); + + // Navigate the top frame. + LoadHTMLWithUrlOverride(kParentFrameHTML, kAutoplayTestOrigin); + + // Check the flags have been not been set. + EXPECT_EQ(blink::mojom::kAutoplayFlagNone, + AutoplayFlagsForFrame(GetMainRenderFrame())); +} + // RenderFrameRemoteInterfacesTest ------------------------------------ namespace {
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc index 7684df4c..f375cfa 100644 --- a/content/renderer/render_frame_proxy.cc +++ b/content/renderer/render_frame_proxy.cc
@@ -673,8 +673,7 @@ // The visible rect that the OOPIF needs to raster depends partially on // parameters that might have changed. If they affect the raster area, resend // the intersection rects. - gfx::Rect new_compositor_visible_rect = - ComputeCompositingRect(last_intersection_rect_); + gfx::Rect new_compositor_visible_rect = web_frame_->GetCompositingRect(); if (new_compositor_visible_rect != last_compositor_visible_rect_) UpdateRemoteViewportIntersection(last_intersection_rect_); } @@ -797,12 +796,11 @@ } void RenderFrameProxy::UpdateRemoteViewportIntersection( - const blink::WebRect& viewportIntersection) { - last_intersection_rect_ = viewportIntersection; - last_compositor_visible_rect_ = - ComputeCompositingRect(gfx::Rect(viewportIntersection)); + const blink::WebRect& viewport_intersection) { + last_intersection_rect_ = viewport_intersection; + last_compositor_visible_rect_ = web_frame_->GetCompositingRect(); Send(new FrameHostMsg_UpdateViewportIntersection( - routing_id_, gfx::Rect(viewportIntersection), + routing_id_, gfx::Rect(viewport_intersection), last_compositor_visible_rect_)); } @@ -902,61 +900,6 @@ #endif } -gfx::Rect RenderFrameProxy::ComputeCompositingRect( - const gfx::Rect& intersection_rect) { - if (!sent_visual_properties_) - return gfx::Rect(); - - gfx::Size visible_viewport_size_in_pixels( - gfx::ScaleToCeiledSize(render_widget_->visible_viewport_size(), - screen_info().device_scale_factor)); - - gfx::Rect screen_space_rect; - if (!IsUseZoomForDSFEnabled()) { - screen_space_rect = - gfx::ScaleToEnclosingRect(sent_visual_properties_->screen_space_rect, - screen_info().device_scale_factor); - } else { - screen_space_rect = sent_visual_properties_->screen_space_rect; - } - - // For iframes that are larger than the window viewport, add a 30% buffer - // to the draw area to try to prevent guttering during scroll. - // TODO(kenrb): The 30% value is arbitrary, it gives 15% overdraw in both - // directions when the iframe extends beyond both edges of the viewport, and - // it seems to make guttering rare with slow to medium speed wheel scrolling. - // Can we collect UMA data to estimate how much extra rastering this causes, - // and possibly how common guttering is? - gfx::SizeF window_viewport = gfx::SizeF(visible_viewport_size_in_pixels); - window_viewport.Scale(1.3f); - gfx::Size viewport_size = gfx::ToFlooredSize(window_viewport); - viewport_size.SetToMin(screen_space_rect.size()); - - gfx::Rect viewport_rect(viewport_size); - if (!intersection_rect.IsEmpty()) { - gfx::RectF viewport_intersection_in_pixels(intersection_rect); - if (!IsUseZoomForDSFEnabled()) { - viewport_intersection_in_pixels.Scale(screen_info().device_scale_factor); - } - float left = intersection_rect.origin().x(); - if (viewport_size.width() > viewport_intersection_in_pixels.width()) { - left -= - (viewport_size.width() - viewport_intersection_in_pixels.width()) / 2; - } - left = std::max(left, 0.f); - viewport_rect.set_x(gfx::ToCeiledInt(left)); - float top = intersection_rect.origin().y(); - if (viewport_size.height() > viewport_intersection_in_pixels.height()) { - top -= - (viewport_size.height() - viewport_intersection_in_pixels.height()) / - 2; - } - top = std::max(top, 0.f); - viewport_rect.set_y(gfx::ToCeiledInt(top)); - } - return viewport_rect; -} - const viz::LocalSurfaceId& RenderFrameProxy::GetLocalSurfaceId() const { return parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); }
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h index 70c302e78..00184f9 100644 --- a/content/renderer/render_frame_proxy.h +++ b/content/renderer/render_frame_proxy.h
@@ -190,7 +190,7 @@ void FrameRectsChanged(const blink::WebRect& local_frame_rect, const blink::WebRect& screen_space_rect) override; void UpdateRemoteViewportIntersection( - const blink::WebRect& viewportIntersection) override; + const blink::WebRect& viewport_intersection) override; void VisibilityChanged(bool visible) override; void SetIsInert(bool) override; void UpdateRenderThrottlingStatus(bool is_throttled, @@ -269,8 +269,6 @@ void SetLayer(std::unique_ptr<blink::WebLayer> web_layer) override; SkBitmap* GetSadPageBitmap() override; - gfx::Rect ComputeCompositingRect(const gfx::Rect& intersection_rect); - const viz::LocalSurfaceId& GetLocalSurfaceId() const; // The routing ID by which this RenderFrameProxy is known.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 672cde1..bb4b36b 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -418,17 +418,17 @@ const viz::CompositorFrame& frame) override { auto new_surface_properties = RenderWidgetSurfaceProperties::FromCompositorFrame(frame); - if (!local_surface_id_.is_valid() || + if (!parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId() + .is_valid() || new_surface_properties != surface_properties_) { - local_surface_id_ = parent_local_surface_id_allocator_.GenerateId(); + parent_local_surface_id_allocator_.GenerateId(); surface_properties_ = new_surface_properties; } - return local_surface_id_; + return parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); } private: viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_; - viz::LocalSurfaceId local_surface_id_; RenderWidgetSurfaceProperties surface_properties_; };
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 7b594bb..839b954 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -1907,7 +1907,6 @@ const gfx::Rect& compositor_visible_rect) { if (auto* frame_widget = GetFrameWidget()) { DCHECK_EQ(popup_type_, WebPopupType::kWebPopupTypeNone); - viewport_intersection_ = viewport_intersection; compositor_visible_rect_ = compositor_visible_rect; frame_widget->SetRemoteViewportIntersection(viewport_intersection); compositor_->SetViewportVisibleRect(ViewportVisibleRect());
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index ba04877..795ed5b 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h
@@ -985,7 +985,6 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - gfx::Rect viewport_intersection_; gfx::Rect compositor_visible_rect_; // Different consumers in the browser process makes different assumptions, so
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc index 52e73b6b..51e1bd5 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -33,7 +33,6 @@ #include "content/child/thread_safe_sender.h" #include "content/common/frame_messages.h" #include "content/common/gpu_stream_constants.h" -#include "content/common/origin_trials/trial_policy_impl.h" #include "content/common/render_message_filter.mojom.h" #include "content/common/wrapper_shared_url_loader_factory.h" #include "content/public/common/content_features.h" @@ -75,7 +74,6 @@ #include "content/renderer/render_thread_impl.h" #include "content/renderer/storage_util.h" #include "content/renderer/web_database_observer_impl.h" -#include "content/renderer/webfileutilities_impl.h" #include "content/renderer/webgraphicscontext3d_provider_impl.h" #include "content/renderer/webpublicsuffixlist_impl.h" #include "content/renderer/worker_thread_registry.h" @@ -233,21 +231,6 @@ //------------------------------------------------------------------------------ -class RendererBlinkPlatformImpl::FileUtilities : public WebFileUtilitiesImpl { - public: - explicit FileUtilities( - scoped_refptr<mojom::ThreadSafeFileUtilitiesHostPtr> host) - : file_utilities_host_(std::move(host)) {} - bool GetFileInfo(const WebString& path, WebFileInfo& result) override; - - private: - mojom::FileUtilitiesHost& GetFileUtilitiesHost() { - return **file_utilities_host_; - } - - scoped_refptr<mojom::ThreadSafeFileUtilitiesHostPtr> file_utilities_host_; -}; - #if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_FUCHSIA) class RendererBlinkPlatformImpl::SandboxSupport : public blink::WebSandboxSupport { @@ -326,9 +309,6 @@ GetInterfaceProvider()->GetInterface( mojo::MakeRequest(&web_database_host_info_)); - - GetInterfaceProvider()->GetInterface( - mojo::MakeRequest(&file_utilities_host_info_)); } RendererBlinkPlatformImpl::~RendererBlinkPlatformImpl() { @@ -432,18 +412,6 @@ return BlinkPlatformImpl::Clipboard(); } -blink::WebFileUtilities* RendererBlinkPlatformImpl::GetFileUtilities() { - if (!file_utilities_) { - file_utilities_.reset( - new FileUtilities(mojom::ThreadSafeFileUtilitiesHostPtr::Create( - std::move(file_utilities_host_info_), - base::CreateSequencedTaskRunnerWithTraits( - {base::WithBaseSyncPrimitives()})))); - file_utilities_->set_sandbox_enabled(sandboxEnabled()); - } - return file_utilities_.get(); -} - blink::WebSandboxSupport* RendererBlinkPlatformImpl::GetSandboxSupport() { #if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_FUCHSIA) // These platforms do not require sandbox support. @@ -629,22 +597,6 @@ //------------------------------------------------------------------------------ -bool RendererBlinkPlatformImpl::FileUtilities::GetFileInfo( - const WebString& path, - WebFileInfo& web_file_info) { - base::Optional<base::File::Info> file_info; - if (!GetFileUtilitiesHost().GetFileInfo(blink::WebStringToFilePath(path), - &file_info) || - !file_info) { - return false; - } - FileInfoToWebFileInfo(file_info.value(), &web_file_info); - web_file_info.platform_path = path; - return true; -} - -//------------------------------------------------------------------------------ - #if defined(OS_MACOSX) bool RendererBlinkPlatformImpl::SandboxSupport::LoadFont(CTFontRef src_font, @@ -1342,14 +1294,6 @@ //------------------------------------------------------------------------------ -std::unique_ptr<blink::WebTrialTokenValidator> -RendererBlinkPlatformImpl::CreateTrialTokenValidator() { - return std::make_unique<WebTrialTokenValidatorImpl>( - TrialPolicyImpl::CreateValidatorForPolicy()); -} - -//------------------------------------------------------------------------------ - blink::WebNotificationManager* RendererBlinkPlatformImpl::GetWebNotificationManager() { if (!thread_safe_sender_.get() || !notification_dispatcher_.get())
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h index cd5a962..c50182f1 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h
@@ -19,9 +19,7 @@ #include "cc/blink/web_compositor_support_impl.h" #include "content/child/blink_platform_impl.h" #include "content/common/content_export.h" -#include "content/common/file_utilities.mojom.h" #include "content/common/possibly_associated_interface_ptr.h" -#include "content/renderer/origin_trials/web_trial_token_validator_impl.h" #include "content/renderer/top_level_blame_context.h" #include "content/renderer/webpublicsuffixlist_impl.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" @@ -78,7 +76,6 @@ } // Platform methods: blink::WebClipboard* Clipboard() override; - blink::WebFileUtilities* GetFileUtilities() override; blink::WebSandboxSupport* GetSandboxSupport() override; blink::WebCookieJar* CookieJar() override; blink::WebThemeEngine* ThemeEngine() override; @@ -208,8 +205,6 @@ const blink::WebString& sample) override; void RecordRapporURL(const char* metric, const blink::WebURL& url) override; blink::WebPushProvider* PushProvider() override; - std::unique_ptr<blink::WebTrialTokenValidator> CreateTrialTokenValidator() - override; blink::WebNotificationManager* GetWebNotificationManager() override; void DidStartWorkerThread() override; void WillStopWorkerThread() override; @@ -292,9 +287,6 @@ std::unique_ptr<blink::WebThread> main_thread_; std::unique_ptr<service_manager::Connector> connector_; - class FileUtilities; - std::unique_ptr<FileUtilities> file_utilities_; - #if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_FUCHSIA) class SandboxSupport; std::unique_ptr<SandboxSupport> sandbox_support_; @@ -340,8 +332,6 @@ blink::mojom::WebDatabaseHostPtrInfo web_database_host_info_; scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr> web_database_host_; - mojom::FileUtilitiesHostPtrInfo file_utilities_host_info_; - scoped_refptr<NotificationDispatcher> notification_dispatcher_; THREAD_CHECKER(main_thread_checker_);
diff --git a/content/renderer/speech_recognition_dispatcher.cc b/content/renderer/speech_recognition_dispatcher.cc index e69f9e5e..25a61f8a 100644 --- a/content/renderer/speech_recognition_dispatcher.cc +++ b/content/renderer/speech_recognition_dispatcher.cc
@@ -13,7 +13,10 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "content/common/speech_recognition_messages.h" +#include "content/public/common/service_names.mojom.h" +#include "content/public/renderer/render_thread.h" #include "content/renderer/render_frame_impl.h" +#include "services/service_manager/public/cpp/connector.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_speech_grammar.h" @@ -32,8 +35,7 @@ SpeechRecognitionDispatcher::SpeechRecognitionDispatcher( RenderFrame* render_frame) - : RenderFrameObserver(render_frame), - next_id_(1) {} + : RenderFrameObserver(render_frame), next_id_(1) {} SpeechRecognitionDispatcher::~SpeechRecognitionDispatcher() = default; @@ -61,7 +63,7 @@ void SpeechRecognitionDispatcher::WasHidden() { #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_WEBRTC) - Send(new SpeechRecognitionHostMsg_AbortAllRequests(routing_id())); + GetSpeechRecognitionHost().AbortAllRequests(); #endif } @@ -73,21 +75,20 @@ recognizer_client_ == recognizer_client); recognizer_client_ = recognizer_client; - SpeechRecognitionHostMsg_StartRequest_Params msg_params; + mojom::StartSpeechRecognitionRequestParamsPtr msg_params = + mojom::StartSpeechRecognitionRequestParams::New(); for (const WebSpeechGrammar& grammar : params.Grammars()) { - msg_params.grammars.push_back(SpeechRecognitionGrammar( + msg_params->grammars.push_back(SpeechRecognitionGrammar( grammar.Src().GetString().Utf8(), grammar.Weight())); } - msg_params.language = params.Language().Utf8(); - msg_params.max_hypotheses = static_cast<uint32_t>(params.MaxAlternatives()); - msg_params.continuous = params.Continuous(); - msg_params.interim_results = params.InterimResults(); - msg_params.origin_url = params.Origin().ToString().Utf8(); - msg_params.render_frame_id = routing_id(); - msg_params.request_id = GetOrCreateIDForHandle(handle); + msg_params->language = params.Language().Utf8(); + msg_params->max_hypotheses = static_cast<uint32_t>(params.MaxAlternatives()); + msg_params->continuous = params.Continuous(); + msg_params->interim_results = params.InterimResults(); + msg_params->origin_url = params.Origin().ToString().Utf8(); + msg_params->request_id = GetOrCreateIDForHandle(handle); - // The handle mapping will be removed in |OnRecognitionEnd|. - Send(new SpeechRecognitionHostMsg_StartRequest(msg_params)); + GetSpeechRecognitionHost().StartRequest(std::move(msg_params)); } void SpeechRecognitionDispatcher::Stop( @@ -96,8 +97,7 @@ // Ignore a |stop| issued without a matching |start|. if (recognizer_client_ != recognizer_client || !HandleExists(handle)) return; - Send(new SpeechRecognitionHostMsg_StopCaptureRequest( - routing_id(), GetOrCreateIDForHandle(handle))); + GetSpeechRecognitionHost().StopCaptureRequest(GetOrCreateIDForHandle(handle)); } void SpeechRecognitionDispatcher::Abort( @@ -106,8 +106,7 @@ // Ignore an |abort| issued without a matching |start|. if (recognizer_client_ != recognizer_client || !HandleExists(handle)) return; - Send(new SpeechRecognitionHostMsg_AbortRequest( - routing_id(), GetOrCreateIDForHandle(handle))); + GetSpeechRecognitionHost().AbortRequest(GetOrCreateIDForHandle(handle)); } void SpeechRecognitionDispatcher::OnRecognitionStarted(int request_id) { @@ -161,7 +160,8 @@ } void SpeechRecognitionDispatcher::OnErrorOccurred( - int request_id, const SpeechRecognitionError& error) { + int request_id, + const SpeechRecognitionError& error) { if (error.code == SPEECH_RECOGNITION_ERROR_NO_MATCH) { recognizer_client_.DidReceiveNoMatch(GetHandleFromID(request_id), WebSpeechRecognitionResult()); @@ -190,7 +190,8 @@ } void SpeechRecognitionDispatcher::OnResultsRetrieved( - int request_id, const SpeechRecognitionResults& results) { + int request_id, + const SpeechRecognitionResults& results) { size_t provisional_count = std::count_if(results.begin(), results.end(), [](const SpeechRecognitionResult& result) { @@ -198,13 +199,14 @@ }); WebVector<WebSpeechRecognitionResult> provisional(provisional_count); - WebVector<WebSpeechRecognitionResult> final( - results.size() - provisional_count); + WebVector<WebSpeechRecognitionResult> final(results.size() - + provisional_count); int provisional_index = 0, final_index = 0; for (const SpeechRecognitionResult& result : results) { - WebSpeechRecognitionResult* webkit_result = result.is_provisional ? - &provisional[provisional_index++] : &final[final_index++]; + WebSpeechRecognitionResult* webkit_result = + result.is_provisional ? &provisional[provisional_index++] + : &final[final_index++]; const size_t num_hypotheses = result.hypotheses.size(); WebVector<WebString> transcripts(num_hypotheses); @@ -257,4 +259,13 @@ return iter->second; } +mojom::SpeechRecognizer& +SpeechRecognitionDispatcher::GetSpeechRecognitionHost() { + if (!speech_recognition_host_) { + render_frame()->GetRemoteInterfaces()->GetInterface( + mojo::MakeRequest(&speech_recognition_host_)); + } + return *speech_recognition_host_; +} + } // namespace content
diff --git a/content/renderer/speech_recognition_dispatcher.h b/content/renderer/speech_recognition_dispatcher.h index d9055e7..64ceda2 100644 --- a/content/renderer/speech_recognition_dispatcher.h +++ b/content/renderer/speech_recognition_dispatcher.h
@@ -9,6 +9,7 @@ #include <memory> #include "base/macros.h" +#include "content/common/speech_recognizer.mojom.h" #include "content/public/common/speech_recognition_result.h" #include "content/public/renderer/render_frame_observer.h" #include "third_party/blink/public/web/web_speech_recognition_handle.h" @@ -60,6 +61,10 @@ const blink::WebSpeechRecognitionHandle& handle); const blink::WebSpeechRecognitionHandle& GetHandleFromID(int handle_id); + mojom::SpeechRecognizer& GetSpeechRecognitionHost(); + + mojom::SpeechRecognizerPtr speech_recognition_host_; + // The Blink client class that we use to send events back to the JS world. blink::WebSpeechRecognizerClient recognizer_client_;
diff --git a/content/renderer/web_ui_extension.cc b/content/renderer/web_ui_extension.cc index eb05e6c..dd7b5fd 100644 --- a/content/renderer/web_ui_extension.cc +++ b/content/renderer/web_ui_extension.cc
@@ -122,7 +122,6 @@ // Send the message up to the browser. render_frame->Send(new FrameHostMsg_WebUISend(render_frame->GetRoutingID(), - frame->GetDocument().Url(), message, *content)); }
diff --git a/content/renderer/webfileutilities_impl.cc b/content/renderer/webfileutilities_impl.cc deleted file mode 100644 index 3aca0260..0000000 --- a/content/renderer/webfileutilities_impl.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/renderer/webfileutilities_impl.h" - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "content/renderer/file_info_util.h" -#include "net/base/filename_util.h" -#include "third_party/blink/public/platform/file_path_conversion.h" -#include "third_party/blink/public/platform/web_file_info.h" -#include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/public/platform/web_url.h" - -using blink::WebString; - -namespace content { - -WebFileUtilitiesImpl::WebFileUtilitiesImpl() - : sandbox_enabled_(true) { -} - -WebFileUtilitiesImpl::~WebFileUtilitiesImpl() = default; - -bool WebFileUtilitiesImpl::GetFileInfo(const WebString& path, - blink::WebFileInfo& web_file_info) { - if (sandbox_enabled_) { - NOTREACHED(); - return false; - } - base::File::Info file_info; - if (!base::GetFileInfo(blink::WebStringToFilePath(path), &file_info)) - return false; - - FileInfoToWebFileInfo(file_info, &web_file_info); - web_file_info.platform_path = path; - return true; -} - -} // namespace content
diff --git a/content/renderer/webfileutilities_impl.h b/content/renderer/webfileutilities_impl.h deleted file mode 100644 index 35495627..0000000 --- a/content/renderer/webfileutilities_impl.h +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_RENDERER_WEBFILEUTILITIES_IMPL_H_ -#define CONTENT_RENDERER_WEBFILEUTILITIES_IMPL_H_ - -#include "content/common/content_export.h" -#include "third_party/blink/public/platform/web_file_info.h" -#include "third_party/blink/public/platform/web_file_utilities.h" - -namespace content { - -class CONTENT_EXPORT WebFileUtilitiesImpl : public blink::WebFileUtilities { - public: - WebFileUtilitiesImpl(); - virtual ~WebFileUtilitiesImpl(); - - // WebFileUtilities methods: - bool GetFileInfo(const blink::WebString& path, - blink::WebFileInfo& result) override; - - void set_sandbox_enabled(bool sandbox_enabled) { - sandbox_enabled_ = sandbox_enabled; - } - - protected: - bool sandbox_enabled_; -}; - -} // namespace content - -#endif // CONTENT_RENDERER_WEBFILEUTILITIES_IMPL_H_
diff --git a/content/shell/common/shell_content_client.cc b/content/shell/common/shell_content_client.cc index c73f498..9adc56d0 100644 --- a/content/shell/common/shell_content_client.cc +++ b/content/shell/common/shell_content_client.cc
@@ -87,7 +87,7 @@ resource_id); } -OriginTrialPolicy* ShellContentClient::GetOriginTrialPolicy() { +blink::OriginTrialPolicy* ShellContentClient::GetOriginTrialPolicy() { return &origin_trial_policy_; }
diff --git a/content/shell/common/shell_content_client.h b/content/shell/common/shell_content_client.h index c33343556..635f493 100644 --- a/content/shell/common/shell_content_client.h +++ b/content/shell/common/shell_content_client.h
@@ -10,7 +10,6 @@ #include "base/compiler_specific.h" #include "content/public/common/content_client.h" -#include "content/public/common/origin_trial_policy.h" #include "content/shell/common/shell_origin_trial_policy.h" namespace content { @@ -30,7 +29,7 @@ base::RefCountedMemory* GetDataResourceBytes( int resource_id) const override; gfx::Image& GetNativeImageNamed(int resource_id) const override; - OriginTrialPolicy* GetOriginTrialPolicy() override; + blink::OriginTrialPolicy* GetOriginTrialPolicy() override; private: ShellOriginTrialPolicy origin_trial_policy_;
diff --git a/content/shell/common/shell_origin_trial_policy.cc b/content/shell/common/shell_origin_trial_policy.cc index a90ea35..7654e8a9 100644 --- a/content/shell/common/shell_origin_trial_policy.cc +++ b/content/shell/common/shell_origin_trial_policy.cc
@@ -4,6 +4,10 @@ #include "content/shell/common/shell_origin_trial_policy.h" +#include "base/feature_list.h" +#include "content/public/common/content_features.h" +#include "content/public/common/origin_util.h" + namespace content { namespace { @@ -17,6 +21,7 @@ 0x9a, 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f, 0x64, 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0, }; + } // namespace ShellOriginTrialPolicy::ShellOriginTrialPolicy() @@ -26,8 +31,16 @@ ShellOriginTrialPolicy::~ShellOriginTrialPolicy() {} +bool ShellOriginTrialPolicy::IsOriginTrialsSupported() const { + return base::FeatureList::IsEnabled(features::kOriginTrials); +} + base::StringPiece ShellOriginTrialPolicy::GetPublicKey() const { return public_key_; } +bool ShellOriginTrialPolicy::IsOriginSecure(const GURL& url) const { + return content::IsOriginSecure(url); +} + } // namespace content
diff --git a/content/shell/common/shell_origin_trial_policy.h b/content/shell/common/shell_origin_trial_policy.h index c55584fe..08b8e3ff 100644 --- a/content/shell/common/shell_origin_trial_policy.h +++ b/content/shell/common/shell_origin_trial_policy.h
@@ -7,17 +7,19 @@ #include "base/macros.h" #include "base/strings/string_piece.h" -#include "content/public/common/origin_trial_policy.h" +#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h" namespace content { -class ShellOriginTrialPolicy : public OriginTrialPolicy { +class ShellOriginTrialPolicy : public blink::OriginTrialPolicy { public: ShellOriginTrialPolicy(); ~ShellOriginTrialPolicy() override; - // OriginTrialPolicy interface + // blink::OriginTrialPolicy interface + bool IsOriginTrialsSupported() const override; base::StringPiece GetPublicKey() const override; + bool IsOriginSecure(const GURL& url) const override; private: base::StringPiece public_key_;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 09d66ad..ff23f1a3 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -293,7 +293,6 @@ "//base/third_party/dynamic_annotations", "//cc:test_support", "//cc/blink", - "//cc/ipc", "//components/services/leveldb/public/interfaces", "//components/viz/host", "//components/viz/service", @@ -742,6 +741,7 @@ "../browser/frame_host/render_frame_host_impl_browsertest.cc", "../browser/frame_host/render_frame_host_manager_browsertest.cc", "../browser/frame_host/render_frame_message_filter_browsertest.cc", + "../browser/frame_host/webui_navigation_browsertest.cc", "../browser/generic_sensor/generic_sensor_browsertest.cc", "../browser/gpu/gpu_ipc_browsertests.cc", "../browser/gpu/in_process_gpu_thread_browsertests.cc", @@ -1672,7 +1672,6 @@ "//base/third_party/dynamic_annotations", "//cc", "//cc:test_support", - "//cc/ipc", "//components/cbor", "//components/network_session_configurator/browser", "//components/network_session_configurator/common", @@ -2105,7 +2104,6 @@ deps = [ "//base/test:test_support", "//cc", - "//cc/ipc", "//content/browser:for_content_tests", "//content/public/browser", "//content/public/common",
diff --git a/content/test/data/frame_tree/page_with_large_scrollable_frame.html b/content/test/data/frame_tree/page_with_large_scrollable_frame.html new file mode 100644 index 0000000..c050ea18 --- /dev/null +++ b/content/test/data/frame_tree/page_with_large_scrollable_frame.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<style> +div { + width: 100px; + height: 200px; + overflow: scroll; +} +iframe { + width: 100px; + height: 10000px; +} +</style> +<html> +<body> +<div> +<iframe src="/cross-site/baz.com/title1.html"></iframe> +</div> +This page contains a positioned cross-origin iframe. +</body> +</html>
diff --git a/content/test/data/frame_tree/page_with_scaled_large_frame.html b/content/test/data/frame_tree/page_with_scaled_large_frame.html new file mode 100644 index 0000000..2f4e0c4 --- /dev/null +++ b/content/test/data/frame_tree/page_with_scaled_large_frame.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<style> +div { + transform: scale(0.2); + width: 100px; + height: 200px; + overflow: scroll; +} +iframe { + width: 100px; + height: 10000px; +} +</style> +<html> +<body> +<div> +<iframe src="/cross-site/baz.com/title1.html"></iframe> +</div> +</body> +</html>
diff --git a/content/test/data/frame_tree/page_with_scaled_large_frames_nested.html b/content/test/data/frame_tree/page_with_scaled_large_frames_nested.html new file mode 100644 index 0000000..cdf6432 --- /dev/null +++ b/content/test/data/frame_tree/page_with_scaled_large_frames_nested.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<style> +div { + transform: scale(0.2); + width: 150px; + height: 200px; +} +iframe { + width: 150px; + height: 200px; +} +</style> +<html> +<body> +<div> +<iframe src="about:blank"></iframe> +</div> +</body> +</html>
diff --git a/content/test/gpu/gpu_tests/context_lost_expectations.py b/content/test/gpu/gpu_tests/context_lost_expectations.py index 9e0d5d0..6c60e91 100644 --- a/content/test/gpu/gpu_tests/context_lost_expectations.py +++ b/content/test/gpu/gpu_tests/context_lost_expectations.py
@@ -54,8 +54,12 @@ self.Fail('ContextLost_WebGLContextLostFromQuantity', ['android', ('qualcomm', 'Adreno (TM) 420')], bug=611906) - # Nexus 9 and Nvidia Shield TV + # Android WebGLBlocked/Unblocked self.Fail('ContextLost_WebGLBlockedAfterJSNavigation', ['android', 'nvidia'], bug=832886) + self.Flaky('ContextLost_WebGLBlockedAfterJSNavigation', + ['android', 'qualcomm'], bug=832886) self.Fail('ContextLost_WebGLUnblockedAfterUserInitiatedReload', ['android', 'nvidia'], bug=832886) + self.Flaky('ContextLost_WebGLUnblockedAfterUserInitiatedReload', + ['android', 'qualcomm'], bug=832886)
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc index 4c0203d..a681ff9 100644 --- a/content/test/test_blink_web_unit_test_support.cc +++ b/content/test/test_blink_web_unit_test_support.cc
@@ -179,8 +179,6 @@ // Initialize libraries for media. media::InitializeMediaLibrary(); - file_utilities_.set_sandbox_enabled(false); - if (!file_system_root_.CreateUniqueTempDir()) { LOG(WARNING) << "Failed to create a temp dir for the filesystem." "FileSystem feature will be disabled."; @@ -209,10 +207,6 @@ return mock_clipboard_.get(); } -blink::WebFileUtilities* TestBlinkWebUnitTestSupport::GetFileUtilities() { - return &file_utilities_; -} - blink::WebIDBFactory* TestBlinkWebUnitTestSupport::IdbFactory() { NOTREACHED() << "IndexedDB cannot be tested with in-process harnesses.";
diff --git a/content/test/test_blink_web_unit_test_support.h b/content/test/test_blink_web_unit_test_support.h index c3dd2efd..869f24d 100644 --- a/content/test/test_blink_web_unit_test_support.h +++ b/content/test/test_blink_web_unit_test_support.h
@@ -14,7 +14,6 @@ #include "build/build_config.h" #include "cc/blink/web_compositor_support_impl.h" #include "content/child/blink_platform_impl.h" -#include "content/renderer/webfileutilities_impl.h" #include "content/test/mock_webblob_registry_impl.h" #include "content/test/mock_webclipboard_impl.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" @@ -35,7 +34,6 @@ blink::WebBlobRegistry* GetBlobRegistry() override; blink::WebClipboard* Clipboard() override; - blink::WebFileUtilities* GetFileUtilities() override; blink::WebIDBFactory* IdbFactory() override; std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory() @@ -74,7 +72,6 @@ private: MockWebBlobRegistryImpl blob_registry_; std::unique_ptr<MockWebClipboardImpl> mock_clipboard_; - WebFileUtilitiesImpl file_utilities_; base::ScopedTempDir file_system_root_; std::unique_ptr<blink::WebURLLoaderMockFactory> url_loader_factory_; cc_blink::WebCompositorSupportImpl compositor_support_;
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h index 1243caef..08f092c 100644 --- a/device/bluetooth/test/mock_bluetooth_adapter.h +++ b/device/bluetooth/test/mock_bluetooth_adapter.h
@@ -24,7 +24,7 @@ class Observer : public BluetoothAdapter::Observer { public: Observer(); - virtual ~Observer(); + ~Observer() override; MOCK_METHOD2(AdapterPresentChanged, void(BluetoothAdapter*, bool)); MOCK_METHOD2(AdapterPoweredChanged, void(BluetoothAdapter*, bool)); @@ -36,7 +36,7 @@ MockBluetoothAdapter(); - virtual bool IsInitialized() const { return true; } + bool IsInitialized() const override { return true; } #if defined(OS_CHROMEOS) || defined(OS_LINUX) void Shutdown() override; @@ -99,7 +99,7 @@ void StartDiscoverySessionWithFilter( std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, const DiscoverySessionCallback& callback, - const ErrorCallback& error_callback); + const ErrorCallback& error_callback) override; // BluetoothAdapter is supposed to manage the lifetime of BluetoothDevices. // This method takes ownership of the MockBluetoothDevice. This is only for @@ -146,7 +146,7 @@ const base::Closure& callback, const AdvertisementErrorCallback& error_callback) override; #endif - virtual ~MockBluetoothAdapter(); + ~MockBluetoothAdapter() override; MOCK_METHOD1(RemovePairingDelegateInternal, void(BluetoothDevice::PairingDelegate* pairing_delegate));
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h index 3be556b..7e9ca84 100644 --- a/device/bluetooth/test/mock_bluetooth_device.h +++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -33,7 +33,7 @@ const std::string& address, bool paired, bool connected); - virtual ~MockBluetoothDevice(); + ~MockBluetoothDevice() override; MOCK_CONST_METHOD0(GetBluetoothClass, uint32_t()); MOCK_CONST_METHOD0(GetType, BluetoothTransport());
diff --git a/device/bluetooth/test/mock_bluetooth_discovery_session.h b/device/bluetooth/test/mock_bluetooth_discovery_session.h index 61201e02..92f27210 100644 --- a/device/bluetooth/test/mock_bluetooth_discovery_session.h +++ b/device/bluetooth/test/mock_bluetooth_discovery_session.h
@@ -17,7 +17,7 @@ class MockBluetoothDiscoverySession : public BluetoothDiscoverySession { public: MockBluetoothDiscoverySession(); - virtual ~MockBluetoothDiscoverySession(); + ~MockBluetoothDiscoverySession() override; MOCK_CONST_METHOD0(IsActive, bool()); MOCK_METHOD2(Stop,
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h index 039e248..ff419da 100644 --- a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h +++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
@@ -33,7 +33,7 @@ bool is_local, Properties properties, Permissions permissions); - virtual ~MockBluetoothGattCharacteristic(); + ~MockBluetoothGattCharacteristic() override; MOCK_CONST_METHOD0(GetIdentifier, std::string()); MOCK_CONST_METHOD0(GetUUID, BluetoothUUID());
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_connection.h b/device/bluetooth/test/mock_bluetooth_gatt_connection.h index ccc736d0..661d6b0 100644 --- a/device/bluetooth/test/mock_bluetooth_gatt_connection.h +++ b/device/bluetooth/test/mock_bluetooth_gatt_connection.h
@@ -16,7 +16,7 @@ public: MockBluetoothGattConnection(scoped_refptr<device::BluetoothAdapter> adapter, const std::string& device_address); - virtual ~MockBluetoothGattConnection(); + ~MockBluetoothGattConnection() override; MOCK_CONST_METHOD0(GetDeviceAddress, std::string()); MOCK_METHOD0(IsConnected, bool());
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h b/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h index f0a0430f..1ae5d29 100644 --- a/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h +++ b/device/bluetooth/test/mock_bluetooth_gatt_descriptor.h
@@ -29,7 +29,7 @@ const BluetoothUUID& uuid, bool is_local, BluetoothRemoteGattCharacteristic::Permissions permissions); - virtual ~MockBluetoothGattDescriptor(); + ~MockBluetoothGattDescriptor() override; MOCK_CONST_METHOD0(GetIdentifier, std::string()); MOCK_CONST_METHOD0(GetUUID, BluetoothUUID());
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h index a00cecc..90f0bf5 100644 --- a/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h +++ b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h
@@ -24,7 +24,7 @@ public: explicit MockBluetoothGattNotifySession( base::WeakPtr<BluetoothRemoteGattCharacteristic> characteristic); - virtual ~MockBluetoothGattNotifySession(); + ~MockBluetoothGattNotifySession() override; MOCK_METHOD0(IsActive, bool()); MOCK_METHOD1(Stop, void(const base::Closure&));
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_service.h b/device/bluetooth/test/mock_bluetooth_gatt_service.h index 7a8a381..dd841ff 100644 --- a/device/bluetooth/test/mock_bluetooth_gatt_service.h +++ b/device/bluetooth/test/mock_bluetooth_gatt_service.h
@@ -27,7 +27,7 @@ const BluetoothUUID& uuid, bool is_primary, bool is_local); - virtual ~MockBluetoothGattService(); + ~MockBluetoothGattService() override; MOCK_CONST_METHOD0(GetIdentifier, std::string()); MOCK_CONST_METHOD0(GetUUID, BluetoothUUID());
diff --git a/device/bluetooth/test/mock_bluetooth_socket.h b/device/bluetooth/test/mock_bluetooth_socket.h index 2d74cd7..518123dc 100644 --- a/device/bluetooth/test/mock_bluetooth_socket.h +++ b/device/bluetooth/test/mock_bluetooth_socket.h
@@ -33,7 +33,7 @@ const ErrorCompletionCallback& error_callback)); protected: - virtual ~MockBluetoothSocket(); + ~MockBluetoothSocket() override; }; } // namespace device
diff --git a/device/fido/fido_constants.cc b/device/fido/fido_constants.cc index 20de24e..2525b15 100644 --- a/device/fido/fido_constants.cc +++ b/device/fido/fido_constants.cc
@@ -43,11 +43,6 @@ const size_t kMaxKeyHandleLength = 255; const size_t kU2fParameterLength = 32; -const std::array<uint8_t, 2> kLegacyVersionSuffix = {0x00, 0x00}; - -const std::array<uint8_t, 6> kU2fVersionResponse = {'U', '2', 'F', - '_', 'V', '2'}; - const base::TimeDelta kDeviceTimeout = base::TimeDelta::FromSeconds(3); const base::TimeDelta kHidKeepAliveDelay = base::TimeDelta::FromMilliseconds(100);
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h index b98529d..0bf6a66 100644 --- a/device/fido/fido_constants.h +++ b/device/fido/fido_constants.h
@@ -274,15 +274,6 @@ COMPONENT_EXPORT(DEVICE_FIDO) extern const size_t kMaxKeyHandleLength; COMPONENT_EXPORT(DEVICE_FIDO) extern const size_t kU2fParameterLength; -// Suffix added to APDU encoded command for legacy version request. -COMPONENT_EXPORT(DEVICE_FIDO) -extern const std::array<uint8_t, 2> kLegacyVersionSuffix; - -// Expected response data for version request from U2F device. -// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#getversion-request-and-response---u2f_version -COMPONENT_EXPORT(DEVICE_FIDO) -extern const std::array<uint8_t, 6> kU2fVersionResponse; - // Maximum wait time before client error outs on device. COMPONENT_EXPORT(DEVICE_FIDO) extern const base::TimeDelta kDeviceTimeout;
diff --git a/device/fido/fido_hid_device_unittest.cc b/device/fido/fido_hid_device_unittest.cc index 8778286..e52e1b53 100644 --- a/device/fido/fido_hid_device_unittest.cc +++ b/device/fido/fido_hid_device_unittest.cc
@@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "device/fido/fido_hid_device.h" + +#include <memory> #include <tuple> #include "base/bind.h" @@ -11,14 +14,10 @@ #include "base/strings/string_number_conversions.h" #include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" -#include "components/apdu/apdu_command.h" -#include "components/apdu/apdu_response.h" #include "device/fido/fake_hid_impl_for_testing.h" #include "device/fido/fido_constants.h" -#include "device/fido/fido_hid_device.h" #include "device/fido/fido_parsing_utils.h" #include "device/fido/test_callback_receiver.h" -#include "device/fido/u2f_request.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "services/device/public/cpp/hid/hid_device_filter.h" @@ -33,10 +32,17 @@ namespace { -// HID_MSG(83), followed by payload length(0008), followed by 'U2F_V2', followed -// by APDU response code(9000). -constexpr uint8_t kMockVersionResponseSuffix[] = { - 0x83, 0x00, 0x08, 0x55, 0x32, 0x46, 0x5f, 0x56, 0x32, 0x90, 0x00}; +// HID_MSG(83), followed by payload length(000b), followed by response data +// "MOCK_DATA", followed by APDU SW_NO_ERROR response code(9000). +constexpr uint8_t kU2fMockResponseMessage[] = { + 0x83, 0x00, 0x0b, 0x4d, 0x4f, 0x43, 0x4b, + 0x5f, 0x44, 0x41, 0x54, 0x41, 0x90, 0x00, +}; + +// APDU encoded success response with data "MOCK_DATA" followed by a SW_NO_ERROR +// APDU response code(9000). +constexpr uint8_t kU2fMockResponseData[] = {0x4d, 0x4f, 0x43, 0x4b, 0x5f, 0x44, + 0x41, 0x54, 0x41, 0x90, 0x00}; // HID_KEEP_ALIVE(bb), followed by payload length(0001), followed by // status processing(01) byte. @@ -49,6 +55,10 @@ 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, }; +// Mock APDU encoded U2F request with empty data and mock P1 parameter(0x04). +constexpr uint8_t kMockU2fRequest[] = {0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00}; + // Returns HID_INIT request to send to device with mock connection. std::vector<uint8_t> CreateMockInitResponse( base::span<const uint8_t> nonce, @@ -70,7 +80,7 @@ } // Returns "U2F_v2" as a mock response to version request with given channel id. -std::vector<uint8_t> CreateMockResponse( +std::vector<uint8_t> CreateMockResponseWithChannelId( base::span<const uint8_t> channel_id, base::span<const uint8_t> response_buffer) { auto response = fido_parsing_utils::Materialize(channel_id); @@ -79,12 +89,9 @@ return response; } -// Returns U2F_V2 version response formatted in APDU response encoding. -std::vector<uint8_t> GetValidU2fVersionResponse() { - return apdu::ApduResponse(std::vector<uint8_t>(kU2fVersionResponse.begin(), - kU2fVersionResponse.end()), - apdu::ApduResponse::Status::SW_NO_ERROR) - .GetEncodedResponse(); +// Returns a APDU encoded U2F version request for testing. +std::vector<uint8_t> GetMockDeviceRequest() { + return fido_parsing_utils::Materialize(kMockU2fRequest); } device::mojom::HidDeviceInfoPtr TestHidDevice() { @@ -170,14 +177,13 @@ // Add pending transactions manually and ensure they are processed. TestDeviceCallbackReceiver receiver_1; - device->pending_transactions_.emplace(U2fRequest::GetU2fVersionApduCommand(), + device->pending_transactions_.emplace(GetMockDeviceRequest(), receiver_1.callback()); TestDeviceCallbackReceiver receiver_2; - device->pending_transactions_.emplace(U2fRequest::GetU2fVersionApduCommand(), + device->pending_transactions_.emplace(GetMockDeviceRequest(), receiver_2.callback()); TestDeviceCallbackReceiver receiver_3; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(), - receiver_3.callback()); + device->DeviceTransact(GetMockDeviceRequest(), receiver_3.callback()); EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_); @@ -206,21 +212,19 @@ device->state_ = FidoDevice::State::kReady; TestDeviceCallbackReceiver receiver_0; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(), - receiver_0.callback()); + device->DeviceTransact(GetMockDeviceRequest(), receiver_0.callback()); EXPECT_FALSE(receiver_0.value()); EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_); // Add pending transactions manually and ensure they are processed. TestDeviceCallbackReceiver receiver_1; - device->pending_transactions_.emplace(U2fRequest::GetU2fVersionApduCommand(), + device->pending_transactions_.emplace(GetMockDeviceRequest(), receiver_1.callback()); TestDeviceCallbackReceiver receiver_2; - device->pending_transactions_.emplace(U2fRequest::GetU2fVersionApduCommand(), + device->pending_transactions_.emplace(GetMockDeviceRequest(), receiver_2.callback()); TestDeviceCallbackReceiver receiver_3; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(), - receiver_3.callback()); + device->DeviceTransact(GetMockDeviceRequest(), receiver_3.callback()); FakeHidConnection::mock_connection_error_ = false; EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_); @@ -270,10 +274,10 @@ // Version response from the authenticator. .WillOnce(Invoke( [&mock_connection](device::mojom::HidConnection::ReadCallback* cb) { - std::move(*cb).Run( - true, 0, - CreateMockResponse(mock_connection.connection_channel_id(), - kMockVersionResponseSuffix)); + std::move(*cb).Run(true, 0, + CreateMockResponseWithChannelId( + mock_connection.connection_channel_id(), + kU2fMockResponseMessage)); })); // Add device and set mock connection to fake hid manager. @@ -289,13 +293,12 @@ auto& device = u2f_devices.front(); TestDeviceCallbackReceiver cb; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(false), - cb.callback()); + device->DeviceTransact(GetMockDeviceRequest(), cb.callback()); cb.WaitForCallback(); const auto& value = cb.value(); ASSERT_TRUE(value); - EXPECT_THAT(*value, testing::ElementsAreArray(GetValidU2fVersionResponse())); + EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData)); } TEST_F(FidoHidDeviceTest, TestKeepAliveMessage) { @@ -337,10 +340,10 @@ kDeviceTimeout - base::TimeDelta::FromMicroseconds(1); scoped_task_environment_.FastForwardBy(almost_time_out); - std::move(*cb).Run( - true, 0, - CreateMockResponse(mock_connection.connection_channel_id(), - kMockVersionResponseSuffix)); + std::move(*cb).Run(true, 0, + CreateMockResponseWithChannelId( + mock_connection.connection_channel_id(), + kU2fMockResponseMessage)); })); // Add device and set mock connection to fake hid manager. @@ -359,12 +362,11 @@ // Keep alive message handling is only supported for CTAP HID device. device->set_supported_protocol(ProtocolVersion::kCtap); TestDeviceCallbackReceiver cb; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(false), - cb.callback()); + device->DeviceTransact(GetMockDeviceRequest(), cb.callback()); cb.WaitForCallback(); const auto& value = cb.value(); ASSERT_TRUE(value); - EXPECT_THAT(*value, testing::ElementsAreArray(GetValidU2fVersionResponse())); + EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData)); } TEST_F(FidoHidDeviceTest, TestDeviceTimeoutAfterKeepAliveMessage) { @@ -403,10 +405,10 @@ // is invoked only after 3 seconds, which should cause device to timeout. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) { scoped_task_environment_.FastForwardBy(kDeviceTimeout); - std::move(*cb).Run( - true, 0, - CreateMockResponse(mock_connection.connection_channel_id(), - kMockVersionResponseSuffix)); + std::move(*cb).Run(true, 0, + CreateMockResponseWithChannelId( + mock_connection.connection_channel_id(), + kU2fMockResponseMessage)); })); // Add device and set mock connection to fake hid manager. @@ -425,8 +427,7 @@ // Keep alive message handling is only supported for CTAP HID device. device->set_supported_protocol(ProtocolVersion::kCtap); TestDeviceCallbackReceiver cb; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(false), - cb.callback()); + device->DeviceTransact(GetMockDeviceRequest(), cb.callback()); cb.WaitForCallback(); const auto& value = cb.value(); EXPECT_FALSE(value); @@ -467,10 +468,10 @@ auto delay = base::TimeDelta::FromSeconds(2); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, - base::BindOnce( - std::move(*cb), true, 0, - CreateMockResponse(mock_connection.connection_channel_id(), - kMockVersionResponseSuffix)), + base::BindOnce(std::move(*cb), true, 0, + CreateMockResponseWithChannelId( + mock_connection.connection_channel_id(), + kU2fMockResponseMessage)), delay); })); @@ -490,8 +491,7 @@ // Keep alive message handling is only supported for CTAP HID device. device->set_supported_protocol(ProtocolVersion::kCtap); TestDeviceCallbackReceiver cb; - device->DeviceTransact(U2fRequest::GetU2fVersionApduCommand(false), - cb.callback()); + device->DeviceTransact(GetMockDeviceRequest(), cb.callback()); device->Cancel(); scoped_task_environment_.FastForwardUntilNoTasksRemain(); }
diff --git a/device/fido/mock_fido_device.cc b/device/fido/mock_fido_device.cc index 0be8b05..42d61e8 100644 --- a/device/fido/mock_fido_device.cc +++ b/device/fido/mock_fido_device.cc
@@ -74,16 +74,6 @@ } // static -void MockFidoDevice::NoErrorVersion(const std::vector<uint8_t>& command, - DeviceCallback& cb) { - std::move(cb).Run( - apdu::ApduResponse(std::vector<uint8_t>(kU2fVersionResponse.cbegin(), - kU2fVersionResponse.cend()), - apdu::ApduResponse::Status::SW_NO_ERROR) - .GetEncodedResponse()); -} - -// static void MockFidoDevice::SignWithCorruptedResponse( const std::vector<uint8_t>& command, DeviceCallback& cb) {
diff --git a/device/fido/mock_fido_device.h b/device/fido/mock_fido_device.h index 9e17e2e2..e8afa2c 100644 --- a/device/fido/mock_fido_device.h +++ b/device/fido/mock_fido_device.h
@@ -51,8 +51,6 @@ DeviceCallback& cb); static void NoErrorRegister(const std::vector<uint8_t>& command, DeviceCallback& cb); - static void NoErrorVersion(const std::vector<uint8_t>& command, - DeviceCallback& cb); static void SignWithCorruptedResponse(const std::vector<uint8_t>& command, DeviceCallback& cb); static void WinkDoNothing(WinkCallback& cb);
diff --git a/device/fido/u2f_request.cc b/device/fido/u2f_request.cc index 8434b48..d44d15b 100644 --- a/device/fido/u2f_request.cc +++ b/device/fido/u2f_request.cc
@@ -13,7 +13,6 @@ #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "components/apdu/apdu_command.h" -#include "components/apdu/apdu_response.h" #include "device/fido/u2f_command_constructor.h" #include "services/service_manager/public/cpp/connector.h" @@ -62,24 +61,6 @@ return command.GetEncodedCommand(); } -// static -std::vector<uint8_t> U2fRequest::GetU2fVersionApduCommand( - bool is_legacy_version) { - apdu::ApduCommand command; - command.set_ins(base::strict_cast<uint8_t>(U2fApduInstruction::kVersion)); - // Set maximum expected response length to maximum length possible. - command.set_response_length(kU2fMaxResponseSize); - // Early U2F drafts defined the U2F version command a format - // incompatible with ISO 7816-4, so 2 additional 0x0 bytes are necessary. - // https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html#implementation-considerations - auto version_cmd = command.GetEncodedCommand(); - if (is_legacy_version) - version_cmd.insert(version_cmd.end(), kLegacyVersionSuffix.cbegin(), - kLegacyVersionSuffix.cend()); - - return version_cmd; -} - base::Optional<std::vector<uint8_t>> U2fRequest::GetU2fSignApduCommand( const std::vector<uint8_t>& application_parameter, const std::vector<uint8_t>& key_handle, @@ -126,31 +107,6 @@ current_device_->DeviceTransact(std::move(*cmd), std::move(callback)); } -void U2fRequest::OnDeviceVersionRequest( - VersionCallback callback, - base::WeakPtr<FidoDevice> device, - bool legacy, - base::Optional<std::vector<uint8_t>> response) { - const auto apdu_response = - response ? apdu::ApduResponse::CreateFromMessage(std::move(*response)) - : base::nullopt; - if (apdu_response && - apdu_response->status() == apdu::ApduResponse::Status::SW_NO_ERROR && - std::equal(apdu_response->data().cbegin(), apdu_response->data().cend(), - kU2fVersionResponse.cbegin(), kU2fVersionResponse.cend())) { - std::move(callback).Run(ProtocolVersion::kU2f); - } else if (!legacy) { - // Standard GetVersion failed, attempt legacy GetVersion command. - device->DeviceTransact( - GetU2fVersionApduCommand(true), - base::BindOnce(&U2fRequest::OnDeviceVersionRequest, - weak_factory_.GetWeakPtr(), std::move(callback), device, - true /* legacy */)); - } else { - std::move(callback).Run(ProtocolVersion::kUnknown); - } -} - void U2fRequest::AbandonCurrentDeviceAndTransition() { DCHECK_NE(nullptr, current_device_); abandoned_devices_.emplace_back(std::exchange(current_device_, nullptr));
diff --git a/device/fido/u2f_request.h b/device/fido/u2f_request.h index 2ed0157..f3ae615 100644 --- a/device/fido/u2f_request.h +++ b/device/fido/u2f_request.h
@@ -54,10 +54,6 @@ // // Returns bogus register command to be used to verify user presence. static std::vector<uint8_t> GetBogusRegisterCommand(); - // Returns APDU formatted U2F version request command. If |is_legacy_version| - // is set to true, suffix {0x00, 0x00} is added at the end. - static std::vector<uint8_t> GetU2fVersionApduCommand( - bool is_legacy_version = false); // Returns APDU U2F request commands. Null optional is returned for // incorrectly formatted parameter. base::Optional<std::vector<uint8_t>> GetU2fSignApduCommand( @@ -83,12 +79,6 @@ // |current_device_|. void InitiateDeviceTransaction(base::Optional<std::vector<uint8_t>> cmd, FidoDevice::DeviceCallback callback); - // Callback function to U2F version request. If non-legacy version request - // fails, retry with legacy version request. - void OnDeviceVersionRequest(VersionCallback callback, - base::WeakPtr<FidoDevice> device, - bool legacy, - base::Optional<std::vector<uint8_t>> response); virtual void TryDevice() = 0;
diff --git a/device/fido/u2f_request_unittest.cc b/device/fido/u2f_request_unittest.cc index 404c459..33124f01 100644 --- a/device/fido/u2f_request_unittest.cc +++ b/device/fido/u2f_request_unittest.cc
@@ -361,47 +361,4 @@ } } -TEST_F(U2fRequestTest, TestEncodeVersionRequest) { - constexpr uint8_t kEncodedU2fVersionRequest[] = {0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00}; - EXPECT_THAT(U2fRequest::GetU2fVersionApduCommand(), - ::testing::ElementsAreArray(kEncodedU2fVersionRequest)); - - // Legacy version command contains 2 extra null bytes compared to ISO 7816-4 - // format. - constexpr uint8_t kEncodedU2fLegacyVersionRequest[] = { - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - EXPECT_THAT(U2fRequest::GetU2fVersionApduCommand(true), - ::testing::ElementsAreArray(kEncodedU2fLegacyVersionRequest)); -} - -// Test a scenario when version request is sent to legacy U2F token. -// After non-legacy version requests fails, legacy version request should be -// sent to device as a retry. -TEST_F(U2fRequestTest, TestLegacyVersionRequest) { - auto* discovery = discovery_factory().ForgeNextHidDiscovery(); - FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice}); - request.Start(); - - auto device0 = std::make_unique<MockFidoDevice>(); - EXPECT_CALL(*device0, GetId()).WillRepeatedly(::testing::Return("device0")); - EXPECT_CALL(*device0, - DeviceTransactPtr(U2fRequest::GetU2fVersionApduCommand(true), _)) - // Success response for legacy version request after retry. - .WillOnce(testing::Invoke(MockFidoDevice::NoErrorVersion)); - - auto* device_ptr = device0.get(); - discovery->AddDevice(std::move(device0)); - - // Represents version callback received from legacy U2F token on initial - // version request. Device responses with invalid protocol version (in this - // case, empty byte array). Retry version request with legacy bit is expected - // to be issued afterwards. - request.OnDeviceVersionRequest(version_callback_receiver().callback(), - device_ptr->GetWeakPtr(), false /* legacy */, - std::vector<uint8_t>()); - - EXPECT_EQ(ProtocolVersion::kU2f, version_callback_receiver().value()); -} - } // namespace device
diff --git a/device/fido/virtual_fido_device.h b/device/fido/virtual_fido_device.h index e022df1..40fdc9aa 100644 --- a/device/fido/virtual_fido_device.h +++ b/device/fido/virtual_fido_device.h
@@ -70,6 +70,10 @@ fido_parsing_utils::SpanLess> registrations; + // If set, this callback is called whenever a "press" is required. It allows + // tests to change the state of the world during processing. + base::RepeatingCallback<void(void)> simulate_press_callback; + // Adds a registration for the specified credential ID with the application // parameter set to be valid for the given relying party ID (which would // typically be a domain, e.g. "example.com").
diff --git a/device/fido/virtual_u2f_device.cc b/device/fido/virtual_u2f_device.cc index 4fef78b..9772008 100644 --- a/device/fido/virtual_u2f_device.cc +++ b/device/fido/virtual_u2f_device.cc
@@ -71,6 +71,8 @@ base::Optional<std::vector<uint8_t>> response; switch (parsed_command->ins()) { + // Version request is defined by the U2F spec, but is never used in + // production code. case base::strict_cast<uint8_t>(U2fApduInstruction::kVersion): break; case base::strict_cast<uint8_t>(U2fApduInstruction::kRegister): @@ -104,6 +106,10 @@ return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH); } + if (mutable_state()->simulate_press_callback) { + mutable_state()->simulate_press_callback.Run(); + } + auto challenge_param = data.first(32); auto application_parameter = data.last(32); @@ -185,6 +191,10 @@ return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_DATA); } + if (mutable_state()->simulate_press_callback) { + mutable_state()->simulate_press_callback.Run(); + } + auto challenge_param = data.first(32); auto application_parameter = data.subspan(32, 32); size_t key_handle_length = data[64];
diff --git a/device/usb/mock_usb_device.h b/device/usb/mock_usb_device.h index f03954e..ef09b0a1 100644 --- a/device/usb/mock_usb_device.h +++ b/device/usb/mock_usb_device.h
@@ -44,7 +44,7 @@ const std::string& serial_number, const std::vector<UsbConfigDescriptor>& configurations); - void Open(OpenCallback callback) { OpenInternal(callback); } + void Open(OpenCallback callback) override { OpenInternal(callback); } MOCK_METHOD1(OpenInternal, void(OpenCallback&)); void AddMockConfig(const UsbConfigDescriptor& config);
diff --git a/device/usb/mojo/device_manager_impl_unittest.cc b/device/usb/mojo/device_manager_impl_unittest.cc index edb46481f..ed3ed79 100644 --- a/device/usb/mojo/device_manager_impl_unittest.cc +++ b/device/usb/mojo/device_manager_impl_unittest.cc
@@ -71,7 +71,7 @@ class MockDeviceManagerClient : public mojom::UsbDeviceManagerClient { public: MockDeviceManagerClient() : binding_(this) {} - ~MockDeviceManagerClient() = default; + ~MockDeviceManagerClient() override = default; UsbDeviceManagerClientPtr CreateInterfacePtrAndBind() { UsbDeviceManagerClientPtr client; @@ -80,12 +80,12 @@ } MOCK_METHOD1(DoOnDeviceAdded, void(mojom::UsbDeviceInfo*)); - void OnDeviceAdded(UsbDeviceInfoPtr device_info) { + void OnDeviceAdded(UsbDeviceInfoPtr device_info) override { DoOnDeviceAdded(device_info.get()); } MOCK_METHOD1(DoOnDeviceRemoved, void(mojom::UsbDeviceInfo*)); - void OnDeviceRemoved(UsbDeviceInfoPtr device_info) { + void OnDeviceRemoved(UsbDeviceInfoPtr device_info) override { DoOnDeviceRemoved(device_info.get()); }
diff --git a/docs/origin_trials_integration.md b/docs/origin_trials_integration.md index 47f6f20..5251dfb3 100644 --- a/docs/origin_trials_integration.md +++ b/docs/origin_trials_integration.md
@@ -137,4 +137,4 @@ [OriginTrialEnabled]: /third_party/blink/renderer/bindings/IDLExtendedAttributes.md#_OriginTrialEnabled_i_m_a_c_ [origin_trials/webexposed]: /third_party/WebKit/LayoutTests/http/tests/origin_trials/webexposed/ [runtime\_enabled\_features.json5]: /third_party/blink/renderer/platform/runtime_enabled_features.json5 -[trial_token_unittest.cc]: /content/common/origin_trials/trial_token_unittest.cc +[trial_token_unittest.cc]: /third_party/blink/common/origin_trials/trial_token_unittest.cc
diff --git a/docs/speed/benchmark/benchmark_ownership.md b/docs/speed/benchmark/benchmark_ownership.md index 1df0fa1..f94ebfe6 100644 --- a/docs/speed/benchmark/benchmark_ownership.md +++ b/docs/speed/benchmark/benchmark_ownership.md
@@ -12,7 +12,7 @@ ## How do I update the owner on a benchmark? ### Telemetry Benchmarks -1. Open [`src/tools/perf/benchmarks/benchmark_name.py`](https://cs.chromium.org/chromium/src/tools/perf/benchmarks), where `benchmark_name` is the part of the benchmark before the “.”, like `smoothness` in `smoothness.top_25_smooth`. +1. Open [`src/tools/perf/benchmarks/benchmark_name.py`](https://cs.chromium.org/chromium/src/tools/perf/benchmarks/), where `benchmark_name` is the part of the benchmark before the “.”, like `smoothness` in `smoothness.top_25_smooth`. 1. Find the class for the benchmark. It has a `Name` method that should match the full name of the benchmark. 1. Add a `benchmark.Owner` decorator above the class.
diff --git a/extensions/browser/api/idle/idle_api_unittest.cc b/extensions/browser/api/idle/idle_api_unittest.cc index 3b17496..72cc783 100644 --- a/extensions/browser/api/idle/idle_api_unittest.cc +++ b/extensions/browser/api/idle/idle_api_unittest.cc
@@ -32,10 +32,10 @@ class MockEventDelegate : public IdleManager::EventDelegate { public: MockEventDelegate() {} - virtual ~MockEventDelegate() {} + ~MockEventDelegate() override {} MOCK_METHOD2(OnStateChanged, void(const std::string&, ui::IdleState)); - virtual void RegisterObserver(EventRouter::Observer* observer) {} - virtual void UnregisterObserver(EventRouter::Observer* observer) {} + void RegisterObserver(EventRouter::Observer* observer) override {} + void UnregisterObserver(EventRouter::Observer* observer) override {} }; class TestIdleProvider : public IdleManager::IdleTimeProvider {
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc b/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc index 6857415..2cc3089 100644 --- a/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc +++ b/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc
@@ -189,40 +189,44 @@ *device_policy_onc, base::DictionaryValue()); } + void SetDeviceProperty(const std::string& device_path, + const std::string& name, + const base::Value& value) { + device_test_->SetDeviceProperty(device_path, name, value, + /*notify_changed=*/false); + } + void SetUpCellular() { // Add a Cellular GSM Device. device_test_->AddDevice(kCellularDevicePath, shill::kTypeCellular, "stub_cellular_device1"); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kCarrierProperty, - base::Value("Cellular1_Carrier")); + SetDeviceProperty(kCellularDevicePath, shill::kCarrierProperty, + base::Value("Cellular1_Carrier")); base::DictionaryValue home_provider; home_provider.SetString("name", "Cellular1_Provider"); home_provider.SetString("code", "000000"); home_provider.SetString("country", "us"); - device_test_->SetDeviceProperty( - kCellularDevicePath, shill::kHomeProviderProperty, home_provider); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kTechnologyFamilyProperty, - base::Value(shill::kNetworkTechnologyGsm)); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMeidProperty, - base::Value("test_meid")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kImeiProperty, - base::Value("test_imei")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kIccidProperty, - base::Value("test_iccid")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kImsiProperty, - base::Value("test_imsi")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kEsnProperty, - base::Value("test_esn")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMdnProperty, - base::Value("test_mdn")); - device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMinProperty, - base::Value("test_min")); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kModelIdProperty, - base::Value("test_model_id")); + SetDeviceProperty(kCellularDevicePath, shill::kHomeProviderProperty, + home_provider); + SetDeviceProperty(kCellularDevicePath, shill::kTechnologyFamilyProperty, + base::Value(shill::kNetworkTechnologyGsm)); + SetDeviceProperty(kCellularDevicePath, shill::kMeidProperty, + base::Value("test_meid")); + SetDeviceProperty(kCellularDevicePath, shill::kImeiProperty, + base::Value("test_imei")); + SetDeviceProperty(kCellularDevicePath, shill::kIccidProperty, + base::Value("test_iccid")); + SetDeviceProperty(kCellularDevicePath, shill::kImsiProperty, + base::Value("test_imsi")); + SetDeviceProperty(kCellularDevicePath, shill::kEsnProperty, + base::Value("test_esn")); + SetDeviceProperty(kCellularDevicePath, shill::kMdnProperty, + base::Value("test_mdn")); + SetDeviceProperty(kCellularDevicePath, shill::kMinProperty, + base::Value("test_min")); + SetDeviceProperty(kCellularDevicePath, shill::kModelIdProperty, + base::Value("test_model_id")); std::unique_ptr<base::DictionaryValue> apn = DictionaryBuilder() .Set(shill::kApnProperty, "test-apn") @@ -231,8 +235,8 @@ .Build(); std::unique_ptr<base::ListValue> apn_list = ListBuilder().Append(apn->CreateDeepCopy()).Build(); - device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kCellularApnListProperty, *apn_list); + SetDeviceProperty(kCellularDevicePath, shill::kCellularApnListProperty, + *apn_list); service_test_->AddService(kCellularServicePath, kCellularGuid, kCellularName, shill::kTypeCellular,
diff --git a/extensions/browser/api/printer_provider/printer_provider_apitest.cc b/extensions/browser/api/printer_provider/printer_provider_apitest.cc index 2b5d573..eccef57 100644 --- a/extensions/browser/api/printer_provider/printer_provider_apitest.cc +++ b/extensions/browser/api/printer_provider/printer_provider_apitest.cc
@@ -8,10 +8,6 @@ #include <vector> #include "base/bind.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" #include "base/json/json_string_value_serializer.h" #include "base/macros.h" #include "base/memory/ref_counted_memory.h" @@ -95,16 +91,11 @@ public: enum PrintRequestDataType { PRINT_REQUEST_DATA_TYPE_NOT_SET, - PRINT_REQUEST_DATA_TYPE_FILE, - PRINT_REQUEST_DATA_TYPE_FILE_DELETED, PRINT_REQUEST_DATA_TYPE_BYTES }; PrinterProviderApiTest() {} - ~PrinterProviderApiTest() override { - base::ScopedAllowBlockingForTesting allow_blocking; - ignore_result(data_dir_.Delete()); - } + ~PrinterProviderApiTest() override = default; void StartGetPrintersRequest( const PrinterProviderAPI::GetPrintersCallback& callback) { @@ -152,29 +143,6 @@ ->DispatchPrintRequested(job, std::move(callback)); } - bool StartPrintRequestUsingFileInfo( - const std::string& extension_id, - PrinterProviderAPI::PrintCallback callback) { - PrinterProviderPrintJob job; - - const char kBytes[] = {'b', 'y', 't', 'e', 's'}; - if (!CreateTempFileWithContents(kBytes, static_cast<int>(arraysize(kBytes)), - &job.document_path, &job.file_info)) { - ADD_FAILURE() << "Failed to create test file."; - return false; - } - - job.printer_id = extension_id + ":printer_id"; - job.job_title = base::ASCIIToUTF16("Print job"); - job.ticket_json = "{}"; - job.content_type = "image/pwg-raster"; - - PrinterProviderAPIFactory::GetInstance() - ->GetForBrowserContext(browser_context()) - ->DispatchPrintRequested(job, std::move(callback)); - return true; - } - void StartCapabilityRequest( const std::string& extension_id, PrinterProviderAPI::GetCapabilityCallback callback) { @@ -240,17 +208,6 @@ case PRINT_REQUEST_DATA_TYPE_NOT_SET: StartPrintRequestWithNoData(extension_id, std::move(callback)); break; - case PRINT_REQUEST_DATA_TYPE_FILE: - ASSERT_TRUE( - StartPrintRequestUsingFileInfo(extension_id, std::move(callback))); - break; - case PRINT_REQUEST_DATA_TYPE_FILE_DELETED: { - ASSERT_TRUE( - StartPrintRequestUsingFileInfo(extension_id, std::move(callback))); - base::ScopedAllowBlockingForTesting allow_blocking; - ASSERT_TRUE(data_dir_.Delete()); - break; - } case PRINT_REQUEST_DATA_TYPE_BYTES: StartPrintRequestUsingDocumentBytes(extension_id, std::move(callback)); break; @@ -352,27 +309,6 @@ device::MockUsbService usb_service_; private: - // Initializes |data_dir_| if needed and creates a file in it containing - // provided data. - bool CreateTempFileWithContents(const char* data, - int size, - base::FilePath* path, - base::File::Info* file_info) { - base::ScopedAllowBlockingForTesting allow_blocking; - if (!data_dir_.IsValid() && !data_dir_.CreateUniqueTempDir()) - return false; - - *path = data_dir_.GetPath().AppendASCII("data.pwg"); - int written = base::WriteFile(*path, data, size); - if (written != size) - return false; - if (!base::GetFileInfo(*path, file_info)) - return false; - return true; - } - - base::ScopedTempDir data_dir_; - DISALLOW_COPY_AND_ASSIGN(PrinterProviderApiTest); }; @@ -386,16 +322,6 @@ RunPrintRequestTestApp("OK", PRINT_REQUEST_DATA_TYPE_BYTES, "OK"); } -IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobWithFileSuccess) { - RunPrintRequestTestApp("OK", PRINT_REQUEST_DATA_TYPE_FILE, "OK"); -} - -IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, - PrintJobWithFile_FileDeletedBeforeDispatch) { - RunPrintRequestTestApp("OK", PRINT_REQUEST_DATA_TYPE_FILE_DELETED, - "INVALID_DATA"); -} - IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobAsyncSuccess) { RunPrintRequestTestApp("ASYNC_RESPONSE", PRINT_REQUEST_DATA_TYPE_BYTES, "OK"); }
diff --git a/extensions/browser/api/printer_provider/printer_provider_print_job.h b/extensions/browser/api/printer_provider/printer_provider_print_job.h index a9f4b4b5..682e150 100644 --- a/extensions/browser/api/printer_provider/printer_provider_print_job.h +++ b/extensions/browser/api/printer_provider/printer_provider_print_job.h
@@ -7,8 +7,6 @@ #include <string> -#include "base/files/file.h" -#include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/ref_counted_memory.h" #include "base/strings/string16.h" @@ -17,9 +15,7 @@ // Struct describing print job that should be forwarded to an extension via // chrome.printerProvider.onPrintRequested event. -// TODO(tbarzic): This should probably be a class and have some methods, e.g. -// whether the job is initialized and whether the data is described using a file -// or bytes. +// TODO(tbarzic): This should probably be a class and have some methods. struct PrinterProviderPrintJob { PrinterProviderPrintJob(); PrinterProviderPrintJob(const PrinterProviderPrintJob& other); @@ -41,17 +37,8 @@ // Content type of the document that should be printed. std::string content_type; - // The document data that should be printed. Should be NULL if document data - // is kept in a file. + // The document data that should be printed. scoped_refptr<base::RefCountedMemory> document_bytes; - - // Path of the file which contains data to be printed. Should be set only if - // |document_bytes| are NULL. - base::FilePath document_path; - - // Information about the file which contains data to be printed. Should be - // set only if |document_path| is set. - base::File::Info file_info; }; } // namespace extensions
diff --git a/extensions/browser/api/printer_provider_internal/printer_provider_internal_api.cc b/extensions/browser/api/printer_provider_internal/printer_provider_internal_api.cc index 220f9d8..3400e9d70 100644 --- a/extensions/browser/api/printer_provider_internal/printer_provider_internal_api.cc +++ b/extensions/browser/api/printer_provider_internal/printer_provider_internal_api.cc
@@ -194,25 +194,17 @@ if (!job) return RespondNow(Error("Print request not found.")); - if (job->document_bytes.get()) { - // |job->document_bytes| are passed to the callback to make sure the ref - // counted memory does not go away before the memory backed blob is created. - content::BrowserContext::CreateMemoryBackedBlob( - browser_context(), job->document_bytes->front_as<char>(), - job->document_bytes->size(), "", - base::Bind(&PrinterProviderInternalGetPrintDataFunction::OnBlob, this, - job->content_type, job->document_bytes->size(), - job->document_bytes)); - } else if (!job->document_path.empty()) { - content::BrowserContext::CreateFileBackedBlob( - browser_context(), job->document_path, 0 /* offset */, - job->file_info.size, job->file_info.last_modified, - base::Bind(&PrinterProviderInternalGetPrintDataFunction::OnBlob, this, - job->content_type, job->file_info.size, - scoped_refptr<base::RefCountedMemory>())); - } else { + if (!job->document_bytes) return RespondNow(Error("Job data not set")); - } + + // |job->document_bytes| are passed to the callback to make sure the ref + // counted memory does not go away before the memory backed blob is created. + content::BrowserContext::CreateMemoryBackedBlob( + browser_context(), job->document_bytes->front_as<char>(), + job->document_bytes->size(), "", + base::BindOnce(&PrinterProviderInternalGetPrintDataFunction::OnBlob, this, + job->content_type, job->document_bytes->size(), + job->document_bytes)); return RespondLater(); }
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc index 65f1fda..3dc9d8fd 100644 --- a/extensions/browser/api/web_request/web_request_permissions.cc +++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -90,7 +90,9 @@ // https://crbug.com/831812 origin.host() == "sync-confirmation" || // https://crbug.com/831813 - origin.host() == "inspect"; + origin.host() == "inspect" || + // https://crbug.com/837420 + origin.host() == "discards"; } } // namespace
diff --git a/extensions/browser/extension_host.cc b/extensions/browser/extension_host.cc index a5a8218..1ef809a 100644 --- a/extensions/browser/extension_host.cc +++ b/extensions/browser/extension_host.cc
@@ -417,8 +417,10 @@ } } - delegate_->CreateTab( - new_contents, extension_id_, disposition, initial_rect, user_gesture); + // TODO(erikchen): Refactor AddNewContents to take strong ownership semantics. + // https://crbug.com/832879. + delegate_->CreateTab(base::WrapUnique(new_contents), extension_id_, + disposition, initial_rect, user_gesture); } void ExtensionHost::RenderViewReady() {
diff --git a/extensions/browser/extension_host_delegate.h b/extensions/browser/extension_host_delegate.h index a7c908c..c7b0096 100644 --- a/extensions/browser/extension_host_delegate.h +++ b/extensions/browser/extension_host_delegate.h
@@ -45,7 +45,7 @@ // Creates a new tab or popup window with |web_contents|. The embedder may // choose to do nothing if tabs and popups are not supported. - virtual void CreateTab(content::WebContents* web_contents, + virtual void CreateTab(std::unique_ptr<content::WebContents> web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect,
diff --git a/extensions/browser/extension_pref_value_map_unittest.cc b/extensions/browser/extension_pref_value_map_unittest.cc index 36c2fb93..5c0c728 100644 --- a/extensions/browser/extension_pref_value_map_unittest.cc +++ b/extensions/browser/extension_pref_value_map_unittest.cc
@@ -76,7 +76,7 @@ : public ExtensionPrefValueMap::Observer { public: ExtensionPrefValueMapObserverMock() {} - virtual ~ExtensionPrefValueMapObserverMock() {} + ~ExtensionPrefValueMapObserverMock() override {} MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); MOCK_METHOD0(OnInitializationCompleted, void());
diff --git a/extensions/browser/updater/update_data_provider.cc b/extensions/browser/updater/update_data_provider.cc index 8c0efc3..baf81a3 100644 --- a/extensions/browser/updater/update_data_provider.cc +++ b/extensions/browser/updater/update_data_provider.cc
@@ -4,6 +4,8 @@ #include "extensions/browser/updater/update_data_provider.h" +#include <utility> + #include "base/base64.h" #include "base/bind.h" #include "base/files/file_path.h" @@ -58,12 +60,12 @@ browser_context_ = nullptr; } -void UpdateDataProvider::GetData( - const ExtensionUpdateDataMap& update_info, - const std::vector<std::string>& ids, - std::vector<update_client::CrxComponent>* data) { +std::vector<std::unique_ptr<update_client::CrxComponent>> +UpdateDataProvider::GetData(const ExtensionUpdateDataMap& update_info, + const std::vector<std::string>& ids) { + std::vector<std::unique_ptr<update_client::CrxComponent>> data; if (!browser_context_) - return; + return data; const ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); const ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); for (const auto& id : ids) { @@ -72,8 +74,8 @@ continue; DCHECK_GT(update_info.count(id), 0ULL); const ExtensionUpdateData& extension_data = update_info.at(id); - data->push_back(update_client::CrxComponent()); - update_client::CrxComponent* info = &data->back(); + data.push_back(std::make_unique<update_client::CrxComponent>()); + update_client::CrxComponent* info = data.back().get(); std::string pubkey_bytes; base::Base64Decode(extension->public_key(), &pubkey_bytes); info->pk_hash.resize(crypto::kSHA256Length, 0); @@ -105,6 +107,7 @@ info->install_location = ManifestFetchData::GetSimpleLocationString(extension->location()); } + return data; } void UpdateDataProvider::RunInstallCallback(
diff --git a/extensions/browser/updater/update_data_provider.h b/extensions/browser/updater/update_data_provider.h index 83b04bf..61a212b 100644 --- a/extensions/browser/updater/update_data_provider.h +++ b/extensions/browser/updater/update_data_provider.h
@@ -6,6 +6,7 @@ #define EXTENSIONS_BROWSER_UPDATER_UPDATE_DATA_PROVIDER_H_ #include <map> +#include <memory> #include <string> #include <vector> @@ -47,9 +48,9 @@ void Shutdown(); // Matches update_client::UpdateClient::CrxDataCallback - void GetData(const ExtensionUpdateDataMap& update_info, - const std::vector<std::string>& ids, - std::vector<update_client::CrxComponent>* data); + std::vector<std::unique_ptr<update_client::CrxComponent>> GetData( + const ExtensionUpdateDataMap& update_info, + const std::vector<std::string>& ids); private: friend class base::RefCounted<UpdateDataProvider>;
diff --git a/extensions/browser/updater/update_data_provider_unittest.cc b/extensions/browser/updater/update_data_provider_unittest.cc index 2a1910e..39b9237 100644 --- a/extensions/browser/updater/update_data_provider_unittest.cc +++ b/extensions/browser/updater/update_data_provider_unittest.cc
@@ -138,9 +138,8 @@ scoped_refptr<UpdateDataProvider> data_provider = base::MakeRefCounted<UpdateDataProvider>(nullptr); - std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(ExtensionUpdateDataMap(), ids, &data); + const auto data = + data_provider->GetData(ExtensionUpdateDataMap(), {kExtensionId1}); EXPECT_EQ(0UL, data.size()); } @@ -156,14 +155,13 @@ update_data[kExtensionId1] = {}; std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = data_provider->GetData(update_data, ids); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ(version, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - EXPECT_EQ(0UL, data[0].disabled_reasons.size()); - EXPECT_EQ("internal", data[0].install_location); + EXPECT_EQ(version, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + EXPECT_EQ(0UL, data[0]->disabled_reasons.size()); + EXPECT_EQ("internal", data[0]->install_location); } TEST_F(UpdateDataProviderTest, GetData_EnabledExtensionWithData) { @@ -180,16 +178,14 @@ info.is_corrupt_reinstall = true; info.install_source = "webstore"; - std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = data_provider->GetData(update_data, {kExtensionId1}); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ("0.0.0.0", data[0].version.GetString()); - EXPECT_EQ("webstore", data[0].install_source); - EXPECT_EQ("external", data[0].install_location); - EXPECT_NE(nullptr, data[0].installer.get()); - EXPECT_EQ(0UL, data[0].disabled_reasons.size()); + EXPECT_EQ("0.0.0.0", data[0]->version.GetString()); + EXPECT_EQ("webstore", data[0]->install_source); + EXPECT_EQ("external", data[0]->install_location); + EXPECT_NE(nullptr, data[0]->installer.get()); + EXPECT_EQ(0UL, data[0]->disabled_reasons.size()); } TEST_F(UpdateDataProviderTest, GetData_DisabledExtension_WithNoReason) { @@ -204,17 +200,15 @@ ExtensionUpdateDataMap update_data; update_data[kExtensionId1] = {}; - std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = data_provider->GetData(update_data, {kExtensionId1}); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ(version, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - ASSERT_EQ(1UL, data[0].disabled_reasons.size()); + EXPECT_EQ(version, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + ASSERT_EQ(1UL, data[0]->disabled_reasons.size()); EXPECT_EQ(disable_reason::DisableReason::DISABLE_NONE, - data[0].disabled_reasons[0]); - EXPECT_EQ("external", data[0].install_location); + data[0]->disabled_reasons[0]); + EXPECT_EQ("external", data[0]->install_location); } TEST_F(UpdateDataProviderTest, GetData_DisabledExtension_UnknownReason) { @@ -229,17 +223,15 @@ ExtensionUpdateDataMap update_data; update_data[kExtensionId1] = {}; - std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = data_provider->GetData(update_data, {kExtensionId1}); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ(version, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - ASSERT_EQ(1UL, data[0].disabled_reasons.size()); + EXPECT_EQ(version, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + ASSERT_EQ(1UL, data[0]->disabled_reasons.size()); EXPECT_EQ(disable_reason::DisableReason::DISABLE_NONE, - data[0].disabled_reasons[0]); - EXPECT_EQ("other", data[0].install_location); + data[0]->disabled_reasons[0]); + EXPECT_EQ("other", data[0]->install_location); } TEST_F(UpdateDataProviderTest, GetData_DisabledExtension_WithReasons) { @@ -255,19 +247,17 @@ ExtensionUpdateDataMap update_data; update_data[kExtensionId1] = {}; - std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = data_provider->GetData(update_data, {kExtensionId1}); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ(version, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - ASSERT_EQ(2UL, data[0].disabled_reasons.size()); + EXPECT_EQ(version, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + ASSERT_EQ(2UL, data[0]->disabled_reasons.size()); EXPECT_EQ(disable_reason::DisableReason::DISABLE_USER_ACTION, - data[0].disabled_reasons[0]); + data[0]->disabled_reasons[0]); EXPECT_EQ(disable_reason::DisableReason::DISABLE_CORRUPTED, - data[0].disabled_reasons[1]); - EXPECT_EQ("policy", data[0].install_location); + data[0]->disabled_reasons[1]); + EXPECT_EQ("policy", data[0]->install_location); } TEST_F(UpdateDataProviderTest, @@ -285,21 +275,19 @@ ExtensionUpdateDataMap update_data; update_data[kExtensionId1] = {}; - std::vector<std::string> ids({kExtensionId1}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = data_provider->GetData(update_data, {kExtensionId1}); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ(version, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - ASSERT_EQ(3UL, data[0].disabled_reasons.size()); + EXPECT_EQ(version, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + ASSERT_EQ(3UL, data[0]->disabled_reasons.size()); EXPECT_EQ(disable_reason::DisableReason::DISABLE_NONE, - data[0].disabled_reasons[0]); + data[0]->disabled_reasons[0]); EXPECT_EQ(disable_reason::DisableReason::DISABLE_USER_ACTION, - data[0].disabled_reasons[1]); + data[0]->disabled_reasons[1]); EXPECT_EQ(disable_reason::DisableReason::DISABLE_CORRUPTED, - data[0].disabled_reasons[2]); - EXPECT_EQ("external", data[0].install_location); + data[0]->disabled_reasons[2]); + EXPECT_EQ("external", data[0]->install_location); } TEST_F(UpdateDataProviderTest, GetData_MultipleExtensions) { @@ -319,19 +307,18 @@ update_data[kExtensionId1] = {}; update_data[kExtensionId2] = {}; - std::vector<std::string> ids({kExtensionId1, kExtensionId2}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = + data_provider->GetData(update_data, {kExtensionId1, kExtensionId2}); ASSERT_EQ(2UL, data.size()); - EXPECT_EQ(version1, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - EXPECT_EQ(0UL, data[0].disabled_reasons.size()); - EXPECT_EQ("external", data[0].install_location); - EXPECT_EQ(version2, data[1].version.GetString()); - EXPECT_NE(nullptr, data[1].installer.get()); - EXPECT_EQ(0UL, data[1].disabled_reasons.size()); - EXPECT_EQ("other", data[1].install_location); + EXPECT_EQ(version1, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + EXPECT_EQ(0UL, data[0]->disabled_reasons.size()); + EXPECT_EQ("external", data[0]->install_location); + EXPECT_EQ(version2, data[1]->version.GetString()); + EXPECT_NE(nullptr, data[1]->installer.get()); + EXPECT_EQ(0UL, data[1]->disabled_reasons.size()); + EXPECT_EQ("other", data[1]->install_location); } TEST_F(UpdateDataProviderTest, GetData_MultipleExtensions_DisabledExtension) { @@ -352,22 +339,21 @@ update_data[kExtensionId1] = {}; update_data[kExtensionId2] = {}; - std::vector<std::string> ids({kExtensionId1, kExtensionId2}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = + data_provider->GetData(update_data, {kExtensionId1, kExtensionId2}); ASSERT_EQ(2UL, data.size()); - EXPECT_EQ(version1, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - ASSERT_EQ(1UL, data[0].disabled_reasons.size()); + EXPECT_EQ(version1, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + ASSERT_EQ(1UL, data[0]->disabled_reasons.size()); EXPECT_EQ(disable_reason::DisableReason::DISABLE_CORRUPTED, - data[0].disabled_reasons[0]); - EXPECT_EQ("internal", data[0].install_location); + data[0]->disabled_reasons[0]); + EXPECT_EQ("internal", data[0]->install_location); - EXPECT_EQ(version2, data[1].version.GetString()); - EXPECT_NE(nullptr, data[1].installer.get()); - EXPECT_EQ(0UL, data[1].disabled_reasons.size()); - EXPECT_EQ("external", data[1].install_location); + EXPECT_EQ(version2, data[1]->version.GetString()); + EXPECT_NE(nullptr, data[1]->installer.get()); + EXPECT_EQ(0UL, data[1]->disabled_reasons.size()); + EXPECT_EQ("external", data[1]->install_location); } TEST_F(UpdateDataProviderTest, @@ -385,15 +371,14 @@ update_data[kExtensionId1] = {}; update_data[kExtensionId2] = {}; - std::vector<std::string> ids({kExtensionId1, kExtensionId2}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = + data_provider->GetData(update_data, {kExtensionId1, kExtensionId2}); ASSERT_EQ(1UL, data.size()); - EXPECT_EQ(version, data[0].version.GetString()); - EXPECT_NE(nullptr, data[0].installer.get()); - EXPECT_EQ(0UL, data[0].disabled_reasons.size()); - EXPECT_EQ("other", data[0].install_location); + EXPECT_EQ(version, data[0]->version.GetString()); + EXPECT_NE(nullptr, data[0]->installer.get()); + EXPECT_EQ(0UL, data[0]->disabled_reasons.size()); + EXPECT_EQ("other", data[0]->install_location); } TEST_F(UpdateDataProviderTest, GetData_MultipleExtensions_CorruptExtension) { @@ -420,21 +405,20 @@ info2.is_corrupt_reinstall = true; info2.install_source = "sideload"; - std::vector<std::string> ids({kExtensionId1, kExtensionId2}); - std::vector<update_client::CrxComponent> data; - data_provider->GetData(update_data, ids, &data); + const auto data = + data_provider->GetData(update_data, {kExtensionId1, kExtensionId2}); ASSERT_EQ(2UL, data.size()); - EXPECT_EQ(version1, data[0].version.GetString()); - EXPECT_EQ("webstore", data[0].install_source); - EXPECT_EQ("other", data[0].install_location); - EXPECT_NE(nullptr, data[0].installer.get()); - EXPECT_EQ(0UL, data[0].disabled_reasons.size()); - EXPECT_EQ(initial_version, data[1].version.GetString()); - EXPECT_EQ("sideload", data[1].install_source); - EXPECT_EQ("policy", data[1].install_location); - EXPECT_NE(nullptr, data[1].installer.get()); - EXPECT_EQ(0UL, data[1].disabled_reasons.size()); + EXPECT_EQ(version1, data[0]->version.GetString()); + EXPECT_EQ("webstore", data[0]->install_source); + EXPECT_EQ("other", data[0]->install_location); + EXPECT_NE(nullptr, data[0]->installer.get()); + EXPECT_EQ(0UL, data[0]->disabled_reasons.size()); + EXPECT_EQ(initial_version, data[1]->version.GetString()); + EXPECT_EQ("sideload", data[1]->install_source); + EXPECT_EQ("policy", data[1]->install_location); + EXPECT_NE(nullptr, data[1]->installer.get()); + EXPECT_EQ(0UL, data[1]->disabled_reasons.size()); } } // namespace
diff --git a/extensions/browser/updater/update_service_unittest.cc b/extensions/browser/updater/update_service_unittest.cc index ad80d16..5692007 100644 --- a/extensions/browser/updater/update_service_unittest.cc +++ b/extensions/browser/updater/update_service_unittest.cc
@@ -48,7 +48,9 @@ // Returns the data we've gotten from the CrxDataCallback for ids passed to // the Update function. - std::vector<update_client::CrxComponent>* data() { return &data_; } + std::vector<std::unique_ptr<update_client::CrxComponent>>* data() { + return &data_; + } // Used for tests that uninstall pings get requested properly. struct UninstallPing { @@ -120,7 +122,7 @@ friend class base::RefCounted<FakeUpdateClient>; ~FakeUpdateClient() override {} - std::vector<update_client::CrxComponent> data_; + std::vector<std::unique_ptr<update_client::CrxComponent>> data_; std::vector<UninstallPing> uninstall_pings_; std::vector<Observer*> observers_; @@ -137,7 +139,7 @@ CrxDataCallback crx_data_callback, bool is_foreground, update_client::Callback callback) { - std::move(crx_data_callback).Run(ids, &data_); + data_ = std::move(crx_data_callback).Run(ids); if (delay_update()) { delayed_requests_.push_back({ids, std::move(callback)}); @@ -329,12 +331,12 @@ update_check_params, base::BindOnce([](bool* executed) { *executed = true; }, &executed)); ASSERT_TRUE(executed); - std::vector<update_client::CrxComponent>* data = update_client()->data(); + const auto* data = update_client()->data(); ASSERT_NE(nullptr, data); ASSERT_EQ(1u, data->size()); - ASSERT_EQ(data->at(0).version, extension1->version()); - update_client::CrxInstaller* installer = data->at(0).installer.get(); + ASSERT_EQ(data->at(0)->version, extension1->version()); + update_client::CrxInstaller* installer = data->at(0)->installer.get(); ASSERT_NE(installer, nullptr); // The GetInstalledFile method is used when processing differential updates
diff --git a/extensions/common/api/system_display.idl b/extensions/common/api/system_display.idl index 396c33e..56f5552c 100644 --- a/extensions/common/api/system_display.idl +++ b/extensions/common/api/system_display.idl
@@ -263,7 +263,7 @@ DisplayMode? displayMode; // If set, updates the zoom associated with the display. This zoom performs - // relayout and repaint thus resulting in a better quality zoom than just + // re-layout and repaint thus resulting in a better quality zoom than just // performing a pixel by pixel stretch enlargement. double? displayZoomFactor; }; @@ -349,9 +349,9 @@ static void setDisplayLayout(DisplayLayout[] layouts, optional SetDisplayLayoutCallback callback); - // Enables/disables the unified desktop feature. Note that this simply - // enables the feature, but will not change the actual desktop mode. - // (That is, if the desktop is in mirror mode, it will stay in mirror mode) + // Enables/disables the unified desktop feature. If enabled while mirroring + // is active, the desktop mode will not change until mirroring is turned + // off. Otherwise, the desktop mode will switch to unified immediately. // NOTE: This is only available to Chrome OS Kiosk apps and Web UI. // |enabled|: True if unified desktop should be enabled. static void enableUnifiedDesktop(boolean enabled); @@ -363,7 +363,7 @@ static void overscanCalibrationStart(DOMString id); // Adjusts the current overscan insets for a display. Typically this should - // etiher move the display along an axis (e.g. left+right have the same + // either move the display along an axis (e.g. left+right have the same // value) or scale it along an axis (e.g. top+bottom have opposite values). // Each Adjust call is cumulative with previous calls since Start. // |id|: The display's unique identifier. @@ -383,7 +383,7 @@ // Displays the native touch calibration UX for the display with |id| as // display id. This will show an overlay on the screen with required // instructions on how to proceed. The callback will be invoked in case of - // successful calibraion only. If the calibration fails, this will throw an + // successful calibration only. If the calibration fails, this will throw an // error. // |id|: The display's unique identifier. // |callback|: Optional callback to inform the caller that the touch
diff --git a/extensions/renderer/native_extension_bindings_system_test_base.h b/extensions/renderer/native_extension_bindings_system_test_base.h index 0641f11b..92950b43 100644 --- a/extensions/renderer/native_extension_bindings_system_test_base.h +++ b/extensions/renderer/native_extension_bindings_system_test_base.h
@@ -44,7 +44,7 @@ class TestIPCMessageSender : public IPCMessageSender { public: TestIPCMessageSender(); - ~TestIPCMessageSender(); + ~TestIPCMessageSender() override; // IPCMessageSender: void SendRequestIPC(ScriptContext* context,
diff --git a/extensions/shell/browser/shell_extension_host_delegate.cc b/extensions/shell/browser/shell_extension_host_delegate.cc index efd3f48f..dfa5f5a 100644 --- a/extensions/shell/browser/shell_extension_host_delegate.cc +++ b/extensions/shell/browser/shell_extension_host_delegate.cc
@@ -35,11 +35,12 @@ return NULL; } -void ShellExtensionHostDelegate::CreateTab(content::WebContents* web_contents, - const std::string& extension_id, - WindowOpenDisposition disposition, - const gfx::Rect& initial_rect, - bool user_gesture) { +void ShellExtensionHostDelegate::CreateTab( + std::unique_ptr<content::WebContents> web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) { // TODO(jamescook): Should app_shell support opening popup windows? NOTREACHED(); }
diff --git a/extensions/shell/browser/shell_extension_host_delegate.h b/extensions/shell/browser/shell_extension_host_delegate.h index 6c981b39..fc03bdcb 100644 --- a/extensions/shell/browser/shell_extension_host_delegate.h +++ b/extensions/shell/browser/shell_extension_host_delegate.h
@@ -20,7 +20,7 @@ void OnExtensionHostCreated(content::WebContents* web_contents) override; void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) override; content::JavaScriptDialogManager* GetJavaScriptDialogManager() override; - void CreateTab(content::WebContents* web_contents, + void CreateTab(std::unique_ptr<content::WebContents> web_contents, const std::string& extension_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect,
diff --git a/gin/converter.h b/gin/converter.h index 38c1f7f..19ec02a3 100644 --- a/gin/converter.h +++ b/gin/converter.h
@@ -220,19 +220,23 @@ std::conditional_t<ToV8ReturnsMaybe<T>::value, v8::MaybeLocal<v8::Value>, v8::Local<v8::Value>> -ConvertToV8(v8::Isolate* isolate, T input) { +ConvertToV8(v8::Isolate* isolate, const T& input) { return Converter<T>::ToV8(isolate, input); } template <typename T> -std::enable_if_t<ToV8ReturnsMaybe<T>::value, bool> -TryConvertToV8(v8::Isolate* isolate, T input, v8::Local<v8::Value>* output) { +std::enable_if_t<ToV8ReturnsMaybe<T>::value, bool> TryConvertToV8( + v8::Isolate* isolate, + const T& input, + v8::Local<v8::Value>* output) { return ConvertToV8(isolate, input).ToLocal(output); } template <typename T> -std::enable_if_t<!ToV8ReturnsMaybe<T>::value, bool> -TryConvertToV8(v8::Isolate* isolate, T input, v8::Local<v8::Value>* output) { +std::enable_if_t<!ToV8ReturnsMaybe<T>::value, bool> TryConvertToV8( + v8::Isolate* isolate, + const T& input, + v8::Local<v8::Value>* output) { *output = ConvertToV8(isolate, input); return true; }
diff --git a/gin/dictionary.h b/gin/dictionary.h index 5a61cc7..2645d32 100644 --- a/gin/dictionary.h +++ b/gin/dictionary.h
@@ -41,8 +41,8 @@ return ConvertFromV8(isolate_, val, out); } - template<typename T> - bool Set(const std::string& key, T val) { + template <typename T> + bool Set(const std::string& key, const T& val) { v8::Local<v8::Value> v8_value; if (!TryConvertToV8(isolate_, val, &v8_value)) return false;
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.cc b/gpu/ipc/service/direct_composition_child_surface_win.cc index 58ef9f80..7ac6ebf 100644 --- a/gpu/ipc/service/direct_composition_child_surface_win.cc +++ b/gpu/ipc/service/direct_composition_child_surface_win.cc
@@ -7,7 +7,6 @@ #include <d3d11_1.h> #include <dcomptypes.h> -#include "base/debug/alias.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/synchronization/waitable_event.h" @@ -147,10 +146,6 @@ } else if (!will_discard) { DXGI_PRESENT_PARAMETERS params = {}; RECT dirty_rect = swap_rect_.ToRECT(); - // TODO(sunnyps): Remove Alias calls once crbug.com/776403 is fixed. - base::debug::Alias(&dirty_rect); - gfx::Size surface_size = size_; - base::debug::Alias(&surface_size); params.DirtyRectsCount = 1; params.pDirtyRects = &dirty_rect; swap_chain_->Present1(first_swap_ || disable_vsync_ ? 0 : 1, 0, ¶ms);
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc index a2856be7..b87de21 100644 --- a/gpu/ipc/service/direct_composition_surface_win.cc +++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -828,9 +828,6 @@ HANDLE handle; HRESULT hr = create_surface_handle_function_(COMPOSITIONOBJECT_ALL_ACCESS, nullptr, &handle); - // TODO(crbug/792806): Remove Alias and CHECK after issue is fixed. - base::debug::Alias(&hr); - CHECK(SUCCEEDED(hr)); swap_chain_handle_.Set(handle); if (is_yuy2_swapchain_ != yuy2) {
diff --git a/infra/config/global/luci-milo-dev.cfg b/infra/config/global/luci-milo-dev.cfg index 06f74a3..ec2539c 100644 --- a/infra/config/global/luci-milo-dev.cfg +++ b/infra/config/global/luci-milo-dev.cfg
@@ -2085,10 +2085,6 @@ category: "default" } builders: { - name: "buildbot/chromium.fyi/Android deterministic" - category: "deterministic" - } - builders: { name: "buildbot/chromium.fyi/Mac deterministic" category: "deterministic" } @@ -2101,14 +2097,6 @@ category: "deterministic" } builders: { - name: "buildbot/chromium.fyi/Android deterministic (dbg)" - category: "deterministic" - } - builders: { - name: "buildbot/chromium.fyi/Linux deterministic (dbg)" - category: "deterministic" - } - builders: { name: "buildbot/chromium.fyi/Mac deterministic (dbg)" category: "deterministic" }
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg index a8aad34..3e5d5d4 100644 --- a/infra/config/global/luci-milo.cfg +++ b/infra/config/global/luci-milo.cfg
@@ -424,7 +424,6 @@ short_name: "64" } builders: { - name: "buildbot/chromium.fyi/Linux deterministic (dbg)" name: "buildbucket/luci.chromium.ci/Deterministic Linux (dbg)" category: "chromium.linux|debug" short_name: "det" @@ -1598,38 +1597,6 @@ } builders: { - name: "buildbucket/luci.chromium.ci/Linux TSan Builder" - category: "chromium.memory|tsan|builder" - short_name: "ᕕ(ᐛ)ᕗ" - } - builders: { - name: "buildbot/chromium.memory/Linux TSan Builder" - category: "chromium.memory|tsan|builder" - short_name: "(ಥ_ಥ)" - } - builders: { - name: "buildbucket/luci.chromium.ci/Linux TSan Tests" - category: "chromium.memory|tsan|tester" - short_name: "ᕕ(ᐛ)ᕗ" - } - builders: { - name: "buildbot/chromium.memory/Linux TSan Tests" - category: "chromium.memory|tsan|tester" - short_name: "(ಥ_ಥ)" - } - - builders: { - name: "buildbot/chromium.fyi/Linux deterministic (dbg)" - category: "chromium.linux|debug" - short_name: "bb" - } - builders: { - name: "buildbucket/luci.chromium.ci/Deterministic Linux (dbg)" - category: "chromium.linux|debug" - short_name: "ci" - } - - builders: { name: "buildbot/chromium.android/Android Cronet Builder" category: "chromium.android|cronet" short_name: "bb" @@ -1640,26 +1607,6 @@ short_name: "ci" } builders: { - name: "buildbot/chromium.fyi/Android deterministic" - category: "chromium.android|release" - short_name: "bb" - } - builders: { - name: "buildbucket/luci.chromium.ci/Deterministic Android" - category: "chromium.android|release" - short_name: "ci" - } - builders: { - name: "buildbot/chromium.fyi/Android deterministic (dbg)" - category: "chromium.android|debug" - short_name: "bb" - } - builders: { - name: "buildbucket/luci.chromium.ci/Deterministic Android (dbg)" - category: "chromium.android|debug" - short_name: "ci" - } - builders: { name: "buildbot/chromium.mac/ios-simulator" category: "chromium.mac|ios-simulator" short_name: "bb" @@ -1812,13 +1759,11 @@ short_name: "64" } builders: { - name: "buildbot/chromium.fyi/Android deterministic" name: "buildbucket/luci.chromium.ci/Deterministic Android" category: "builder|det" short_name: "rel" } builders: { - name: "buildbot/chromium.fyi/Android deterministic (dbg)" name: "buildbucket/luci.chromium.ci/Deterministic Android (dbg)" category: "builder|det" short_name: "dbg" @@ -2356,10 +2301,6 @@ category: "default" } builders: { - name: "buildbot/chromium.fyi/Android deterministic" - category: "deterministic" - } - builders: { name: "buildbot/chromium.fyi/Mac deterministic" category: "deterministic" } @@ -2372,14 +2313,6 @@ category: "deterministic" } builders: { - name: "buildbot/chromium.fyi/Android deterministic (dbg)" - category: "deterministic" - } - builders: { - name: "buildbot/chromium.fyi/Linux deterministic (dbg)" - category: "deterministic" - } - builders: { name: "buildbot/chromium.fyi/Mac deterministic (dbg)" category: "deterministic" }
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index 4276bb49..c59a357 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -1437,7 +1437,7 @@ } - (void)showHistory { - if (experimental_flags::IsCollectionsUIRebootEnabled()) { + if (IsUIRefreshPhase1Enabled()) { // New History UIReboot coordinator. _historyCoordinator = [[HistoryCoordinator alloc] initWithBaseViewController:self.currentBVC
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm index 85fd529..f9fb5380 100644 --- a/ios/chrome/browser/about_flags.mm +++ b/ios/chrome/browser/about_flags.mm
@@ -296,6 +296,10 @@ flags_ui::kOsIos, FEATURE_VALUE_TYPE( autofill::features::kAutofillEnforceMinRequiredFieldsForUpload)}, + {"browser-container-fullscreen", + flag_descriptions::kBrowserContainerFullscreenName, + flag_descriptions::kBrowserContainerFullscreenDescription, + flags_ui::kOsIos, FEATURE_VALUE_TYPE(kBrowserContainerFullscreen)}, }; // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc index c9dd7e5..5c408f7e 100644 --- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -43,6 +43,11 @@ const char kAutofillRestrictUnownedFieldsToFormlessCheckoutDescription[] = "Restrict extraction of formless forms to checkout flows"; +const char kBrowserContainerFullscreenName[] = "Browser Container Fullscreen"; +const char kBrowserContainerFullscreenDescription[] = + "When enabled, the BrowserContainer is fullscreen. No UI change should be " + "visible."; + const char kBrowserTaskScheduler[] = "Task Scheduler"; const char kBrowserTaskSchedulerDescription[] = "Enables redirection of some task posting APIs to the task scheduler.";
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h index ae87cda..3760fef 100644 --- a/ios/chrome/browser/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -28,6 +28,10 @@ extern const char kAutofillRestrictUnownedFieldsToFormlessCheckoutName[]; extern const char kAutofillRestrictUnownedFieldsToFormlessCheckoutDescription[]; +// Title and description for the flag to make browser container fullscreen. +extern const char kBrowserContainerFullscreenName[]; +extern const char kBrowserContainerFullscreenDescription[]; + // Title and description for the flag to control redirection to the task // scheduler. extern const char kBrowserTaskScheduler[];
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn index 7f018607..bcc4008e9 100644 --- a/ios/chrome/browser/ui/BUILD.gn +++ b/ios/chrome/browser/ui/BUILD.gn
@@ -343,6 +343,7 @@ "//ios/chrome/browser/store_kit", "//ios/chrome/browser/tabs", "//ios/chrome/browser/translate", + "//ios/chrome/browser/ui:feature_flags", "//ios/chrome/browser/ui/activity_services:coordinator", "//ios/chrome/browser/ui/activity_services/requirements", "//ios/chrome/browser/ui/alert_coordinator",
diff --git a/ios/chrome/browser/ui/authentication/unified_consent_coordinator.h b/ios/chrome/browser/ui/authentication/unified_consent_coordinator.h index da260a33..cc5208d0 100644 --- a/ios/chrome/browser/ui/authentication/unified_consent_coordinator.h +++ b/ios/chrome/browser/ui/authentication/unified_consent_coordinator.h
@@ -19,6 +19,11 @@ - (void)unifiedConsentCoordinatorDidTapSettingsLink: (UnifiedConsentCoordinator*)coordinator; +// Called when the user scrolls down to the bottom (or when the view controller +// is loaded with no scroll needed). +- (void)unifiedConsentCoordinatorDidReachBottom: + (UnifiedConsentCoordinator*)coordinator; + @end // UnityConsentCoordinator coordinates UnityConsentViewController, which is a @@ -38,6 +43,8 @@ @property(nonatomic, readonly) int openSettingsStringId; // View controller used to display the view. @property(nonatomic, strong, readonly) UIViewController* viewController; +// Returns YES if the consent view is scrolled to the bottom. +@property(nonatomic, readonly) BOOL isScrolledToBottom; // Starts this coordinator. - (void)start; @@ -46,6 +53,9 @@ // the way they appear on the screen. - (const std::vector<int>&)consentStringIds; +// Scrolls the consent view to the bottom. +- (void)scrollToBottom; + @end #endif // IOS_CHROME_BROWSER_UI_AUTHENTICATION_UNIFIED_CONSENT_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/authentication/unified_consent_coordinator.mm b/ios/chrome/browser/ui/authentication/unified_consent_coordinator.mm index 6521eb3..cbf66704 100644 --- a/ios/chrome/browser/ui/authentication/unified_consent_coordinator.mm +++ b/ios/chrome/browser/ui/authentication/unified_consent_coordinator.mm
@@ -60,6 +60,14 @@ return [self.unifiedConsentViewController consentStringIds]; } +- (void)scrollToBottom { + [self.unifiedConsentViewController scrollToBottom]; +} + +- (BOOL)isScrolledToBottom { + return self.unifiedConsentViewController.isScrolledToBottom; +} + #pragma mark - UnifiedConsentViewControllerDelegate - (void)unifiedConsentViewControllerDidTapSettingsLink: @@ -74,4 +82,10 @@ // TODO(crbug.com/827072): Needs implementation. } +- (void)unifiedConsentViewControllerDidReachBottom: + (UnifiedConsentViewController*)controller { + DCHECK_EQ(self.unifiedConsentViewController, controller); + [self.delegate unifiedConsentCoordinatorDidReachBottom:self]; +} + @end
diff --git a/ios/chrome/browser/ui/authentication/unified_consent_view_controller.h b/ios/chrome/browser/ui/authentication/unified_consent_view_controller.h index b79197b4..b5291932 100644 --- a/ios/chrome/browser/ui/authentication/unified_consent_view_controller.h +++ b/ios/chrome/browser/ui/authentication/unified_consent_view_controller.h
@@ -9,7 +9,6 @@ #include <vector> -@class ChromeIdentity; @class UnifiedConsentViewController; // Delegate protocol for UnityConsentViewController. @@ -23,6 +22,11 @@ - (void)unifiedConsentViewControllerDidTapIdentityPickerView: (UnifiedConsentViewController*)controller; +// Called when the user scrolls down to the bottom (or when the view controller +// is loaded with no scroll needed). +- (void)unifiedConsentViewControllerDidReachBottom: + (UnifiedConsentViewController*)controller; + @end // UnityConsentViewController is a sub view controller to ask for the user @@ -35,6 +39,8 @@ @property(nonatomic, weak) id<UnifiedConsentViewControllerDelegate> delegate; // String id for text to open the settings (related to record the user consent). @property(nonatomic, readonly) int openSettingsStringId; +// Returns YES if the consent view is scrolled to the bottom. +@property(nonatomic, readonly) BOOL isScrolledToBottom; // -[UnifiedConsentViewController init] should be used. - (instancetype)initWithNibName:(NSString*)nibNameOrNil @@ -55,6 +61,9 @@ // Hides the IdentityPickerView. - (void)hideIdentityPickerView; +// Scrolls the consent view to the bottom. +- (void)scrollToBottom; + @end #endif // IOS_CHROME_BROWSER_UI_AUTHENTICATION_UNIFIED_CONSENT_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/authentication/unified_consent_view_controller.mm b/ios/chrome/browser/ui/authentication/unified_consent_view_controller.mm index 198947b..d2b5f58 100644 --- a/ios/chrome/browser/ui/authentication/unified_consent_view_controller.mm +++ b/ios/chrome/browser/ui/authentication/unified_consent_view_controller.mm
@@ -61,7 +61,7 @@ const char* kSettingsSyncURL = "internal://settings-sync"; } // namespace -@interface UnifiedConsentViewController () { +@interface UnifiedConsentViewController ()<UIScrollViewDelegate> { std::vector<int> _consentStringIds; } @@ -118,6 +118,22 @@ self.noIdentityConstraint.active = YES; } +- (void)scrollToBottom { + CGPoint bottomOffset = + CGPointMake(0, self.scrollView.contentSize.height - + self.scrollView.bounds.size.height + + self.scrollView.contentInset.bottom); + [self.scrollView setContentOffset:bottomOffset animated:YES]; +} + +- (BOOL)isScrolledToBottom { + CGFloat scrollPosition = + self.scrollView.contentOffset.y + self.scrollView.frame.size.height; + CGFloat scrollLimit = + self.scrollView.contentSize.height + self.scrollView.contentInset.bottom; + return scrollPosition >= scrollLimit; +} + #pragma mark - UIViewController - (void)viewDidLoad { @@ -297,6 +313,22 @@ completion:nil]; } +- (void)didMoveToParentViewController:(UIViewController*)parent { + if (!parent) + return; + [parent.view layoutIfNeeded]; + // Needs to add the scroll view delegate only when all the view layouts are + // fully done. + dispatch_async(dispatch_get_main_queue(), ^{ + // Having a layout of the parent view makes the scroll view not being + // presented at the top. Scrolling to the top is required. + CGPoint topOffset = CGPointMake(0, -self.scrollView.contentInset.top); + [self.scrollView setContentOffset:topOffset animated:NO]; + self.scrollView.delegate = self; + [self sendDidReachBottomIfReached]; + }); +} + #pragma mark - UI actions - (void)identityPickerAction:(id)sender { @@ -394,6 +426,10 @@ self.scrollView.contentInset = UIEdgeInsetsMake(statusBarHeight, 0, 0, 0); self.imageBackgroundViewHeightConstraint.constant = statusBarHeight; } + if (self.scrollView.delegate == self) { + // Don't send the notification if the delegate is not configured yet. + [self sendDidReachBottomIfReached]; + } } // Notifies |delegate| that the user tapped on "Settings" link. @@ -401,4 +437,19 @@ [self.delegate unifiedConsentViewControllerDidTapSettingsLink:self]; } +// Sends notification to the delegate if the scroll view is scrolled to the +// bottom. +- (void)sendDidReachBottomIfReached { + if (self.isScrolledToBottom) { + [self.delegate unifiedConsentViewControllerDidReachBottom:self]; + } +} + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewDidScroll:(UIScrollView*)scrollView { + DCHECK_EQ(self.scrollView, scrollView); + [self sendDidReachBottomIfReached]; +} + @end
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm index 71a5278b7..af9fdb6 100644 --- a/ios/chrome/browser/ui/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -217,6 +217,7 @@ #import "ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h" #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_item.h" #import "ios/chrome/browser/ui/translate/language_selection_coordinator.h" +#include "ios/chrome/browser/ui/ui_feature_flags.h" #include "ios/chrome/browser/ui/ui_util.h" #import "ios/chrome/browser/ui/uikit_ui_util.h" #import "ios/chrome/browser/ui/util/constraints_ui_util.h" @@ -1614,8 +1615,10 @@ // Perform additional set up after loading the view, typically from a nib. - (void)viewDidLoad { CGRect initialViewsRect = self.view.bounds; - initialViewsRect.origin.y += StatusBarHeight(); - initialViewsRect.size.height -= StatusBarHeight(); + if (!base::FeatureList::IsEnabled(kBrowserContainerFullscreen)) { + initialViewsRect.origin.y += StatusBarHeight(); + initialViewsRect.size.height -= StatusBarHeight(); + } UIViewAutoresizing initialViewAutoresizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -2372,9 +2375,11 @@ // Adjust the content area to be under the toolbar, for fullscreen or below // the toolbar is not fullscreen. CGRect contentFrame = self.contentArea.frame; - CGFloat marginWithHeader = StatusBarHeight(); - contentFrame.size.height = CGRectGetMaxY(contentFrame) - marginWithHeader; - contentFrame.origin.y = marginWithHeader; + if (!base::FeatureList::IsEnabled(kBrowserContainerFullscreen)) { + CGFloat marginWithHeader = StatusBarHeight(); + contentFrame.size.height = CGRectGetMaxY(contentFrame) - marginWithHeader; + contentFrame.origin.y = marginWithHeader; + } self.contentArea.frame = contentFrame; if (initialLayout) { @@ -2545,8 +2550,12 @@ height += CGRectGetHeight([header.view frame]) - header.inset; } } + CGFloat statusBarOffset = 0; + if (!base::FeatureList::IsEnabled(kBrowserContainerFullscreen)) { + statusBarOffset = StatusBarHeight(); + } - return height - StatusBarHeight(); + return height - statusBarOffset; } - (void)setFramesForHeaders:(NSArray<HeaderDefinition*>*)headers @@ -5121,8 +5130,10 @@ // Restore content area frame, which was resized to fullscreen for // NTP opening animation. CGRect contentAreaFrame = self.view.bounds; - contentAreaFrame.origin.y += StatusBarHeight(); - contentAreaFrame.size.height -= StatusBarHeight(); + if (!base::FeatureList::IsEnabled(kBrowserContainerFullscreen)) { + contentAreaFrame.origin.y += StatusBarHeight(); + contentAreaFrame.size.height -= StatusBarHeight(); + } self.contentArea.frame = contentAreaFrame; }); } else {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm index dcee0f7..2f19695 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
@@ -33,7 +33,8 @@ } } if (!content_suggestions::IsRegularXRegularSizeClass(self.collectionView)) - minimumHeight -= ntp_header::ToolbarHeight() + topSafeArea; + minimumHeight -= ntp_header::ToolbarHeight() + topSafeArea + + self.collectionView.contentInset.bottom; CGSize contentSize = [super collectionViewContentSize]; if (contentSize.height < minimumHeight) {
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm index d181cf7..f88943d 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -201,7 +201,8 @@ // offset, taking into account the size of the toolbar. offset = MAX(0, MIN(offset, collection.contentSize.height - collection.bounds.size.height - - ntp_header::ToolbarHeight())); + ntp_header::ToolbarHeight() + + collection.contentInset.bottom)); collection.contentOffset = CGPointMake(0, offset); // Update the constraints in case the omnibox needs to be moved. [self.suggestionsViewController updateConstraints];
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn index 48de76a..d4bba32 100644 --- a/ios/chrome/browser/ui/history/BUILD.gn +++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -114,7 +114,6 @@ "//components/url_formatter", "//components/url_formatter", "//ios/chrome/app/strings", - "//ios/chrome/browser", "//ios/chrome/browser/browser_state", "//ios/chrome/browser/metrics:metrics_internal", "//ios/chrome/browser/sync", @@ -228,6 +227,8 @@ "//ios/chrome/browser/ui/authentication:authentication_ui", "//ios/chrome/browser/ui/authentication:eg_test_support", "//ios/chrome/browser/ui/settings", + "//ios/chrome/browser/ui/table_view", + "//ios/chrome/browser/ui/table_view/cells", "//ios/chrome/browser/ui/tools_menu", "//ios/chrome/browser/ui/tools_menu/public", "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/history/history_entry_inserter.mm b/ios/chrome/browser/ui/history/history_entry_inserter.mm index fa6c09e..b0de08c 100644 --- a/ios/chrome/browser/ui/history/history_entry_inserter.mm +++ b/ios/chrome/browser/ui/history/history_entry_inserter.mm
@@ -7,12 +7,12 @@ #include "base/mac/foundation_util.h" #include "base/strings/sys_string_conversions.h" #include "base/time/time.h" -#include "ios/chrome/browser/experimental_flags.h" #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h" #import "ios/chrome/browser/ui/history/history_entry_item_interface.h" #include "ios/chrome/browser/ui/history/history_util.h" #import "ios/chrome/browser/ui/list_model/list_model.h" #import "ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.h" +#include "ios/chrome/browser/ui/ui_util.h" #include "url/gurl.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -120,7 +120,7 @@ usingComparator:comparator]; [_dates insertObject:date atIndex:index]; NSInteger insertionIndex = _firstSectionIndex + index; - if (experimental_flags::IsCollectionsUIRebootEnabled()) { + if (IsUIRefreshPhase1Enabled()) { TableViewTextHeaderFooterItem* header = [[TableViewTextHeaderFooterItem alloc] initWithType:kItemTypeEnumZero]; header.text =
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm index 9ada1aa..ba1450d 100644 --- a/ios/chrome/browser/ui/history/history_table_view_controller.mm +++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -156,6 +156,8 @@ self.searchController.searchBar.delegate = self; self.searchController.searchResultsUpdater = self; self.searchController.searchBar.backgroundColor = [UIColor whiteColor]; + self.searchController.searchBar.accessibilityIdentifier = + l10n_util::GetNSStringWithFixup(IDS_IOS_ICON_SEARCH); // UIKit needs to know which controller will be presenting the // searchController. If we don't add this trying to dismiss while // SearchController is active will fail. @@ -844,6 +846,7 @@ style:UIBarButtonItemStylePlain target:self action:@selector(animateViewsConfigurationForEditingChange)]; + _cancelButton.accessibilityIdentifier = titleString; } return _cancelButton; } @@ -859,6 +862,7 @@ style:UIBarButtonItemStylePlain target:self action:@selector(openPrivacySettings)]; + _clearBrowsingDataButton.accessibilityIdentifier = titleString; _clearBrowsingDataButton.tintColor = [UIColor redColor]; } return _clearBrowsingDataButton; @@ -873,6 +877,7 @@ style:UIBarButtonItemStylePlain target:self action:@selector(deleteSelectedItemsFromHistory)]; + _deleteButton.accessibilityIdentifier = titleString; _deleteButton.tintColor = [UIColor redColor]; } return _deleteButton; @@ -887,6 +892,7 @@ style:UIBarButtonItemStylePlain target:self action:@selector(animateViewsConfigurationForEditingChange)]; + _editButton.accessibilityIdentifier = titleString; } return _editButton; }
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm index 6ac91ce..65083ae2 100644 --- a/ios/chrome/browser/ui/history/history_ui_egtest.mm +++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -18,6 +18,8 @@ #import "ios/chrome/browser/ui/authentication/signin_promo_view.h" #import "ios/chrome/browser/ui/history/legacy_history_entry_item.h" #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h" +#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h" +#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h" #include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h" #import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h" #include "ios/chrome/browser/ui/ui_util.h" @@ -59,26 +61,48 @@ // Matcher for entry in history for URL and title. id<GREYMatcher> HistoryEntry(const GURL& url, const std::string& title) { - NSString* url_spec_text = base::SysUTF8ToNSString(url.spec()); + NSString* url_spec_text = nil; NSString* title_text = base::SysUTF8ToNSString(title); - MatchesBlock matches = ^BOOL(LegacyHistoryEntryCell* cell) { - return [cell.textLabel.text isEqual:title_text] && - [cell.detailTextLabel.text isEqual:url_spec_text]; - }; + if (IsUIRefreshPhase1Enabled()) { + url_spec_text = base::SysUTF8ToNSString(url.GetOrigin().spec()); - DescribeToBlock describe = ^(id<GREYDescription> description) { - [description appendText:@"view containing URL text: "]; - [description appendText:url_spec_text]; - [description appendText:@" title text: "]; - [description appendText:title_text]; - }; + MatchesBlock matches = ^BOOL(TableViewURLCell* cell) { + return [cell.titleLabel.text isEqual:title_text] && + [cell.URLLabel.text isEqual:url_spec_text]; + }; - return grey_allOf( - grey_kindOfClass([LegacyHistoryEntryCell class]), - [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches - descriptionBlock:describe], - grey_sufficientlyVisible(), nil); + DescribeToBlock describe = ^(id<GREYDescription> description) { + [description appendText:@"view containing URL text: "]; + [description appendText:url_spec_text]; + [description appendText:@" title text: "]; + [description appendText:title_text]; + }; + return grey_allOf( + grey_kindOfClass([TableViewURLCell class]), + [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches + descriptionBlock:describe], + grey_sufficientlyVisible(), nil); + } else { + url_spec_text = base::SysUTF8ToNSString(url.spec()); + + MatchesBlock matches = ^BOOL(LegacyHistoryEntryCell* cell) { + return [cell.textLabel.text isEqual:title_text] && + [cell.detailTextLabel.text isEqual:url_spec_text]; + }; + + DescribeToBlock describe = ^(id<GREYDescription> description) { + [description appendText:@"view containing URL text: "]; + [description appendText:url_spec_text]; + [description appendText:@" title text: "]; + [description appendText:title_text]; + }; + return grey_allOf( + grey_kindOfClass([LegacyHistoryEntryCell class]), + [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches + descriptionBlock:describe], + grey_sufficientlyVisible(), nil); + } } // Matcher for the history button in the tools menu. id<GREYMatcher> HistoryButton() { @@ -86,19 +110,34 @@ } // Matcher for the edit button in the navigation bar. id<GREYMatcher> NavigationEditButton() { - return ButtonWithAccessibilityLabelId(IDS_HISTORY_START_EDITING_BUTTON); + if (IsUIRefreshPhase1Enabled()) { + return grey_accessibilityID( + l10n_util::GetNSStringWithFixup(IDS_HISTORY_START_EDITING_BUTTON)); + } else { + return ButtonWithAccessibilityLabelId(IDS_HISTORY_START_EDITING_BUTTON); + } } // Matcher for the delete button. id<GREYMatcher> DeleteHistoryEntriesButton() { // Include class restriction to exclude MDCCollectionViewInfoBar, which is // hidden. - return grey_allOf(ButtonWithAccessibilityLabelId( - IDS_HISTORY_DELETE_SELECTED_ENTRIES_BUTTON), - grey_kindOfClass([UIButton class]), nil); + if (IsUIRefreshPhase1Enabled()) { + return grey_accessibilityID(l10n_util::GetNSStringWithFixup( + IDS_HISTORY_DELETE_SELECTED_ENTRIES_BUTTON)); + } else { + return grey_allOf(ButtonWithAccessibilityLabelId( + IDS_HISTORY_DELETE_SELECTED_ENTRIES_BUTTON), + grey_kindOfClass([UIButton class]), nil); + } } // Matcher for the search button. id<GREYMatcher> SearchIconButton() { - return ButtonWithAccessibilityLabelId(IDS_IOS_ICON_SEARCH); + if (IsUIRefreshPhase1Enabled()) { + return grey_accessibilityID( + l10n_util::GetNSStringWithFixup(IDS_IOS_ICON_SEARCH)); + } else { + return ButtonWithAccessibilityLabelId(IDS_IOS_ICON_SEARCH); + } } // Matcher for the cancel button. id<GREYMatcher> CancelButton() { @@ -106,8 +145,13 @@ } // Matcher for the button to open the clear browsing data panel. id<GREYMatcher> OpenClearBrowsingDataButton() { - return ButtonWithAccessibilityLabelId( - IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG); + if (IsUIRefreshPhase1Enabled()) { + return grey_accessibilityID(l10n_util::GetNSStringWithFixup( + IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG)); + } else { + return ButtonWithAccessibilityLabelId( + IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG); + } } // Matcher for the Open in New Incognito Tab option in the context menu. id<GREYMatcher> OpenInNewIncognitoTabButton() { @@ -407,8 +451,14 @@ [self openHistoryPanel]; chrome_test_util::VerifyAccessibilityForCurrentScreen(); // Close history. - [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()] - performAction:grey_tap()]; + if (IsUIRefreshPhase1Enabled()) { + id<GREYMatcher> exitMatcher = + grey_accessibilityID(kTableViewNavigationDismissButtonId); + [[EarlGrey selectElementWithMatcher:exitMatcher] performAction:grey_tap()]; + } else { + [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()] + performAction:grey_tap()]; + } } #pragma mark Helper Methods
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm index 69229535..c07853c3 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
@@ -206,8 +206,15 @@ } - (void)setContentInset:(UIEdgeInsets)contentInset { + // UIKit will adjust the contentOffset sometimes when changing the + // contentInset.bottom. We don't want the NTP to scroll, so store and re-set + // the contentOffset after setting the contentInset. + CGPoint contentOffset = self.contentSuggestionsCoordinator.viewController + .collectionView.contentOffset; self.contentSuggestionsCoordinator.viewController.collectionView .contentInset = contentInset; + self.contentSuggestionsCoordinator.viewController.collectionView + .contentOffset = contentOffset; } #pragma mark - CRWNativeContent
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm index 3bbb272..c6b8eccd 100644 --- a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm +++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_egtest.mm
@@ -148,11 +148,17 @@ assertWithMatcher:grey_sufficientlyVisible()]; // Close History. - [[EarlGrey - selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel( - l10n_util::GetNSString( - IDS_IOS_NAVIGATION_BAR_DONE_BUTTON))] - performAction:grey_tap()]; + if (IsUIRefreshPhase1Enabled()) { + id<GREYMatcher> exitMatcher = + grey_accessibilityID(kTableViewNavigationDismissButtonId); + [[EarlGrey selectElementWithMatcher:exitMatcher] performAction:grey_tap()]; + } else { + [[EarlGrey + selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel( + l10n_util::GetNSString( + IDS_IOS_NAVIGATION_BAR_DONE_BUTTON))] + performAction:grey_tap()]; + } // Close tab. chrome_test_util::CloseCurrentTab();
diff --git a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h index a5e3f17..b68bb385 100644 --- a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h +++ b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.h
@@ -52,10 +52,11 @@ void OnInputStateChanged() override; void OnFocusChanged(OmniboxFocusState state, OmniboxFocusChangeReason reason) override; - void OnResultChanged(const AutocompleteResult& result, - bool default_match_changed, - const base::Callback<void(const SkBitmap& bitmap)>& - on_bitmap_fetched) override; + void OnResultChanged( + const AutocompleteResult& result, + bool default_match_changed, + const base::Callback<void(int result_index, const SkBitmap& bitmap)>& + on_bitmap_fetched) override; void OnCurrentMatchChanged(const AutocompleteMatch& match) override; void OnURLOpenedFromOmnibox(OmniboxLog* log) override; void OnBookmarkLaunched() override;
diff --git a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm index 9c8c7cec..95bddeda 100644 --- a/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm +++ b/ios/chrome/browser/ui/omnibox/chrome_omnibox_client_ios.mm
@@ -157,7 +157,8 @@ void ChromeOmniboxClientIOS::OnResultChanged( const AutocompleteResult& result, bool default_match_changed, - const base::Callback<void(const SkBitmap& bitmap)>& on_bitmap_fetched) { + const base::Callback<void(int result_index, const SkBitmap& bitmap)>& + on_bitmap_fetched) { if (result.empty()) { return; }
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm index 51f4fe04..893289c 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -817,7 +817,7 @@ sectionIsCollapsed:[self.tableViewModel sectionIdentifierForSection:section]]; DisclosureDirection direction = - collapsed ? DisclosureDirectionRight : DisclosureDirectionDown; + collapsed ? DisclosureDirectionUp : DisclosureDirectionDown; [disclosureHeaderView animateHighlightAndRotateToDirection:direction]; disclosureItem.collapsed = collapsed;
diff --git a/ios/chrome/browser/ui/stack_view/card_set.mm b/ios/chrome/browser/ui/stack_view/card_set.mm index 5f4c103..80b8d78 100644 --- a/ios/chrome/browser/ui/stack_view/card_set.mm +++ b/ios/chrome/browser/ui/stack_view/card_set.mm
@@ -120,8 +120,7 @@ // TODO(stuartmorgan): Fix this in TabModel; this is dumb. if (currentTabIndex == NSNotFound) return nil; - DCHECK(currentTabIndex < [self.cards count]); - return [self.cards objectAtIndex:currentTabIndex]; + return currentTabIndex < self.cards.count ? self.cards[currentTabIndex] : nil; } - (void)setCurrentCard:(StackCard*)card {
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.h index c973f32..0996a57 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.h +++ b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.h
@@ -25,7 +25,7 @@ @interface TableViewDisclosureHeaderFooterView : UITableViewHeaderFooterView // Indicates in what direction the disclosure accessory should point. typedef NS_ENUM(NSInteger, DisclosureDirection) { - DisclosureDirectionRight = 0, + DisclosureDirectionUp = 2, DisclosureDirectionDown, }; // Shows the text of the TableViewDisclosureHeaderFooterItem.
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm index 9a61221..ec37efe 100644 --- a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm +++ b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
@@ -15,11 +15,10 @@ #endif namespace { -// Rotates the disclosure back to its original position. -const float kToggleDisclosureOriginalPositionAngle = 0; - -// 90 degree CCW rotation -constexpr float kRotationNinetyCCW = (90 / 180.0) * M_PI; +// Identity rotation angle that positions disclosure pointing down. +constexpr float kRotationNinetyCW = (90 / 180.0) * M_PI; +// Identity rotation angle that positions disclosure pointing up. +constexpr float kRotationNinetyCCW = -(90 / 180.0) * M_PI; } @implementation TableViewDisclosureHeaderFooterItem @@ -48,7 +47,7 @@ header.titleLabel.text = self.text; header.subtitleLabel.text = self.subtitleText; DisclosureDirection direction = - self.collapsed ? DisclosureDirectionRight : DisclosureDirectionDown; + self.collapsed ? DisclosureDirectionUp : DisclosureDirectionDown; [header setInitialDirection:direction]; } @@ -193,9 +192,8 @@ DisclosureDirection originalDirection = self.disclosureDirection; if (originalDirection != direction) { self.disclosureDirection = direction; - CGFloat angle = direction == DisclosureDirectionDown - ? kRotationNinetyCCW - : kToggleDisclosureOriginalPositionAngle; + CGFloat angle = direction == DisclosureDirectionDown ? kRotationNinetyCW + : kRotationNinetyCCW; if (animate) { __weak TableViewDisclosureHeaderFooterView* weakSelf = self; [self.cellAnimator addAnimations:^{
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc index 3606679..c3c201cf 100644 --- a/ios/chrome/browser/ui/ui_feature_flags.cc +++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -15,3 +15,6 @@ const base::Feature kCollectionsUIReboot{"CollectionsUIReboot", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kBrowserContainerFullscreen{ + "BrowserContainerFullscreen", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h index b3e4061..4b0beceb1 100644 --- a/ios/chrome/browser/ui/ui_feature_flags.h +++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -26,4 +26,6 @@ // experimental_flags::IsCollectionsUIRebootEnabled() extern const base::Feature kCollectionsUIReboot; +// Used to make BrowserContainerViewController fullscreen. +extern const base::Feature kBrowserContainerFullscreen; #endif // IOS_CHROME_BROWSER_UI_UI_FEATURE_FLAGS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm index 7fff857f..42a0d5ca 100644 --- a/ios/chrome/test/earl_grey/chrome_test_case.mm +++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -123,7 +123,18 @@ error:&error]; if (error != nil) { NSLog(@"System alert view is present, so skipping all tests!"); +#if TARGET_IPHONE_SIMULATOR return @[]; +#else + // Invoke XCTFail via call to stubbed out test. + NSMethodSignature* signature = + [ChromeTestCase instanceMethodSignatureForSelector:@selector + (failAllTestsDueToSystemAlertVisible)]; + NSInvocation* systemAlertTest = + [NSInvocation invocationWithMethodSignature:signature]; + systemAlertTest.selector = @selector(failAllTestsDueToSystemAlertVisible); + return @[ systemAlertTest ]; +#endif } // Return specific list of tests based on the target. @@ -322,4 +333,10 @@ return multitaskingTestNames; } +#pragma mark - Handling system alerts + +- (void)failAllTestsDueToSystemAlertVisible { + XCTFail("System alerts are present on device. Skipping all tests."); +} + @end
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium index 492a1e8..54268bd2 100644 --- a/ios/third_party/material_components_ios/README.chromium +++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@ Name: Material Components for iOS URL: https://github.com/material-components/material-components-ios Version: 0 -Revision: a0c6b4d6bbade404fb51a654c0c6529422e2c477 +Revision: 34aabeea14450bc357b81b5e6206c86d346fffe6 License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm index 8d66f36..d1a6ed6 100644 --- a/ios/web/navigation/crw_session_controller.mm +++ b/ios/web/navigation/crw_session_controller.mm
@@ -554,6 +554,10 @@ andItem:(web::NavigationItem*)secondItem { if (!firstItem || !secondItem || firstItem == secondItem) return NO; + + if (firstItem->GetURL().GetOrigin() != secondItem->GetURL().GetOrigin()) + return NO; + int firstIndex = [self indexOfItem:firstItem]; int secondIndex = [self indexOfItem:secondItem]; if (firstIndex == -1 || secondIndex == -1)
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm index 299b68e..9ebf1617 100644 --- a/ios/web/navigation/crw_session_controller_unittest.mm +++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -887,6 +887,8 @@ // Push state navigation. items.push_back(CreateNavigationItem("http://foo.com/bar#bar", "http://foo.com/bar", @"Sixth")); + items.push_back(CreateNavigationItem("http://fooz.com/bar#bar", + "http://fooz.com/bar", @"Seventh")); CRWSessionController* controller = [[CRWSessionController alloc] initWithBrowserState:&browser_state_ navigationItems:std::move(items) @@ -898,9 +900,11 @@ web::NavigationItemImpl* item3 = [controller items][3].get(); web::NavigationItemImpl* item4 = [controller items][4].get(); web::NavigationItemImpl* item5 = [controller items][5].get(); + web::NavigationItemImpl* item6 = [controller items][6].get(); item1->SetIsCreatedFromPushState(true); item4->SetIsCreatedFromHashChange(true); item5->SetIsCreatedFromPushState(true); + item6->SetIsCreatedFromHashChange(true); EXPECT_FALSE( [controller isSameDocumentNavigationBetweenItem:item0 andItem:item0]); @@ -916,6 +920,8 @@ [controller isSameDocumentNavigationBetweenItem:item0 andItem:item5]); EXPECT_FALSE( [controller isSameDocumentNavigationBetweenItem:item2 andItem:item4]); + EXPECT_FALSE( + [controller isSameDocumentNavigationBetweenItem:item6 andItem:item5]); } TEST_F(CRWSessionControllerTest, TestBackwardForwardItems) {
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 7de9666..95bd03fb 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -576,11 +576,9 @@ // not be null. - (void)loadNativeViewWithSuccess:(BOOL)loadSuccess navigationContext:(web::NavigationContextImpl*)context; -// Loads the correct HTML page for |error| in a native controller, retrieved -// from the native provider. -- (void)loadErrorInNativeViewForNavigationItem:(web::NavigationItemImpl*)item - navigationContext: - (web::NavigationContextImpl*)context; +// Loads the error page. +- (void)loadErrorPageForNavigationItem:(web::NavigationItemImpl*)item + navigationContext:(web::NavigationContextImpl*)context; // Aborts any load for both the web view and web controller. - (void)abortLoad; // Updates the internal state and informs the delegate that any outstanding load @@ -1721,9 +1719,8 @@ } } -- (void)loadErrorInNativeViewForNavigationItem:(web::NavigationItemImpl*)item - navigationContext: - (web::NavigationContextImpl*)context { +- (void)loadErrorPageForNavigationItem:(web::NavigationItemImpl*)item + navigationContext:(web::NavigationContextImpl*)context { const GURL currentURL = item ? item->GetVirtualURL() : GURL::EmptyGURL(); NSError* error = context->GetError(); DCHECK(error); @@ -2998,8 +2995,8 @@ } if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) { - [self loadErrorInNativeViewForNavigationItem:self.currentNavItem - navigationContext:navigationContext]; + [self loadErrorPageForNavigationItem:self.currentNavItem + navigationContext:navigationContext]; } else { web::ErrorRetryState errorRetryState = self.currentNavItem->GetErrorRetryState(); @@ -3009,13 +3006,13 @@ // history. This can arise if the failure occurs after the navigation is // committed or if this is is a history navigation to a previously loaded // page. - [self loadErrorInNativeViewForNavigationItem:self.currentNavItem - navigationContext:navigationContext]; + [self loadErrorPageForNavigationItem:self.currentNavItem + navigationContext:navigationContext]; } else { // The navigation item that failed to load is not yet in // WKBackForwardList. This can arise when the failure happens during // provisional load of a new page. Kick off a placeholder load to insert a - // WKBackForwardListItem. |loadErrorInNativeViewForNavigationItem| will be + // WKBackForwardListItem. |loadErrorPageForNavigationItem| will be // called when the placeholder finishes loading. DCHECK_EQ(web::ErrorRetryState::kNoNavigationError, errorRetryState); web::NavigationContextImpl* placeholderNavigationContext = @@ -4730,8 +4727,7 @@ } else if (context->GetError()) { item->SetErrorRetryState( web::ErrorRetryState::kReadyToDisplayErrorForFailedNavigation); - [self loadErrorInNativeViewForNavigationItem:item - navigationContext:context]; + [self loadErrorPageForNavigationItem:item navigationContext:context]; } else { // This is a back/forward navigation to a native error page. DCHECK_EQ(
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn index 9496d5d9..67447fc 100644 --- a/ios/web_view/BUILD.gn +++ b/ios/web_view/BUILD.gn
@@ -75,10 +75,10 @@ ios_web_view_public_headers += [ "public/cwv_autofill_controller.h", "public/cwv_autofill_controller_delegate.h", - "public/cwv_autofill_credit_card.h", "public/cwv_autofill_data_manager.h", "public/cwv_autofill_profile.h", "public/cwv_autofill_suggestion.h", + "public/cwv_credit_card.h", "public/cwv_preferences_autofill.h", "public/cwv_web_view_autofill.h", "public/cwv_web_view_configuration_autofill.h", @@ -195,14 +195,14 @@ "internal/autofill/cwv_autofill_client_ios_bridge.h", "internal/autofill/cwv_autofill_controller.mm", "internal/autofill/cwv_autofill_controller_internal.h", - "internal/autofill/cwv_autofill_credit_card.mm", - "internal/autofill/cwv_autofill_credit_card_internal.h", "internal/autofill/cwv_autofill_data_manager.mm", "internal/autofill/cwv_autofill_data_manager_internal.h", "internal/autofill/cwv_autofill_profile.mm", "internal/autofill/cwv_autofill_profile_internal.h", "internal/autofill/cwv_autofill_suggestion.mm", "internal/autofill/cwv_autofill_suggestion_internal.h", + "internal/autofill/cwv_credit_card.mm", + "internal/autofill/cwv_credit_card_internal.h", ] }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm index d215a53..3bb9e2af 100644 --- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm +++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -22,8 +22,8 @@ #import "ios/web/public/web_state/web_state_observer_bridge.h" #include "ios/web_view/internal/app/application_context.h" #import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h" -#import "ios/web_view/internal/autofill/cwv_autofill_credit_card_internal.h" #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h" +#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h" #import "ios/web_view/internal/autofill/web_view_autofill_client_ios.h" #include "ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h" #include "ios/web_view/internal/signin/web_view_identity_manager_factory.h" @@ -265,8 +265,7 @@ if ([_delegate respondsToSelector:@selector (autofillController:decidePolicyForLocalStorageOfCreditCard :decisionHandler:)]) { - CWVAutofillCreditCard* card = - [[CWVAutofillCreditCard alloc] initWithCreditCard:creditCard]; + CWVCreditCard* card = [[CWVCreditCard alloc] initWithCreditCard:creditCard]; __block base::RepeatingClosure scopedCallback = callback; [_delegate autofillController:self decidePolicyForLocalStorageOfCreditCard:card
diff --git a/ios/web_view/internal/autofill/cwv_autofill_data_manager.mm b/ios/web_view/internal/autofill/cwv_autofill_data_manager.mm index 3515a2c..2d92c35 100644 --- a/ios/web_view/internal/autofill/cwv_autofill_data_manager.mm +++ b/ios/web_view/internal/autofill/cwv_autofill_data_manager.mm
@@ -5,8 +5,8 @@ #import "ios/web_view/internal/autofill/cwv_autofill_data_manager_internal.h" #include "components/autofill/core/browser/personal_data_manager.h" -#import "ios/web_view/internal/autofill/cwv_autofill_credit_card_internal.h" #import "ios/web_view/internal/autofill/cwv_autofill_profile_internal.h" +#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -38,12 +38,12 @@ return [profiles copy]; } -- (NSArray<CWVAutofillCreditCard*>*)creditCards { +- (NSArray<CWVCreditCard*>*)creditCards { NSMutableArray* creditCards = [NSMutableArray array]; for (autofill::CreditCard* internalCard : _personalDataManager->GetCreditCards()) { - CWVAutofillCreditCard* creditCard = - [[CWVAutofillCreditCard alloc] initWithCreditCard:*internalCard]; + CWVCreditCard* creditCard = + [[CWVCreditCard alloc] initWithCreditCard:*internalCard]; [creditCards addObject:creditCard]; } return [creditCards copy]; @@ -57,11 +57,11 @@ _personalDataManager->RemoveByGUID(profile.internalProfile->guid()); } -- (void)updateCreditCard:(CWVAutofillCreditCard*)creditCard { +- (void)updateCreditCard:(CWVCreditCard*)creditCard { _personalDataManager->UpdateCreditCard(*creditCard.internalCard); } -- (void)deleteCreditCard:(CWVAutofillCreditCard*)creditCard { +- (void)deleteCreditCard:(CWVCreditCard*)creditCard { _personalDataManager->RemoveByGUID(creditCard.internalCard->guid()); }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_credit_card.mm b/ios/web_view/internal/autofill/cwv_credit_card.mm similarity index 94% rename from ios/web_view/internal/autofill/cwv_autofill_credit_card.mm rename to ios/web_view/internal/autofill/cwv_credit_card.mm index d143f616..38a67d2 100644 --- a/ios/web_view/internal/autofill/cwv_autofill_credit_card.mm +++ b/ios/web_view/internal/autofill/cwv_credit_card.mm
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ios/web_view/internal/autofill/cwv_autofill_credit_card_internal.h" +#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h" #include "base/strings/sys_string_conversions.h" #include "components/autofill/core/browser/credit_card.h" @@ -12,7 +12,7 @@ #error "This file requires ARC support." #endif -@interface CWVAutofillCreditCard () +@interface CWVCreditCard () // Sets |value| for |type| in |_internalCard|. - (void)setValue:(NSString*)value forType:(autofill::ServerFieldType)type; @@ -21,7 +21,7 @@ @end -@implementation CWVAutofillCreditCard { +@implementation CWVCreditCard { autofill::CreditCard _internalCard; }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_credit_card_internal.h b/ios/web_view/internal/autofill/cwv_credit_card_internal.h similarity index 60% rename from ios/web_view/internal/autofill/cwv_autofill_credit_card_internal.h rename to ios/web_view/internal/autofill/cwv_credit_card_internal.h index 42c33f3..cac5c7d 100644 --- a/ios/web_view/internal/autofill/cwv_autofill_credit_card_internal.h +++ b/ios/web_view/internal/autofill/cwv_credit_card_internal.h
@@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_AUTOFILL_CREDIT_CARD_INTERNAL_H_ -#define IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_AUTOFILL_CREDIT_CARD_INTERNAL_H_ +#ifndef IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_CREDIT_CARD_INTERNAL_H_ +#define IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_CREDIT_CARD_INTERNAL_H_ -#import "ios/web_view/public/cwv_autofill_credit_card.h" +#import "ios/web_view/public/cwv_credit_card.h" namespace autofill { class CreditCard; } // namespace autofill -@interface CWVAutofillCreditCard () +@interface CWVCreditCard () // The internal autofill credit card that is wrapped by this object. @property(nonatomic, readonly) autofill::CreditCard* internalCard; @@ -21,4 +21,4 @@ @end -#endif // IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_AUTOFILL_CREDIT_CARD_INTERNAL_H_ +#endif // IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_CREDIT_CARD_INTERNAL_H_
diff --git a/ios/web_view/public/cwv_autofill_controller_delegate.h b/ios/web_view/public/cwv_autofill_controller_delegate.h index 6708d4ff..16dffb29 100644 --- a/ios/web_view/public/cwv_autofill_controller_delegate.h +++ b/ios/web_view/public/cwv_autofill_controller_delegate.h
@@ -12,8 +12,8 @@ NS_ASSUME_NONNULL_BEGIN @class CWVAutofillController; -@class CWVAutofillCreditCard; @class CWVAutofillFormSuggestion; +@class CWVCreditCard; // Storage policies for autofill data. typedef NS_ENUM(NSInteger, CWVStoragePolicy) { @@ -67,7 +67,7 @@ // Pass final decision to |decisionHandler|. Must only be called once. // If not implemented, assumes CWVStoragePolicyReject. - (void)autofillController:(CWVAutofillController*)autofillController - decidePolicyForLocalStorageOfCreditCard:(CWVAutofillCreditCard*)creditCard + decidePolicyForLocalStorageOfCreditCard:(CWVCreditCard*)creditCard decisionHandler:(void (^)(CWVStoragePolicy policy)) decisionHandler;
diff --git a/ios/web_view/public/cwv_autofill_data_manager.h b/ios/web_view/public/cwv_autofill_data_manager.h index 04780f28..922fcca 100644 --- a/ios/web_view/public/cwv_autofill_data_manager.h +++ b/ios/web_view/public/cwv_autofill_data_manager.h
@@ -11,8 +11,8 @@ NS_ASSUME_NONNULL_BEGIN -@class CWVAutofillCreditCard; @class CWVAutofillProfile; +@class CWVCreditCard; CWV_EXPORT // Exposes saved autofill data such as address profiles and credit cards. @@ -22,7 +22,7 @@ @property(nonatomic, readonly) NSArray<CWVAutofillProfile*>* profiles; // Returns all saved credit cards for payment autofill. -@property(nonatomic, readonly) NSArray<CWVAutofillCreditCard*>* creditCards; +@property(nonatomic, readonly) NSArray<CWVCreditCard*>* creditCards; - (instancetype)init NS_UNAVAILABLE; @@ -33,10 +33,10 @@ - (void)deleteProfile:(CWVAutofillProfile*)profile; // Updates the card. -- (void)updateCreditCard:(CWVAutofillCreditCard*)creditCard; +- (void)updateCreditCard:(CWVCreditCard*)creditCard; // Deletes the card. -- (void)deleteCreditCard:(CWVAutofillCreditCard*)creditCard; +- (void)deleteCreditCard:(CWVCreditCard*)creditCard; @end
diff --git a/ios/web_view/public/cwv_autofill_credit_card.h b/ios/web_view/public/cwv_credit_card.h similarity index 86% rename from ios/web_view/public/cwv_autofill_credit_card.h rename to ios/web_view/public/cwv_credit_card.h index 44b0d0b..ed8509f 100644 --- a/ios/web_view/public/cwv_autofill_credit_card.h +++ b/ios/web_view/public/cwv_credit_card.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef IOS_WEB_VIEW_PUBLIC_CWV_AUTOFILL_CREDIT_CARD_H_ -#define IOS_WEB_VIEW_PUBLIC_CWV_AUTOFILL_CREDIT_CARD_H_ +#ifndef IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_H_ +#define IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_H_ #import <Foundation/Foundation.h> @@ -13,7 +13,7 @@ CWV_EXPORT // Represents a credit card for autofilling payment forms. -@interface CWVAutofillCreditCard : NSObject +@interface CWVCreditCard : NSObject // The full name of the card holder. e.g. "John Doe". @property(nonatomic, copy, nullable) NSString* cardHolderFullName; @@ -38,4 +38,4 @@ NS_ASSUME_NONNULL_END -#endif // IOS_WEB_VIEW_PUBLIC_CWV_AUTOFILL_CREDIT_CARD_H_ +#endif // IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_H_
diff --git a/ios/web_view/shell/shell_autofill_delegate.m b/ios/web_view/shell/shell_autofill_delegate.m index 2de3aa7..7e7133a 100644 --- a/ios/web_view/shell/shell_autofill_delegate.m +++ b/ios/web_view/shell/shell_autofill_delegate.m
@@ -107,7 +107,7 @@ } - (void)autofillController:(CWVAutofillController*)autofillController - decidePolicyForLocalStorageOfCreditCard:(CWVAutofillCreditCard*)creditCard + decidePolicyForLocalStorageOfCreditCard:(CWVCreditCard*)creditCard decisionHandler: (void (^)(CWVStoragePolicy))decisionHandler { NSString* cardSummary = [NSString
diff --git a/media/audio/alsa/alsa_output_unittest.cc b/media/audio/alsa/alsa_output_unittest.cc index a5b316b..c63dd8f 100644 --- a/media/audio/alsa/alsa_output_unittest.cc +++ b/media/audio/alsa/alsa_output_unittest.cc
@@ -117,7 +117,7 @@ mock_manager_.reset(new StrictMock<MockAudioManagerAlsa>()); } - virtual ~AlsaPcmOutputStreamTest() { mock_manager_->Shutdown(); } + ~AlsaPcmOutputStreamTest() override { mock_manager_->Shutdown(); } AlsaPcmOutputStream* CreateStream(ChannelLayout layout) { return CreateStream(layout, kTestFramesPerPacket);
diff --git a/media/audio/audio_debug_recording_helper_unittest.cc b/media/audio/audio_debug_recording_helper_unittest.cc index d93475e..a96f633 100644 --- a/media/audio/audio_debug_recording_helper_unittest.cc +++ b/media/audio/audio_debug_recording_helper_unittest.cc
@@ -41,7 +41,7 @@ ~MockAudioDebugFileWriter() override = default; MOCK_METHOD1(DoStart, void(bool)); - void Start(base::File file) { DoStart(file.IsValid()); } + void Start(base::File file) override { DoStart(file.IsValid()); } MOCK_METHOD0(Stop, void()); // Functions with move-only types as arguments can't be mocked directly, so
diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc index aefdd81..96a23fb2 100644 --- a/media/audio/audio_input_controller_unittest.cc +++ b/media/audio/audio_input_controller_unittest.cc
@@ -61,7 +61,7 @@ public: MockAudioInputControllerEventHandler() = default; - void OnLog(base::StringPiece) {} + void OnLog(base::StringPiece) override {} MOCK_METHOD1(OnCreated, void(bool initially_muted)); MOCK_METHOD1(OnError, void(AudioInputController::ErrorCode error_code)); @@ -87,7 +87,7 @@ public: MockUserInputMonitor() = default; - uint32_t GetKeyPressCount() const { return 0; } + uint32_t GetKeyPressCount() const override { return 0; } MOCK_METHOD0(StartKeyboardMonitoring, void()); MOCK_METHOD0(StopKeyboardMonitoring, void()); @@ -98,14 +98,14 @@ MockAudioInputStream() {} ~MockAudioInputStream() override {} - void Start(AudioInputCallback*) {} - void Stop() {} - void Close() {} - double GetMaxVolume() { return kMaxVolume; } - double GetVolume() { return 0; } - bool SetAutomaticGainControl(bool) { return false; } - bool GetAutomaticGainControl() { return false; } - bool IsMuted() { return false; } + void Start(AudioInputCallback*) override {} + void Stop() override {} + void Close() override {} + double GetMaxVolume() override { return kMaxVolume; } + double GetVolume() override { return 0; } + bool SetAutomaticGainControl(bool) override { return false; } + bool GetAutomaticGainControl() override { return false; } + bool IsMuted() override { return false; } MOCK_METHOD0(Open, bool()); MOCK_METHOD1(SetVolume, void(double));
diff --git a/media/audio/audio_input_stream_data_interceptor_unittest.cc b/media/audio/audio_input_stream_data_interceptor_unittest.cc index 05b8357..0a0260c 100644 --- a/media/audio/audio_input_stream_data_interceptor_unittest.cc +++ b/media/audio/audio_input_stream_data_interceptor_unittest.cc
@@ -28,7 +28,7 @@ class MockStream : public AudioInputStream { public: MockStream() = default; - ~MockStream() = default; + ~MockStream() override = default; MOCK_METHOD0(Open, bool()); MOCK_METHOD1(Start, void(AudioInputStream::AudioInputCallback*)); MOCK_METHOD0(Stop, void()); @@ -44,14 +44,14 @@ class MockDebugRecorder : public AudioDebugRecorder { public: MockDebugRecorder() = default; - ~MockDebugRecorder() = default; + ~MockDebugRecorder() override = default; MOCK_METHOD1(OnData, void(const AudioBus* source)); }; class MockCallback : public AudioInputStream::AudioInputCallback { public: MockCallback() = default; - ~MockCallback() = default; + ~MockCallback() override = default; MOCK_METHOD3(OnData, void(const AudioBus*, base::TimeTicks, double)); MOCK_METHOD0(OnError, void());
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc index f654b07..99e82bd8 100644 --- a/media/audio/audio_output_controller_unittest.cc +++ b/media/audio/audio_output_controller_unittest.cc
@@ -66,7 +66,7 @@ MOCK_METHOD0(OnControllerPlaying, void()); MOCK_METHOD0(OnControllerPaused, void()); MOCK_METHOD0(OnControllerError, void()); - void OnLog(base::StringPiece) {} + void OnLog(base::StringPiece) override {} private: DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerEventHandler);
diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc index 51d8a26..9a8675d 100644 --- a/media/audio/audio_output_device_unittest.cc +++ b/media/audio/audio_output_device_unittest.cc
@@ -55,7 +55,7 @@ class MockRenderCallback : public AudioRendererSink::RenderCallback { public: MockRenderCallback() = default; - virtual ~MockRenderCallback() = default; + ~MockRenderCallback() override = default; MOCK_METHOD4(Render, int(base::TimeDelta delay, @@ -68,7 +68,7 @@ class MockAudioOutputIPC : public AudioOutputIPC { public: MockAudioOutputIPC() = default; - virtual ~MockAudioOutputIPC() = default; + ~MockAudioOutputIPC() override = default; MOCK_METHOD3(RequestDeviceAuthorization, void(AudioOutputIPCDelegate* delegate, @@ -88,7 +88,7 @@ class AudioOutputDeviceTest : public testing::Test { public: AudioOutputDeviceTest(); - ~AudioOutputDeviceTest(); + ~AudioOutputDeviceTest() override; void ReceiveAuthorization(OutputDeviceStatus device_status); void StartAudioDevice();
diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc index 81d463e..2053a20 100644 --- a/media/audio/audio_output_proxy_unittest.cc +++ b/media/audio/audio_output_proxy_unittest.cc
@@ -70,17 +70,17 @@ FakeAudioOutputStream::MakeFakeStream(manager, params_)) { } - void Start(AudioSourceCallback* callback) { + void Start(AudioSourceCallback* callback) override { start_called_ = true; fake_output_stream_->Start(callback); } - void Stop() { + void Stop() override { stop_called_ = true; fake_output_stream_->Stop(); } - ~MockAudioOutputStream() = default; + ~MockAudioOutputStream() override = default; bool start_called() { return start_called_; } bool stop_called() { return stop_called_; }
diff --git a/media/audio/mock_audio_source_callback.h b/media/audio/mock_audio_source_callback.h index 7283c6c..f7c95111 100644 --- a/media/audio/mock_audio_source_callback.h +++ b/media/audio/mock_audio_source_callback.h
@@ -17,7 +17,7 @@ class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { public: MockAudioSourceCallback(); - virtual ~MockAudioSourceCallback(); + ~MockAudioSourceCallback() override; MOCK_METHOD4(OnMoreData, int(base::TimeDelta, base::TimeTicks, int, AudioBus*));
diff --git a/media/audio/virtual_audio_input_stream_unittest.cc b/media/audio/virtual_audio_input_stream_unittest.cc index e5bae4a..004870c 100644 --- a/media/audio/virtual_audio_input_stream_unittest.cc +++ b/media/audio/virtual_audio_input_stream_unittest.cc
@@ -42,7 +42,7 @@ InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal)); } - virtual ~MockInputCallback() = default; + ~MockInputCallback() override = default; MOCK_METHOD3(OnData, void(const AudioBus* source,
diff --git a/media/audio/virtual_audio_output_stream_unittest.cc b/media/audio/virtual_audio_output_stream_unittest.cc index 8998d01..11cce9c 100644 --- a/media/audio/virtual_audio_output_stream_unittest.cc +++ b/media/audio/virtual_audio_output_stream_unittest.cc
@@ -33,7 +33,7 @@ kParams, worker_task_runner, base::Bind(&base::DeletePointer<VirtualAudioInputStream>)) {} - ~MockVirtualAudioInputStream() = default; + ~MockVirtualAudioInputStream() override = default; MOCK_METHOD2(AddInputProvider, void(AudioConverter::InputCallback* input,
diff --git a/media/base/audio_renderer_mixer_input_unittest.cc b/media/base/audio_renderer_mixer_input_unittest.cc index e7941e5d..e6735a2 100644 --- a/media/base/audio_renderer_mixer_input_unittest.cc +++ b/media/base/audio_renderer_mixer_input_unittest.cc
@@ -58,7 +58,7 @@ const AudioParameters& params, AudioLatency::LatencyType latency, const std::string& device_id, - OutputDeviceStatus* device_status) { + OutputDeviceStatus* device_status) override { EXPECT_TRUE(params.IsValid()); if (device_id == kNonexistentDeviceId) { if (device_status) @@ -95,7 +95,7 @@ OutputDeviceInfo GetOutputDeviceInfo(int source_render_frame_id, int session_id, - const std::string& device_id) { + const std::string& device_id) override { OutputDeviceStatus status = OUTPUT_DEVICE_STATUS_OK; if (device_id == kNonexistentDeviceId) status = OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND; @@ -121,7 +121,7 @@ AudioRendererMixer* GetInputMixer() { return mixer_input_->mixer_; } protected: - virtual ~AudioRendererMixerInputTest() = default; + ~AudioRendererMixerInputTest() override = default; base::test::ScopedTaskEnvironment scoped_task_environment_; AudioParameters audio_parameters_;
diff --git a/media/base/audio_renderer_mixer_unittest.cc b/media/base/audio_renderer_mixer_unittest.cc index 101f7447..f45f608 100644 --- a/media/base/audio_renderer_mixer_unittest.cc +++ b/media/base/audio_renderer_mixer_unittest.cc
@@ -99,7 +99,7 @@ return mixer_.get(); }; - void ReturnMixer(AudioRendererMixer* mixer) { + void ReturnMixer(AudioRendererMixer* mixer) override { EXPECT_EQ(mixer_.get(), mixer); }
diff --git a/media/base/mock_audio_renderer_sink.h b/media/base/mock_audio_renderer_sink.h index 11a6f83..4551a2e 100644 --- a/media/base/mock_audio_renderer_sink.h +++ b/media/base/mock_audio_renderer_sink.h
@@ -31,9 +31,9 @@ MOCK_METHOD1(SetVolume, bool(double volume)); MOCK_METHOD0(CurrentThreadIsRenderingThread, bool()); - OutputDeviceInfo GetOutputDeviceInfo(); + OutputDeviceInfo GetOutputDeviceInfo() override; - bool IsOptimizedForHardwareParameters(); + bool IsOptimizedForHardwareParameters() override; void SwitchOutputDevice(const std::string& device_id, const OutputDeviceStatusCB& callback) override;
diff --git a/media/base/mock_demuxer_host.h b/media/base/mock_demuxer_host.h index 6ab7814..55329b7 100644 --- a/media/base/mock_demuxer_host.h +++ b/media/base/mock_demuxer_host.h
@@ -15,7 +15,7 @@ class MockDemuxerHost : public DemuxerHost { public: MockDemuxerHost(); - virtual ~MockDemuxerHost(); + ~MockDemuxerHost() override; MOCK_METHOD1(OnBufferedTimeRangesChanged, void(const Ranges<base::TimeDelta>&));
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h index ba1aa367..3b38ba5 100644 --- a/media/base/mock_filters.h +++ b/media/base/mock_filters.h
@@ -68,7 +68,7 @@ class MockPipeline : public Pipeline { public: MockPipeline(); - virtual ~MockPipeline(); + ~MockPipeline() override; // Note: Start() and Resume() declarations are not actually overrides; they // take unique_ptr* instead of unique_ptr so that they can be mock methods. @@ -89,11 +89,11 @@ const PipelineStatusCB&)); void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& id, - base::OnceClosure callback) { + base::OnceClosure callback) override { MockOnEnabledAudioTracksChanged(id, callback); } void OnSelectedVideoTrackChanged(base::Optional<MediaTrack::Id> id, - base::OnceClosure callback) { + base::OnceClosure callback) override { MockOnSelectedVideoTrackChanged(id, callback); } @@ -140,10 +140,10 @@ class MockDemuxer : public Demuxer { public: MockDemuxer(); - virtual ~MockDemuxer(); + ~MockDemuxer() override; // Demuxer implementation. - virtual std::string GetDisplayName() const; + std::string GetDisplayName() const override; MOCK_METHOD3(Initialize, void(DemuxerHost* host, const PipelineStatusCB& cb, bool)); MOCK_METHOD1(StartWaitingForSeek, void(base::TimeDelta)); @@ -159,12 +159,12 @@ void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& id, base::TimeDelta time, - TrackChangeCB cb) { + TrackChangeCB cb) override { MockOnEnabledAudioTracksChanged(id, time, cb); } void OnSelectedVideoTrackChanged(const std::vector<MediaTrack::Id>& id, base::TimeDelta time, - TrackChangeCB cb) { + TrackChangeCB cb) override { MockOnSelectedVideoTrackChanged(id, time, cb); } @@ -184,7 +184,7 @@ class MockDemuxerStream : public DemuxerStream { public: explicit MockDemuxerStream(DemuxerStream::Type type); - virtual ~MockDemuxerStream(); + ~MockDemuxerStream() override; // DemuxerStream implementation. Type type() const override; @@ -212,10 +212,10 @@ public: explicit MockVideoDecoder( const std::string& decoder_name = "MockVideoDecoder"); - virtual ~MockVideoDecoder(); + ~MockVideoDecoder() override; // VideoDecoder implementation. - virtual std::string GetDisplayName() const; + std::string GetDisplayName() const override; MOCK_METHOD6( Initialize, void(const VideoDecoderConfig& config, @@ -239,10 +239,10 @@ public: explicit MockAudioDecoder( const std::string& decoder_name = "MockAudioDecoder"); - virtual ~MockAudioDecoder(); + ~MockAudioDecoder() override; // AudioDecoder implementation. - virtual std::string GetDisplayName() const; + std::string GetDisplayName() const override; MOCK_METHOD5( Initialize, void(const AudioDecoderConfig& config, @@ -280,7 +280,7 @@ class MockVideoRenderer : public VideoRenderer { public: MockVideoRenderer(); - virtual ~MockVideoRenderer(); + ~MockVideoRenderer() override; // VideoRenderer implementation. MOCK_METHOD5(Initialize, @@ -301,7 +301,7 @@ class MockAudioRenderer : public AudioRenderer { public: MockAudioRenderer(); - virtual ~MockAudioRenderer(); + ~MockAudioRenderer() override; // AudioRenderer implementation. MOCK_METHOD4(Initialize, @@ -321,7 +321,7 @@ class MockRenderer : public Renderer { public: MockRenderer(); - virtual ~MockRenderer(); + ~MockRenderer() override; // Renderer implementation. MOCK_METHOD3(Initialize, @@ -340,7 +340,7 @@ const CdmAttachedCB& cdm_attached_cb)); void OnSelectedVideoTracksChanged(const std::vector<DemuxerStream*>& id, - base::OnceClosure cb) { + base::OnceClosure cb) override { MockOnSelectedVideoTrackChanged(id, cb); } @@ -362,7 +362,7 @@ class MockTimeSource : public TimeSource { public: MockTimeSource(); - virtual ~MockTimeSource(); + ~MockTimeSource() override; // TimeSource implementation. MOCK_METHOD0(StartTicking, void()); @@ -381,7 +381,7 @@ class MockTextTrack : public TextTrack { public: MockTextTrack(); - virtual ~MockTextTrack(); + ~MockTextTrack() override; MOCK_METHOD5(addWebVTTCue, void(const base::TimeDelta& start, const base::TimeDelta& end, @@ -431,7 +431,7 @@ class MockDecryptor : public Decryptor { public: MockDecryptor(); - virtual ~MockDecryptor(); + ~MockDecryptor() override; MOCK_METHOD2(RegisterNewKeyCB, void(StreamType stream_type, const NewKeyCB& new_key_cb));
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc index 9d1f0fe4..07229db 100644 --- a/media/base/pipeline_impl_unittest.cc +++ b/media/base/pipeline_impl_unittest.cc
@@ -123,7 +123,7 @@ EXPECT_CALL(*demuxer_, GetStartTime()).WillRepeatedly(Return(start_time_)); } - virtual ~PipelineImplTest() { + ~PipelineImplTest() override { if (pipeline_->IsRunning()) { ExpectDemuxerStop();
diff --git a/media/base/text_renderer_unittest.cc b/media/base/text_renderer_unittest.cc index 4190174..c176d1a 100644 --- a/media/base/text_renderer_unittest.cc +++ b/media/base/text_renderer_unittest.cc
@@ -34,9 +34,7 @@ : destroy_cb_(destroy_cb), config_(config) { } - virtual ~FakeTextTrack() { - destroy_cb_.Run(); - } + ~FakeTextTrack() override { destroy_cb_.Run(); } MOCK_METHOD5(addWebVTTCue, void(const base::TimeDelta& start, const base::TimeDelta& end,
diff --git a/media/blink/mock_webassociatedurlloader.h b/media/blink/mock_webassociatedurlloader.h index fccb7b30..6b7c320 100644 --- a/media/blink/mock_webassociatedurlloader.h +++ b/media/blink/mock_webassociatedurlloader.h
@@ -15,7 +15,7 @@ class MockWebAssociatedURLLoader : public blink::WebAssociatedURLLoader { public: MockWebAssociatedURLLoader(); - virtual ~MockWebAssociatedURLLoader(); + ~MockWebAssociatedURLLoader() override; MOCK_METHOD2(LoadAsynchronously, void(const blink::WebURLRequest& request,
diff --git a/media/blink/multibuffer_data_source_unittest.cc b/media/blink/multibuffer_data_source_unittest.cc index 96647c0..b1843d6 100644 --- a/media/blink/multibuffer_data_source_unittest.cc +++ b/media/blink/multibuffer_data_source_unittest.cc
@@ -161,7 +161,7 @@ class MockBufferedDataSourceHost : public BufferedDataSourceHost { public: MockBufferedDataSourceHost() = default; - virtual ~MockBufferedDataSourceHost() = default; + ~MockBufferedDataSourceHost() override = default; MOCK_METHOD1(SetTotalBytes, void(int64_t total_bytes)); MOCK_METHOD2(AddBufferedByteRange, void(int64_t start, int64_t end));
diff --git a/media/blink/video_decode_stats_reporter_unittest.cc b/media/blink/video_decode_stats_reporter_unittest.cc index 6352b95..54761fc 100644 --- a/media/blink/video_decode_stats_reporter_unittest.cc +++ b/media/blink/video_decode_stats_reporter_unittest.cc
@@ -71,7 +71,7 @@ ~RecordInterceptor() override = default; // Until move-only types work. - void StartNewRecord(mojom::PredictionFeaturesPtr features) { + void StartNewRecord(mojom::PredictionFeaturesPtr features) override { MockStartNewRecord(features->profile, features->video_size, features->frames_per_sec); } @@ -81,7 +81,7 @@ const gfx::Size& natural_size, int frames_per_sec)); - void UpdateRecord(mojom::PredictionTargetsPtr targets) { + void UpdateRecord(mojom::PredictionTargetsPtr targets) override { MockUpdateRecord(targets->frames_decoded, targets->frames_dropped, targets->frames_decoded_power_efficient); }
diff --git a/media/blink/video_frame_compositor_unittest.cc b/media/blink/video_frame_compositor_unittest.cc index 62eab1a2..69e39a8 100644 --- a/media/blink/video_frame_compositor_unittest.cc +++ b/media/blink/video_frame_compositor_unittest.cc
@@ -46,7 +46,7 @@ VideoFrameCompositorTest() : client_(new StrictMock<MockWebVideoFrameSubmitter>()) {} - void SetUp() { + void SetUp() override { if (IsSurfaceLayerForVideoEnabled()) { feature_list_.InitFromCommandLine("UseSurfaceLayerForVideo", "");
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc index 9dc5a75..b9700d6d 100644 --- a/media/blink/webmediaplayer_impl_unittest.cc +++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -187,7 +187,7 @@ class MockWebMediaPlayerDelegate : public WebMediaPlayerDelegate { public: MockWebMediaPlayerDelegate() = default; - ~MockWebMediaPlayerDelegate() = default; + ~MockWebMediaPlayerDelegate() override = default; // WebMediaPlayerDelegate implementation. int AddObserver(Observer* observer) override { @@ -290,10 +290,10 @@ MockVideoFrameCompositor( const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) : VideoFrameCompositor(task_runner, nullptr) {} - ~MockVideoFrameCompositor() = default; + ~MockVideoFrameCompositor() override = default; // MOCK_METHOD doesn't like OnceCallback. - void SetOnNewProcessedFrameCallback(OnNewProcessedFrameCB cb) {} + void SetOnNewProcessedFrameCallback(OnNewProcessedFrameCB cb) override {} MOCK_METHOD0(GetCurrentFrameAndUpdateIfStale, scoped_refptr<VideoFrame>()); MOCK_METHOD2(EnableSubmission, void(const viz::FrameSinkId&, media::VideoRotation));
diff --git a/media/capture/video/file_video_capture_device_unittest.cc b/media/capture/video/file_video_capture_device_unittest.cc index c48e33b..b6c88be 100644 --- a/media/capture/video/file_video_capture_device_unittest.cc +++ b/media/capture/video/file_video_capture_device_unittest.cc
@@ -30,7 +30,7 @@ int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp, - int frame_feedback_id = 0) {} + int frame_feedback_id = 0) override {} MOCK_METHOD3(ReserveOutputBuffer, Buffer(const gfx::Size&, VideoPixelFormat, int));
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc index 5702097..1372e1f 100644 --- a/media/capture/video/video_capture_device_unittest.cc +++ b/media/capture/video/video_capture_device_unittest.cc
@@ -202,7 +202,7 @@ } Buffer ResurrectLastOutputBuffer(const gfx::Size& dimensions, VideoPixelFormat format, - int frame_feedback_id) { + int frame_feedback_id) override { DoResurrectLastOutputBuffer(); NOTREACHED() << "This should never be called"; return Buffer();
diff --git a/media/cast/net/pacing/mock_paced_packet_sender.h b/media/cast/net/pacing/mock_paced_packet_sender.h index 1920ef2..0193ce8 100644 --- a/media/cast/net/pacing/mock_paced_packet_sender.h +++ b/media/cast/net/pacing/mock_paced_packet_sender.h
@@ -14,7 +14,7 @@ class MockPacedPacketSender : public PacedPacketSender { public: MockPacedPacketSender(); - virtual ~MockPacedPacketSender(); + ~MockPacedPacketSender() override; MOCK_METHOD1(SendPackets, bool(const SendPacketVector& packets)); MOCK_METHOD2(ResendPackets, bool(const SendPacketVector& packets,
diff --git a/media/cast/net/rtp/mock_rtp_payload_feedback.h b/media/cast/net/rtp/mock_rtp_payload_feedback.h index ab329d19..90c0943 100644 --- a/media/cast/net/rtp/mock_rtp_payload_feedback.h +++ b/media/cast/net/rtp/mock_rtp_payload_feedback.h
@@ -14,7 +14,7 @@ class MockRtpPayloadFeedback : public RtpPayloadFeedback { public: MockRtpPayloadFeedback(); - virtual ~MockRtpPayloadFeedback(); + ~MockRtpPayloadFeedback() override; MOCK_METHOD1(CastFeedback, void(const RtcpCastMessage& cast_feedback)); };
diff --git a/media/cast/test/mock_cast_transport.h b/media/cast/test/mock_cast_transport.h index aa81dc11..80164427 100644 --- a/media/cast/test/mock_cast_transport.h +++ b/media/cast/test/mock_cast_transport.h
@@ -16,7 +16,7 @@ class MockCastTransport : public CastTransport { public: MockCastTransport(); - virtual ~MockCastTransport(); + ~MockCastTransport() override; MOCK_METHOD2(InsertFrame, void(uint32_t ssrc, const EncodedFrame& frame)); MOCK_METHOD3(SendSenderReport,
diff --git a/media/cdm/simple_cdm_allocator_unittest.cc b/media/cdm/simple_cdm_allocator_unittest.cc index 9cc1352..5f9d1d7 100644 --- a/media/cdm/simple_cdm_allocator_unittest.cc +++ b/media/cdm/simple_cdm_allocator_unittest.cc
@@ -22,14 +22,14 @@ } // cdm::Buffer implementation. - void Destroy() { + void Destroy() override { DestroyCalled(); delete this; } - uint32_t Capacity() const { return buffer_.size(); } - uint8_t* Data() { return buffer_.data(); } - void SetSize(uint32_t size) { size_ = size > Capacity() ? 0 : size; } - uint32_t Size() const { return size_; } + uint32_t Capacity() const override { return buffer_.size(); } + uint8_t* Data() override { return buffer_.data(); } + void SetSize(uint32_t size) override { size_ = size > Capacity() ? 0 : size; } + uint32_t Size() const override { return size_; } private: TestCdmBuffer(uint32_t capacity) : buffer_(capacity), size_(0) {
diff --git a/media/filters/audio_decoder_selector_unittest.cc b/media/filters/audio_decoder_selector_unittest.cc index 5ac7d46..6526741b 100644 --- a/media/filters/audio_decoder_selector_unittest.cc +++ b/media/filters/audio_decoder_selector_unittest.cc
@@ -66,7 +66,7 @@ // InitializeDecoderSelector(). } - ~AudioDecoderSelectorTest() { base::RunLoop().RunUntilIdle(); } + ~AudioDecoderSelectorTest() override { base::RunLoop().RunUntilIdle(); } MOCK_METHOD2(OnDecoderSelected, void(AudioDecoder*, DecryptingDemuxerStream*));
diff --git a/media/filters/blocking_url_protocol_unittest.cc b/media/filters/blocking_url_protocol_unittest.cc index dda4000..9509e7b9 100644 --- a/media/filters/blocking_url_protocol_unittest.cc +++ b/media/filters/blocking_url_protocol_unittest.cc
@@ -27,9 +27,7 @@ CHECK(data_source_.Initialize(GetTestDataFilePath("bear-320x240.webm"))); } - virtual ~BlockingUrlProtocolTest() { - data_source_.Stop(); - } + ~BlockingUrlProtocolTest() override { data_source_.Stop(); } MOCK_METHOD0(OnDataSourceError, void());
diff --git a/media/filters/decrypting_audio_decoder_unittest.cc b/media/filters/decrypting_audio_decoder_unittest.cc index 2aeeb7c..f68e048a 100644 --- a/media/filters/decrypting_audio_decoder_unittest.cc +++ b/media/filters/decrypting_audio_decoder_unittest.cc
@@ -65,9 +65,7 @@ decoded_frame_(NULL), decoded_frame_list_() {} - virtual ~DecryptingAudioDecoderTest() { - Destroy(); - } + ~DecryptingAudioDecoderTest() override { Destroy(); } void InitializeAndExpectResult(const AudioDecoderConfig& config, bool success) {
diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc index 6b712e36..2650a45a 100644 --- a/media/filters/decrypting_demuxer_stream_unittest.cc +++ b/media/filters/decrypting_demuxer_stream_unittest.cc
@@ -82,7 +82,7 @@ encrypted_buffer_(CreateFakeEncryptedStreamBuffer(false)), decrypted_buffer_(new DecoderBuffer(kFakeBufferSize)) {} - virtual ~DecryptingDemuxerStreamTest() { + ~DecryptingDemuxerStreamTest() override { if (is_initialized_) EXPECT_CALL(*decryptor_, CancelDecrypt(_)); demuxer_stream_.reset();
diff --git a/media/filters/decrypting_video_decoder_unittest.cc b/media/filters/decrypting_video_decoder_unittest.cc index 9bf42d6..095dc3a 100644 --- a/media/filters/decrypting_video_decoder_unittest.cc +++ b/media/filters/decrypting_video_decoder_unittest.cc
@@ -59,9 +59,7 @@ VideoFrame::CreateBlackFrame(TestVideoConfig::NormalCodedSize())), null_video_frame_(scoped_refptr<VideoFrame>()) {} - virtual ~DecryptingVideoDecoderTest() { - Destroy(); - } + ~DecryptingVideoDecoderTest() override { Destroy(); } enum CdmType { CDM_WITHOUT_DECRYPTOR, CDM_WITH_DECRYPTOR };
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index 905f356..d4b55e6 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -106,7 +106,7 @@ protected: FFmpegDemuxerTest() = default; - virtual ~FFmpegDemuxerTest() { Shutdown(); } + ~FFmpegDemuxerTest() override { Shutdown(); } void Shutdown() { if (demuxer_)
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index 5644e95..cab1fcd 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -69,9 +69,7 @@ corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame"); } - virtual ~FFmpegVideoDecoderTest() { - Destroy(); - } + ~FFmpegVideoDecoderTest() override { Destroy(); } void Initialize() { InitializeWithConfig(TestVideoConfig::Normal());
diff --git a/media/filters/pipeline_controller_unittest.cc b/media/filters/pipeline_controller_unittest.cc index 1d033fe..390ed4a 100644 --- a/media/filters/pipeline_controller_unittest.cc +++ b/media/filters/pipeline_controller_unittest.cc
@@ -149,8 +149,8 @@ const AddTextTrackDoneCB& done_cb) override {} void OnWaitingForDecryptionKey() override {} void OnVideoNaturalSizeChange(const gfx::Size& size) override {} - void OnAudioConfigChange(const AudioDecoderConfig& config) {} - void OnVideoConfigChange(const VideoDecoderConfig& config) {} + void OnAudioConfigChange(const AudioDecoderConfig& config) override {} + void OnVideoConfigChange(const VideoDecoderConfig& config) override {} void OnVideoOpacityChange(bool opaque) override {} void OnVideoAverageKeyframeDistanceUpdate() override {} void OnAudioDecoderChange(const std::string& name) override {}
diff --git a/media/filters/video_decoder_selector_unittest.cc b/media/filters/video_decoder_selector_unittest.cc index 0bf1757..aeb300d 100644 --- a/media/filters/video_decoder_selector_unittest.cc +++ b/media/filters/video_decoder_selector_unittest.cc
@@ -64,7 +64,7 @@ // InitializeDecoderSelector(). } - ~VideoDecoderSelectorTest() { base::RunLoop().RunUntilIdle(); } + ~VideoDecoderSelectorTest() override { base::RunLoop().RunUntilIdle(); } MOCK_METHOD2(OnDecoderSelected, void(VideoDecoder*, DecryptingDemuxerStream*));
diff --git a/media/formats/webm/webm_parser_unittest.cc b/media/formats/webm/webm_parser_unittest.cc index b24fb48..11f3380 100644 --- a/media/formats/webm/webm_parser_unittest.cc +++ b/media/formats/webm/webm_parser_unittest.cc
@@ -27,7 +27,7 @@ class MockWebMParserClient : public WebMParserClient { public: - virtual ~MockWebMParserClient() = default; + ~MockWebMParserClient() override = default; // WebMParserClient methods. MOCK_METHOD1(OnListStart, WebMParserClient*(int));
diff --git a/media/gpu/accelerated_video_decoder.h b/media/gpu/accelerated_video_decoder.h index d2c08c2c..6b7bd96 100644 --- a/media/gpu/accelerated_video_decoder.h +++ b/media/gpu/accelerated_video_decoder.h
@@ -9,6 +9,7 @@ #include <stdint.h> #include "base/macros.h" +#include "media/base/decrypt_config.h" #include "media/gpu/media_gpu_export.h" #include "ui/gfx/geometry/size.h" @@ -28,6 +29,14 @@ // the passed stream |id|. virtual void SetStream(int32_t id, const uint8_t* ptr, size_t size) = 0; + // This is the same as SetStream() but is for setting an encrypted buffer. + // |decrypt_config| specifies the decryption configuration of the specified + // buffer. If the stream is set with this method, Decode() may return kNoKey. + virtual void SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) = 0; + // Have the decoder flush its state and trigger output of all previously // decoded surfaces. Return false on failure. virtual bool Flush() WARN_UNUSED_RESULT = 0; @@ -49,6 +58,8 @@ kRanOutOfSurfaces, // Waiting for the client to free up output surfaces. kNeedContextUpdate, // Waiting for the client to update decoding context // with data acquired from the accelerator. + kNoKey, // The buffer is encrypted and could not be processed because the + // key for decryption is missing. }; // Try to decode more of the stream, returning decoded frames asynchronously.
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc index ebc7e2b..084ff56a 100644 --- a/media/gpu/h264_decoder.cc +++ b/media/gpu/h264_decoder.cc
@@ -601,8 +601,8 @@ default: // May be recoverable. DVLOG(1) << "Invalid modification_of_pic_nums_idc=" - << list_mod->modification_of_pic_nums_idc - << " in position " << i; + << list_mod->modification_of_pic_nums_idc << " in position " + << i; break; } @@ -627,8 +627,8 @@ } DVLOG_IF(1, pic->pic_order_cnt < last_output_poc_) - << "Outputting out of order, likely a broken stream: " - << last_output_poc_ << " -> " << pic->pic_order_cnt; + << "Outputting out of order, likely a broken stream: " << last_output_poc_ + << " -> " << pic->pic_order_cnt; last_output_poc_ = pic->pic_order_cnt; DVLOG(4) << "Posting output task for POC: " << pic->pic_order_cnt; @@ -1234,6 +1234,14 @@ parser_.SetStream(ptr, size); } +void H264Decoder::SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) { + state_ = kError; + NOTIMPLEMENTED(); +} + H264Decoder::DecodeResult H264Decoder::Decode() { if (state_ == kError) { DVLOG(1) << "Decoder in error state";
diff --git a/media/gpu/h264_decoder.h b/media/gpu/h264_decoder.h index 40d274c..f054c68 100644 --- a/media/gpu/h264_decoder.h +++ b/media/gpu/h264_decoder.h
@@ -106,6 +106,10 @@ // AcceleratedVideoDecoder implementation. void SetStream(int32_t id, const uint8_t* ptr, size_t size) override; + void SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) override; bool Flush() override WARN_UNUSED_RESULT; void Reset() override; DecodeResult Decode() override WARN_UNUSED_RESULT;
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc index 7d51640..856c004 100644 --- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -1433,6 +1433,13 @@ VLOGF(1) << "Error decoding stream"; NOTIFY_ERROR(PLATFORM_FAILURE); return; + + case AcceleratedVideoDecoder::kNoKey: + NOTREACHED() << "Should not reach here unless this class accepts " + "encrypted streams."; + DVLOGF(4) << "No key for decoding stream."; + NOTIFY_ERROR(PLATFORM_FAILURE); + return; } } }
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc index a6d995a8..cac05e1 100644 --- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc +++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -448,6 +448,13 @@ RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", PLATFORM_FAILURE, ); return; + + case AcceleratedVideoDecoder::kNoKey: + NOTREACHED() << "Should not reach here unless this class accepts " + "encrypted streams."; + RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", + PLATFORM_FAILURE, ); + return; } } }
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc index 7c2159da0..59b79e9 100644 --- a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc +++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
@@ -45,6 +45,11 @@ ~MockAcceleratedVideoDecoder() override = default; MOCK_METHOD3(SetStream, void(int32_t id, const uint8_t* ptr, size_t size)); + MOCK_METHOD4(SetEncryptedStream, + void(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& config)); MOCK_METHOD0(Flush, bool()); MOCK_METHOD0(Reset, void()); MOCK_METHOD0(Decode, DecodeResult());
diff --git a/media/gpu/vp8_decoder.cc b/media/gpu/vp8_decoder.cc index 1ae037e..49e5d84 100644 --- a/media/gpu/vp8_decoder.cc +++ b/media/gpu/vp8_decoder.cc
@@ -38,6 +38,14 @@ frame_size_ = size; } +void VP8Decoder::SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) { + state_ = kError; + NOTIMPLEMENTED(); +} + void VP8Decoder::Reset() { curr_pic_ = nullptr; curr_frame_hdr_ = nullptr;
diff --git a/media/gpu/vp8_decoder.h b/media/gpu/vp8_decoder.h index 779722d9..6a331bb 100644 --- a/media/gpu/vp8_decoder.h +++ b/media/gpu/vp8_decoder.h
@@ -68,6 +68,10 @@ // AcceleratedVideoDecoder implementation. void SetStream(int32_t id, const uint8_t* ptr, size_t size) override; + void SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) override; bool Flush() override WARN_UNUSED_RESULT; void Reset() override; DecodeResult Decode() override WARN_UNUSED_RESULT;
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc index d5053b8..777ae0f 100644 --- a/media/gpu/vp9_decoder.cc +++ b/media/gpu/vp9_decoder.cc
@@ -36,6 +36,14 @@ parser_.SetStream(ptr, size); } +void VP9Decoder::SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) { + state_ = kError; + NOTIMPLEMENTED(); +} + bool VP9Decoder::Flush() { DVLOG(2) << "Decoder flush"; Reset();
diff --git a/media/gpu/vp9_decoder.h b/media/gpu/vp9_decoder.h index 672c816..4128a0e 100644 --- a/media/gpu/vp9_decoder.h +++ b/media/gpu/vp9_decoder.h
@@ -97,6 +97,10 @@ // AcceleratedVideoDecoder implementation. void SetStream(int32_t id, const uint8_t* ptr, size_t size) override; + void SetEncryptedStream(int32_t id, + const uint8_t* ptr, + size_t size, + const DecryptConfig& decrypt_config) override; bool Flush() override WARN_UNUSED_RESULT; void Reset() override; DecodeResult Decode() override WARN_UNUSED_RESULT;
diff --git a/media/mojo/clients/mojo_audio_decoder_unittest.cc b/media/mojo/clients/mojo_audio_decoder_unittest.cc index 2991646..26ccc67 100644 --- a/media/mojo/clients/mojo_audio_decoder_unittest.cc +++ b/media/mojo/clients/mojo_audio_decoder_unittest.cc
@@ -65,7 +65,7 @@ message_loop_.task_runner(), std::move(remote_audio_decoder))); } - virtual ~MojoAudioDecoderTest() { + ~MojoAudioDecoderTest() override { // Destroy |mojo_audio_decoder_| first so that the service will be // destructed. Then stop the service thread. Otherwise we'll leak memory. mojo_audio_decoder_.reset();
diff --git a/media/mojo/clients/mojo_cdm_unittest.cc b/media/mojo/clients/mojo_cdm_unittest.cc index aea9c8a..8318571 100644 --- a/media/mojo/clients/mojo_cdm_unittest.cc +++ b/media/mojo/clients/mojo_cdm_unittest.cc
@@ -82,7 +82,7 @@ &mojo_cdm_service_context_)), cdm_binding_(mojo_cdm_service_.get()) {} - virtual ~MojoCdmTest() = default; + ~MojoCdmTest() override = default; void Initialize(ExpectedResult expected_result) { // TODO(xhwang): Add pending init support.
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc index fc2b93da..a8d5d1d 100644 --- a/media/mojo/clients/mojo_renderer_unittest.cc +++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -86,7 +86,7 @@ .WillRepeatedly(Return(base::TimeDelta())); } - virtual ~MojoRendererTest() = default; + ~MojoRendererTest() override = default; void Destroy() { mojo_renderer_.reset();
diff --git a/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc b/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc index 1b78b16..ab450be0 100644 --- a/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc +++ b/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc
@@ -18,7 +18,7 @@ class MojoAudioInputStreamObserverTest : public testing::Test { public: MojoAudioInputStreamObserverTest() {} - ~MojoAudioInputStreamObserverTest() {} + ~MojoAudioInputStreamObserverTest() override {} std::unique_ptr<MojoAudioInputStreamObserver> CreateObserver( media::mojom::AudioInputStreamObserverRequest request) {
diff --git a/media/mojo/services/mojo_audio_input_stream_unittest.cc b/media/mojo/services/mojo_audio_input_stream_unittest.cc index 5271b9227..b73dd5f 100644 --- a/media/mojo/services/mojo_audio_input_stream_unittest.cc +++ b/media/mojo/services/mojo_audio_input_stream_unittest.cc
@@ -59,7 +59,7 @@ class MockDelegate : public AudioInputDelegate { public: MockDelegate() = default; - ~MockDelegate() = default; + ~MockDelegate() override = default; MOCK_METHOD0(GetStreamId, int()); MOCK_METHOD0(OnRecordStream, void());
diff --git a/media/mojo/services/mojo_audio_output_stream_unittest.cc b/media/mojo/services/mojo_audio_output_stream_unittest.cc index 9df6d45..54e0a92c 100644 --- a/media/mojo/services/mojo_audio_output_stream_unittest.cc +++ b/media/mojo/services/mojo_audio_output_stream_unittest.cc
@@ -58,7 +58,7 @@ class MockDelegate : public AudioOutputDelegate { public: MockDelegate() = default; - ~MockDelegate() = default; + ~MockDelegate() override = default; MOCK_METHOD0(GetStreamId, int()); MOCK_METHOD0(OnPlayStream, void());
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc index 32ffa9f8..e938c6d 100644 --- a/media/renderers/audio_renderer_impl_unittest.cc +++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -141,7 +141,7 @@ tick_clock_.Advance(base::TimeDelta::FromSeconds(1)); } - virtual ~AudioRendererImplTest() { + ~AudioRendererImplTest() override { SCOPED_TRACE("~AudioRendererImplTest()"); }
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc index fc31c7dd..7e9f6266 100644 --- a/media/renderers/paint_canvas_video_renderer.cc +++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -1021,13 +1021,13 @@ DCHECK(thread_checker_.CalledOnValidThread()); // Clear cached values. last_image_ = cc::PaintImage(); - last_timestamp_ = kNoTimestamp; + last_id_.reset(); } bool PaintCanvasVideoRenderer::UpdateLastImage( const scoped_refptr<VideoFrame>& video_frame, const Context3D& context_3d) { - if (!last_image_ || video_frame->timestamp() != last_timestamp_ || + if (!last_image_ || video_frame->unique_id() != last_id_ || !last_image_.GetSkImage()->getBackendTexture(true).isValid()) { ResetCache(); @@ -1062,7 +1062,7 @@ CorrectLastImageDimensions(gfx::RectToSkIRect(video_frame->visible_rect())); if (!last_image_) // Couldn't create the SkImage. return false; - last_timestamp_ = video_frame->timestamp(); + last_id_ = video_frame->unique_id(); } last_image_deleting_timer_.Reset(); DCHECK(!!last_image_);
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h index 821443b..d6b559c 100644 --- a/media/renderers/paint_canvas_video_renderer.h +++ b/media/renderers/paint_canvas_video_renderer.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/optional.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "base/timer/timer.h" @@ -160,8 +161,10 @@ // Last image used to draw to the canvas. cc::PaintImage last_image_; - // Timestamp of the videoframe used to generate |last_image_|. - base::TimeDelta last_timestamp_ = media::kNoTimestamp; + + // VideoFrame::unique_id() of the videoframe used to generate |last_image_|. + base::Optional<int> last_id_; + // If |last_image_| is not used for a while, it's deleted to save memory. base::DelayTimer last_image_deleting_timer_; // Stable paint image id to provide to draw image calls.
diff --git a/media/renderers/renderer_impl_unittest.cc b/media/renderers/renderer_impl_unittest.cc index cac4c02..bea7a37d 100644 --- a/media/renderers/renderer_impl_unittest.cc +++ b/media/renderers/renderer_impl_unittest.cc
@@ -94,7 +94,7 @@ EXPECT_CALL(*demuxer_, GetAllStreams()).WillRepeatedly(Return(streams_)); } - virtual ~RendererImplTest() { Destroy(); } + ~RendererImplTest() override { Destroy(); } protected: void Destroy() {
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc index 7c40e1c2..ce74ddd0 100644 --- a/media/renderers/video_renderer_impl_unittest.cc +++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -103,7 +103,7 @@ scoped_refptr<DecoderBuffer>(new DecoderBuffer(0)))); } - virtual ~VideoRendererImplTest() = default; + ~VideoRendererImplTest() override = default; void Initialize() { InitializeWithLowDelay(false);
diff --git a/media/video/mock_video_decode_accelerator.h b/media/video/mock_video_decode_accelerator.h index a129bbf..bcbdded 100644 --- a/media/video/mock_video_decode_accelerator.h +++ b/media/video/mock_video_decode_accelerator.h
@@ -24,7 +24,7 @@ class MockVideoDecodeAccelerator : public VideoDecodeAccelerator { public: MockVideoDecodeAccelerator(); - virtual ~MockVideoDecodeAccelerator(); + ~MockVideoDecodeAccelerator() override; MOCK_METHOD2(Initialize, bool(const Config& config, Client* client)); MOCK_METHOD1(Decode, void(const BitstreamBuffer& bitstream_buffer));
diff --git a/media/video/mock_video_encode_accelerator.h b/media/video/mock_video_encode_accelerator.h index 89c9286..1557b7e0 100644 --- a/media/video/mock_video_encode_accelerator.h +++ b/media/video/mock_video_encode_accelerator.h
@@ -15,7 +15,7 @@ class MockVideoEncodeAccelerator : public VideoEncodeAccelerator { public: MockVideoEncodeAccelerator(); - virtual ~MockVideoEncodeAccelerator(); + ~MockVideoEncodeAccelerator() override; MOCK_METHOD0(GetSupportedProfiles, VideoEncodeAccelerator::SupportedProfiles());
diff --git a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h index 2a3bef2..bb0ee53 100644 --- a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h +++ b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
@@ -10,7 +10,6 @@ #include "mojo/public/cpp/bindings/clone_traits.h" #include "mojo/public/cpp/bindings/equals_traits.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h"
diff --git a/mojo/public/cpp/bindings/struct_traits.h b/mojo/public/cpp/bindings/struct_traits.h index c4640dce..ff86f421 100644 --- a/mojo/public/cpp/bindings/struct_traits.h +++ b/mojo/public/cpp/bindings/struct_traits.h
@@ -49,8 +49,8 @@ // Value of any type that has an EnumTraits defined. // // For any nullable string/struct/array/map/union field you could also -// return value or reference of base::Optional<T>/WTF::Optional<T>, if T -// has the right *Traits defined. +// return value or reference of base::Optional<T>, if T has the right +// *Traits defined. // // During serialization, getters for all fields are called exactly once. It // is therefore reasonably effecient for a getter to construct and return
diff --git a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc index da0ec384e..1064e41 100644 --- a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc +++ b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
@@ -89,17 +89,17 @@ closure.Run(); } -void ExpectStringArray(WTF::Optional<WTF::Vector<WTF::String>>* expected_arr, +void ExpectStringArray(base::Optional<WTF::Vector<WTF::String>>* expected_arr, const base::Closure& closure, - const WTF::Optional<WTF::Vector<WTF::String>>& arr) { + const base::Optional<WTF::Vector<WTF::String>>& arr) { EXPECT_EQ(*expected_arr, arr); closure.Run(); } void ExpectStringMap( - WTF::Optional<WTF::HashMap<WTF::String, WTF::String>>* expected_map, + base::Optional<WTF::HashMap<WTF::String, WTF::String>>* expected_map, const base::Closure& closure, - const WTF::Optional<WTF::HashMap<WTF::String, WTF::String>>& map) { + const base::Optional<WTF::HashMap<WTF::String, WTF::String>>& map) { EXPECT_EQ(*expected_map, map); closure.Run(); } @@ -213,7 +213,7 @@ blink::TestWTFPtr ptr; TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(MakeRequest(&ptr))); - WTF::Optional<WTF::Vector<WTF::String>> arrs[3]; + base::Optional<WTF::Vector<WTF::String>> arrs[3]; // arrs[0] is empty. arrs[0].emplace(); // arrs[1] is null. @@ -221,13 +221,13 @@ for (size_t i = 0; i < arraysize(arrs); ++i) { base::RunLoop loop; - // Test that a WTF::Optional<WTF::Vector<WTF::String>> is unchanged after + // Test that a base::Optional<WTF::Vector<WTF::String>> is unchanged after // the following conversion: // - serialized; // - deserialized as // base::Optional<std::vector<base::Optional<std::string>>>; // - serialized; - // - deserialized as WTF::Optional<WTF::Vector<WTF::String>>. + // - deserialized as base::Optional<WTF::Vector<WTF::String>>. ptr->EchoStringArray( arrs[i], base::Bind(&ExpectStringArray, base::Unretained(&arrs[i]), loop.QuitClosure())); @@ -239,7 +239,7 @@ blink::TestWTFPtr ptr; TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(MakeRequest(&ptr))); - WTF::Optional<WTF::HashMap<WTF::String, WTF::String>> maps[3]; + base::Optional<WTF::HashMap<WTF::String, WTF::String>> maps[3]; // maps[0] is empty. maps[0].emplace(); // maps[1] is null. @@ -247,13 +247,13 @@ for (size_t i = 0; i < arraysize(maps); ++i) { base::RunLoop loop; - // Test that a WTF::Optional<WTF::HashMap<WTF::String, WTF::String>> is + // Test that a base::Optional<WTF::HashMap<WTF::String, WTF::String>> is // unchanged after the following conversion: // - serialized; // - deserialized as base::Optional< // base::flat_map<std::string, base::Optional<std::string>>>; // - serialized; - // - deserialized as WTF::Optional<WTF::HashMap<WTF::String, + // - deserialized as base::Optional<WTF::HashMap<WTF::String, // WTF::String>>. ptr->EchoStringMap(maps[i], base::Bind(&ExpectStringMap, base::Unretained(&maps[i]),
diff --git a/mojo/public/tools/bindings/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn index 371a453..49796173 100644 --- a/mojo/public/tools/bindings/BUILD.gn +++ b/mojo/public/tools/bindings/BUILD.gn
@@ -61,6 +61,7 @@ "$mojom_generator_root/generators/js_templates/externs/interface_definition.tmpl", "$mojom_generator_root/generators/js_templates/externs/module.externs.tmpl", "$mojom_generator_root/generators/js_templates/externs/struct_definition.tmpl", + "$mojom_generator_root/generators/js_templates/fuzzing.tmpl", "$mojom_generator_root/generators/js_templates/interface_definition.tmpl", "$mojom_generator_root/generators/js_templates/module.amd.tmpl", "$mojom_generator_root/generators/js_templates/module_definition.tmpl",
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl index f1b22bf..1537b1c 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -71,7 +71,6 @@ #include "mojo/public/cpp/bindings/lib/wtf_hash_util.h" #include "third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h" #include "third_party/blink/renderer/platform/wtf/hash_functions.h" -#include "third_party/blink/renderer/platform/wtf/optional.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" {%- endif %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl index efd3517..e03fd0c 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -13,14 +13,10 @@ static {{constant|format_constant_declaration(nested=True)}}; {%- endfor %} -{%- set in_place_marker = "WTF::in_place" - if for_blink else "base::in_place" %} - template <typename... Args> static {{struct.name}}Ptr New(Args&&... args) { return {{struct.name}}Ptr( - {{in_place_marker}}, - std::forward<Args>(args)...); + base::in_place, std::forward<Args>(args)...); } template <typename U>
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl index ca7f48f..a0be109 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
@@ -4,11 +4,8 @@ using Data_ = internal::{{union.name}}_Data; using Tag = Data_::{{union.name}}_Tag; -{%- set in_place_marker = "WTF::in_place" - if for_blink else "base::in_place" %} - static {{union.name}}Ptr New() { - return {{union.name}}Ptr({{in_place_marker}}); + return {{union.name}}Ptr(base::in_place); } {%- for field in union.fields %} @@ -16,7 +13,7 @@ static {{union.name}}Ptr New{{field.name|under_to_camel}}( {{field.kind|cpp_wrapper_param_type_new}} {{field.name}}) { - auto result = {{union.name}}Ptr({{in_place_marker}}); + auto result = {{union.name}}Ptr(base::in_place); result->set_{{field.name}}(std::move({{field.name}})); return result; }
diff --git a/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl b/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl new file mode 100644 index 0000000..fb535fe --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl
@@ -0,0 +1,125 @@ +{%- macro get_handle_deps(kind, name) -%} +{%- if kind|is_struct_kind or kind|is_union_kind -%} +{{name}}.getHandleDeps() +{%- elif kind|is_array_kind -%} +[].concat.apply([], {{name}}.map(function(val) { + if (val) { + return {{get_handle_deps(kind.kind, 'val')|indent(4)}}; + } + return []; +})) +{%- elif kind|is_map_kind -%} +[].concat.apply([], Array.from({{name}}.values()).map(function(val) { + if (val) { + return {{get_handle_deps(kind.value_kind, 'val')|indent(4)}}; + } + return []; +})) +{%- elif kind|is_any_handle_or_interface_kind -%} + ["{{kind|fuzz_handle_name}}"] +{%- else -%} + [] +{%- endif -%} +{%- endmacro -%} + +{%- macro set_handles(kind, name) -%} +{%- if kind|is_struct_kind or kind|is_union_kind -%} +idx = {{name}}.setHandlesInternal_(handles, idx) +{%- elif kind|is_array_kind -%} +{{name}}.forEach(function(val) { + {{set_handles(kind.kind, 'val')|indent(2)}}; +}) +{%- elif kind|is_map_kind -%} +{{name}}.forEach(function(val) { + {{set_handles(kind.value_kind, 'val')|indent(2)}}; +}) +{%- elif kind|is_any_handle_or_interface_kind -%} +{{name}} = handles[idx++]; +{%- endif -%} +{%- endmacro -%} + +{%- macro build_call(obj, operation, type, name) -%} +{%- if name -%} +{{obj}}.{{operation}}{{type}}({{((name,) + varargs)|join(', ')}}) +{%- else -%} +{{obj}}.{{operation}}{{type}}({{varargs|join(', ')}}) +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_enum(obj, operation, kind, name) -%} +{%- if kind.max_value is not none -%} +{{build_call(obj, operation, 'Enum', name, '0', kind.max_value)}} +{%- else -%} +{{build_call(obj, operation, 'Enum', name)}} +{%- endif %} +{%- endmacro -%} + +{%- macro generate_or_mutate_array(obj, operation, kind, name) -%} +{%- if operation == 'mutate' -%} +{{obj}}.{{operation}}Array({{name}}, function(val) { + return {{generate_or_mutate(obj, operation, kind.kind, 'val')|indent(2)}}; +}) +{%- else -%} +{{obj}}.{{operation}}Array(function() { + return {{generate_or_mutate(obj, operation, kind.kind)|indent(2)}}; +}) +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_map(obj, operation, kind, name) -%} +{%- if operation == 'mutate' -%} +{{obj}}.{{operation}}Map({{name}}, + function(val) { + return {{generate_or_mutate(obj, operation, kind.key_kind, 'val')|indent(4)}}; + }, + function(val) { + return {{generate_or_mutate(obj, operation, kind.value_kind, 'val')|indent(4)}}; + }) +{%- else -%} +{{obj}}.{{operation}}Map( + function() { + return {{generate_or_mutate(obj, operation, kind.key_kind)|indent(4)}}; + }, + function() { + return {{generate_or_mutate(obj, operation, kind.value_kind)|indent(4)}}; + }) +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_primitive(obj, operation, kind, name) -%} +{%- if kind|is_reference_kind -%} +{{build_call(obj, operation, kind|primitive_to_fuzz_type, name, kind.is_nullable|to_js_boolean)}} +{%- else -%} +{{build_call(obj, operation, kind|primitive_to_fuzz_type, name)}} +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_interface(obj, operation, kind, name) -%} +{%- if kind|is_interface_request_kind -%} +{{build_call(obj, operation, 'InterfaceRequest', name, '"' ~ kind.kind.module.namespace ~ '.' ~ kind.kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_interface_kind -%} +{{build_call(obj, operation, 'Interface', name, '"' ~ kind.module.namespace ~ '.' ~ kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_associated_interface_request_kind -%} +{{build_call(obj, operation, 'AssociatedInterfaceRequest', name, '"' ~ kind.kind.module.namespace ~ '.' ~ kind.kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_associated_interface_kind -%} +{{build_call(obj, operation, 'AssociatedInterface', name, '"' ~ kind.kind.module.namespace ~ '.' ~ kind.kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate(obj, operation, kind, name='') -%} +{%- if kind|is_primitive_kind -%} +{{generate_or_mutate_primitive(obj, operation, kind, name)}} +{%- elif kind|is_any_interface_kind -%} +{{generate_or_mutate_interface(obj, operation, kind, name)}} +{%- elif kind|is_enum_kind -%} +{{generate_or_mutate_enum(obj, operation, kind, name)}} +{%- elif kind|is_struct_kind -%} +{{build_call(obj, operation, 'Struct', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_union_kind -%} +{{build_call(obj, operation, 'Union', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_array_kind -%} +{{generate_or_mutate_array(obj, operation, kind, name)}} +{%- elif kind|is_map_kind -%} +{{generate_or_mutate_map(obj, operation, kind, name)}} +{%- endif -%} +{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl index 988ca0a..356fc5bb 100644 --- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -226,6 +226,18 @@ {%- else %} validateResponse: null, {%- endif %} +{%- if generate_fuzzing %} + mojomId: '{{module.path}}', + fuzzMethods: { + {%- for method in interface.methods %} + {%- set interface_method_id = + interface.mojom_name ~ "_" ~ method.mojom_name %} + {{ method.name }}: { + params: {{interface_method_id}}_Params, + }, + {%- endfor %} + }, +{%- endif %} }; {#--- Interface Constants #} {%- for constant in interface.constants %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl index 21165dd..a6736b5 100644 --- a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
@@ -17,7 +17,7 @@ {#--- Union definitions #} {%- from "union_definition.tmpl" import union_def %} {%- for union in unions %} -{{union_def(union)|indent(2)}} +{{union_def(union, generate_fuzzing)|indent(2)}} {%- endfor %} {#--- Interface definitions #}
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl index 7bffe60..176f1b89 100644 --- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -30,6 +30,59 @@ } }; +{#--- Fuzzing #} +{%- if generate_fuzzing %} + {{struct.name}}.generate = function(generator_) { + var generated = new {{struct.name}}; +{%- for field in struct.fields %} +{%- if not field.kind|is_any_handle_or_interface_kind %} +{%- from "fuzzing.tmpl" import generate_or_mutate %} + generated.{{field.name}} = {{generate_or_mutate('generator_', 'generate', field.kind)|indent(4)}}; +{%- endif %} +{%- endfor %} + return generated; + }; + + {{struct.name}}.prototype.mutate = function(mutator_) { +{%- for field in struct.fields %} +{%- if not field.kind|is_any_handle_or_interface_kind %} + if (mutator_.chooseMutateField()) { +{%- from "fuzzing.tmpl" import generate_or_mutate %} + this.{{field.name}} = {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; + } +{%- endif %} +{%- endfor %} + return this; + }; + + {{struct.name}}.prototype.getHandleDeps = function() { + var handles = []; +{%- for field in struct.fields %} +{%- if field.kind|contains_handles_or_interfaces %} + if (this.{{field.name}} !== null) { +{%- from "fuzzing.tmpl" import get_handle_deps %} + Array.prototype.push.apply(handles, {{get_handle_deps(field.kind, 'this.' ~ field.name)|indent(6)}}); + } +{%- endif %} +{%- endfor %} + return handles; + }; + + {{struct.name}}.prototype.setHandles = function() { + this.setHandlesInternal_(arguments, 0); + }; + + {{struct.name}}.prototype.setHandlesInternal_ = function(handles, idx) { +{%- for field in struct.fields %} +{%- if field.kind|contains_handles_or_interfaces %} +{%- from "fuzzing.tmpl" import set_handles %} + {{set_handles(field.kind, 'this.' ~ field.name)|indent(4)}}; +{%- endif %} +{%- endfor %} + return idx; + }; +{%- endif %} + {#--- Validation #} {{struct.name}}.validate = function(messageValidator, offset) {
diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl index dde69aef..d00e641 100644 --- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -1,4 +1,4 @@ -{%- macro union_def(union) %} +{%- macro union_def(union, generate_fuzzing=false) %} function {{union.name}}(value) { this.initDefault_(); this.initValue_(value); @@ -39,6 +39,71 @@ this[keys[0]] = value[keys[0]]; } +{% if generate_fuzzing %} +{{union.name}}.generate = function(generator_) { + var generated = new {{union.name}}; + var generators = [ +{%- for field in union.fields %} + { + field: "{{field.name}}", + +{%- if not field.kind|is_any_handle_or_interface_kind %} +{%- from "fuzzing.tmpl" import generate_or_mutate %} + generator: function() { return {{generate_or_mutate('generator_', 'generate', field.kind)|indent(6)}}; }, +{%- endif %} + }, +{%- endfor %} + ]; + + var result = generator_.generateUnionField(generators); + generated[result.field] = result.value; + return generated; +} + +{{union.name}}.prototype.mutate = function(mutator_) { + var mutators = [ +{%- for field in union.fields %} + { + field: "{{field.name}}", + +{%- if not field.kind|is_any_handle_or_interface_kind %} +{%- from "fuzzing.tmpl" import generate_or_mutate %} + mutator: function() { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; }, +{%- endif %} + }, +{%- endfor %} + ]; + + var result = mutator_.mutateUnionField(this, mutators); + generated[result.field] = result.value; + return this; +} + +{{union.name}}.prototype.getHandleDeps = function() { +{%- for field in union.fields %} +{%- if field.kind|contains_handles_or_interfaces %} + if (this.$tag == {{union.name}}.Tags.{{field.name}}) { +{%- from "fuzzing.tmpl" import get_handle_deps %} + return {{get_handle_deps(field.kind, 'this.' ~ field.name)}}; + } +{%- endif %} +{%- endfor %} + return []; +} + +{{union.name}}.prototype.setHandles = function() { +{%- for field in union.fields %} +{%- if field.kind|contains_handles_or_interfaces %} + if (this.$tag == {{union.name}}.Tags.{{field.name}}) { +{%- from "fuzzing.tmpl" import set_handles %} + return {{set_handles(field.kind, 'this.' ~ field.name)}}; + } +{%- endif %} +{%- endfor %} + return []; +} +{%- endif %} + {%- for field in union.fields %} Object.defineProperty({{union.name}}.prototype, "{{field.name}}", { get: function() {
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index f36099c6..ceded69 100644 --- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -517,8 +517,7 @@ def _GetCppWrapperType(self, kind, add_same_module_namespaces=False): def _AddOptional(type_name): - pattern = "WTF::Optional<%s>" if self.for_blink else "base::Optional<%s>" - return pattern % type_name + return "base::Optional<%s>" % type_name if self._IsTypemappedKind(kind): type_name = self._GetNativeTypeName(kind)
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py index f4d42d1..0f2b618 100644 --- a/mojo/public/tools/bindings/generators/mojom_java_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -93,6 +93,11 @@ 'short': 'Short', } +_java_reserved_types = [ + # These two may clash with commonly used classes on Android. + 'Manifest', + 'R' +] def NameToComponent(name): """ Returns a list of lowercase words corresponding to a given name. """ @@ -125,7 +130,10 @@ def GetNameForElement(element): if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or mojom.IsStructKind(element) or mojom.IsUnionKind(element)): - return UpperCamelCase(element.name) + name = UpperCamelCase(element.name) + if name in _java_reserved_types: + return name + '_' + return name if mojom.IsInterfaceRequestKind(element) or mojom.IsAssociatedKind(element): return GetNameForElement(element.kind) if isinstance(element, (mojom.Method,
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py index 5a4de76..724268d 100644 --- a/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -136,6 +136,32 @@ 'yield', ] +_primitive_kind_to_fuzz_type = { + mojom.BOOL: "Bool", + mojom.INT8: "Int8", + mojom.UINT8: "Uint8", + mojom.INT16: "Int16", + mojom.UINT16: "Uint16", + mojom.INT32: "Int32", + mojom.UINT32: "Uint32", + mojom.FLOAT: "Float", + mojom.INT64: "Int64", + mojom.UINT64: "Uint64", + mojom.DOUBLE: "Double", + mojom.STRING: "String", + mojom.NULLABLE_STRING: "String", + mojom.HANDLE: "Handle", + mojom.DCPIPE: "DataPipeConsumer", + mojom.DPPIPE: "DataPipeProducer", + mojom.MSGPIPE: "MessagePipe", + mojom.SHAREDBUFFER: "SharedBuffer", + mojom.NULLABLE_HANDLE: "Handle", + mojom.NULLABLE_DCPIPE: "DataPipeConsumer", + mojom.NULLABLE_DPPIPE: "DataPipeProducer", + mojom.NULLABLE_MSGPIPE: "MessagePipe", + mojom.NULLABLE_SHAREDBUFFER: "SharedBuffer", +} + def JavaScriptPayloadSize(packed): packed_fields = packed.packed_fields @@ -207,6 +233,7 @@ "module": self.module, "structs": self.module.structs + self._GetStructsFromMethods(), "unions": self.module.unions, + "generate_fuzzing": self.generate_fuzzing, } @staticmethod @@ -231,10 +258,12 @@ "is_bool_kind": mojom.IsBoolKind, "is_enum_kind": mojom.IsEnumKind, "is_any_handle_kind": mojom.IsAnyHandleKind, + "is_any_interface_kind": mojom.IsAnyInterfaceKind, "is_interface_kind": mojom.IsInterfaceKind, "is_interface_request_kind": mojom.IsInterfaceRequestKind, "is_map_kind": mojom.IsMapKind, "is_object_kind": mojom.IsObjectKind, + "is_reference_kind": mojom.IsReferenceKind, "is_string_kind": mojom.IsStringKind, "is_struct_kind": mojom.IsStructKind, "is_union_kind": mojom.IsUnionKind, @@ -253,6 +282,11 @@ "validate_struct_params": self._JavaScriptValidateStructParams, "validate_union_params": self._JavaScriptValidateUnionParams, "sanitize_identifier": self._JavaScriptSanitizeIdentifier, + "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces, + "fuzz_handle_name": self._FuzzHandleName, + "is_primitive_kind": self._IsPrimitiveKind, + "primitive_to_fuzz_type": self._PrimitiveToFuzzType, + "to_js_boolean": self._ToJsBoolean, } return js_filters @@ -540,3 +574,37 @@ if method.response_param_struct is not None: result.append(method.response_param_struct) return result + + def _FuzzHandleName(self, kind): + if mojom.IsInterfaceRequestKind(kind): + return '{0}.{1}Request'.format(kind.kind.module.namespace, + kind.kind.name) + elif mojom.IsInterfaceKind(kind): + return '{0}.{1}Ptr'.format(kind.module.namespace, + kind.name) + elif mojom.IsAssociatedInterfaceRequestKind(kind): + return '{0}.{1}AssociatedRequest'.format(kind.kind.module.namespace, + kind.kind.name) + elif mojom.IsAssociatedInterfaceKind(kind): + return '{0}.{1}AssociatedPtr'.format(kind.kind.module.namespace, + kind.kind.name) + elif mojom.IsSharedBufferKind(kind): + return 'handle<shared_buffer>' + elif mojom.IsDataPipeConsumerKind(kind): + return 'handle<data_pipe_consumer>' + elif mojom.IsDataPipeProducerKind(kind): + return 'handle<data_pipe_producer>' + elif mojom.IsMessagePipeKind(kind): + return 'handle<message_pipe>' + + def _ToJsBoolean(self, value): + if value: + return 'true' + + return 'false' + + def _IsPrimitiveKind(self, kind): + return kind in mojom.PRIMITIVES + + def _PrimitiveToFuzzType(self, kind): + return _primitive_kind_to_fuzz_type[kind]
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni index 317d8c0..11f6395 100644 --- a/mojo/public/tools/bindings/mojom.gni +++ b/mojo/public/tools/bindings/mojom.gni
@@ -13,6 +13,7 @@ import("//build/config/nacl/config.gni") import("//components/nacl/features.gni") import("//third_party/jinja2/jinja2.gni") +import("//tools/ipc_fuzzer/ipc_fuzzer.gni") declare_args() { # Indicates whether typemapping should be supported in this build @@ -1128,7 +1129,7 @@ } } - if (!defined(invoker.cpp_only) || !invoker.cpp_only) { + if (enable_ipc_fuzzer || !defined(invoker.cpp_only) || !invoker.cpp_only) { if (defined(invoker.sources)) { generator_js_target_name = "${target_name}_js__generator" generator_js_outputs = [ @@ -1170,6 +1171,10 @@ inputs += message_scrambling_inputs args += message_scrambling_args } + + if (enable_ipc_fuzzer) { + args += [ "--generate_fuzzing" ] + } } }
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py index 4117629..affbe79 100755 --- a/mojo/public/tools/bindings/mojom_bindings_generator.py +++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -214,7 +214,8 @@ support_lazy_serialization=args.support_lazy_serialization, disallow_native_types=args.disallow_native_types, disallow_interfaces=args.disallow_interfaces, - generate_message_ids=args.generate_message_ids) + generate_message_ids=args.generate_message_ids, + generate_fuzzing=args.generate_fuzzing) filtered_args = [] if hasattr(generator_module, 'GENERATOR_PREFIX'): prefix = '--' + generator_module.GENERATOR_PREFIX + '_' @@ -466,6 +467,10 @@ help="Generates only the message IDs header for C++ bindings. Note that " "this flag only matters if --generate_non_variant_code is also " "specified.", action="store_true") + generate_parser.add_argument( + "--generate_fuzzing", + action="store_true", + help="Generates additional bindings for fuzzing in JS.") generate_parser.set_defaults(func=_Generate) precompile_parser = subparsers.add_parser("precompile",
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py index c7acbb79..acf029f 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -164,7 +164,8 @@ js_bindings_mode="new", export_attribute=None, export_header=None, generate_non_variant_code=False, support_lazy_serialization=False, disallow_native_types=False, - disallow_interfaces=False, generate_message_ids=False): + disallow_interfaces=False, generate_message_ids=False, + generate_fuzzing=False): self.module = module self.output_dir = output_dir self.typemap = typemap or {} @@ -180,6 +181,7 @@ self.disallow_native_types = disallow_native_types self.disallow_interfaces = disallow_interfaces self.generate_message_ids = generate_message_ids + self.generate_fuzzing = generate_fuzzing def Write(self, contents, filename): if self.output_dir is None:
diff --git a/net/android/cellular_signal_strength.cc b/net/android/cellular_signal_strength.cc index 1b9e8ad..3242461 100644 --- a/net/android/cellular_signal_strength.cc +++ b/net/android/cellular_signal_strength.cc
@@ -34,10 +34,18 @@ Java_AndroidCellularSignalStrength_getSignalStrengthLevel( base::android::AttachCurrentThread()); if (signal_strength_level == ERROR_NOT_SUPPORTED) - return base::Optional<int32_t>(); + return base::nullopt; - DCHECK_LE(0, signal_strength_level); - DCHECK_GE(4, signal_strength_level); + // |signal_strength_level| is expected to be between 0 and 4 (both inclusive). + // See + // https://developer.android.com/reference/android/telephony/SignalStrength.html#getLevel(). + // On some devices, |signal_strength_level| may have a value outside this + // range (e.g., see https://crbug.com/835701). For such devices, cap the value + // to be between 0 and 4. + if (signal_strength_level < 0) + return 0; + if (signal_strength_level > 4) + return 4; return signal_strength_level; }
diff --git a/net/android/cellular_signal_strength.h b/net/android/cellular_signal_strength.h index e87c9a7..367161c 100644 --- a/net/android/cellular_signal_strength.h +++ b/net/android/cellular_signal_strength.h
@@ -19,7 +19,9 @@ // Returns the signal strength level (between 0 and 4, both inclusive) of the // currently registered cellular connection. If the value is unavailable, an -// empty value is returned. +// empty value is returned. If the signal strength value returned by platform +// API is less than 0, this method returns 0. If the platform API returns a +// value larger than 4, then this method returns 4. NET_EXPORT_PRIVATE base::Optional<int32_t> GetSignalStrengthLevel(); } // namespace cellular_signal_strength
diff --git a/net/cert/x509_util.cc b/net/cert/x509_util.cc index 9e6e2144..d1053023 100644 --- a/net/cert/x509_util.cc +++ b/net/cert/x509_util.cc
@@ -328,7 +328,7 @@ } scoped_refptr<X509Certificate> CreateX509CertificateFromBuffers( - STACK_OF(CRYPTO_BUFFER) * buffers) { + const STACK_OF(CRYPTO_BUFFER) * buffers) { if (sk_CRYPTO_BUFFER_num(buffers) == 0) { NOTREACHED(); return nullptr;
diff --git a/net/cert/x509_util.h b/net/cert/x509_util.h index 6e4d97a..85b37f738 100644 --- a/net/cert/x509_util.h +++ b/net/cert/x509_util.h
@@ -110,7 +110,7 @@ // Creates a new X509Certificate from the chain in |buffers|, which must have at // least one element. scoped_refptr<X509Certificate> CreateX509CertificateFromBuffers( - STACK_OF(CRYPTO_BUFFER) * buffers); + const STACK_OF(CRYPTO_BUFFER) * buffers); // Returns the default ParseCertificateOptions for the net stack. ParseCertificateOptions DefaultParseCertificateOptions();
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc index 8ab479ac..407a302 100644 --- a/net/cookies/cookie_monster_unittest.cc +++ b/net/cookies/cookie_monster_unittest.cc
@@ -68,7 +68,7 @@ MOCK_METHOD1(UpdateCookieAccessTime, void(const CanonicalCookie& cc)); MOCK_METHOD1(DeleteCookie, void(const CanonicalCookie& cc)); MOCK_METHOD1(SetBeforeFlushCallback, void(base::RepeatingClosure)); - virtual void Flush(base::OnceClosure callback) { + void Flush(base::OnceClosure callback) override { if (!callback.is_null()) base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback)); @@ -76,7 +76,7 @@ MOCK_METHOD0(SetForceKeepSessionState, void()); private: - virtual ~NewMockPersistentCookieStore() = default; + ~NewMockPersistentCookieStore() override = default; }; // False means 'less than or equal', so we test both ways for full equal.
diff --git a/net/dns/mdns_cache_unittest.cc b/net/dns/mdns_cache_unittest.cc index 9c6abf9a..087a021 100644 --- a/net/dns/mdns_cache_unittest.cc +++ b/net/dns/mdns_cache_unittest.cc
@@ -108,7 +108,7 @@ public: MDnsCacheTest() : default_time_(base::Time::FromDoubleT(1234.0)) {} - virtual ~MDnsCacheTest() = default; + ~MDnsCacheTest() override = default; protected: base::Time default_time_;
diff --git a/net/dns/mdns_client_unittest.cc b/net/dns/mdns_client_unittest.cc index 2fc2f08b1..5093a5fa 100644 --- a/net/dns/mdns_client_unittest.cc +++ b/net/dns/mdns_client_unittest.cc
@@ -364,7 +364,7 @@ class MockClock : public base::Clock { public: MockClock() = default; - virtual ~MockClock() = default; + ~MockClock() override = default; MOCK_CONST_METHOD0(Now, base::Time()); @@ -375,11 +375,11 @@ class MockTimer : public base::MockTimer { public: MockTimer() : base::MockTimer(false, false) {} - ~MockTimer() = default; + ~MockTimer() override = default; void Start(const base::Location& posted_from, base::TimeDelta delay, - const base::Closure& user_task) { + const base::Closure& user_task) override { StartObserver(posted_from, delay, user_task); base::MockTimer::Start(posted_from, delay, user_task); } @@ -1115,7 +1115,7 @@ class MockMDnsConnectionDelegate : public MDnsConnection::Delegate { public: - virtual void HandlePacket(DnsResponse* response, int size) { + void HandlePacket(DnsResponse* response, int size) override { HandlePacketInternal(std::string(response->io_buffer()->data(), size)); }
diff --git a/net/http/http_proxy_client_socket_wrapper_unittest.cc b/net/http/http_proxy_client_socket_wrapper_unittest.cc index eefd51fd..81be118 100644 --- a/net/http/http_proxy_client_socket_wrapper_unittest.cc +++ b/net/http/http_proxy_client_socket_wrapper_unittest.cc
@@ -347,7 +347,7 @@ #endif INSTANTIATE_TEST_CASE_P( - VersionIncludeStreamDependencySequnece, + VersionIncludeStreamDependencySequence, HttpProxyClientSocketWrapperTest, ::testing::Combine(::testing::ValuesIn(AllSupportedTransportVersions()), ::testing::Bool()));
diff --git a/net/http/http_stream_factory_job.h b/net/http/http_stream_factory_job.h index 31d6977..cd6a6a1 100644 --- a/net/http/http_stream_factory_job.h +++ b/net/http/http_stream_factory_job.h
@@ -36,7 +36,7 @@ namespace test { -class HttpStreamFactoryImplJobPeer; +class HttpStreamFactoryJobPeer; } // namespace test @@ -254,7 +254,7 @@ NetErrorDetails* net_error_details() { return &net_error_details_; } private: - friend class test::HttpStreamFactoryImplJobPeer; + friend class test::HttpStreamFactoryJobPeer; enum State { STATE_START,
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc index ecee7cc..6fd9786 100644 --- a/net/http/http_stream_factory_job_controller_unittest.cc +++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -135,11 +135,11 @@ } // anonymous namespace -class HttpStreamFactoryImplJobPeer { +class HttpStreamFactoryJobPeer { public: static void Start(HttpStreamFactory::Job* job, HttpStreamRequest::StreamType stream_type) { - // Start() is mocked for MockHttpStreamFactoryImplJob. + // Start() is mocked for MockHttpStreamFactoryJob. // This is the alternative method to invoke real Start() method on Job. job->stream_type_ = stream_type; job->StartInternal(); @@ -188,9 +188,9 @@ } }; -class HttpStreamFactoryImplJobControllerTest : public ::testing::Test { +class HttpStreamFactoryJobControllerTest : public ::testing::Test { public: - HttpStreamFactoryImplJobControllerTest() { session_deps_.enable_quic = true; } + HttpStreamFactoryJobControllerTest() { session_deps_.enable_quic = true; } void UseAlternativeProxy() { ASSERT_FALSE(test_proxy_delegate_); @@ -264,7 +264,7 @@ return test_proxy_delegate_; } - ~HttpStreamFactoryImplJobControllerTest() override { + ~HttpStreamFactoryJobControllerTest() override { if (quic_data_) { EXPECT_TRUE(quic_data_->AllReadDataConsumed()); EXPECT_TRUE(quic_data_->AllWriteDataConsumed()); @@ -332,10 +332,10 @@ TestProxyDelegate* test_proxy_delegate_ = nullptr; bool create_job_controller_ = true; - DISALLOW_COPY_AND_ASSIGN(HttpStreamFactoryImplJobControllerTest); + DISALLOW_COPY_AND_ASSIGN(HttpStreamFactoryJobControllerTest); }; -TEST_F(HttpStreamFactoryImplJobControllerTest, ProxyResolutionFailsSync) { +TEST_F(HttpStreamFactoryJobControllerTest, ProxyResolutionFailsSync) { ProxyConfig proxy_config; proxy_config.set_pac_url(GURL("http://fooproxyurl")); proxy_config.set_pac_mandatory(true); @@ -368,7 +368,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, ProxyResolutionFailsAsync) { +TEST_F(HttpStreamFactoryJobControllerTest, ProxyResolutionFailsAsync) { ProxyConfig proxy_config; proxy_config.set_pac_url(GURL("http://fooproxyurl")); proxy_config.set_pac_mandatory(true); @@ -405,7 +405,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, NoSupportedProxies) { +TEST_F(HttpStreamFactoryJobControllerTest, NoSupportedProxies) { session_deps_.proxy_resolution_service = ProxyResolutionService::CreateFixedFromPacResult( "QUIC myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS); @@ -431,7 +431,7 @@ } class JobControllerReconsiderProxyAfterErrorTest - : public HttpStreamFactoryImplJobControllerTest, + : public HttpStreamFactoryJobControllerTest, public ::testing::WithParamInterface<::testing::tuple<bool, int>> { public: void Initialize( @@ -742,8 +742,7 @@ EXPECT_TRUE(tcp_data_2.AllWriteDataConsumed()); } -TEST_F(HttpStreamFactoryImplJobControllerTest, - OnStreamFailedWithNoAlternativeJob) { +TEST_F(HttpStreamFactoryJobControllerTest, OnStreamFailedWithNoAlternativeJob) { tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0); tcp_data_->set_connect_data(MockConnect(ASYNC, ERR_FAILED)); @@ -766,8 +765,7 @@ base::RunLoop().RunUntilIdle(); } -TEST_F(HttpStreamFactoryImplJobControllerTest, - OnStreamReadyWithNoAlternativeJob) { +TEST_F(HttpStreamFactoryJobControllerTest, OnStreamReadyWithNoAlternativeJob) { tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0); tcp_data_->set_connect_data(MockConnect(ASYNC, OK)); @@ -791,7 +789,7 @@ // Test we cancel Jobs correctly when the Request is explicitly canceled // before any Job is bound to Request. -TEST_F(HttpStreamFactoryImplJobControllerTest, CancelJobsBeforeBinding) { +TEST_F(HttpStreamFactoryJobControllerTest, CancelJobsBeforeBinding) { // Use COLD_START to make the alt job pending. crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::COLD_START); @@ -826,7 +824,7 @@ // Test that the controller does not create alternative job when the advertised // versions in AlternativeServiceInfo do not contain any version that is // supported. -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, DoNotCreateAltJobIfQuicVersionsUnsupported) { tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0); tcp_data_->set_connect_data(MockConnect(ASYNC, OK)); @@ -852,7 +850,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, OnStreamFailedForBothJobs) { +TEST_F(HttpStreamFactoryJobControllerTest, OnStreamFailedForBothJobs) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddConnect(ASYNC, ERR_FAILED); tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0); @@ -882,8 +880,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, - AltJobFailsAfterMainJobSucceeds) { +TEST_F(HttpStreamFactoryJobControllerTest, AltJobFailsAfterMainJobSucceeds) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddRead(ASYNC, ERR_FAILED); crypto_client_stream_factory_.set_handshake_mode( @@ -926,7 +923,7 @@ } // Tests that when alt job succeeds, main job is destroyed. -TEST_F(HttpStreamFactoryImplJobControllerTest, AltJobSucceedsMainJobDestroyed) { +TEST_F(HttpStreamFactoryJobControllerTest, AltJobSucceedsMainJobDestroyed) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Use cold start and complete alt job manually. @@ -956,8 +953,8 @@ std::make_unique<ClientSocketHandle>(), false, false); EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, http_stream.get())); - HttpStreamFactoryImplJobPeer::SetStream(job_factory_.alternative_job(), - std::move(http_stream)); + HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(), + std::move(http_stream)); job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig()); base::RunLoop().RunUntilIdle(); @@ -973,7 +970,7 @@ // Tests that if alt job succeeds and main job is blocked, main job should be // cancelled immediately. |request_| completion will clean up the JobController. // Regression test for crbug.com/678768. -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, AltJobSucceedsMainJobBlockedControllerDestroyed) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddWrite(SYNCHRONOUS, @@ -1012,7 +1009,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, SpdySessionKeyHasOriginHostPortPair) { session_deps_.enable_http2_alternative_service = true; @@ -1037,14 +1034,13 @@ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY); HostPortPair main_host_port_pair = - HttpStreamFactoryImplJobPeer::GetSpdySessionKey( - job_controller_->main_job()) + HttpStreamFactoryJobPeer::GetSpdySessionKey(job_controller_->main_job()) .host_port_pair(); EXPECT_EQ(origin_host, main_host_port_pair.host()); EXPECT_EQ(origin_port, main_host_port_pair.port()); HostPortPair alternative_host_port_pair = - HttpStreamFactoryImplJobPeer::GetSpdySessionKey( + HttpStreamFactoryJobPeer::GetSpdySessionKey( job_controller_->alternative_job()) .host_port_pair(); EXPECT_EQ(origin_host, alternative_host_port_pair.host()); @@ -1053,7 +1049,7 @@ // Tests that if an orphaned job completes after |request_| is gone, // JobController will be cleaned up. -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, OrphanedJobCompletesControllerDestroyed) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING); @@ -1099,16 +1095,15 @@ // Make |alternative_job| succeed. auto http_stream = std::make_unique<HttpBasicStream>( std::make_unique<ClientSocketHandle>(), false, false); - HttpStreamFactoryImplJobPeer::SetStream(job_factory_.alternative_job(), - std::move(http_stream)); + HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(), + std::move(http_stream)); // This should not call request_delegate_::OnStreamReady. job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig()); // Make sure that controller does not leak. EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, - AltJobSucceedsAfterMainJobFailed) { +TEST_F(HttpStreamFactoryJobControllerTest, AltJobSucceedsAfterMainJobFailed) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Use cold start and complete alt job manually. @@ -1145,8 +1140,8 @@ std::make_unique<ClientSocketHandle>(), false, false); EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, http_stream.get())); - HttpStreamFactoryImplJobPeer::SetStream(job_factory_.alternative_job(), - std::move(http_stream)); + HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(), + std::move(http_stream)); job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig()); // |alternative_job| succeeds and should report status to Request. @@ -1155,8 +1150,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, - MainJobSucceedsAfterAltJobFailed) { +TEST_F(HttpStreamFactoryJobControllerTest, MainJobSucceedsAfterAltJobFailed) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddConnect(SYNCHRONOUS, ERR_FAILED); @@ -1199,7 +1193,7 @@ // Verifies that if the alternative job fails due to a connection change event, // then the alternative service is not marked as broken. -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, MainJobSucceedsAfterConnectionChanged) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddConnect(SYNCHRONOUS, ERR_NETWORK_CHANGED); @@ -1241,7 +1235,7 @@ // Regression test for crbug/621069. // Get load state after main job fails and before alternative job succeeds. -TEST_F(HttpStreamFactoryImplJobControllerTest, GetLoadStateAfterMainJobFailed) { +TEST_F(HttpStreamFactoryJobControllerTest, GetLoadStateAfterMainJobFailed) { // Use COLD_START to complete alt job manually. quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING); @@ -1280,15 +1274,15 @@ std::make_unique<ClientSocketHandle>(), false, false); EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, http_stream.get())); - HttpStreamFactoryImplJobPeer::SetStream(job_factory_.alternative_job(), - std::move(http_stream)); + HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(), + std::move(http_stream)); job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig()); request_.reset(); EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, ResumeMainJobWhenAltJobStalls) { +TEST_F(HttpStreamFactoryJobControllerTest, ResumeMainJobWhenAltJobStalls) { // Use COLD_START to stall alt job. quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING); @@ -1321,7 +1315,7 @@ base::RunLoop().RunUntilIdle(); } -TEST_F(HttpStreamFactoryImplJobControllerTest, InvalidPortForQuic) { +TEST_F(HttpStreamFactoryJobControllerTest, InvalidPortForQuic) { HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); @@ -1347,7 +1341,7 @@ // Verifies that the main job is not resumed until after the alt job completes // host resolution. -TEST_F(HttpStreamFactoryImplJobControllerTest, HostResolutionHang) { +TEST_F(HttpStreamFactoryJobControllerTest, HostResolutionHang) { NetTestSuite::SetScopedTaskEnvironment( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME); @@ -1433,7 +1427,7 @@ NetTestSuite::ResetScopedTaskEnvironment(); } -TEST_F(HttpStreamFactoryImplJobControllerTest, DelayedTCP) { +TEST_F(HttpStreamFactoryJobControllerTest, DelayedTCP) { NetTestSuite::SetScopedTaskEnvironment( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME); @@ -1505,7 +1499,7 @@ } // Regression test for crbug.com/789560. -TEST_F(HttpStreamFactoryImplJobControllerTest, ResumeMainJobLaterCanceled) { +TEST_F(HttpStreamFactoryJobControllerTest, ResumeMainJobLaterCanceled) { NetTestSuite::SetScopedTaskEnvironment( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME); @@ -1560,8 +1554,7 @@ // version will be bumped. That is enough for the job controller to restart // the jobs. proxy_resolution_service_raw->ForceReloadProxyConfig(); - HttpStreamFactoryImplJobPeer::SetShouldReconsiderProxy( - job_factory_.main_job()); + HttpStreamFactoryJobPeer::SetShouldReconsiderProxy(job_factory_.main_job()); // Now the alt service is marked as broken (e.g. through a different request), // so only non-alt job is restarted. session_->http_server_properties()->MarkAlternativeServiceBroken( @@ -1592,7 +1585,7 @@ // http_server_properties cached an inappropriate large srtt for the server, // which would potentially delay the main job for a extremely long time in // delayed tcp case. -TEST_F(HttpStreamFactoryImplJobControllerTest, DelayedTCPWithLargeSrtt) { +TEST_F(HttpStreamFactoryJobControllerTest, DelayedTCPWithLargeSrtt) { NetTestSuite::SetScopedTaskEnvironment( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME); @@ -1665,7 +1658,7 @@ NetTestSuite::ResetScopedTaskEnvironment(); } -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, ResumeMainJobImmediatelyOnStreamFailed) { NetTestSuite::SetScopedTaskEnvironment( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME); @@ -1747,7 +1740,7 @@ // Verifies that the alternative proxy server job is not created if the URL // scheme is HTTPS. -TEST_F(HttpStreamFactoryImplJobControllerTest, HttpsURL) { +TEST_F(HttpStreamFactoryJobControllerTest, HttpsURL) { // Using hanging resolver will cause the alternative job to hang indefinitely. session_deps_.host_resolver = std::make_unique<HangingResolver>(); @@ -1770,7 +1763,7 @@ // Verifies that the alternative proxy server job is not created if the main job // does not fetch the resource through a proxy. -TEST_F(HttpStreamFactoryImplJobControllerTest, HttpURLWithNoProxy) { +TEST_F(HttpStreamFactoryJobControllerTest, HttpURLWithNoProxy) { // Using hanging resolver will cause the alternative job to hang indefinitely. session_deps_.host_resolver = std::make_unique<HangingResolver>(); @@ -1794,7 +1787,7 @@ // Verifies that the main job is resumed properly after a delay when the // alternative proxy server job hangs. -TEST_F(HttpStreamFactoryImplJobControllerTest, DelayedTCPAlternativeProxy) { +TEST_F(HttpStreamFactoryJobControllerTest, DelayedTCPAlternativeProxy) { NetTestSuite::SetScopedTaskEnvironment( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME); @@ -1871,7 +1864,7 @@ // Verifies that if the alternative proxy server job fails immediately, the // main job is not blocked. -TEST_F(HttpStreamFactoryImplJobControllerTest, FailAlternativeProxy) { +TEST_F(HttpStreamFactoryJobControllerTest, FailAlternativeProxy) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddConnect(SYNCHRONOUS, ERR_FAILED); tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0); @@ -1919,7 +1912,7 @@ // Verifies that if the alternative proxy server job fails due to network // disconnection, then the proxy delegate is not notified. -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, InternetDisconnectedAlternativeProxy) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddConnect(SYNCHRONOUS, ERR_INTERNET_DISCONNECTED); @@ -1963,7 +1956,7 @@ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, AlternativeProxyServerJobFailsAfterMainJobSucceeds) { base::HistogramTester histogram_tester; @@ -2017,8 +2010,7 @@ 1); } -TEST_F(HttpStreamFactoryImplJobControllerTest, - PreconnectToHostWithValidAltSvc) { +TEST_F(HttpStreamFactoryJobControllerTest, PreconnectToHostWithValidAltSvc) { quic_data_ = std::make_unique<MockQuicData>(); quic_data_->AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1, nullptr)); @@ -2046,7 +2038,7 @@ } // When preconnect to a H2 supported server, only 1 connection is opened. -TEST_F(HttpStreamFactoryImplJobControllerTest, +TEST_F(HttpStreamFactoryJobControllerTest, PreconnectMultipleStreamsToH2Server) { tcp_data_ = std::make_unique<SequencedSocketData>(nullptr, 0, nullptr, 0); tcp_data_->set_connect_data(MockConnect(ASYNC, OK)); @@ -2068,15 +2060,15 @@ EXPECT_EQ(HttpStreamFactory::PRECONNECT, job_controller_->main_job()->job_type()); // There is only 1 connect even though multiple streams were requested. - EXPECT_EQ(1, HttpStreamFactoryImplJobPeer::GetNumStreams( - job_controller_->main_job())); + EXPECT_EQ( + 1, HttpStreamFactoryJobPeer::GetNumStreams(job_controller_->main_job())); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } class JobControllerLimitMultipleH2Requests - : public HttpStreamFactoryImplJobControllerTest { + : public HttpStreamFactoryJobControllerTest { protected: const int kNumRequests = 5; void SetUp() override { SkipCreatingJobController(); } @@ -2444,16 +2436,16 @@ } } -class HttpStreamFactoryImplJobControllerMisdirectedRequestRetry - : public HttpStreamFactoryImplJobControllerTest, +class HttpStreamFactoryJobControllerMisdirectedRequestRetry + : public HttpStreamFactoryJobControllerTest, public ::testing::WithParamInterface<::testing::tuple<bool, bool>> {}; INSTANTIATE_TEST_CASE_P( /* no prefix */, - HttpStreamFactoryImplJobControllerMisdirectedRequestRetry, + HttpStreamFactoryJobControllerMisdirectedRequestRetry, ::testing::Combine(::testing::Bool(), ::testing::Bool())); -TEST_P(HttpStreamFactoryImplJobControllerMisdirectedRequestRetry, +TEST_P(HttpStreamFactoryJobControllerMisdirectedRequestRetry, DisableIPBasedPoolingAndAlternativeServices) { const bool enable_ip_based_pooling = ::testing::get<0>(GetParam()); const bool enable_alternative_services = ::testing::get<1>(GetParam()); @@ -2499,8 +2491,8 @@ base::RunLoop().RunUntilIdle(); } -class HttpStreamFactoryImplJobControllerPreconnectTest - : public HttpStreamFactoryImplJobControllerTest, +class HttpStreamFactoryJobControllerPreconnectTest + : public HttpStreamFactoryJobControllerTest, public ::testing::WithParamInterface<bool> { protected: void SetUp() override { @@ -2541,11 +2533,10 @@ INSTANTIATE_TEST_CASE_P( /* no prefix */, - HttpStreamFactoryImplJobControllerPreconnectTest, + HttpStreamFactoryJobControllerPreconnectTest, ::testing::Bool()); -TEST_P(HttpStreamFactoryImplJobControllerPreconnectTest, - LimitEarlyPreconnects) { +TEST_P(HttpStreamFactoryJobControllerPreconnectTest, LimitEarlyPreconnects) { std::list<SequencedSocketData> providers; std::list<SSLSocketDataProvider> ssl_providers; const int kNumPreconects = 5; @@ -2562,9 +2553,8 @@ Initialize(); Preconnect(kNumPreconects); // If experiment is enabled, only 1 stream is requested. - EXPECT_EQ( - (int)actual_num_connects, - HttpStreamFactoryImplJobPeer::GetNumStreams(job_controller_->main_job())); + EXPECT_EQ((int)actual_num_connects, HttpStreamFactoryJobPeer::GetNumStreams( + job_controller_->main_job())); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_)); } @@ -2572,7 +2562,7 @@ // Test that GetAlternativeServiceInfoFor will include a list of advertised // versions, which contains a version that is supported. Returns an empty list // if advertised versions are missing in HttpServerProperties. -TEST_F(HttpStreamFactoryImplJobControllerTest, GetAlternativeServiceInfoFor) { +TEST_F(HttpStreamFactoryJobControllerTest, GetAlternativeServiceInfoFor) { HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); @@ -2653,7 +2643,7 @@ // Tests that if HttpNetworkSession has a non-empty QUIC host whitelist, // then GetAlternativeServiceFor() will not return any QUIC alternative service // that's not on the whitelist. -TEST_F(HttpStreamFactoryImplJobControllerTest, QuicHostWhitelist) { +TEST_F(HttpStreamFactoryJobControllerTest, QuicHostWhitelist) { HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com");
diff --git a/net/http/http_stream_factory_test_util.cc b/net/http/http_stream_factory_test_util.cc index 7cbfc88..8ab4c97 100644 --- a/net/http/http_stream_factory_test_util.cc +++ b/net/http/http_stream_factory_test_util.cc
@@ -15,7 +15,7 @@ MockHttpStreamRequestDelegate::~MockHttpStreamRequestDelegate() = default; -MockHttpStreamFactoryImplJob::MockHttpStreamFactoryImplJob( +MockHttpStreamFactoryJob::MockHttpStreamFactoryJob( HttpStreamFactory::Job::Delegate* delegate, HttpStreamFactory::JobType job_type, HttpNetworkSession* session, @@ -51,7 +51,7 @@ DCHECK(!is_waiting()); } -MockHttpStreamFactoryImplJob::~MockHttpStreamFactoryImplJob() = default; +MockHttpStreamFactoryJob::~MockHttpStreamFactoryJob() = default; TestJobFactory::TestJobFactory() : main_job_(nullptr), @@ -77,7 +77,7 @@ if (override_main_job_url_) origin_url = main_job_alternative_url_; - auto main_job = std::make_unique<MockHttpStreamFactoryImplJob>( + auto main_job = std::make_unique<MockHttpStreamFactoryJob>( delegate, job_type, session, request_info, priority, proxy_info, SSLConfig(), SSLConfig(), destination, origin_url, kProtoUnknown, QUIC_VERSION_UNSUPPORTED, ProxyServer(), is_websocket, @@ -105,7 +105,7 @@ bool is_websocket, bool enable_ip_based_pooling, NetLog* net_log) { - auto alternative_job = std::make_unique<MockHttpStreamFactoryImplJob>( + auto alternative_job = std::make_unique<MockHttpStreamFactoryJob>( delegate, job_type, session, request_info, priority, proxy_info, SSLConfig(), SSLConfig(), destination, origin_url, alternative_protocol, quic_version, ProxyServer(), is_websocket, enable_ip_based_pooling, @@ -132,7 +132,7 @@ bool is_websocket, bool enable_ip_based_pooling, NetLog* net_log) { - auto alternative_job = std::make_unique<MockHttpStreamFactoryImplJob>( + auto alternative_job = std::make_unique<MockHttpStreamFactoryJob>( delegate, job_type, session, request_info, priority, proxy_info, SSLConfig(), SSLConfig(), destination, origin_url, kProtoUnknown, QUIC_VERSION_UNSUPPORTED, alternative_proxy_server, is_websocket,
diff --git a/net/http/http_stream_factory_test_util.h b/net/http/http_stream_factory_test_util.h index 0fdf685..74c75da 100644 --- a/net/http/http_stream_factory_test_util.h +++ b/net/http/http_stream_factory_test_util.h
@@ -105,33 +105,33 @@ DISALLOW_COPY_AND_ASSIGN(MockHttpStreamRequestDelegate); }; -class MockHttpStreamFactoryImplJob : public HttpStreamFactory::Job { +class MockHttpStreamFactoryJob : public HttpStreamFactory::Job { public: - MockHttpStreamFactoryImplJob(HttpStreamFactory::Job::Delegate* delegate, - HttpStreamFactory::JobType job_type, - HttpNetworkSession* session, - const HttpRequestInfo& request_info, - RequestPriority priority, - ProxyInfo proxy_info, - const SSLConfig& server_ssl_config, - const SSLConfig& proxy_ssl_config, - HostPortPair destination, - GURL origin_url, - NextProto alternative_protocol, - QuicTransportVersion quic_version, - const ProxyServer& alternative_proxy_server, - bool is_websocket, - bool enable_ip_based_pooling, - NetLog* net_log); + MockHttpStreamFactoryJob(HttpStreamFactory::Job::Delegate* delegate, + HttpStreamFactory::JobType job_type, + HttpNetworkSession* session, + const HttpRequestInfo& request_info, + RequestPriority priority, + ProxyInfo proxy_info, + const SSLConfig& server_ssl_config, + const SSLConfig& proxy_ssl_config, + HostPortPair destination, + GURL origin_url, + NextProto alternative_protocol, + QuicTransportVersion quic_version, + const ProxyServer& alternative_proxy_server, + bool is_websocket, + bool enable_ip_based_pooling, + NetLog* net_log); - ~MockHttpStreamFactoryImplJob() override; + ~MockHttpStreamFactoryJob() override; MOCK_METHOD0(Resume, void()); MOCK_METHOD0(Orphan, void()); }; -// JobFactory for creating MockHttpStreamFactoryImplJobs. +// JobFactory for creating MockHttpStreamFactoryJobs. class TestJobFactory : public HttpStreamFactory::JobFactory { public: TestJobFactory(); @@ -185,10 +185,8 @@ bool enable_ip_based_pooling, NetLog* net_log) override; - MockHttpStreamFactoryImplJob* main_job() const { return main_job_; } - MockHttpStreamFactoryImplJob* alternative_job() const { - return alternative_job_; - } + MockHttpStreamFactoryJob* main_job() const { return main_job_; } + MockHttpStreamFactoryJob* alternative_job() const { return alternative_job_; } void UseDifferentURLForMainJob(GURL url) { override_main_job_url_ = true; @@ -196,8 +194,8 @@ } private: - MockHttpStreamFactoryImplJob* main_job_; - MockHttpStreamFactoryImplJob* alternative_job_; + MockHttpStreamFactoryJob* main_job_; + MockHttpStreamFactoryJob* alternative_job_; bool override_main_job_url_; GURL main_job_alternative_url_; };
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc index 8bc8182..20a8399 100644 --- a/net/http/http_stream_factory_unittest.cc +++ b/net/http/http_stream_factory_unittest.cc
@@ -165,14 +165,14 @@ }; // HttpStreamFactory subclass that can wait until a preconnect is complete. -class MockHttpStreamFactoryImplForPreconnect : public HttpStreamFactory { +class MockHttpStreamFactoryForPreconnect : public HttpStreamFactory { public: - explicit MockHttpStreamFactoryImplForPreconnect(HttpNetworkSession* session) + explicit MockHttpStreamFactoryForPreconnect(HttpNetworkSession* session) : HttpStreamFactory(session), preconnect_done_(false), waiting_for_preconnect_(false) {} - ~MockHttpStreamFactoryImplForPreconnect() override {} + ~MockHttpStreamFactoryForPreconnect() override {} void WaitForPreconnects() { while (!preconnect_done_) { @@ -358,8 +358,8 @@ const GURL& url, HttpNetworkSession* session) { HttpNetworkSessionPeer peer(session); - MockHttpStreamFactoryImplForPreconnect* mock_factory = - new MockHttpStreamFactoryImplForPreconnect(session); + MockHttpStreamFactoryForPreconnect* mock_factory = + new MockHttpStreamFactoryForPreconnect(session); peer.SetHttpStreamFactory(std::unique_ptr<HttpStreamFactory>(mock_factory)); HttpRequestInfo request; @@ -2474,7 +2474,7 @@ }; INSTANTIATE_TEST_CASE_P( - VersionIncludeStreamDependencySequnece, + VersionIncludeStreamDependencySequence, HttpStreamFactoryBidirectionalQuicTest, ::testing::Combine(::testing::ValuesIn(AllSupportedTransportVersions()), ::testing::Bool())); @@ -2746,7 +2746,7 @@ } #if defined(OS_ANDROID) -// Verify HttpStreamFactoryImplJob passes socket tag along properly and that +// Verify HttpStreamFactory::Job passes socket tag along properly and that // SpdySessions have unique socket tags (e.g. one sessions should not be shared // amongst streams with different socket tags). TEST_F(HttpStreamFactoryTest, Tag) { @@ -2884,7 +2884,7 @@ HttpNetworkSession::NORMAL_SOCKET_POOL))); } -// Verify HttpStreamFactoryImplJob passes socket tag along properly to QUIC +// Verify HttpStreamFactory::Job passes socket tag along properly to QUIC // sessions and that QuicSessions have unique socket tags (e.g. one sessions // should not be shared amongst streams with different socket tags). TEST_P(HttpStreamFactoryBidirectionalQuicTest, Tag) {
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc index fcbaa13..b44c027 100644 --- a/net/quic/chromium/quic_chromium_client_session.cc +++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -762,6 +762,7 @@ crypto_config)); connection->set_debug_visitor(logger_.get()); connection->set_creator_debug_delegate(logger_.get()); + migrate_back_to_default_timer_.SetTaskRunner(task_runner_); net_log_.BeginEvent( NetLogEventType::QUIC_SESSION, base::Bind(NetLogQuicClientSessionCallback, &session_key.server_id(),
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc index ced22239..d65d664e 100644 --- a/net/quic/chromium/quic_chromium_client_session_test.cc +++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -223,7 +223,7 @@ }; INSTANTIATE_TEST_CASE_P( - VersionIncludeStreamDependencySequnece, + VersionIncludeStreamDependencySequence, QuicChromiumClientSessionTest, ::testing::Combine(::testing::ValuesIn(AllSupportedTransportVersions()), ::testing::Bool()));
diff --git a/net/quic/chromium/quic_http_stream_test.cc b/net/quic/chromium/quic_http_stream_test.cc index 35bf7db..72ea1560 100644 --- a/net/quic/chromium/quic_http_stream_test.cc +++ b/net/quic/chromium/quic_http_stream_test.cc
@@ -669,7 +669,7 @@ }; INSTANTIATE_TEST_CASE_P( - VersionIncludeStreamDependencySequnece, + VersionIncludeStreamDependencySequence, QuicHttpStreamTest, ::testing::Combine(::testing::ValuesIn(AllSupportedTransportVersions()), ::testing::Bool()));
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc index b12815b9..9eb9181 100644 --- a/net/quic/chromium/quic_network_transaction_unittest.cc +++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -6182,7 +6182,7 @@ SSLSocketDataProvider ssl_data_; }; -INSTANTIATE_TEST_CASE_P(VersionIncludeStreamDependencySequnece, +INSTANTIATE_TEST_CASE_P(VersionIncludeStreamDependencySequence, QuicNetworkTransactionWithDestinationTest, ::testing::ValuesIn(GetPoolingTestParams()));
diff --git a/net/quic/chromium/quic_proxy_client_socket_unittest.cc b/net/quic/chromium/quic_proxy_client_socket_unittest.cc index f9ce4b15..50b305c 100644 --- a/net/quic/chromium/quic_proxy_client_socket_unittest.cc +++ b/net/quic/chromium/quic_proxy_client_socket_unittest.cc
@@ -1542,7 +1542,7 @@ } INSTANTIATE_TEST_CASE_P( - VersionIncludeStreamDependencySequnece, + VersionIncludeStreamDependencySequence, QuicProxyClientSocketTest, ::testing::Combine(::testing::ValuesIn(AllSupportedTransportVersions()), ::testing::Bool()));
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc index c273c98..8df5b18 100644 --- a/net/quic/chromium/quic_stream_factory_test.cc +++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/strings/string_util.h" +#include "base/test/test_mock_time_task_runner.h" #include "build/build_config.h" #include "net/base/completion_once_callback.h" #include "net/base/mock_network_change_notifier.h" @@ -101,6 +102,8 @@ const char kServer2Url[] = "https://mail.example.org/"; const char kServer3Url[] = "https://docs.example.org/"; const char kServer4Url[] = "https://images.example.org/"; +const int kDefaultRTTMilliSecs = 300; +const size_t kMinRetryTimeForDefaultNetworkSecs = 1; // Run QuicStreamFactoryTest instances with all value combinations of version // and enable_connection_racting. @@ -838,7 +841,7 @@ GetParam().client_headers_include_h2_stream_dependency) {} }; -INSTANTIATE_TEST_CASE_P(VersionIncludeStreamDependencySequnece, +INSTANTIATE_TEST_CASE_P(VersionIncludeStreamDependencySequence, QuicStreamFactoryTest, ::testing::ValuesIn(GetTestParams())); @@ -3061,6 +3064,153 @@ EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); } +// This test verifies that the connection migrates to the alternate network +// early when path degrading is detected. +TEST_P(QuicStreamFactoryTest, MigrateEarlyOnPathDegrading) { + InitializeConnectionMigrationV2Test( + {kDefaultNetworkForTests, kNewNetworkForTests}); + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + + // Using a testing task runner so that we can control time. + auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); + QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get()); + + scoped_mock_network_change_notifier_->mock_network_change_notifier() + ->QueueNetworkMadeDefault(kDefaultNetworkForTests); + + MockQuicData quic_data1; + QuicStreamOffset header_stream_offset = 0; + quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging Read. + quic_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset)); + quic_data1.AddWrite(SYNCHRONOUS, ConstructGetRequestPacket( + 2, GetNthClientInitiatedStreamId(0), true, true, &header_stream_offset)); + quic_data1.AddSocketDataToFactory(socket_factory_.get()); + + // Set up the second socket data provider that is used after migration. + // The response to the earlier request is read on the new socket. + MockQuicData quic_data2; + // Connectivity probe to be sent on the new path. + quic_data2.AddWrite(SYNCHRONOUS, + client_maker_.MakeConnectivityProbingPacket(3, true, 1338)); + quic_data2.AddRead(ASYNC, ERR_IO_PENDING); // Pause + // Connectivity probe to receive from the server. + quic_data2.AddRead(ASYNC, + server_maker_.MakeConnectivityProbingPacket(1, false, 1338)); + // Ping packet to send after migration is completed. + quic_data2.AddWrite(ASYNC, + client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1)); + quic_data2.AddRead(ASYNC, ConstructOkResponsePacket( + 2, GetNthClientInitiatedStreamId(0), false, false)); + quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); + quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeAckAndRstPacket( + 5, false, GetNthClientInitiatedStreamId(0), QUIC_STREAM_CANCELLED, 2, 2, + 1, true)); + quic_data2.AddSocketDataToFactory(socket_factory_.get()); + + // Create request and QuicHttpStream. + QuicStreamRequest request(factory_.get()); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, version_, privacy_mode_, + DEFAULT_PRIORITY, SocketTag(), + /*cert_verify_flags=*/0, url_, net_log_, + &net_error_details_, callback_.callback())); + EXPECT_THAT(callback_.WaitForResult(), IsOk()); + std::unique_ptr<HttpStream> stream = CreateStream(&request); + EXPECT_TRUE(stream.get()); + + // Cause QUIC stream to be created. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = url_; + request_info.traffic_annotation = + MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY, + net_log_, CompletionOnceCallback())); + + // Ensure that session is alive and active. + QuicChromiumClientSession* session = GetActiveSession(host_port_pair_); + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + + // Send GET request on stream. + HttpResponseInfo response; + HttpRequestHeaders request_headers; + EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, + callback_.callback())); + + // Notify the session that path is degrading. Session will start to probe the + // alternate network. + session->OnPathDegrading(); + + // Next connectivity probe is scheduled to be sent in 2 * + // kDefaultRTTMilliSecs. + EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); + base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs), + next_task_delay); + + // The connection should still be alive, and not marked as going away. + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + EXPECT_EQ(1u, session->GetNumActiveStreams()); + EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); + + // Resume quic data and a connectivity probe response will be read on the new + // socket. + quic_data2.Resume(); + + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + EXPECT_EQ(1u, session->GetNumActiveStreams()); + + // There should be three pending tasks, the nearest one will complete + // migration to the new network. + EXPECT_EQ(3u, task_runner->GetPendingTaskCount()); + next_task_delay = task_runner->NextPendingTaskDelay(); + EXPECT_EQ(base::TimeDelta(), next_task_delay); + task_runner->FastForwardBy(next_task_delay); + + // Response headers are received over the new network. + EXPECT_THAT(callback_.WaitForResult(), IsOk()); + EXPECT_EQ(200, response.headers->response_code()); + + // Now there are two pending tasks, the nearest one was to send connectivity + // probe and has been cancelled due to successful migration. + EXPECT_EQ(2u, task_runner->GetPendingTaskCount()); + next_task_delay = task_runner->NextPendingTaskDelay(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs), + next_task_delay); + task_runner->FastForwardBy(next_task_delay); + + // There's one more task to mgirate back to the default network in 0.4s. + EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); + next_task_delay = task_runner->NextPendingTaskDelay(); + base::TimeDelta expected_delay = + base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs) - + base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs); + EXPECT_EQ(expected_delay, next_task_delay); + + // Deliver a signal that the alternate network now becomes default to session, + // this will cancel mgirate back to default network timer. + scoped_mock_network_change_notifier_->mock_network_change_notifier() + ->NotifyNetworkMadeDefault(kNewNetworkForTests); + + task_runner->FastForwardBy(next_task_delay); + EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); + + // Verify that the session is still alive. + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + + stream.reset(); + EXPECT_TRUE(quic_data1.AllReadDataConsumed()); + EXPECT_TRUE(quic_data1.AllWriteDataConsumed()); + EXPECT_TRUE(quic_data2.AllReadDataConsumed()); + EXPECT_TRUE(quic_data2.AllWriteDataConsumed()); +} + TEST_P(QuicStreamFactoryTest, OnNetworkChangeDisconnectedPauseBeforeConnected) { InitializeConnectionMigrationTest({kDefaultNetworkForTests}); ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); @@ -5620,7 +5770,7 @@ sequenced_socket_data_vector_; }; -INSTANTIATE_TEST_CASE_P(VersionIncludeStreamDependencySequnece, +INSTANTIATE_TEST_CASE_P(VersionIncludeStreamDependencySequence, QuicStreamFactoryWithDestinationTest, ::testing::ValuesIn(GetPoolingTestParams()));
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc index 87b3d21..c6f8056 100644 --- a/net/quic/chromium/quic_test_packet_maker.cc +++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -53,6 +53,35 @@ host_.assign(host); } +std::unique_ptr<QuicReceivedPacket> +QuicTestPacketMaker::MakeConnectivityProbingPacket( + QuicPacketNumber num, + bool include_version, + QuicByteCount packet_length) { + QuicPacketHeader header; + header.connection_id = connection_id_; + header.reset_flag = false; + header.version_flag = ShouldIncludeVersion(include_version); + header.long_packet_type = long_header_type_; + header.packet_number_length = GetPacketNumberLength(); + header.packet_number = num; + + QuicFramer framer( + SupportedVersions(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version_)), + clock_->Now(), perspective_); + + char buffer[kMaxPacketSize]; + size_t length = + framer.BuildConnectivityProbingPacket(header, buffer, packet_length); + size_t encrypted_size = framer.EncryptInPlace( + ENCRYPTION_NONE, header.packet_number, + GetStartOfEncryptedData(framer.transport_version(), header), length, + kMaxPacketSize, buffer); + EXPECT_NE(0u, encrypted_size); + QuicReceivedPacket encrypted(buffer, encrypted_size, clock_->Now(), false); + return std::unique_ptr<QuicReceivedPacket>(encrypted.Clone()); +} + std::unique_ptr<QuicReceivedPacket> QuicTestPacketMaker::MakePingPacket( QuicPacketNumber num, bool include_version) {
diff --git a/net/quic/chromium/quic_test_packet_maker.h b/net/quic/chromium/quic_test_packet_maker.h index e4d24d4..98b7969 100644 --- a/net/quic/chromium/quic_test_packet_maker.h +++ b/net/quic/chromium/quic_test_packet_maker.h
@@ -48,6 +48,10 @@ ~QuicTestPacketMaker(); void set_hostname(const std::string& host); + std::unique_ptr<QuicReceivedPacket> MakeConnectivityProbingPacket( + QuicPacketNumber num, + bool include_version, + QuicByteCount packet_length); std::unique_ptr<QuicReceivedPacket> MakePingPacket(QuicPacketNumber num, bool include_version); std::unique_ptr<QuicReceivedPacket> MakeAckAndPingPacket(
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 2570d6a..e83cfab 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -3808,9 +3808,7 @@ pool_->AddHigherLayeredPool(this); } - ~MockLayeredPool() { - pool_->RemoveHigherLayeredPool(this); - } + ~MockLayeredPool() override { pool_->RemoveHigherLayeredPool(this); } int RequestSocket(TestClientSocketPool* pool) { scoped_refptr<TestSocketParams> params(new TestSocketParams());
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc index 0727da41..5568b86 100644 --- a/net/socket/ssl_server_socket_impl.cc +++ b/net/socket/ssl_server_socket_impl.cc
@@ -651,7 +651,8 @@ int rv = SSL_do_handshake(ssl_.get()); if (rv == 1) { completed_handshake_ = true; - STACK_OF(CRYPTO_BUFFER)* certs = SSL_get0_peer_certificates(ssl_.get()); + const STACK_OF(CRYPTO_BUFFER)* certs = + SSL_get0_peer_certificates(ssl_.get()); if (certs) { client_cert_ = x509_util::CreateX509CertificateFromBuffers(certs); if (!client_cert_)
diff --git a/net/spdy/chromium/spdy_stream_unittest.cc b/net/spdy/chromium/spdy_stream_unittest.cc index bc1ea24c..effcfda 100644 --- a/net/spdy/chromium/spdy_stream_unittest.cc +++ b/net/spdy/chromium/spdy_stream_unittest.cc
@@ -389,6 +389,12 @@ EXPECT_THAT(push_delegate.WaitForClose(), IsOk()); EXPECT_EQ("200", delegate.GetResponseHeaderValue(kHttp2StatusHeader)); EXPECT_EQ(pushed_msg, push_delegate.TakeReceivedData()); + + // Finish async network reads and writes. + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(data.AllWriteDataConsumed()); + EXPECT_TRUE(data.AllReadDataConsumed()); } TEST_F(SpdyStreamTest, StreamError) {
diff --git a/net/ssl/ssl_config_service_unittest.cc b/net/ssl/ssl_config_service_unittest.cc index b0d41a3..44441d0 100644 --- a/net/ssl/ssl_config_service_unittest.cc +++ b/net/ssl/ssl_config_service_unittest.cc
@@ -37,7 +37,7 @@ class MockSSLConfigServiceObserver : public SSLConfigService::Observer { public: MockSSLConfigServiceObserver() = default; - virtual ~MockSSLConfigServiceObserver() = default; + ~MockSSLConfigServiceObserver() override = default; MOCK_METHOD0(OnSSLConfigChanged, void()); };
diff --git a/net/url_request/url_request_throttler_manager.h b/net/url_request/url_request_throttler_manager.h index 1321cab..3626ebb8 100644 --- a/net/url_request/url_request_throttler_manager.h +++ b/net/url_request/url_request_throttler_manager.h
@@ -32,7 +32,7 @@ // are registered, and does garbage collection from time to time in order to // clean out outdated entries. URL ID consists of lowercased scheme, host, port // and path. All URLs converted to the same ID will share the same entry. -class NET_EXPORT URLRequestThrottlerManager +class NET_EXPORT_PRIVATE URLRequestThrottlerManager : public NetworkChangeNotifier::IPAddressObserver, public NetworkChangeNotifier::ConnectionTypeObserver { public:
diff --git a/net/websockets/websocket_basic_stream_adapters_test.cc b/net/websockets/websocket_basic_stream_adapters_test.cc index f7246c8..3c6e882f 100644 --- a/net/websockets/websocket_basic_stream_adapters_test.cc +++ b/net/websockets/websocket_basic_stream_adapters_test.cc
@@ -287,7 +287,7 @@ class MockDelegate : public WebSocketSpdyStreamAdapter::Delegate { public: - virtual ~MockDelegate() = default; + ~MockDelegate() override = default; MOCK_METHOD0(OnHeadersSent, void()); MOCK_METHOD1(OnHeadersReceived, void(const SpdyHeaderBlock&)); MOCK_METHOD1(OnClose, void(int));
diff --git a/pdf/pdfium/DEPS b/pdf/pdfium/DEPS index 00a544d..5e8aa4c 100644 --- a/pdf/pdfium/DEPS +++ b/pdf/pdfium/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+gin/array_buffer.h", "+gin/public", + "+printing/nup_parameters.h", "+third_party/pdfium/public", "+ui/gfx/codec/jpeg_codec.h", ]
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index 1d0303c..6a25cd0 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc
@@ -50,6 +50,7 @@ #include "ppapi/cpp/url_response_info.h" #include "ppapi/cpp/var.h" #include "ppapi/cpp/var_dictionary.h" +#include "printing/nup_parameters.h" #include "printing/units.h" #include "third_party/pdfium/public/cpp/fpdf_scopers.h" #include "third_party/pdfium/public/fpdf_annot.h" @@ -709,6 +710,30 @@ } } +// UI should have done parameter sanity check, when execution +// reaches here, |num_pages_per_sheet| should be a positive integer. +bool ShouldDoNup(int num_pages_per_sheet) { + return num_pages_per_sheet > 1; +} + +// Check the source doc orientation. Returns true if the doc is landscape. +// For now the orientation of the doc is determined by its first page's +// orientation. Improvement can be added in the future to better determine the +// orientation of the source docs that have mixed orientation. +// TODO(xlou): rotate pages if the source doc has mixed orientation. So that +// the orientation of all pages of the doc are uniform. Pages of square size +// will not be rotated. +bool IsSourcePdfLandscape(FPDF_DOCUMENT doc) { + DCHECK(doc); + + ScopedFPDFPage pdf_page(FPDF_LoadPage(doc, 0)); + DCHECK(pdf_page); + + bool is_source_landscape = + FPDF_GetPageWidth(pdf_page.get()) > FPDF_GetPageHeight(pdf_page.get()); + return is_source_landscape; +} + } // namespace bool InitializeSDK() { @@ -1707,16 +1732,21 @@ pp::Buffer_Dev buffer; if (i == pages_to_print.size()) { FPDF_CopyViewerPreferences(output_doc, doc_); - FitContentsToPrintableAreaIfRequired(output_doc, print_settings); - // Now flatten all the output pages. - buffer = GetFlattenedPrintData(output_doc); + if (ShouldDoNup(print_settings.num_pages_per_sheet)) { + buffer = NupPdfToPdf(output_doc, print_settings); + } else { + FitContentsToPrintableAreaIfRequired(output_doc, print_settings); + buffer = GetPrintData(output_doc); + } } + FPDF_CloseDocument(output_doc); return buffer; } -pp::Buffer_Dev PDFiumEngine::GetFlattenedPrintData(FPDF_DOCUMENT doc) { - pp::Buffer_Dev buffer; +bool PDFiumEngine::FlattenPrintData(FPDF_DOCUMENT doc) { + DCHECK(doc); + ScopedSubstFont scoped_subst_font(this); int page_count = FPDF_GetPageCount(doc); for (int i = 0; i < page_count; ++i) { @@ -1725,9 +1755,15 @@ int flatten_ret = FPDFPage_Flatten(page, FLAT_PRINT); FPDF_ClosePage(page); if (flatten_ret == FLATTEN_FAIL) - return buffer; + return false; } + return true; +} +pp::Buffer_Dev PDFiumEngine::GetPrintData(FPDF_DOCUMENT doc) { + DCHECK(doc); + + pp::Buffer_Dev buffer; PDFiumMemBufferFileWrite output_file_write; if (FPDF_SaveAsCopy(doc, &output_file_write, 0)) { size_t size = output_file_write.size(); @@ -1738,6 +1774,41 @@ return buffer; } +pp::Buffer_Dev PDFiumEngine::GetFlattenedPrintData(FPDF_DOCUMENT doc) { + DCHECK(doc); + + pp::Buffer_Dev buffer; + if (FlattenPrintData(doc)) + buffer = GetPrintData(doc); + return buffer; +} + +pp::Buffer_Dev PDFiumEngine::NupPdfToPdf( + FPDF_DOCUMENT doc, + const PP_PrintSettings_Dev& print_settings) { + DCHECK(doc); + + PP_Size page_size = print_settings.paper_size; + + printing::NupParameters nup_params; + bool is_landscape = IsSourcePdfLandscape(doc); + nup_params.SetParameters(print_settings.num_pages_per_sheet, is_landscape); + + // Import n pages to one. + bool paper_is_landscape = page_size.width > page_size.height; + if (nup_params.landscape() != paper_is_landscape) + std::swap(page_size.width, page_size.height); + + ScopedFPDFDocument output_doc_nup(FPDF_ImportNPagesToOne( + doc, page_size.width, page_size.height, nup_params.num_pages_on_x_axis(), + nup_params.num_pages_on_y_axis())); + if (!output_doc_nup) + return pp::Buffer_Dev(); + + FitContentsToPrintableAreaIfRequired(output_doc_nup.get(), print_settings); + return GetPrintData(output_doc_nup.get()); +} + pp::Buffer_Dev PDFiumEngine::PrintPagesAsPDF( const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count, @@ -1746,7 +1817,7 @@ return pp::Buffer_Dev(); DCHECK(doc_); - FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); + ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); if (!output_doc) return pp::Buffer_Dev(); @@ -1772,17 +1843,24 @@ pages_[page_number]->Unload(); } - FPDF_CopyViewerPreferences(output_doc, doc_); - if (!FPDF_ImportPages(output_doc, doc_, page_number_str.c_str(), 0)) { - FPDF_CloseDocument(output_doc); + FPDF_CopyViewerPreferences(output_doc.get(), doc_); + if (!FPDF_ImportPages(output_doc.get(), doc_, page_number_str.c_str(), 0)) { return pp::Buffer_Dev(); } - FitContentsToPrintableAreaIfRequired(output_doc, print_settings); - // Now flatten all the output pages. - pp::Buffer_Dev buffer = GetFlattenedPrintData(output_doc); - FPDF_CloseDocument(output_doc); + if (!FlattenPrintData(output_doc.get())) { + return pp::Buffer_Dev(); + } + + pp::Buffer_Dev buffer; + if (ShouldDoNup(print_settings.num_pages_per_sheet)) { + buffer = NupPdfToPdf(output_doc.get(), print_settings); + } else { + FitContentsToPrintableAreaIfRequired(output_doc.get(), print_settings); + buffer = GetPrintData(output_doc.get()); + } + return buffer; }
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h index b5be0c1..63596eb 100644 --- a/pdf/pdfium/pdfium_engine.h +++ b/pdf/pdfium/pdfium_engine.h
@@ -331,7 +331,16 @@ uint32_t page_range_count, const PP_PrintSettings_Dev& print_settings); + bool FlattenPrintData(FPDF_DOCUMENT doc); + pp::Buffer_Dev GetPrintData(FPDF_DOCUMENT doc); pp::Buffer_Dev GetFlattenedPrintData(FPDF_DOCUMENT doc); + + // Perform N-up PDF generation from |doc| based on the parameters in + // |print_settings|. On success, the returned buffer contains the N-up version + // of |doc|. On failure, the returned buffer is empty. + pp::Buffer_Dev NupPdfToPdf(FPDF_DOCUMENT doc, + const PP_PrintSettings_Dev& print_settings); + void FitContentsToPrintableAreaIfRequired( FPDF_DOCUMENT doc, const PP_PrintSettings_Dev& print_settings);
diff --git a/ppapi/api/dev/pp_print_settings_dev.idl b/ppapi/api/dev/pp_print_settings_dev.idl index 010d1b8c..cf349fd2 100644 --- a/ppapi/api/dev/pp_print_settings_dev.idl +++ b/ppapi/api/dev/pp_print_settings_dev.idl
@@ -32,7 +32,7 @@ PP_PRINTSCALINGOPTION_LAST = PP_PRINTSCALINGOPTION_SOURCE_SIZE }; -[assert_size(60)] +[assert_size(64)] struct PP_PrintSettings_Dev { /** This is the size of the printable area in points (1/72 of an inch). */ PP_Rect printable_area; @@ -44,4 +44,6 @@ PP_Bool grayscale; /** Note that Chrome currently only supports PDF printing. */ PP_PrintOutputFormat_Dev format; + /** Note the following parameter is for N-up mode. */ + uint32_t num_pages_per_sheet; };
diff --git a/ppapi/c/dev/pp_print_settings_dev.h b/ppapi/c/dev/pp_print_settings_dev.h index 67c37b2..29e7908 100644 --- a/ppapi/c/dev/pp_print_settings_dev.h +++ b/ppapi/c/dev/pp_print_settings_dev.h
@@ -68,8 +68,10 @@ PP_Bool grayscale; /** Note that Chrome currently only supports PDF printing. */ PP_PrintOutputFormat_Dev format; + /** Note the following parameter is for N-up mode. */ + uint32_t num_pages_per_sheet; }; -PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(PP_PrintSettings_Dev, 60); +PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(PP_PrintSettings_Dev, 64); /** * @} */
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index ff05c1a..c56d388 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h
@@ -220,6 +220,7 @@ IPC_STRUCT_TRAITS_MEMBER(print_scaling_option) IPC_STRUCT_TRAITS_MEMBER(grayscale) IPC_STRUCT_TRAITS_MEMBER(format) + IPC_STRUCT_TRAITS_MEMBER(num_pages_per_sheet) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(PP_PdfPrintPresetOptions_Dev)
diff --git a/printing/print_settings.cc b/printing/print_settings.cc index b95ba27..ae8ad92 100644 --- a/printing/print_settings.cc +++ b/printing/print_settings.cc
@@ -181,6 +181,7 @@ printer_type_ = PrintSettings::PrinterType::TYPE_NONE; #endif is_modifiable_ = true; + num_pages_per_sheet_ = 1; } void PrintSettings::SetPrinterPrintableArea(
diff --git a/printing/print_settings.h b/printing/print_settings.h index 569955b..e6ecec7 100644 --- a/printing/print_settings.h +++ b/printing/print_settings.h
@@ -187,6 +187,11 @@ void set_is_modifiable(bool is_modifiable) { is_modifiable_ = is_modifiable; } bool is_modifiable() const { return is_modifiable_; } + int num_pages_per_sheet() const { return num_pages_per_sheet_; } + void set_num_pages_per_sheet(int num_pages_per_sheet) { + num_pages_per_sheet_ = num_pages_per_sheet; + } + // Cookie generator. It is used to initialize PrintedDocument with its // associated PrintSettings, to be sure that each generated PrintedPage is // correctly associated with its corresponding PrintedDocument. @@ -262,6 +267,9 @@ // If margin type is custom, this is what was requested. PageMargins requested_custom_margins_in_points_; + + // Number of pages per sheet. + int num_pages_per_sheet_; }; } // namespace printing
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc index 98caf9c1..5bb2923c 100644 --- a/printing/print_settings_conversion.cc +++ b/printing/print_settings_conversion.cc
@@ -179,6 +179,7 @@ int copies = 1; int scale_factor = 100; bool rasterize_pdf = false; + int num_pages_per_sheet = 1; if (!job_settings.GetBoolean(kSettingCollate, &collate) || !job_settings.GetInteger(kSettingCopies, &copies) || @@ -216,6 +217,9 @@ #endif } + // TODO(xlou): Add logic to get |num_pages_per_sheet| from |job_settings|. + settings->set_num_pages_per_sheet(num_pages_per_sheet); + return true; }
diff --git a/remoting/host/config_file_watcher_unittest.cc b/remoting/host/config_file_watcher_unittest.cc index 976d3b59b..9077d01 100644 --- a/remoting/host/config_file_watcher_unittest.cc +++ b/remoting/host/config_file_watcher_unittest.cc
@@ -26,7 +26,7 @@ class ConfigFileWatcherDelegate : public ConfigFileWatcher::Delegate { public: ConfigFileWatcherDelegate() = default; - virtual ~ConfigFileWatcherDelegate() = default; + ~ConfigFileWatcherDelegate() override = default; MOCK_METHOD1(OnConfigUpdated, void(const std::string&)); MOCK_METHOD0(OnConfigWatcherError, void());
diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h index 3053787..268defb 100644 --- a/remoting/host/host_mock_objects.h +++ b/remoting/host/host_mock_objects.h
@@ -134,7 +134,8 @@ MOCK_METHOD1(StartPtr, void(protocol::ClipboardStub* client_clipboard)); - void Start(std::unique_ptr<protocol::ClipboardStub> client_clipboard); + void Start( + std::unique_ptr<protocol::ClipboardStub> client_clipboard) override; private: DISALLOW_COPY_AND_ASSIGN(MockInputInjector);
diff --git a/remoting/host/ipc_desktop_environment_unittest.cc b/remoting/host/ipc_desktop_environment_unittest.cc index e9364e6d..03e28fe6 100644 --- a/remoting/host/ipc_desktop_environment_unittest.cc +++ b/remoting/host/ipc_desktop_environment_unittest.cc
@@ -64,7 +64,7 @@ class MockScreenCapturerCallback : public webrtc::DesktopCapturer::Callback { public: MockScreenCapturerCallback() = default; - virtual ~MockScreenCapturerCallback() = default; + ~MockScreenCapturerCallback() override = default; MOCK_METHOD2(OnCaptureResultPtr, void(webrtc::DesktopCapturer::Result result,
diff --git a/remoting/protocol/fake_connection_to_host.h b/remoting/protocol/fake_connection_to_host.h index 76b497b..747892ac 100644 --- a/remoting/protocol/fake_connection_to_host.h +++ b/remoting/protocol/fake_connection_to_host.h
@@ -24,7 +24,7 @@ void set_client_stub(protocol::ClientStub* client_stub) override; void set_clipboard_stub(protocol::ClipboardStub* clipboard_stub) override; void set_video_renderer(protocol::VideoRenderer* video_renderer) override; - virtual void InitializeAudio( + void InitializeAudio( scoped_refptr<base::SingleThreadTaskRunner> audio_decode_task_runner, base::WeakPtr<protocol::AudioStub> audio_stub) override; void Connect(std::unique_ptr<protocol::Session> session,
diff --git a/remoting/protocol/video_frame_pump_unittest.cc b/remoting/protocol/video_frame_pump_unittest.cc index c538312..722f09f 100644 --- a/remoting/protocol/video_frame_pump_unittest.cc +++ b/remoting/protocol/video_frame_pump_unittest.cc
@@ -55,13 +55,14 @@ class MockVideoEncoder : public VideoEncoder { public: MockVideoEncoder() = default; - ~MockVideoEncoder() = default; + ~MockVideoEncoder() override = default; MOCK_METHOD1(SetLosslessEncode, void(bool)); MOCK_METHOD1(SetLosslessColor, void(bool)); MOCK_METHOD1(EncodePtr, VideoPacket*(const webrtc::DesktopFrame&)); - std::unique_ptr<VideoPacket> Encode(const webrtc::DesktopFrame& frame) { + std::unique_ptr<VideoPacket> Encode( + const webrtc::DesktopFrame& frame) override { return base::WrapUnique(EncodePtr(frame)); } };
diff --git a/services/BUILD.gn b/services/BUILD.gn index 9226215e..f6cb317 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn
@@ -132,6 +132,19 @@ } } +if (!is_ios) { + service_test("services_perftests") { + deps = [ + "//services/viz/public/cpp/compositing:perftests", + ] + catalog = ":services_perftests_catalog" + } + + catalog("services_perftests_catalog") { + testonly = true + } +} + if (is_android) { junit_binary("service_junit_tests") { java_files = [
diff --git a/services/audio/input_stream_unittest.cc b/services/audio/input_stream_unittest.cc index 5a4d334..c6db8ab 100644 --- a/services/audio/input_stream_unittest.cc +++ b/services/audio/input_stream_unittest.cc
@@ -118,7 +118,7 @@ stream_factory_binding_(&stream_factory_, mojo::MakeRequest(&stream_factory_ptr_)) {} - ~AudioServiceInputStreamTest() { audio_manager_.Shutdown(); } + ~AudioServiceInputStreamTest() override { audio_manager_.Shutdown(); } void SetUp() override { mojo::edk::SetDefaultProcessErrorCallback(
diff --git a/services/audio/output_controller_unittest.cc b/services/audio/output_controller_unittest.cc index 0a9824eb..5f581687 100644 --- a/services/audio/output_controller_unittest.cc +++ b/services/audio/output_controller_unittest.cc
@@ -79,7 +79,7 @@ MOCK_METHOD0(OnControllerPlaying, void()); MOCK_METHOD0(OnControllerPaused, void()); MOCK_METHOD0(OnControllerError, void()); - void OnLog(base::StringPiece) {} + void OnLog(base::StringPiece) override {} private: DISALLOW_COPY_AND_ASSIGN(MockOutputControllerEventHandler);
diff --git a/services/network/cross_origin_read_blocking.cc b/services/network/cross_origin_read_blocking.cc index 089c413..2d01d02 100644 --- a/services/network/cross_origin_read_blocking.cc +++ b/services/network/cross_origin_read_blocking.cc
@@ -16,6 +16,7 @@ #include "base/metrics/histogram_macros.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" +#include "net/base/mime_sniffer.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" @@ -395,10 +396,107 @@ kCorsSafelistedHeaders + arraysize(kCorsSafelistedHeaders)); } +// An interface to enable incremental content sniffing. These are instantiated +// for each each request; thus they can be stateful. +class CrossOriginReadBlocking::ResponseAnalyzer::ConfirmationSniffer { + public: + virtual ~ConfirmationSniffer() = default; + + // Called after data is read from the network. |sniffing_buffer| contains the + // entire response body delivered thus far. To support streaming, + // |new_data_offset| gives the offset into |sniffing_buffer| at which new data + // was appended since the last read. + virtual void OnDataAvailable(base::StringPiece sniffing_buffer, + size_t new_data_offset) = 0; + + // Returns true if the return value of IsConfirmedContentType() might change + // with the addition of more data. Returns false if a final decision is + // available. + virtual bool WantsMoreData() const = 0; + + // Returns true if the data has been confirmed to be of the CORB-protected + // content type that this sniffer is intended to detect. + virtual bool IsConfirmedContentType() const = 0; + + // Helper for reporting the right UMA. + virtual bool IsParserBreakerSniffer() const = 0; +}; + +// A ConfirmationSniffer that wraps one of the sniffing functions from +// network::CrossOriginReadBlocking. +class SimpleConfirmationSniffer + : public CrossOriginReadBlocking::ResponseAnalyzer::ConfirmationSniffer { + public: + // The function pointer type corresponding to one of the available sniffing + // functions from network::CrossOriginReadBlocking. + using SnifferFunction = + decltype(&network::CrossOriginReadBlocking::SniffForHTML); + + explicit SimpleConfirmationSniffer(SnifferFunction sniffer_function) + : sniffer_function_(sniffer_function) {} + ~SimpleConfirmationSniffer() override = default; + + void OnDataAvailable(base::StringPiece sniffing_buffer, + size_t new_data_offset) final { + DCHECK_LE(new_data_offset, sniffing_buffer.length()); + if (new_data_offset == sniffing_buffer.length()) { + // No new data -- do nothing. This happens at end-of-stream. + return; + } + // The sniffing functions don't support streaming, so with each new chunk of + // data, call the sniffer on the whole buffer. + last_sniff_result_ = (*sniffer_function_)(sniffing_buffer); + } + + bool WantsMoreData() const final { + // kNo and kYes results are final, meaning that sniffing can stop once they + // occur. A kMaybe result corresponds to an indeterminate state, that could + // change to kYes or kNo with more data. + return last_sniff_result_ == SniffingResult::kMaybe; + } + + bool IsConfirmedContentType() const final { + // Only confirm the mime type if an affirmative pattern (e.g. an HTML tag, + // if using the HTML sniffer) was detected. + // + // Note that if the stream ends (or net::kMaxBytesToSniff has been reached) + // and |last_sniff_result_| is kMaybe, the response is allowed to go + // through. + return last_sniff_result_ == SniffingResult::kYes; + } + + bool IsParserBreakerSniffer() const override { return false; } + + private: + // The function that actually knows how to sniff for a content type. + SnifferFunction sniffer_function_; + + // Result of sniffing the data available thus far. + SniffingResult last_sniff_result_ = SniffingResult::kMaybe; + + DISALLOW_COPY_AND_ASSIGN(SimpleConfirmationSniffer); +}; + +// A ConfirmationSniffer for parser breakers (fetch-only resources). This logs +// to an UMA histogram whenever it is the reason for a response being blocked. +class FetchOnlyResourceSniffer : public SimpleConfirmationSniffer { + public: + FetchOnlyResourceSniffer() + : SimpleConfirmationSniffer( + &network::CrossOriginReadBlocking::SniffForFetchOnlyResource) {} + + bool IsParserBreakerSniffer() const override { return true; } + + private: + DISALLOW_COPY_AND_ASSIGN(FetchOnlyResourceSniffer); +}; + CrossOriginReadBlocking::ResponseAnalyzer::ResponseAnalyzer( const net::URLRequest& request, const ResourceResponse& response) { should_block_based_on_headers_ = ShouldBlockBasedOnHeaders(request, response); + if (should_block_based_on_headers_ == kNeedToSniffMore) + CreateSniffers(); } CrossOriginReadBlocking::ResponseAnalyzer::~ResponseAnalyzer() = default; @@ -540,4 +638,95 @@ return kBlock; } +void CrossOriginReadBlocking::ResponseAnalyzer::CreateSniffers() { + // Create one or more |sniffers_| to confirm that the body is actually the + // MIME type advertised in the Content-Type header. + DCHECK_EQ(kNeedToSniffMore, should_block_based_on_headers_); + DCHECK(sniffers_.empty()); + + // When the MIME type is "text/plain", create sniffers for HTML, XML and + // JSON. If any of these sniffers match, the response will be blocked. + const bool use_all = canonical_mime_type() == MimeType::kPlain; + + // HTML sniffer. + if (use_all || canonical_mime_type() == MimeType::kHtml) { + sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>( + &network::CrossOriginReadBlocking::SniffForHTML)); + } + + // XML sniffer. + if (use_all || canonical_mime_type() == MimeType::kXml) { + sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>( + &network::CrossOriginReadBlocking::SniffForXML)); + } + + // JSON sniffer. + if (use_all || canonical_mime_type() == MimeType::kJson) { + sniffers_.push_back(std::make_unique<SimpleConfirmationSniffer>( + &network::CrossOriginReadBlocking::SniffForJSON)); + } + + // Parser-breaker sniffer. + // + // Because these prefixes are an XSSI-defeating mechanism, CORB considers + // them distinctive enough to be worth blocking no matter the Content-Type + // header. So this sniffer is created unconditionally. + // + // For MimeType::kOthers, this will be the only sniffer that's active. + sniffers_.push_back(std::make_unique<FetchOnlyResourceSniffer>()); +} + +void CrossOriginReadBlocking::ResponseAnalyzer::SniffResponseBody( + base::StringPiece data, + size_t new_data_offset) { + DCHECK_EQ(kNeedToSniffMore, should_block_based_on_headers_); + DCHECK(!sniffers_.empty()); + DCHECK_LE(data.size(), static_cast<size_t>(net::kMaxBytesToSniff)); + DCHECK_LT(new_data_offset, data.size()); + + for (size_t i = 0; i < sniffers_.size();) { + sniffers_[i]->OnDataAvailable(data, new_data_offset); + + if (sniffers_[i]->WantsMoreData()) { + i++; + continue; + } + + if (sniffers_[i]->IsConfirmedContentType()) { + if (sniffers_[i]->IsParserBreakerSniffer()) + found_parser_breaker_ = true; + + found_blockable_content_ = true; + sniffers_.clear(); + break; + } else { + // This response is CORB-exempt as far as this sniffer is concerned; + // remove it from the list. + sniffers_.erase(sniffers_.begin() + i); + } + } +} + +bool CrossOriginReadBlocking::ResponseAnalyzer::should_allow() const { + switch (should_block_based_on_headers_) { + case kAllow: + return true; + case kNeedToSniffMore: + return sniffers_.empty() && !found_blockable_content_; + case kBlock: + return false; + } +} + +bool CrossOriginReadBlocking::ResponseAnalyzer::should_block() const { + switch (should_block_based_on_headers_) { + case kAllow: + return false; + case kNeedToSniffMore: + return sniffers_.empty() && found_blockable_content_; + case kBlock: + return true; + } +} + } // namespace network
diff --git a/services/network/cross_origin_read_blocking.h b/services/network/cross_origin_read_blocking.h index e0274135..ca80c842 100644 --- a/services/network/cross_origin_read_blocking.h +++ b/services/network/cross_origin_read_blocking.h
@@ -5,6 +5,7 @@ #ifndef SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_H_ #define SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_H_ +#include <memory> #include <string> #include <vector> @@ -54,18 +55,36 @@ ~ResponseAnalyzer(); - bool should_allow_based_on_headers() { - return should_block_based_on_headers_ == kAllow; - } + // true if either 1) ShouldBlockBasedOnHeaders decided to allow the response + // based on headers alone or 2) ShouldBlockBasedOnHeaders decided to sniff + // the response body and SniffResponseBody decided to allow the response + // (e.g. because none of sniffers found blockable content). false + // otherwise. + bool should_allow() const; - bool needs_sniffing() { + // true if either 1) ShouldBlockBasedOnHeaders decided to block the response + // based on headers alone or 2) ShouldBlockBasedOnHeaders decided to sniff + // the response body and SniffResponseBody confirmed that the response + // contains blockable content. false otherwise. + bool should_block() const; + + // Whether ShouldBlockBasedOnHeaders asked to sniff the body. + bool needs_sniffing() const { return should_block_based_on_headers_ == kNeedToSniffMore; } - const CrossOriginReadBlocking::MimeType& canonical_mime_type() { + // The MIME type determined by ShouldBlockBasedOnHeaders. + const CrossOriginReadBlocking::MimeType& canonical_mime_type() const { return canonical_mime_type_; } + // Allows ResponseAnalyzer to sniff the response body. + void SniffResponseBody(base::StringPiece data, size_t new_data_offset); + + bool found_parser_breaker() const { return found_parser_breaker_; } + + class ConfirmationSniffer; + private: // Three conclusions are possible from looking at the headers: // - Allow: response doesn't need to be blocked (e.g. if it is same-origin @@ -81,14 +100,24 @@ const net::URLRequest& request, const ResourceResponse& response); - // Outcome of ShouldBlockBasedOnHeaders recorder inside the Create method. + // Populates |sniffers_| container based on |canonical_mime_type_|. Called + // if ShouldBlockBasedOnHeaders returns kNeedToSniffMore + void CreateSniffers(); + + // Outcome of ShouldBlockBasedOnHeaders recorded inside the Create method. BlockingDecision should_block_based_on_headers_; // Canonical MIME type detected by ShouldBlockBasedOnHeaders. Used to // determine if blocking the response is needed, as well as which type of // sniffing to perform. - CrossOriginReadBlocking::MimeType canonical_mime_type_ = - CrossOriginReadBlocking::MimeType::kInvalidMimeType; + MimeType canonical_mime_type_ = MimeType::kInvalidMimeType; + + // The sniffers to be used. + std::vector<std::unique_ptr<ConfirmationSniffer>> sniffers_; + + // Sniffing results. + bool found_blockable_content_ = false; + bool found_parser_breaker_ = false; DISALLOW_COPY_AND_ASSIGN(ResponseAnalyzer); };
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom index 89191d8..2848942 100644 --- a/services/network/public/mojom/network_service.mojom +++ b/services/network/public/mojom/network_service.mojom
@@ -304,8 +304,10 @@ uint32 routing_id, uint32 request_id, url.mojom.Url url, + url.mojom.Url site_for_cookies, bool first_auth_attempt, AuthChallengeInfo auth_info, + int32 resource_type, AuthChallengeResponder auth_challenge_responder); // Called when an SSL certificate requested message is received for client // authentication.
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 76fa7024..acf5f6e 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -481,7 +481,8 @@ base::BindOnce(&URLLoader::DeleteSelf, base::Unretained(this))); network_service_client_->OnAuthRequired( process_id_, render_frame_id_, request_id_, url_request_->url(), - first_auth_attempt_, auth_info, std::move(auth_challenge_responder)); + url_request_->site_for_cookies(), first_auth_attempt_, auth_info, + resource_type_, std::move(auth_challenge_responder)); first_auth_attempt_ = false; }
diff --git a/services/preferences/public/cpp/tracked/mock_validation_delegate.cc b/services/preferences/public/cpp/tracked/mock_validation_delegate.cc index f26ff4f0..1835dd04f 100644 --- a/services/preferences/public/cpp/tracked/mock_validation_delegate.cc +++ b/services/preferences/public/cpp/tracked/mock_validation_delegate.cc
@@ -60,7 +60,7 @@ void MockValidationDelegate::OnAtomicPreferenceValidation( const std::string& pref_path, - std::unique_ptr<base::Value> value, + base::Optional<base::Value> value, prefs::mojom::TrackedPreferenceValidationDelegate::ValueState value_state, prefs::mojom::TrackedPreferenceValidationDelegate::ValueState external_validation_value_state,
diff --git a/services/preferences/public/cpp/tracked/mock_validation_delegate.h b/services/preferences/public/cpp/tracked/mock_validation_delegate.h index 41f4ed6..4b8b439 100644 --- a/services/preferences/public/cpp/tracked/mock_validation_delegate.h +++ b/services/preferences/public/cpp/tracked/mock_validation_delegate.h
@@ -12,6 +12,8 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/optional.h" +#include "base/values.h" #include "services/preferences/public/mojom/preferences.mojom.h" #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h" @@ -92,7 +94,7 @@ // TrackedPreferenceValidationDelegate implementation. void OnAtomicPreferenceValidation( const std::string& pref_path, - std::unique_ptr<base::Value> value, + base::Optional<base::Value> value, prefs::mojom::TrackedPreferenceValidationDelegate::ValueState value_state, prefs::mojom::TrackedPreferenceValidationDelegate::ValueState external_validation_value_state,
diff --git a/services/preferences/public/mojom/tracked_preference_validation_delegate.mojom b/services/preferences/public/mojom/tracked_preference_validation_delegate.mojom index 370f95f..918b6593 100644 --- a/services/preferences/public/mojom/tracked_preference_validation_delegate.mojom +++ b/services/preferences/public/mojom/tracked_preference_validation_delegate.mojom
@@ -4,7 +4,7 @@ module prefs.mojom; -import "mojo/common/values.mojom"; +import "mojo/public/mojom/base/values.mojom"; // A TrackedPreferenceValidationDelegate is notified of the results of each // tracked preference validation event. @@ -38,7 +38,7 @@ // or not the value may contain personal information. OnAtomicPreferenceValidation( string pref_path, - mojo.common.mojom.Value? value, + mojo_base.mojom.Value? value, ValueState value_state, ValueState external_validation_value_state, bool is_personal);
diff --git a/services/preferences/tracked/tracked_atomic_preference.cc b/services/preferences/tracked/tracked_atomic_preference.cc index 8f2e4de..c4f820a 100644 --- a/services/preferences/tracked/tracked_atomic_preference.cc +++ b/services/preferences/tracked/tracked_atomic_preference.cc
@@ -56,8 +56,8 @@ if (delegate_) { delegate_->OnAtomicPreferenceValidation( - pref_path_, value ? value->CreateDeepCopy() : nullptr, value_state, - external_validation_value_state, helper_.IsPersonal()); + pref_path_, value ? base::make_optional(value->Clone()) : base::nullopt, + value_state, external_validation_value_state, helper_.IsPersonal()); } TrackedPreferenceHelper::ResetAction reset_action = helper_.GetAction(value_state);
diff --git a/services/preferences/unittest_common.h b/services/preferences/unittest_common.h index 1237252..d5eed7d 100644 --- a/services/preferences/unittest_common.h +++ b/services/preferences/unittest_common.h
@@ -21,7 +21,7 @@ class PrefStoreObserverMock : public PrefStore::Observer { public: PrefStoreObserverMock(); - ~PrefStoreObserverMock(); + ~PrefStoreObserverMock() override; MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); MOCK_METHOD1(OnInitializationCompleted, void(bool)); };
diff --git a/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc b/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc index cf5db721..ba00708 100644 --- a/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc +++ b/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc
@@ -192,17 +192,18 @@ void(base::trace_event::HeapProfilingMode mode, EnableHeapProfilingCallback& callback)); - void RequestChromeMemoryDump(const MemoryDumpRequestArgs& args, - RequestChromeMemoryDumpCallback callback) { + void RequestChromeMemoryDump( + const MemoryDumpRequestArgs& args, + RequestChromeMemoryDumpCallback callback) override { RequestChromeMemoryDumpMock(args, callback); } void RequestOSMemoryDump(mojom::MemoryMapOption option, const std::vector<base::ProcessId>& args, - RequestOSMemoryDumpCallback callback) { + RequestOSMemoryDumpCallback callback) override { RequestOSMemoryDumpMock(option, args, callback); } void EnableHeapProfiling(base::trace_event::HeapProfilingMode mode, - EnableHeapProfilingCallback callback) { + EnableHeapProfilingCallback callback) override { EnableHeapProfilingMock(mode, callback); }
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn index 9715f817..1cf95aa 100644 --- a/services/tracing/BUILD.gn +++ b/services/tracing/BUILD.gn
@@ -31,8 +31,12 @@ if (is_mac || is_linux || is_android) { sources += [ + "perfetto/json_trace_exporter.cc", + "perfetto/json_trace_exporter.h", "perfetto/perfetto_service.cc", "perfetto/perfetto_service.h", + "perfetto/perfetto_tracing_coordinator.cc", + "perfetto/perfetto_tracing_coordinator.h", "perfetto/producer_host.cc", "perfetto/producer_host.h", ] @@ -70,12 +74,16 @@ sources = [ "agent_registry_unittest.cc", "coordinator_unittest.cc", - "public/cpp/chrome_trace_event_agent_unittest.cc", + "public/cpp/trace_event_agent_unittest.cc", "recorder_unittest.cc", "test_util.cc", "test_util.h", ] + if (is_mac || is_linux || is_android) { + sources += [ "public/cpp/perfetto/trace_event_data_source_unittest.cc" ] + } + if (!is_android) { sources += [ "tracing_service_unittest.cc" ] } @@ -94,9 +102,13 @@ ] if (is_mac || is_linux || is_android) { - sources += [ "perfetto/perfetto_integration_unittest.cc" ] + sources += [ + "perfetto/json_trace_exporter_unittest.cc", + "perfetto/perfetto_integration_unittest.cc", + ] deps += [ + "//third_party/perfetto/include/perfetto/protozero:protozero", "//third_party/perfetto/protos/perfetto/common", "//third_party/perfetto/protos/perfetto/trace:lite", "//third_party/perfetto/protos/perfetto/trace:zero",
diff --git a/services/tracing/perfetto/json_trace_exporter.cc b/services/tracing/perfetto/json_trace_exporter.cc new file mode 100644 index 0000000..a459239 --- /dev/null +++ b/services/tracing/perfetto/json_trace_exporter.cc
@@ -0,0 +1,195 @@ +// Copyright 2018 The Chromium 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 "services/tracing/perfetto/json_trace_exporter.h" + +#include "base/json/string_escape.h" +#include "base/logging.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event.h" +#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h" +#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" + +namespace { + +void OutputJSONFromTraceEventProto( + const perfetto::protos::ChromeTraceEvent& event, + std::string* out) { + char phase = static_cast<char>(event.phase()); + base::StringAppendF(out, + "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 + ",\"ph\":\"%c\",\"cat\":\"%s\",\"name\":", + event.process_id(), event.thread_id(), event.timestamp(), + phase, event.category_group_name().c_str()); + base::EscapeJSONString(event.name(), true, out); + + if (event.has_duration()) { + base::StringAppendF(out, ",\"dur\":%" PRId64, event.duration()); + } + + if (event.has_thread_duration()) { + base::StringAppendF(out, ",\"tdur\":%" PRId64, event.thread_duration()); + } + + if (event.has_thread_timestamp()) { + base::StringAppendF(out, ",\"tts\":%" PRId64, event.thread_timestamp()); + } + + // Output async tts marker field if flag is set. + if (event.flags() & TRACE_EVENT_FLAG_ASYNC_TTS) { + base::StringAppendF(out, ", \"use_async_tts\":1"); + } + + // If id_ is set, print it out as a hex string so we don't loose any + // bits (it might be a 64-bit pointer). + unsigned int id_flags = + event.flags() & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID | + TRACE_EVENT_FLAG_HAS_GLOBAL_ID); + if (id_flags) { + if (event.has_scope()) { + base::StringAppendF(out, ",\"scope\":\"%s\"", event.scope().c_str()); + } + + DCHECK(event.has_id()); + switch (id_flags) { + case TRACE_EVENT_FLAG_HAS_ID: + base::StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"", + static_cast<uint64_t>(event.id())); + break; + + case TRACE_EVENT_FLAG_HAS_LOCAL_ID: + base::StringAppendF(out, ",\"id2\":{\"local\":\"0x%" PRIx64 "\"}", + static_cast<uint64_t>(event.id())); + break; + + case TRACE_EVENT_FLAG_HAS_GLOBAL_ID: + base::StringAppendF(out, ",\"id2\":{\"global\":\"0x%" PRIx64 "\"}", + static_cast<uint64_t>(event.id())); + break; + + default: + NOTREACHED() << "More than one of the ID flags are set"; + break; + } + } + + if (event.flags() & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING) + base::StringAppendF(out, ",\"bp\":\"e\""); + + if (event.has_bind_id()) { + base::StringAppendF(out, ",\"bind_id\":\"0x%" PRIx64 "\"", + static_cast<uint64_t>(event.bind_id())); + } + + if (event.flags() & TRACE_EVENT_FLAG_FLOW_IN) + base::StringAppendF(out, ",\"flow_in\":true"); + if (event.flags() & TRACE_EVENT_FLAG_FLOW_OUT) + base::StringAppendF(out, ",\"flow_out\":true"); + + // Instant events also output their scope. + if (phase == TRACE_EVENT_PHASE_INSTANT) { + char scope = '?'; + switch (event.flags() & TRACE_EVENT_FLAG_SCOPE_MASK) { + case TRACE_EVENT_SCOPE_GLOBAL: + scope = TRACE_EVENT_SCOPE_NAME_GLOBAL; + break; + + case TRACE_EVENT_SCOPE_PROCESS: + scope = TRACE_EVENT_SCOPE_NAME_PROCESS; + break; + + case TRACE_EVENT_SCOPE_THREAD: + scope = TRACE_EVENT_SCOPE_NAME_THREAD; + break; + } + base::StringAppendF(out, ",\"s\":\"%c\"", scope); + } + + *out += ",\"args\":\"__stripped__\""; + + *out += "}"; +} + +} // namespace + +namespace tracing { + +JSONTraceExporter::JSONTraceExporter(const std::string& config, + perfetto::Service* service) + : config_(config) { + consumer_endpoint_ = service->ConnectConsumer(this); +} + +JSONTraceExporter::~JSONTraceExporter() = default; + +void JSONTraceExporter::OnConnect() { + // Start tracing. + perfetto::TraceConfig trace_config; + trace_config.add_buffers()->set_size_kb(4096 * 100); + auto* ds_config = trace_config.add_data_sources()->mutable_config(); + ds_config->set_name(mojom::kTraceEventDataSourceName); + ds_config->set_target_buffer(0); + auto* chrome_config = ds_config->mutable_chrome_config(); + chrome_config->set_trace_config(config_); + + consumer_endpoint_->EnableTracing(trace_config); +} + +void JSONTraceExporter::OnDisconnect() {} + +void JSONTraceExporter::StopAndFlush(OnTraceEventJSONCallback callback) { + DCHECK(!json_callback_ && callback); + json_callback_ = callback; + + consumer_endpoint_->DisableTracing(); + consumer_endpoint_->ReadBuffers(); +} + +void JSONTraceExporter::OnTraceData(std::vector<perfetto::TracePacket> packets, + bool has_more) { + DCHECK(json_callback_); + DCHECK(!packets.empty() || !has_more); + + std::string out; + + if (!has_output_json_preamble_) { + out = "{\"traceEvents\":["; + has_output_json_preamble_ = true; + } + + for (auto& encoded_packet : packets) { + perfetto::protos::TracePacket packet; + bool decoded = encoded_packet.Decode(&packet); + DCHECK(decoded); + + if (!packet.has_chrome_events()) { + continue; + } + + const perfetto::protos::ChromeEventBundle& bundle = packet.chrome_events(); + for (const perfetto::protos::ChromeTraceEvent& event : + bundle.trace_events()) { + if (has_output_first_event_) { + out += ","; + } else { + has_output_first_event_ = true; + } + + OutputJSONFromTraceEventProto(event, &out); + } + } + + if (!has_more) { + out += "]}"; + } + + json_callback_.Run(out, has_more); +} + +} // namespace tracing
diff --git a/services/tracing/perfetto/json_trace_exporter.h b/services/tracing/perfetto/json_trace_exporter.h new file mode 100644 index 0000000..2efdc61 --- /dev/null +++ b/services/tracing/perfetto/json_trace_exporter.h
@@ -0,0 +1,58 @@ +// Copyright 2018 The Chromium 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 SERVICES_TRACING_PERFETTO_JSON_TRACE_EXPORTER_H_ +#define SERVICES_TRACING_PERFETTO_JSON_TRACE_EXPORTER_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" + +#include "third_party/perfetto/include/perfetto/tracing/core/consumer.h" +#include "third_party/perfetto/include/perfetto/tracing/core/service.h" + +namespace tracing { + +// This is a Perfetto consumer which will enable Perfetto tracing +// and subscribe to ChromeTraceEvent data sources. Any received +// protos will be converted to the legacy JSON Chrome Tracing +// format. +class JSONTraceExporter : public perfetto::Consumer { + public: + // The owner of JSONTraceExporter should make sure to destroy + // |service| before destroying this. + JSONTraceExporter(const std::string& config, perfetto::Service* service); + + ~JSONTraceExporter() override; + + using OnTraceEventJSONCallback = + base::RepeatingCallback<void(const std::string& json, bool has_more)>; + void StopAndFlush(OnTraceEventJSONCallback callback); + + // perfetto::Consumer implementation. + // This gets called by the Perfetto service as control signals, + // and to send finished protobufs over. + void OnConnect() override; + void OnDisconnect() override; + void OnTracingDisabled() override{}; + void OnTraceData(std::vector<perfetto::TracePacket> packets, + bool has_more) override; + + private: + OnTraceEventJSONCallback json_callback_; + bool has_output_json_preamble_ = false; + bool has_output_first_event_ = false; + std::string config_; + + // Keep last to avoid edge-cases where its callbacks come in mid-destruction. + std::unique_ptr<perfetto::Service::ConsumerEndpoint> consumer_endpoint_; + DISALLOW_COPY_AND_ASSIGN(JSONTraceExporter); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PERFETTO_JSON_TRACE_EXPORTER_H_
diff --git a/services/tracing/perfetto/json_trace_exporter_unittest.cc b/services/tracing/perfetto/json_trace_exporter_unittest.cc new file mode 100644 index 0000000..0bb40352 --- /dev/null +++ b/services/tracing/perfetto/json_trace_exporter_unittest.cc
@@ -0,0 +1,216 @@ +// Copyright 2018 The Chromium 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 "services/tracing/perfetto/json_trace_exporter.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/trace_event/trace_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h" +#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" + +namespace tracing { + +class MockService : public perfetto::Service { + public: + explicit MockService(base::MessageLoop* message_loop); + + void OnTracingEnabled(const std::string& config); + void WaitForTracingEnabled(); + + void OnTracingDisabled(); + void WaitForTracingDisabled(); + + const std::string& tracing_enabled_with_config() const { + return tracing_enabled_with_config_; + } + + // perfetto::Service implementation. + std::unique_ptr<ProducerEndpoint> ConnectProducer( + perfetto::Producer*, + uid_t uid, + const std::string& name, + size_t shared_buffer_size_hint_bytes = 0) override; + + std::unique_ptr<ConsumerEndpoint> ConnectConsumer( + perfetto::Consumer*) override; + + private: + base::MessageLoop* message_loop_; + + base::RunLoop wait_for_tracing_enabled_; + base::RunLoop wait_for_tracing_disabled_; + std::string tracing_enabled_with_config_; +}; + +class MockConsumerEndpoint : public perfetto::Service::ConsumerEndpoint { + public: + explicit MockConsumerEndpoint(MockService* mock_service) + : mock_service_(mock_service) { + CHECK(mock_service); + } + + void EnableTracing( + const perfetto::TraceConfig& config, + perfetto::base::ScopedFile = perfetto::base::ScopedFile()) override { + EXPECT_EQ(1, config.data_sources_size()); + mock_service_->OnTracingEnabled( + config.data_sources()[0].config().chrome_config().trace_config()); + } + + void DisableTracing() override { mock_service_->OnTracingDisabled(); } + void ReadBuffers() override {} + void FreeBuffers() override {} + void Flush(uint32_t timeout_ms, FlushCallback) override {} + + private: + MockService* mock_service_; +}; + +MockService::MockService(base::MessageLoop* message_loop) + : message_loop_(message_loop) { + DCHECK(message_loop); +} + +void MockService::OnTracingEnabled(const std::string& config) { + tracing_enabled_with_config_ = config; + wait_for_tracing_enabled_.Quit(); +} + +void MockService::WaitForTracingEnabled() { + wait_for_tracing_enabled_.Run(); +} + +void MockService::OnTracingDisabled() { + wait_for_tracing_disabled_.Quit(); +} + +void MockService::WaitForTracingDisabled() { + wait_for_tracing_disabled_.Run(); +} + +// perfetto::Service implementation. +std::unique_ptr<perfetto::Service::ProducerEndpoint> +MockService::ConnectProducer(perfetto::Producer*, + uid_t uid, + const std::string& name, + size_t shared_buffer_size_hint_bytes) { + NOTREACHED(); + return nullptr; +} + +std::unique_ptr<perfetto::Service::ConsumerEndpoint> +MockService::ConnectConsumer(perfetto::Consumer* consumer) { + message_loop_->task_runner()->PostTask( + FROM_HERE, base::BindOnce(&perfetto::Consumer::OnConnect, + base::Unretained(consumer))); + + return std::make_unique<MockConsumerEndpoint>(this); +} + +class JSONTraceExporterTest : public testing::Test { + public: + void SetUp() override { + message_loop_ = std::make_unique<base::MessageLoop>(); + service_ = std::make_unique<MockService>(message_loop_.get()); + } + + void TearDown() override { + service_.reset(); + json_trace_exporter_.reset(); + message_loop_.reset(); + } + + void CreateJSONTraceExporter(const std::string& config) { + json_trace_exporter_.reset(new JSONTraceExporter(config, service_.get())); + } + + void StopAndFlush() { + json_trace_exporter_->StopAndFlush(base::BindRepeating( + &JSONTraceExporterTest::OnTraceEventJSON, base::Unretained(this))); + } + + void OnTraceEventJSON(const std::string& json, bool has_more) { + last_received_json_ = json; + } + + void EmitTestPacket() { + perfetto::protos::TracePacket trace_packet_proto; + + auto* event_bundle = trace_packet_proto.mutable_chrome_events(); + auto* new_trace_event = event_bundle->add_trace_events(); + + new_trace_event->set_name("name"); + new_trace_event->set_timestamp(42); + new_trace_event->set_flags(TRACE_EVENT_FLAG_HAS_GLOBAL_ID | + TRACE_EVENT_FLAG_FLOW_OUT); + + new_trace_event->set_process_id(2); + new_trace_event->set_thread_id(3); + new_trace_event->set_category_group_name("cat"); + new_trace_event->set_phase(TRACE_EVENT_PHASE_INSTANT); + new_trace_event->set_duration(4); + new_trace_event->set_thread_duration(5); + new_trace_event->set_thread_timestamp(6); + new_trace_event->set_id(7); + new_trace_event->set_bind_id(8); + + std::string scope; + scope += TRACE_EVENT_SCOPE_NAME_GLOBAL; + new_trace_event->set_scope(scope); + + perfetto::TracePacket trace_packet; + std::string ser_buf = trace_packet_proto.SerializeAsString(); + trace_packet.AddSlice(&ser_buf[0], ser_buf.size()); + + std::vector<perfetto::TracePacket> packets; + packets.emplace_back(std::move(trace_packet)); + + json_trace_exporter_->OnTraceData(std::move(packets), false); + } + + void ValidateTestPacket() { + EXPECT_EQ( + "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":42,\"ph\":\"I\",\"cat\":" + "\"cat\",\"name\":\"name\",\"dur\":4,\"tdur\":5,\"tts\":6,\"scope\":" + "\"g\",\"id2\":{\"global\":\"0x7\"},\"bind_id\":\"0x8\",\"flow_out\":" + "true,\"s\":\"g\",\"args\":\"__stripped__\"}]}", + last_received_json_); + } + + std::unique_ptr<MockService> service_; + + private: + std::unique_ptr<JSONTraceExporter> json_trace_exporter_; + std::unique_ptr<base::MessageLoop> message_loop_; + std::string last_received_json_; +}; + +TEST_F(JSONTraceExporterTest, EnableTracingWithGivenConfig) { + const char kDummyTraceConfig[] = "trace_all_the_things"; + CreateJSONTraceExporter(kDummyTraceConfig); + service_->WaitForTracingEnabled(); + EXPECT_EQ(kDummyTraceConfig, service_->tracing_enabled_with_config()); +} + +TEST_F(JSONTraceExporterTest, TestEvent) { + CreateJSONTraceExporter("foo"); + service_->WaitForTracingEnabled(); + + StopAndFlush(); + EmitTestPacket(); + + service_->WaitForTracingDisabled(); + ValidateTestPacket(); +} + +} // namespace tracing
diff --git a/services/tracing/perfetto/perfetto_integration_unittest.cc b/services/tracing/perfetto/perfetto_integration_unittest.cc index b367488..751f947 100644 --- a/services/tracing/perfetto/perfetto_integration_unittest.cc +++ b/services/tracing/perfetto/perfetto_integration_unittest.cc
@@ -38,7 +38,11 @@ class PerfettoIntegrationTest : public testing::Test { public: PerfettoIntegrationTest() { - perfetto_service_ = std::make_unique<PerfettoService>(); + perfetto_service_ = std::make_unique<PerfettoService>( + base::SequencedTaskRunnerHandle::Get()); + // The actual Perfetto service is created async on the given task_runner; + // wait until that's done. + base::RunLoop().RunUntilIdle(); } PerfettoService* perfetto_service() const { return perfetto_service_.get(); } @@ -48,7 +52,7 @@ base::test::ScopedTaskEnvironment scoped_task_environment_; }; -class TestDataSource : public ProducerClient::DataSourceBase { +class TestDataSource { public: TestDataSource(ProducerClient* producer_client, size_t send_packet_count, @@ -101,14 +105,10 @@ size_t send_packet_count() const { return send_packet_count_; } - TestDataSource* enabled_data_source_instance() const { - return static_cast<TestDataSource*>(enabled_data_source_instance_.get()); - } - void CreateDataSourceInstance( uint64_t id, mojom::DataSourceConfigPtr data_source_config) override { - enabled_data_source_instance_ = std::make_unique<TestDataSource>( + enabled_data_source_ = std::make_unique<TestDataSource>( this, send_packet_count_, data_source_config->trace_config, data_source_config->target_buffer); @@ -118,7 +118,7 @@ } void TearDownDataSourceInstance(uint64_t id) override { - ProducerClient::TearDownDataSourceInstance(id); + enabled_data_source_.reset(); if (client_disabled_callback_) { std::move(client_disabled_callback_).Run(); @@ -154,12 +154,15 @@ return all_client_commit_data_requests_; } + TestDataSource* data_source() { return enabled_data_source_.get(); } + private: base::OnceClosure client_enabled_callback_; base::OnceClosure client_disabled_callback_; const size_t send_packet_count_; std::string all_client_commit_data_requests_; + std::unique_ptr<TestDataSource> enabled_data_source_; }; class MockConsumer : public perfetto::Consumer { @@ -268,7 +271,7 @@ kPerfettoTestDataSourceName, producer_initialized_runloop.QuitClosure()); new_producer->Initialize(dummy_client->CreateAndBindProducerClient(), dummy_client->CreateProducerHostRequest(), - perfetto_service()->service(), + perfetto_service()->GetService(), kPerfettoProducerName); producer_initialized_runloop.Run(); @@ -277,7 +280,7 @@ TEST_F(PerfettoIntegrationTest, ClientEnabledAndDisabled) { base::RunLoop on_trace_packets; - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, [&on_trace_packets](bool has_more) { EXPECT_FALSE(has_more); @@ -293,7 +296,7 @@ auto producer = std::make_unique<MockProducer>(kPerfettoTestDataSourceName); producer->Initialize(client->CreateAndBindProducerClient(), client->CreateProducerHostRequest(), - perfetto_service()->service(), kPerfettoProducerName); + perfetto_service()->GetService(), kPerfettoProducerName); client_enabled_callback.Run(); @@ -322,10 +325,10 @@ auto producer = std::make_unique<MockProducer>(kPerfettoTestDataSourceName); producer->Initialize(client->CreateAndBindProducerClient(), client->CreateProducerHostRequest(), - perfetto_service()->service(), kPerfettoProducerName); + perfetto_service()->GetService(), kPerfettoProducerName); base::RunLoop no_more_packets_runloop; - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, [&no_more_packets_runloop](bool has_more) { if (!has_more) { @@ -352,7 +355,7 @@ const size_t kNumPackets = 10; base::RunLoop no_more_packets_runloop; - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, [&no_more_packets_runloop](bool has_more) { if (!has_more) { @@ -368,7 +371,7 @@ std::make_unique<MockProducer>(kPerfettoTestDataSourceName); new_producer->Initialize(client->CreateAndBindProducerClient(), client->CreateProducerHostRequest(), - perfetto_service()->service(), + perfetto_service()->GetService(), kPerfettoProducerName); client_enabled_callback.Run(); @@ -388,7 +391,7 @@ const size_t kNumPackets = 100; base::RunLoop no_more_packets_runloop; - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, [&no_more_packets_runloop](bool has_more) { if (!has_more) { @@ -403,12 +406,12 @@ std::make_unique<MockProducer>(kPerfettoTestDataSourceName); new_producer->Initialize(client->CreateAndBindProducerClient(), client->CreateProducerHostRequest(), - perfetto_service()->service(), + perfetto_service()->GetService(), kPerfettoProducerName); client_enabled_callback.Run(); - client->enabled_data_source_instance()->WritePacketBigly(); + client->data_source()->WritePacketBigly(); client->FlushTaskRunner(); @@ -429,7 +432,7 @@ const size_t kNumPackets = 10; base::RunLoop no_more_packets_runloop; - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, [&no_more_packets_runloop](bool has_more) { if (!has_more) { @@ -445,7 +448,7 @@ std::make_unique<MockProducer>(kPerfettoTestDataSourceName); new_producer->Initialize(client->CreateAndBindProducerClient(), client->CreateProducerHostRequest(), - perfetto_service()->service(), + perfetto_service()->GetService(), kPerfettoProducerName); client_enabled_callback.Run(); @@ -498,12 +501,12 @@ "fake", producer_initialized_runloop.QuitClosure()); new_producer->Initialize(client->CreateAndBindProducerClient(), client->CreateProducerHostRequest(), - perfetto_service()->service(), + perfetto_service()->GetService(), kPerfettoProducerName); producer_initialized_runloop.Run(); base::RunLoop no_more_packets_runloop; - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, [&no_more_packets_runloop](bool has_more) { if (!has_more) { @@ -534,14 +537,16 @@ auto producer1 = std::make_unique<MockProducer>(kPerfettoTestDataSourceName); producer1->Initialize(client1->CreateAndBindProducerClient(), client1->CreateProducerHostRequest(), - perfetto_service()->service(), kPerfettoProducerName); + perfetto_service()->GetService(), + kPerfettoProducerName); auto producer2 = std::make_unique<MockProducer>(kPerfettoTestDataSourceName); producer2->Initialize(client2->CreateAndBindProducerClient(), client2->CreateProducerHostRequest(), - perfetto_service()->service(), kPerfettoProducerName); + perfetto_service()->GetService(), + kPerfettoProducerName); - MockConsumer consumer(perfetto_service()->service(), + MockConsumer consumer(perfetto_service()->GetService(), kPerfettoTestDataSourceName, nullptr); client1_enabled_callback.Run();
diff --git a/services/tracing/perfetto/perfetto_service.cc b/services/tracing/perfetto/perfetto_service.cc index 4fd07198..45869a3 100644 --- a/services/tracing/perfetto/perfetto_service.cc +++ b/services/tracing/perfetto/perfetto_service.cc
@@ -27,14 +27,40 @@ } // namespace -// TODO(oysteine): Figure out if this is the correct TaskRunner to use. -PerfettoService::PerfettoService() - : perfetto_task_runner_(base::SequencedTaskRunnerHandle::Get()) { - service_ = perfetto::Service::CreateInstance( - std::make_unique<MojoSharedMemory::Factory>(), &perfetto_task_runner_); - DCHECK(service_); +/* +TODO(oysteine): Right now the Perfetto service runs on a dedicated +thread for a couple of reasons: +* The sequence needs to be locked to a specific thread, or Perfetto's + thread-checker will barf. +* The PerfettoTracingCoordinator uses + mojo::BlockingCopyFromString to pass the string to the tracing + controller, which requires the WithBaseSyncPrimitives task trait and + SingleThreadTaskRunners which use this trait need to be running on a + dedicated trait to avoid blocking other sequences. +* If a client fills up its Shared Memory Buffer when writing a Perfetto + event proto, it'll stall until the Perfetto service clears up space. + This won't happen if the client and the service happens to run on the same + thread (the Mojo calls will never be executed). + +The above should be resolved before we move the Perfetto usage out from the +flag so we can run this on non-thread-bound sequence. +*/ + +PerfettoService::PerfettoService( + scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing) + : perfetto_task_runner_( + task_runner_for_testing + ? task_runner_for_testing + : base::CreateSingleThreadTaskRunnerWithTraits( + {base::MayBlock(), base::WithBaseSyncPrimitives(), + base::TaskPriority::BACKGROUND}, + base::SingleThreadTaskRunnerThreadMode::DEDICATED)) { DCHECK(!g_perfetto_service); g_perfetto_service = this; + DETACH_FROM_SEQUENCE(sequence_checker_); + perfetto_task_runner_.task_runner()->PostTask( + FROM_HERE, base::BindOnce(&PerfettoService::CreateServiceOnSequence, + base::Unretained(this))); } PerfettoService::~PerfettoService() { @@ -47,15 +73,45 @@ return g_perfetto_service; } +// static +void PerfettoService::DestroyOnSequence( + std::unique_ptr<PerfettoService> service) { + PerfettoService::GetInstance()->task_runner()->DeleteSoon(FROM_HERE, + std::move(service)); +} + +void PerfettoService::CreateServiceOnSequence() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + service_ = perfetto::Service::CreateInstance( + std::make_unique<MojoSharedMemory::Factory>(), &perfetto_task_runner_); + DCHECK(service_); +} + +perfetto::Service* PerfettoService::GetService() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return service_.get(); +} + void PerfettoService::BindRequest( mojom::PerfettoServiceRequest request, const service_manager::BindSourceInfo& source_info) { - bindings_.AddBinding(this, std::move(request), source_info.identity); + perfetto_task_runner_.task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&PerfettoService::BindOnSequence, base::Unretained(this), + std::move(request), source_info.identity)); +} + +void PerfettoService::BindOnSequence( + mojom::PerfettoServiceRequest request, + const service_manager::Identity& identity) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + bindings_.AddBinding(this, std::move(request), identity); } void PerfettoService::ConnectToProducerHost( mojom::ProducerClientPtr producer_client, mojom::ProducerHostRequest producer_host) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto new_producer = std::make_unique<ProducerHost>(); new_producer->Initialize(std::move(producer_client), std::move(producer_host), service_.get(), kPerfettoProducerName);
diff --git a/services/tracing/perfetto/perfetto_service.h b/services/tracing/perfetto/perfetto_service.h index 12b1af0..8d9d6b2 100644 --- a/services/tracing/perfetto/perfetto_service.h +++ b/services/tracing/perfetto/perfetto_service.h
@@ -9,6 +9,7 @@ #include <set> #include "base/macros.h" +#include "base/sequence_checker.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "services/service_manager/public/cpp/identity.h" #include "services/tracing/public/cpp/perfetto/task_runner.h" @@ -30,10 +31,12 @@ // ProducerHost. class PerfettoService : public mojom::PerfettoService { public: - PerfettoService(); + explicit PerfettoService(scoped_refptr<base::SequencedTaskRunner> + task_runner_for_testing = nullptr); ~PerfettoService() override; static PerfettoService* GetInstance(); + static void DestroyOnSequence(std::unique_ptr<PerfettoService>); void BindRequest(mojom::PerfettoServiceRequest request, const service_manager::BindSourceInfo& source_info); @@ -42,12 +45,20 @@ void ConnectToProducerHost(mojom::ProducerClientPtr producer_client, mojom::ProducerHostRequest producer_host) override; - perfetto::Service* service() const { return service_.get(); } + perfetto::Service* GetService() const; + base::SequencedTaskRunner* task_runner() { + return perfetto_task_runner_.task_runner(); + } private: + void BindOnSequence(mojom::PerfettoServiceRequest request, + const service_manager::Identity& identity); + void CreateServiceOnSequence(); + PerfettoTaskRunner perfetto_task_runner_; std::unique_ptr<perfetto::Service> service_; mojo::BindingSet<mojom::PerfettoService, service_manager::Identity> bindings_; + SEQUENCE_CHECKER(sequence_checker_); DISALLOW_COPY_AND_ASSIGN(PerfettoService); };
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/services/tracing/perfetto/perfetto_tracing_coordinator.cc new file mode 100644 index 0000000..439e208 --- /dev/null +++ b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
@@ -0,0 +1,153 @@ +// Copyright 2018 The Chromium 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 "services/tracing/perfetto/perfetto_tracing_coordinator.h" + +#include <utility> + +#include "mojo/public/cpp/system/data_pipe_utils.h" +#include "services/tracing/perfetto/json_trace_exporter.h" +#include "services/tracing/perfetto/perfetto_service.h" + +namespace tracing { + +// A TracingSession is used to wrap all the associated +// state of an on-going tracing session, for easy setup +// and cleanup. +class PerfettoTracingCoordinator::TracingSession { + public: + TracingSession(const std::string& config, + base::OnceClosure tracing_over_callback) + : tracing_over_callback_(std::move(tracing_over_callback)) { + json_trace_exporter_ = std::make_unique<JSONTraceExporter>( + config, PerfettoService::GetInstance()->GetService()); + } + + ~TracingSession() { + if (!stop_and_flush_callback_.is_null()) { + base::ResetAndReturn(&stop_and_flush_callback_) + .Run(base::Value(base::Value::Type::DICTIONARY)); + } + + stream_.reset(); + } + + void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream, + StopAndFlushCallback callback) { + stream_ = std::move(stream); + stop_and_flush_callback_ = std::move(callback); + + json_trace_exporter_->StopAndFlush(base::BindRepeating( + &TracingSession::OnJSONTraceEventCallback, base::Unretained(this))); + } + + void OnJSONTraceEventCallback(const std::string& json, bool has_more) { + if (stream_.is_valid()) { + mojo::BlockingCopyFromString(json, stream_); + } + + if (!has_more) { + DCHECK(!stop_and_flush_callback_.is_null()); + base::ResetAndReturn(&stop_and_flush_callback_) + .Run(/*metadata=*/base::Value(base::Value::Type::DICTIONARY)); + + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(tracing_over_callback_)); + } + } + + private: + mojo::ScopedDataPipeProducerHandle stream_; + std::unique_ptr<JSONTraceExporter> json_trace_exporter_; + StopAndFlushCallback stop_and_flush_callback_; + base::OnceClosure tracing_over_callback_; + + DISALLOW_COPY_AND_ASSIGN(TracingSession); +}; + +// static +void PerfettoTracingCoordinator::DestroyOnSequence( + std::unique_ptr<PerfettoTracingCoordinator> coordinator) { + PerfettoService::GetInstance()->task_runner()->DeleteSoon( + FROM_HERE, std::move(coordinator)); +} + +PerfettoTracingCoordinator::PerfettoTracingCoordinator() + : binding_(this), weak_factory_(this) { + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +PerfettoTracingCoordinator::~PerfettoTracingCoordinator() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void PerfettoTracingCoordinator::OnClientConnectionError() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + tracing_session_.reset(); +} + +void PerfettoTracingCoordinator::BindCoordinatorRequest( + mojom::CoordinatorRequest request, + const service_manager::BindSourceInfo& source_info) { + PerfettoService::GetInstance()->task_runner()->PostTask( + FROM_HERE, base::BindOnce(&PerfettoTracingCoordinator::BindOnSequence, + base::Unretained(this), std::move(request))); +} + +void PerfettoTracingCoordinator::BindOnSequence( + mojom::CoordinatorRequest request) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + binding_.Bind(std::move(request)); + binding_.set_connection_error_handler( + base::BindOnce(&PerfettoTracingCoordinator::OnClientConnectionError, + base::Unretained(this))); +} + +void PerfettoTracingCoordinator::StartTracing(const std::string& config, + StartTracingCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + tracing_session_ = std::make_unique<TracingSession>( + config, base::BindOnce(&PerfettoTracingCoordinator::OnTracingOverCallback, + weak_factory_.GetWeakPtr())); + std::move(callback).Run(true); +} + +void PerfettoTracingCoordinator::OnTracingOverCallback() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(tracing_session_); + tracing_session_.reset(); +} + +void PerfettoTracingCoordinator::StopAndFlush( + mojo::ScopedDataPipeProducerHandle stream, + StopAndFlushCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(tracing_session_); + tracing_session_->StopAndFlush(std::move(stream), std::move(callback)); +} + +void PerfettoTracingCoordinator::StopAndFlushAgent( + mojo::ScopedDataPipeProducerHandle stream, + const std::string& agent_label, + StopAndFlushCallback callback) { + NOTREACHED(); +} + +void PerfettoTracingCoordinator::IsTracing(IsTracingCallback callback) { + std::move(callback).Run(tracing_session_ != nullptr); +} + +void PerfettoTracingCoordinator::RequestBufferUsage( + RequestBufferUsageCallback callback) { + std::move(callback).Run(false /* success */, 0.0f /* percent_full */, + 0 /* approximate_count */); +} + +// TODO(oysteine): Old tracing and Perfetto need to both be active for +// about://tracing to enumerate categories. +void PerfettoTracingCoordinator::GetCategories(GetCategoriesCallback callback) { + std::move(callback).Run(false /* success */, "" /* categories_list */); +} + +} // namespace tracing
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.h b/services/tracing/perfetto/perfetto_tracing_coordinator.h new file mode 100644 index 0000000..c059b3d9 --- /dev/null +++ b/services/tracing/perfetto/perfetto_tracing_coordinator.h
@@ -0,0 +1,68 @@ +// Copyright 2018 The Chromium 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 SERVICES_TRACING_PERFETTO_PERFETTO_TRACING_COORDINATOR_H_ +#define SERVICES_TRACING_PERFETTO_PERFETTO_TRACING_COORDINATOR_H_ + +#include <memory> +#include <string> + +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/tracing/public/mojom/tracing.mojom.h" + +namespace service_manager { +struct BindSourceInfo; +} // namespace service_manager + +namespace tracing { + +// This provides an alternate implementation of a tracing +// coordinator to clients (like the content::TracingController) +// but which uses Perfetto rather than the TraceLog for +// collecting trace events behind the scenes. +class PerfettoTracingCoordinator : public mojom::Coordinator { + public: + static void DestroyOnSequence(std::unique_ptr<PerfettoTracingCoordinator>); + + PerfettoTracingCoordinator(); + ~PerfettoTracingCoordinator() override; + + void BindCoordinatorRequest( + mojom::CoordinatorRequest request, + const service_manager::BindSourceInfo& source_info); + + // mojom::Coordinator implementation. + // Called by the tracing controller. + void StartTracing(const std::string& config, + StartTracingCallback callback) override; + void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream, + StopAndFlushCallback callback) override; + void StopAndFlushAgent(mojo::ScopedDataPipeProducerHandle stream, + const std::string& agent_label, + StopAndFlushCallback callback) override; + void IsTracing(IsTracingCallback callback) override; + void RequestBufferUsage(RequestBufferUsageCallback callback) override; + void GetCategories(GetCategoriesCallback callback) override; + + private: + void BindOnSequence(mojom::CoordinatorRequest request); + void OnTracingOverCallback(); + void OnClientConnectionError(); + + mojo::Binding<mojom::Coordinator> binding_; + + class TracingSession; + std::unique_ptr<TracingSession> tracing_session_; + + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<PerfettoTracingCoordinator> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PerfettoTracingCoordinator); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PERFETTO_PERFETTO_TRACING_COORDINATOR_H_
diff --git a/services/tracing/perfetto/producer_host.cc b/services/tracing/perfetto/producer_host.cc index 4e98d6f5..4594cf3 100644 --- a/services/tracing/perfetto/producer_host.cc +++ b/services/tracing/perfetto/producer_host.cc
@@ -8,6 +8,7 @@ #include "services/tracing/public/cpp/perfetto/shared_memory.h" #include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h" +#include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" namespace tracing { @@ -31,7 +32,10 @@ base::BindOnce(&ProducerHost::OnConnectionError, base::Unretained(this))); // TODO(oysteine): Figure out an uid once we need it. - producer_endpoint_ = service->ConnectProducer(this, 0 /* uid */, name); + // TODO(oysteine): Figure out a good buffer size. + producer_endpoint_ = service->ConnectProducer( + this, 0 /* uid */, name, + 4 * 1024 * 1024 /* shared_memory_size_hint_bytes */); DCHECK(producer_endpoint_); } @@ -45,6 +49,10 @@ void ProducerHost::OnConnect() { // Register data sources with Perfetto here. + + perfetto::DataSourceDescriptor descriptor; + descriptor.set_name(mojom::kTraceEventDataSourceName); + producer_endpoint_->RegisterDataSource(descriptor); } void ProducerHost::OnDisconnect() {
diff --git a/services/tracing/public/cpp/BUILD.gn b/services/tracing/public/cpp/BUILD.gn index b9f135b..f709bd2 100644 --- a/services/tracing/public/cpp/BUILD.gn +++ b/services/tracing/public/cpp/BUILD.gn
@@ -6,8 +6,8 @@ sources = [ "base_agent.cc", "base_agent.h", - "chrome_trace_event_agent.cc", - "chrome_trace_event_agent.h", + "trace_event_agent.cc", + "trace_event_agent.h", "tracing_features.cc", "tracing_features.h", ] @@ -30,6 +30,8 @@ "perfetto/shared_memory.h", "perfetto/task_runner.cc", "perfetto/task_runner.h", + "perfetto/trace_event_data_source.cc", + "perfetto/trace_event_data_source.h", ] deps = [
diff --git a/services/tracing/public/cpp/chrome_trace_event_agent.cc b/services/tracing/public/cpp/chrome_trace_event_agent.cc deleted file mode 100644 index 3e6c16f5..0000000 --- a/services/tracing/public/cpp/chrome_trace_event_agent.cc +++ /dev/null
@@ -1,138 +0,0 @@ -// 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 "services/tracing/public/cpp/chrome_trace_event_agent.h" - -#include <string> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_memory.h" -#include "base/strings/string_util.h" -#include "base/threading/thread_checker.h" -#include "base/time/time.h" -#include "base/trace_event/trace_log.h" -#include "base/values.h" -#include "build/build_config.h" -#include "services/service_manager/public/cpp/connector.h" -#include "services/tracing/public/mojom/constants.mojom.h" - -namespace { - -const char kChromeTraceEventLabel[] = "traceEvents"; - -tracing::ChromeTraceEventAgent* g_chrome_trace_event_agent; - -} // namespace - -namespace tracing { - -// static -ChromeTraceEventAgent* ChromeTraceEventAgent::GetInstance() { - return g_chrome_trace_event_agent; -} - -ChromeTraceEventAgent::ChromeTraceEventAgent( - service_manager::Connector* connector, - bool request_clock_sync_marker_on_android) - : BaseAgent(connector, - kChromeTraceEventLabel, - mojom::TraceDataType::ARRAY, -#if defined(OS_ANDROID) - request_clock_sync_marker_on_android), -#else - false), -#endif - enabled_tracing_modes_(0) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(!g_chrome_trace_event_agent); - g_chrome_trace_event_agent = this; -} - -ChromeTraceEventAgent::~ChromeTraceEventAgent() { - DCHECK(!trace_log_needs_me_); - g_chrome_trace_event_agent = nullptr; -} - -void ChromeTraceEventAgent::AddMetadataGeneratorFunction( - MetadataGeneratorFunction generator) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - metadata_generator_functions_.push_back(generator); -} - -void ChromeTraceEventAgent::StartTracing(const std::string& config, - base::TimeTicks coordinator_time, - StartTracingCallback callback) { - DCHECK(!recorder_); -#if defined(__native_client__) - // NaCl and system times are offset by a bit, so subtract some time from - // the captured timestamps. The value might be off by a bit due to messaging - // latency. - base::TimeDelta time_offset = TRACE_TIME_TICKS_NOW() - coordinator_time; - TraceLog::GetInstance()->SetTimeOffset(time_offset); -#endif - enabled_tracing_modes_ = base::trace_event::TraceLog::RECORDING_MODE; - const base::trace_event::TraceConfig trace_config(config); - if (!trace_config.event_filters().empty()) - enabled_tracing_modes_ |= base::trace_event::TraceLog::FILTERING_MODE; - base::trace_event::TraceLog::GetInstance()->SetEnabled( - trace_config, enabled_tracing_modes_); - std::move(callback).Run(true); -} - -void ChromeTraceEventAgent::StopAndFlush(mojom::RecorderPtr recorder) { - DCHECK(!recorder_); - recorder_ = std::move(recorder); - base::trace_event::TraceLog::GetInstance()->SetDisabled( - enabled_tracing_modes_); - enabled_tracing_modes_ = 0; - for (const auto& generator : metadata_generator_functions_) { - auto metadata = generator.Run(); - if (metadata) - recorder_->AddMetadata(std::move(*metadata)); - } - trace_log_needs_me_ = true; - base::trace_event::TraceLog::GetInstance()->Flush(base::Bind( - &ChromeTraceEventAgent::OnTraceLogFlush, base::Unretained(this))); -} - -void ChromeTraceEventAgent::RequestClockSyncMarker( - const std::string& sync_id, - Agent::RequestClockSyncMarkerCallback callback) { -#if defined(OS_ANDROID) - base::trace_event::TraceLog::GetInstance()->AddClockSyncMetadataEvent(); - std::move(callback).Run(base::TimeTicks(), base::TimeTicks()); -#else - NOTREACHED(); -#endif -} - -void ChromeTraceEventAgent::RequestBufferStatus( - RequestBufferStatusCallback callback) { - base::trace_event::TraceLogStatus status = - base::trace_event::TraceLog::GetInstance()->GetStatus(); - std::move(callback).Run(status.event_capacity, status.event_count); -} - -void ChromeTraceEventAgent::GetCategories(GetCategoriesCallback callback) { - std::vector<std::string> category_vector; - base::trace_event::TraceLog::GetInstance()->GetKnownCategoryGroups( - &category_vector); - std::move(callback).Run(base::JoinString(category_vector, ",")); -} - -void ChromeTraceEventAgent::OnTraceLogFlush( - const scoped_refptr<base::RefCountedString>& events_str, - bool has_more_events) { - if (!events_str->data().empty()) - recorder_->AddChunk(events_str->data()); - if (!has_more_events) { - trace_log_needs_me_ = false; - recorder_.reset(); - } -} - -} // namespace tracing
diff --git a/services/tracing/public/cpp/chrome_trace_event_agent.h b/services/tracing/public/cpp/chrome_trace_event_agent.h deleted file mode 100644 index 80acfff..0000000 --- a/services/tracing/public/cpp/chrome_trace_event_agent.h +++ /dev/null
@@ -1,73 +0,0 @@ -// 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 SERVICES_TRACING_PUBLIC_CPP_CHROME_TRACE_EVENT_AGENT_H_ -#define SERVICES_TRACING_PUBLIC_CPP_CHROME_TRACE_EVENT_AGENT_H_ - -#include <memory> -#include <string> -#include <vector> - -#include "base/component_export.h" -#include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_memory.h" -#include "base/threading/thread_checker.h" -#include "base/values.h" -#include "services/tracing/public/cpp/base_agent.h" -#include "services/tracing/public/mojom/tracing.mojom.h" - -namespace base { -class TimeTicks; -} // namespace base - -namespace service_manager { -class Connector; -} // namespace service_manager - -namespace tracing { - -class COMPONENT_EXPORT(TRACING_CPP) ChromeTraceEventAgent : public BaseAgent { - public: - using MetadataGeneratorFunction = - base::RepeatingCallback<std::unique_ptr<base::DictionaryValue>()>; - - static ChromeTraceEventAgent* GetInstance(); - - explicit ChromeTraceEventAgent(service_manager::Connector* connector, - bool request_clock_sync_marker_on_android); - - void AddMetadataGeneratorFunction(MetadataGeneratorFunction generator); - - private: - friend std::default_delete<ChromeTraceEventAgent>; // For Testing - friend class ChromeTraceEventAgentTest; // For Testing - - ~ChromeTraceEventAgent() override; - - // mojom::Agent - void StartTracing(const std::string& config, - base::TimeTicks coordinator_time, - StartTracingCallback callback) override; - void StopAndFlush(mojom::RecorderPtr recorder) override; - void RequestClockSyncMarker( - const std::string& sync_id, - Agent::RequestClockSyncMarkerCallback callback) override; - void RequestBufferStatus(RequestBufferStatusCallback callback) override; - void GetCategories(GetCategoriesCallback callback) override; - - void OnTraceLogFlush(const scoped_refptr<base::RefCountedString>& events_str, - bool has_more_events); - - uint8_t enabled_tracing_modes_; - mojom::RecorderPtr recorder_; - std::vector<MetadataGeneratorFunction> metadata_generator_functions_; - bool trace_log_needs_me_ = false; - - THREAD_CHECKER(thread_checker_); - - DISALLOW_COPY_AND_ASSIGN(ChromeTraceEventAgent); -}; - -} // namespace tracing -#endif // SERVICES_TRACING_PUBLIC_CPP_CHROME_TRACE_EVENT_AGENT_H_
diff --git a/services/tracing/public/cpp/perfetto/producer_client.cc b/services/tracing/public/cpp/perfetto/producer_client.cc index 9b164f0..c435a60 100644 --- a/services/tracing/public/cpp/perfetto/producer_client.cc +++ b/services/tracing/public/cpp/perfetto/producer_client.cc
@@ -8,6 +8,7 @@ #include "base/task_scheduler/post_task.h" #include "services/tracing/public/cpp/perfetto/shared_memory.h" +#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h" #include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h" #include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h" @@ -85,12 +86,16 @@ uint64_t id, mojom::DataSourceConfigPtr data_source_config) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // TODO(oysteine): Create the relevant data source instance here. + DCHECK(data_source_config); + + // TODO(oysteine): Support concurrent tracing sessions. + TraceEventDataSource::GetInstance()->StartTracing(this, *data_source_config); } void ProducerClient::TearDownDataSourceInstance(uint64_t id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - enabled_data_source_instance_.reset(); + + TraceEventDataSource::GetInstance()->StopTracing(); // TODO(oysteine): Yak shave: Can only destroy these once the TraceWriters // are all cleaned up; have to figure out the TLS bits. @@ -113,8 +118,6 @@ void ProducerClient::CommitData(const perfetto::CommitDataRequest& commit, CommitDataCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // The CommitDataRequest which the SharedMemoryArbiter uses to // signal Perfetto that individual chunks have finished being // written and is ready for consumption, needs to be serialized @@ -147,7 +150,25 @@ new_data_request->chunks_to_patch.push_back(std::move(new_chunk_patch)); } - producer_host_->CommitData(std::move(new_data_request)); + // TODO(oysteine): Remove the PostTask once Perfetto is fixed to always call + // CommitData on its provided TaskRunner, right now it'll call it on whatever + // thread is requesting a new chunk when the SharedMemoryBuffer is full. Until + // then this is technically not threadsafe on shutdown (when the + // ProducerClient gets destroyed) but should be okay while the Perfetto + // integration is behind a flag. + if (GetTaskRunner()->RunsTasksInCurrentSequence()) { + producer_host_->CommitData(std::move(new_data_request)); + } else { + GetTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&ProducerClient::CommitDataOnSequence, + base::Unretained(this), std::move(new_data_request))); + } +} + +void ProducerClient::CommitDataOnSequence(mojom::CommitDataRequestPtr request) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_host_->CommitData(std::move(request)); } perfetto::SharedMemory* ProducerClient::shared_memory() const {
diff --git a/services/tracing/public/cpp/perfetto/producer_client.h b/services/tracing/public/cpp/perfetto/producer_client.h index ac569a4..48b2bb8 100644 --- a/services/tracing/public/cpp/perfetto/producer_client.h +++ b/services/tracing/public/cpp/perfetto/producer_client.h
@@ -41,14 +41,6 @@ : public mojom::ProducerClient, public perfetto::Service::ProducerEndpoint { public: - // Base class for different DataSource implementations, - // i.e. something which is able to provide data in the - // form of protos of a given type to Perfetto (like trace events). - class DataSourceBase { - public: - virtual ~DataSourceBase() = default; - }; - ProducerClient(); ~ProducerClient() override; @@ -98,11 +90,9 @@ protected: base::SequencedTaskRunner* GetTaskRunner(); - // TODO(oysteine): Allow multiple enabled data source instances. - std::unique_ptr<DataSourceBase> enabled_data_source_instance_; - private: void BindOnSequence(mojom::ProducerClientRequest request); + void CommitDataOnSequence(mojom::CommitDataRequestPtr request); // Keep the TaskRunner first in the member list so it outlives // everything else and no dependent classes will try to use
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc new file mode 100644 index 0000000..a1f2ce6 --- /dev/null +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -0,0 +1,207 @@ +// Copyright 2018 The Chromium 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 "services/tracing/public/cpp/perfetto/trace_event_data_source.h" + +#include <utility> + +#include "base/no_destructor.h" +#include "base/process/process_handle.h" +#include "base/trace_event/trace_event.h" +#include "services/tracing/public/mojom/constants.mojom.h" +#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h" +#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" + +using TraceLog = base::trace_event::TraceLog; +using TraceEvent = base::trace_event::TraceEvent; +using TraceConfig = base::trace_event::TraceConfig; + +namespace tracing { + +class TraceEventDataSource::ThreadLocalEventSink { + public: + explicit ThreadLocalEventSink( + std::unique_ptr<perfetto::TraceWriter> trace_writer) + : trace_writer_(std::move(trace_writer)) {} + + void AddTraceEvent(const TraceEvent& trace_event) { + // TODO(oysteine): Adding trace events to Perfetto will + // stall in some situations, specifically when we overflow + // the buffer and need to make a sync call to flush it, and we're + // running on the same thread as the service. The short-term fix (while + // we're behind a flag) is to run the service on its own thread, the longer + // term fix is most likely to not go via Mojo in that specific case. + + // TODO(oysteine): Temporary workaround for a specific trace event + // which is added while a scheduler lock is held, and will deadlock + // if Perfetto does a PostTask to commit a finished chunk. + if (strcmp(trace_event.name(), "RealTimeDomain::DelayTillNextTask") == 0) { + return; + } + + // TODO(oysteine): Consider batching several trace events per trace packet, + // and only add repeated data once per batch. + auto trace_packet = trace_writer_->NewTracePacket(); + protozero::MessageHandle<perfetto::protos::pbzero::ChromeEventBundle> + event_bundle(trace_packet->set_chrome_events()); + + auto* new_trace_event = event_bundle->add_trace_events(); + new_trace_event->set_name(trace_event.name()); + + new_trace_event->set_timestamp( + trace_event.timestamp().since_origin().InMicroseconds()); + + uint32_t flags = trace_event.flags(); + new_trace_event->set_flags(flags); + + int process_id; + int thread_id; + if ((flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID) && + trace_event.thread_id() != base::kNullProcessId) { + process_id = trace_event.thread_id(); + thread_id = -1; + } else { + process_id = TraceLog::GetInstance()->process_id(); + thread_id = trace_event.thread_id(); + } + + new_trace_event->set_process_id(process_id); + new_trace_event->set_thread_id(thread_id); + + new_trace_event->set_category_group_name( + TraceLog::GetCategoryGroupName(trace_event.category_group_enabled())); + + char phase = trace_event.phase(); + new_trace_event->set_phase(phase); + if (phase == TRACE_EVENT_PHASE_COMPLETE) { + int64_t duration = trace_event.duration().InMicroseconds(); + if (duration != -1) { + new_trace_event->set_duration(duration); + } else { + // TODO(oysteine): Workaround until TRACE_EVENT_PHASE_COMPLETE can be + // split into begin/end pairs. If the duration is -1 and the + // trace-viewer will spend forever generating a warning for each event. + new_trace_event->set_duration(0); + } + + if (!trace_event.thread_timestamp().is_null()) { + int64_t thread_duration = + trace_event.thread_duration().InMicroseconds(); + if (thread_duration != -1) { + new_trace_event->set_thread_duration(thread_duration); + } + } + } + + if (!trace_event.thread_timestamp().is_null()) { + int64_t thread_time_int64 = + trace_event.thread_timestamp().since_origin().InMicroseconds(); + new_trace_event->set_thread_timestamp(thread_time_int64); + } + + if (trace_event.scope() != trace_event_internal::kGlobalScope) { + new_trace_event->set_scope(trace_event.scope()); + } + + if (flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID | + TRACE_EVENT_FLAG_HAS_GLOBAL_ID)) { + new_trace_event->set_id(trace_event.id()); + } + + if ((flags & TRACE_EVENT_FLAG_FLOW_OUT) || + (flags & TRACE_EVENT_FLAG_FLOW_IN)) { + new_trace_event->set_bind_id(trace_event.bind_id()); + } + } + + private: + std::unique_ptr<perfetto::TraceWriter> trace_writer_; +}; + +// static +TraceEventDataSource* TraceEventDataSource::GetInstance() { + static base::NoDestructor<TraceEventDataSource> instance; + return instance.get(); +} + +TraceEventDataSource::TraceEventDataSource() = default; + +TraceEventDataSource::~TraceEventDataSource() = default; + +void TraceEventDataSource::StartTracing( + ProducerClient* producer_client, + const mojom::DataSourceConfig& data_source_config) { + base::AutoLock lock(lock_); + + DCHECK(!producer_client_); + producer_client_ = producer_client; + target_buffer_ = data_source_config.target_buffer; + + TraceLog::GetInstance()->SetAddTraceEventOverride( + &TraceEventDataSource::OnAddTraceEvent); + + TraceLog::GetInstance()->SetEnabled( + TraceConfig(data_source_config.trace_config), TraceLog::RECORDING_MODE); +} + +void TraceEventDataSource::StopTracing( + base::RepeatingClosure stop_complete_callback) { + DCHECK(producer_client_); + + { + base::AutoLock lock(lock_); + + producer_client_ = nullptr; + target_buffer_ = 0; + } + + TraceLog::GetInstance()->SetAddTraceEventOverride(nullptr); + + // We call CancelTracing because we don't want/need TraceLog to do any of + // its own JSON serialization on its own + TraceLog::GetInstance()->CancelTracing(base::BindRepeating( + [](base::RepeatingClosure stop_complete_callback, + const scoped_refptr<base::RefCountedString>&, bool has_more_events) { + if (!has_more_events && stop_complete_callback) { + stop_complete_callback.Run(); + } + }, + std::move(stop_complete_callback))); +} + +TraceEventDataSource::ThreadLocalEventSink* +TraceEventDataSource::CreateThreadLocalEventSink() { + base::AutoLock lock(lock_); + + if (producer_client_) { + return new ThreadLocalEventSink( + producer_client_->CreateTraceWriter(target_buffer_)); + } else { + return nullptr; + } +} + +// static +void TraceEventDataSource::OnAddTraceEvent(const TraceEvent& trace_event) { + static base::NoDestructor<base::ThreadLocalStorage::Slot> + thread_local_event_sink_tls([](void* event_sink) { + delete static_cast<ThreadLocalEventSink*>(event_sink); + }); + + auto* thread_local_event_sink = static_cast<ThreadLocalEventSink*>( + thread_local_event_sink_tls.get()->Get()); + + if (!thread_local_event_sink) { + thread_local_event_sink = GetInstance()->CreateThreadLocalEventSink(); + thread_local_event_sink_tls.get()->Set(thread_local_event_sink); + } + + if (thread_local_event_sink) { + thread_local_event_sink->AddTraceEvent(trace_event); + } +} + +} // namespace tracing
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h new file mode 100644 index 0000000..9871eeb3 --- /dev/null +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -0,0 +1,62 @@ +// Copyright 2018 The Chromium 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 SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACE_EVENT_DATA_SOURCE_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACE_EVENT_DATA_SOURCE_H_ + +#include <memory> +#include <string> + +#include "base/component_export.h" +#include "base/macros.h" +#include "base/threading/thread_local.h" +#include "services/tracing/public/cpp/perfetto/producer_client.h" + +namespace tracing { + +class ProducerClient; + +// This class acts as a bridge between the TraceLog and +// the Perfetto ProducerClient. It converts incoming +// trace events to ChromeTraceEvent protos and writes +// them into the Perfetto shared memory. +class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource { + public: + class ThreadLocalEventSink; + + static TraceEventDataSource* GetInstance(); + + // The ProducerClient is responsible for calling RequestStop + // which will clear the stored pointer to it, before it + // gets destroyed. ProducerClient::CreateTraceWriter can be + // called by the TraceEventDataSource on any thread. + void StartTracing(ProducerClient* producer_client, + const mojom::DataSourceConfig& data_source_config); + + // Called from the ProducerClient. + void StopTracing( + base::RepeatingClosure stop_complete_callback = base::RepeatingClosure()); + + private: + friend class base::NoDestructor<TraceEventDataSource>; + + TraceEventDataSource(); + ~TraceEventDataSource(); + + ThreadLocalEventSink* CreateThreadLocalEventSink(); + + // Callback from TraceLog on any added trace events, can be called from + // any thread. + static void OnAddTraceEvent(const base::trace_event::TraceEvent& trace_event); + + base::Lock lock_; + uint32_t target_buffer_ = 0; + ProducerClient* producer_client_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(TraceEventDataSource); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACE_EVENT_DATA_SOURCE_H_
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc new file mode 100644 index 0000000..bb1c99e --- /dev/null +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -0,0 +1,171 @@ +// Copyright 2018 The Chromium 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 "services/tracing/public/cpp/perfetto/trace_event_data_source.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/trace_event/trace_event.h" +#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/perfetto/include/perfetto/protozero/scattered_stream_null_delegate.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" + +namespace tracing { + +namespace { + +class MockProducerClient : public ProducerClient { + public: + MockProducerClient() + : delegate_(perfetto::base::kPageSize), stream_(&delegate_) {} + + std::unique_ptr<perfetto::TraceWriter> CreateTraceWriter( + perfetto::BufferID target_buffer) override; + + perfetto::protos::pbzero::TracePacket* NewTracePacket() { + trace_packet_.Reset(&stream_); + return &trace_packet_; + } + + std::unique_ptr<perfetto::protos::TracePacket> GetPreviousPacket() { + uint32_t message_size = trace_packet_.Finalize(); + protozero::ContiguousMemoryRange buffer = delegate_.GetNewBuffer(); + EXPECT_GE(buffer.size(), message_size); + + auto proto = std::make_unique<perfetto::protos::TracePacket>(); + proto->ParseFromArray(buffer.begin, buffer.size()); + + return proto; + } + + const google::protobuf::RepeatedPtrField<perfetto::protos::ChromeTraceEvent> + GetChromeTraceEvents() { + auto proto = GetPreviousPacket(); + + auto event_bundle = proto->chrome_events(); + return event_bundle.trace_events(); + } + + private: + perfetto::protos::pbzero::TracePacket trace_packet_; + protozero::ScatteredStreamWriterNullDelegate delegate_; + protozero::ScatteredStreamWriter stream_; +}; + +class MockTraceWriter : public perfetto::TraceWriter { + public: + explicit MockTraceWriter(MockProducerClient* producer_client) + : producer_client_(producer_client) {} + + perfetto::TraceWriter::TracePacketHandle NewTracePacket() override { + return perfetto::TraceWriter::TracePacketHandle( + producer_client_->NewTracePacket()); + } + + void Flush(std::function<void()> callback = {}) override {} + + perfetto::WriterID writer_id() const override { + return perfetto::WriterID(0); + } + + private: + MockProducerClient* producer_client_; +}; + +std::unique_ptr<perfetto::TraceWriter> MockProducerClient::CreateTraceWriter( + perfetto::BufferID target_buffer) { + return std::make_unique<MockTraceWriter>(this); +} + +class TraceEventDataSourceTest : public testing::Test { + public: + void SetUp() override { + message_loop_ = std::make_unique<base::MessageLoop>(); + producer_client_ = std::make_unique<MockProducerClient>(); + } + + void TearDown() override { + base::RunLoop wait_for_tracelog_flush; + + TraceEventDataSource::GetInstance()->StopTracing(base::BindRepeating( + [](const base::RepeatingClosure& quit_closure) { quit_closure.Run(); }, + wait_for_tracelog_flush.QuitClosure())); + + wait_for_tracelog_flush.Run(); + + producer_client_.reset(); + message_loop_.reset(); + } + + void CreateTraceEventDataSource() { + auto data_source_config = mojom::DataSourceConfig::New(); + TraceEventDataSource::GetInstance()->StartTracing(producer_client_.get(), + *data_source_config); + } + + MockProducerClient* producer_client() { return producer_client_.get(); } + + private: + std::unique_ptr<MockProducerClient> producer_client_; + std::unique_ptr<base::MessageLoop> message_loop_; +}; + +TEST_F(TraceEventDataSourceTest, BasicTraceEvent) { + CreateTraceEventDataSource(); + + TRACE_EVENT_BEGIN0("foo", "bar"); + + auto trace_events = producer_client()->GetChromeTraceEvents(); + EXPECT_EQ(trace_events.size(), 1); + + auto trace_event = trace_events[0]; + EXPECT_EQ("bar", trace_event.name()); + EXPECT_EQ("foo", trace_event.category_group_name()); + EXPECT_EQ(TRACE_EVENT_PHASE_BEGIN, trace_event.phase()); +} + +TEST_F(TraceEventDataSourceTest, TimestampedTraceEvent) { + CreateTraceEventDataSource(); + + TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0( + "foo", "bar", 42, 4242, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(424242)); + + auto trace_events = producer_client()->GetChromeTraceEvents(); + EXPECT_EQ(trace_events.size(), 1); + + auto trace_event = trace_events[0]; + EXPECT_EQ("bar", trace_event.name()); + EXPECT_EQ("foo", trace_event.category_group_name()); + EXPECT_EQ(42u, trace_event.id()); + EXPECT_EQ(4242, trace_event.thread_id()); + EXPECT_EQ(424242, trace_event.timestamp()); + EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, trace_event.phase()); +} + +TEST_F(TraceEventDataSourceTest, InstantTraceEvent) { + CreateTraceEventDataSource(); + + TRACE_EVENT_INSTANT0("foo", "bar", TRACE_EVENT_SCOPE_THREAD); + + auto trace_events = producer_client()->GetChromeTraceEvents(); + EXPECT_EQ(trace_events.size(), 1); + + auto trace_event = trace_events[0]; + EXPECT_EQ("bar", trace_event.name()); + EXPECT_EQ("foo", trace_event.category_group_name()); + EXPECT_EQ(TRACE_EVENT_SCOPE_THREAD, trace_event.flags()); + EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, trace_event.phase()); +} + +} // namespace + +} // namespace tracing
diff --git a/services/tracing/public/cpp/trace_event_agent.cc b/services/tracing/public/cpp/trace_event_agent.cc new file mode 100644 index 0000000..3f1b709 --- /dev/null +++ b/services/tracing/public/cpp/trace_event_agent.cc
@@ -0,0 +1,176 @@ +// 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 "services/tracing/public/cpp/trace_event_agent.h" + +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "base/strings/string_util.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "base/trace_event/trace_log.h" +#include "base/values.h" +#include "build/build_config.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/tracing/public/cpp/tracing_features.h" +#include "services/tracing/public/mojom/constants.mojom.h" + +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) +#include "services/tracing/public/cpp/perfetto/producer_client.h" +#endif + +namespace { + +const char kTraceEventLabel[] = "traceEvents"; + +} // namespace + +namespace tracing { + +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) +class PerfettoTraceEventAgent : public TraceEventAgent { + public: + explicit PerfettoTraceEventAgent(service_manager::Connector* connector) { + mojom::PerfettoServicePtr perfetto_service; + connector->BindInterface(mojom::kServiceName, &perfetto_service); + + producer_client_ = std::make_unique<ProducerClient>(); + perfetto_service->ConnectToProducerHost( + producer_client_->CreateAndBindProducerClient(), + producer_client_->CreateProducerHostRequest()); + } + + ~PerfettoTraceEventAgent() override { + ProducerClient::DeleteSoon(std::move(producer_client_)); + } + + private: + std::unique_ptr<ProducerClient> producer_client_; +}; +#endif + +// static +std::unique_ptr<TraceEventAgent> TraceEventAgent::Create( + service_manager::Connector* connector, + bool request_clock_sync_marker_on_android) { + if (TracingUsesPerfettoBackend()) { +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) + return std::make_unique<PerfettoTraceEventAgent>(connector); +#else + LOG(FATAL) << "Perfetto is not yet available for this platform."; + return nullptr; +#endif + } else { + return std::make_unique<TraceEventAgentImpl>( + connector, request_clock_sync_marker_on_android); + } +} + +TraceEventAgent::TraceEventAgent() = default; + +TraceEventAgent::~TraceEventAgent() = default; + +TraceEventAgentImpl::TraceEventAgentImpl( + service_manager::Connector* connector, + bool request_clock_sync_marker_on_android) + : BaseAgent(connector, + kTraceEventLabel, + mojom::TraceDataType::ARRAY, +#if defined(OS_ANDROID) + request_clock_sync_marker_on_android), +#else + false), +#endif + enabled_tracing_modes_(0) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +TraceEventAgentImpl::~TraceEventAgentImpl() { + DCHECK(!trace_log_needs_me_); +} + +void TraceEventAgentImpl::AddMetadataGeneratorFunction( + MetadataGeneratorFunction generator) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + metadata_generator_functions_.push_back(generator); +} + +void TraceEventAgentImpl::StartTracing(const std::string& config, + base::TimeTicks coordinator_time, + StartTracingCallback callback) { + DCHECK(!recorder_); +#if defined(__native_client__) + // NaCl and system times are offset by a bit, so subtract some time from + // the captured timestamps. The value might be off by a bit due to messaging + // latency. + base::TimeDelta time_offset = TRACE_TIME_TICKS_NOW() - coordinator_time; + TraceLog::GetInstance()->SetTimeOffset(time_offset); +#endif + enabled_tracing_modes_ = base::trace_event::TraceLog::RECORDING_MODE; + const base::trace_event::TraceConfig trace_config(config); + if (!trace_config.event_filters().empty()) + enabled_tracing_modes_ |= base::trace_event::TraceLog::FILTERING_MODE; + base::trace_event::TraceLog::GetInstance()->SetEnabled( + trace_config, enabled_tracing_modes_); + std::move(callback).Run(true); +} + +void TraceEventAgentImpl::StopAndFlush(mojom::RecorderPtr recorder) { + DCHECK(!recorder_); + recorder_ = std::move(recorder); + base::trace_event::TraceLog::GetInstance()->SetDisabled( + enabled_tracing_modes_); + enabled_tracing_modes_ = 0; + for (const auto& generator : metadata_generator_functions_) { + auto metadata = generator.Run(); + if (metadata) + recorder_->AddMetadata(std::move(*metadata)); + } + trace_log_needs_me_ = true; + base::trace_event::TraceLog::GetInstance()->Flush(base::Bind( + &TraceEventAgentImpl::OnTraceLogFlush, base::Unretained(this))); +} + +void TraceEventAgentImpl::RequestClockSyncMarker( + const std::string& sync_id, + Agent::RequestClockSyncMarkerCallback callback) { +#if defined(OS_ANDROID) + base::trace_event::TraceLog::GetInstance()->AddClockSyncMetadataEvent(); + std::move(callback).Run(base::TimeTicks(), base::TimeTicks()); +#else + NOTREACHED(); +#endif +} + +void TraceEventAgentImpl::RequestBufferStatus( + RequestBufferStatusCallback callback) { + base::trace_event::TraceLogStatus status = + base::trace_event::TraceLog::GetInstance()->GetStatus(); + std::move(callback).Run(status.event_capacity, status.event_count); +} + +void TraceEventAgentImpl::GetCategories(GetCategoriesCallback callback) { + std::vector<std::string> category_vector; + base::trace_event::TraceLog::GetInstance()->GetKnownCategoryGroups( + &category_vector); + std::move(callback).Run(base::JoinString(category_vector, ",")); +} + +void TraceEventAgentImpl::OnTraceLogFlush( + const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events) { + if (!events_str->data().empty()) + recorder_->AddChunk(events_str->data()); + if (!has_more_events) { + trace_log_needs_me_ = false; + recorder_.reset(); + } +} + +} // namespace tracing
diff --git a/services/tracing/public/cpp/trace_event_agent.h b/services/tracing/public/cpp/trace_event_agent.h new file mode 100644 index 0000000..f944f58 --- /dev/null +++ b/services/tracing/public/cpp/trace_event_agent.h
@@ -0,0 +1,90 @@ +// 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 SERVICES_TRACING_PUBLIC_CPP_TRACE_EVENT_AGENT_H_ +#define SERVICES_TRACING_PUBLIC_CPP_TRACE_EVENT_AGENT_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/component_export.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "base/threading/thread_checker.h" +#include "base/values.h" +#include "services/tracing/public/cpp/base_agent.h" +#include "services/tracing/public/mojom/tracing.mojom.h" + +namespace base { +class TimeTicks; +} // namespace base + +namespace service_manager { +class Connector; +} // namespace service_manager + +namespace tracing { + +class COMPONENT_EXPORT(TRACING_CPP) TraceEventAgent { + public: + using MetadataGeneratorFunction = + base::RepeatingCallback<std::unique_ptr<base::DictionaryValue>()>; + + static std::unique_ptr<TraceEventAgent> Create( + service_manager::Connector* connector, + bool request_clock_sync_marker_on_android); + + TraceEventAgent(); + virtual ~TraceEventAgent(); + + virtual void AddMetadataGeneratorFunction( + MetadataGeneratorFunction generator) {} + + private: + DISALLOW_COPY_AND_ASSIGN(TraceEventAgent); +}; + +class COMPONENT_EXPORT(TRACING_CPP) TraceEventAgentImpl + : public BaseAgent, + public TraceEventAgent { + public: + TraceEventAgentImpl(service_manager::Connector* connector, + bool request_clock_sync_marker_on_android); + + void AddMetadataGeneratorFunction( + MetadataGeneratorFunction generator) override; + + private: + friend std::default_delete<TraceEventAgentImpl>; // For Testing + friend class TraceEventAgentTest; // For Testing + + ~TraceEventAgentImpl() override; + + // mojom::Agent + void StartTracing(const std::string& config, + base::TimeTicks coordinator_time, + StartTracingCallback callback) override; + void StopAndFlush(mojom::RecorderPtr recorder) override; + void RequestClockSyncMarker( + const std::string& sync_id, + Agent::RequestClockSyncMarkerCallback callback) override; + void RequestBufferStatus(RequestBufferStatusCallback callback) override; + void GetCategories(GetCategoriesCallback callback) override; + + void OnTraceLogFlush(const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events); + + uint8_t enabled_tracing_modes_; + mojom::RecorderPtr recorder_; + std::vector<MetadataGeneratorFunction> metadata_generator_functions_; + bool trace_log_needs_me_ = false; + + THREAD_CHECKER(thread_checker_); + + DISALLOW_COPY_AND_ASSIGN(TraceEventAgentImpl); +}; + +} // namespace tracing +#endif // SERVICES_TRACING_PUBLIC_CPP_TRACE_EVENT_AGENT_H_
diff --git a/services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc b/services/tracing/public/cpp/trace_event_agent_unittest.cc similarity index 87% rename from services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc rename to services/tracing/public/cpp/trace_event_agent_unittest.cc index 481e36eb..c4a81a55 100644 --- a/services/tracing/public/cpp/chrome_trace_event_agent_unittest.cc +++ b/services/tracing/public/cpp/trace_event_agent_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" #include <utility> @@ -22,8 +22,8 @@ namespace tracing { namespace { -const char kTestCategory[] = "ChromeTraceEventAgentTestCategory"; -const char kTestMetadataKey[] = "ChromeTraceEventAgentTestMetadata"; +const char kTestCategory[] = "TraceEventAgentTestCategory"; +const char kTestMetadataKey[] = "TraceEventAgentTestMetadata"; } // namespace class MockRecorder : public mojom::Recorder { @@ -79,11 +79,11 @@ base::Closure quit_closure_; }; -class ChromeTraceEventAgentTest : public testing::Test { +class TraceEventAgentTest : public testing::Test { public: void SetUp() override { message_loop_.reset(new base::MessageLoop()); - agent_.reset(new ChromeTraceEventAgent(nullptr, false)); + agent_.reset(new TraceEventAgentImpl(nullptr, false)); } void TearDown() override { @@ -108,15 +108,15 @@ } void AddMetadataGeneratorFunction( - ChromeTraceEventAgent::MetadataGeneratorFunction generator) { + TraceEventAgent::MetadataGeneratorFunction generator) { agent_->AddMetadataGeneratorFunction(generator); } void GetCategories(const std::string& expected_category, base::Closure quit_closure) { agent_->GetCategories(base::BindRepeating( - &ChromeTraceEventAgentTest::OnGetCategoriesReply, - base::Unretained(this), expected_category, quit_closure)); + &TraceEventAgentTest::OnGetCategoriesReply, base::Unretained(this), + expected_category, quit_closure)); } void OnGetCategoriesReply(const std::string& expected_category, @@ -130,11 +130,11 @@ private: std::unique_ptr<base::MessageLoop> message_loop_; - std::unique_ptr<ChromeTraceEventAgent> agent_; + std::unique_ptr<TraceEventAgentImpl> agent_; std::unique_ptr<MockRecorder> recorder_; }; -TEST_F(ChromeTraceEventAgentTest, StartTracing) { +TEST_F(TraceEventAgentTest, StartTracing) { EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled()); base::RunLoop run_loop; StartTracing("*"); @@ -143,7 +143,7 @@ run_loop.Run(); } -TEST_F(ChromeTraceEventAgentTest, StopAndFlushEvents) { +TEST_F(TraceEventAgentTest, StopAndFlushEvents) { EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled()); base::RunLoop run_loop; StartTracing(kTestCategory); @@ -158,14 +158,14 @@ EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled()); } -TEST_F(ChromeTraceEventAgentTest, GetCategories) { +TEST_F(TraceEventAgentTest, GetCategories) { base::RunLoop run_loop; TRACE_EVENT_INSTANT0(kTestCategory, "event1", TRACE_EVENT_SCOPE_THREAD); GetCategories(kTestCategory, run_loop.QuitClosure()); run_loop.Run(); } -TEST_F(ChromeTraceEventAgentTest, StopAndFlushMetadata) { +TEST_F(TraceEventAgentTest, StopAndFlushMetadata) { EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled()); base::RunLoop run_loop; AddMetadataGeneratorFunction(base::BindRepeating([] {
diff --git a/services/tracing/tracing_service.cc b/services/tracing/tracing_service.cc index 701ea444..365cc19 100644 --- a/services/tracing/tracing_service.cc +++ b/services/tracing/tracing_service.cc
@@ -10,6 +10,12 @@ #include "services/service_manager/public/cpp/service_context.h" #include "services/tracing/agent_registry.h" #include "services/tracing/coordinator.h" +#include "services/tracing/public/cpp/tracing_features.h" + +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) +#include "services/tracing/perfetto/perfetto_service.h" +#include "services/tracing/perfetto/perfetto_tracing_coordinator.h" +#endif namespace tracing { @@ -19,7 +25,18 @@ TracingService::TracingService() : weak_factory_(this) {} -TracingService::~TracingService() = default; +TracingService::~TracingService() { +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) + if (perfetto_tracing_coordinator_) { + PerfettoTracingCoordinator::DestroyOnSequence( + std::move(perfetto_tracing_coordinator_)); + } + + if (perfetto_service_) { + PerfettoService::DestroyOnSequence(std::move(perfetto_service_)); + } +#endif +} void TracingService::OnStart() { ref_factory_.reset(new service_manager::ServiceContextRefFactory( @@ -30,10 +47,29 @@ base::BindRepeating(&AgentRegistry::BindAgentRegistryRequest, base::Unretained(tracing_agent_registry_.get()))); - tracing_coordinator_ = std::make_unique<Coordinator>(ref_factory_.get()); - registry_.AddInterface( - base::BindRepeating(&Coordinator::BindCoordinatorRequest, - base::Unretained(tracing_coordinator_.get()))); + if (TracingUsesPerfettoBackend()) { +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) + perfetto_service_ = std::make_unique<tracing::PerfettoService>(); + registry_.AddInterface( + base::BindRepeating(&tracing::PerfettoService::BindRequest, + base::Unretained(perfetto_service_.get()))); + + auto perfetto_coordinator = std::make_unique<PerfettoTracingCoordinator>(); + registry_.AddInterface( + base::BindRepeating(&PerfettoTracingCoordinator::BindCoordinatorRequest, + base::Unretained(perfetto_coordinator.get()))); + perfetto_tracing_coordinator_ = std::move(perfetto_coordinator); +#else + LOG(FATAL) << "Perfetto is not yet available for this platform."; +#endif + } else { + auto tracing_coordinator = + std::make_unique<Coordinator>(ref_factory_.get()); + registry_.AddInterface( + base::BindRepeating(&Coordinator::BindCoordinatorRequest, + base::Unretained(tracing_coordinator.get()))); + tracing_coordinator_ = std::move(tracing_coordinator); + } } void TracingService::OnBindInterface(
diff --git a/services/tracing/tracing_service.h b/services/tracing/tracing_service.h index e70a2902..52ad57bc 100644 --- a/services/tracing/tracing_service.h +++ b/services/tracing/tracing_service.h
@@ -11,6 +11,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "build/build_config.h" #include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_context_ref.h" @@ -19,6 +20,9 @@ namespace tracing { +class PerfettoTracingCoordinator; +class PerfettoService; + class TracingService : public service_manager::Service { public: TracingService(); @@ -43,9 +47,14 @@ const service_manager::BindSourceInfo&> registry_; std::unique_ptr<tracing::AgentRegistry> tracing_agent_registry_; - std::unique_ptr<tracing::Coordinator> tracing_coordinator_; + std::unique_ptr<Coordinator> tracing_coordinator_; std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_; +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) + std::unique_ptr<tracing::PerfettoService> perfetto_service_; + std::unique_ptr<PerfettoTracingCoordinator> perfetto_tracing_coordinator_; +#endif + // WeakPtrFactory members should always come last so WeakPtrs are destructed // before other members. base::WeakPtrFactory<TracingService> weak_factory_;
diff --git a/services/viz/public/cpp/compositing/BUILD.gn b/services/viz/public/cpp/compositing/BUILD.gn index 5a7e9a06..555d5be 100644 --- a/services/viz/public/cpp/compositing/BUILD.gn +++ b/services/viz/public/cpp/compositing/BUILD.gn
@@ -25,3 +25,26 @@ "//ui/gfx/ipc/color:color", ] } + +source_set("perftests") { + testonly = true + + sources = [ + "struct_traits_perftest.cc", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//components/viz/common", + "//components/viz/test:test_support", + "//gpu/ipc/common:struct_traits", + "//mojo/public/cpp/bindings", + "//services/viz/public/interfaces", + "//skia", + "//testing/perf", + "//ui/gfx", + "//ui/gfx:test_support", + "//ui/gfx/geometry", + ] +}
diff --git a/cc/ipc/cc_serialization_perftest.cc b/services/viz/public/cpp/compositing/struct_traits_perftest.cc similarity index 65% rename from cc/ipc/cc_serialization_perftest.cc rename to services/viz/public/cpp/compositing/struct_traits_perftest.cc index 7dc52dcf..3050af0 100644 --- a/cc/ipc/cc_serialization_perftest.cc +++ b/services/viz/public/cpp/compositing/struct_traits_perftest.cc
@@ -6,15 +6,13 @@ #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" -#include "cc/ipc/cc_param_traits.h" #include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/picture_draw_quad.h" +#include "components/viz/common/quads/solid_color_draw_quad.h" +#include "components/viz/common/quads/texture_draw_quad.h" +#include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/test/compositor_frame_helpers.h" #include "gpu/ipc/common/mailbox_holder_struct_traits.h" #include "gpu/ipc/common/mailbox_struct_traits.h" -#include "gpu/ipc/common/sync_token_struct_traits.h" -#include "ipc/ipc_message.h" -#include "mojo/public/cpp/base/time_mojom_traits.h" #include "mojo/public/cpp/bindings/message.h" #include "services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h" #include "services/viz/public/cpp/compositing/compositor_frame_struct_traits.h" @@ -23,14 +21,12 @@ #include "services/viz/public/cpp/compositing/shared_quad_state_struct_traits.h" #include "services/viz/public/cpp/compositing/surface_id_struct_traits.h" #include "services/viz/public/interfaces/compositing/compositor_frame.mojom.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_test.h" -#include "ui/gfx/geometry/mojo/geometry.mojom.h" #include "ui/gfx/geometry/mojo/geometry_struct_traits.h" #include "ui/gfx/mojo/selection_bound_struct_traits.h" #include "ui/latency/mojo/latency_info_struct_traits.h" -namespace cc { +namespace viz { namespace { static const int kTimeLimitMillis = 2000; @@ -39,115 +35,16 @@ enum class UseSingleSharedQuadState { YES, NO }; -class CCSerializationPerfTest : public testing::Test { +class VizSerializationPerfTest : public testing::Test { protected: - static void ReadMessage(const IPC::Message* msg, - viz::CompositorFrame* frame) { - base::PickleIterator iter(*msg); - bool success = - IPC::ParamTraits<viz::CompositorFrame>::Read(msg, &iter, frame); - CHECK(success); - } - - static void RunDeserializationTestParamTraits( - const std::string& test_name, - const viz::CompositorFrame& frame, - UseSingleSharedQuadState single_sqs) { - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - IPC::ParamTraits<viz::CompositorFrame>::Write(&msg, frame); - for (int i = 0; i < kNumWarmupRuns; ++i) { - viz::CompositorFrame compositor_frame; - ReadMessage(&msg, &compositor_frame); - } - - base::TimeTicks start = base::TimeTicks::Now(); - base::TimeTicks end = - start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); - base::TimeTicks now = start; - base::TimeDelta min_time; - size_t count = 0; - while (start < end) { - for (int i = 0; i < kTimeCheckInterval; ++i) { - viz::CompositorFrame compositor_frame; - ReadMessage(&msg, &compositor_frame); - now = base::TimeTicks::Now(); - // We don't count iterations after the end time. - if (now < end) - ++count; - } - - if (now - start < min_time || min_time.is_zero()) - min_time = now - start; - start = now; - } - - perf_test::PrintResult( - "ParamTraits deserialization: min_frame_deserialization_time", - single_sqs == UseSingleSharedQuadState::YES - ? "_per_render_pass_shared_quad_state" - : "_per_quad_shared_quad_state", - test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us", - true); - perf_test::PrintResult("ParamTraits deserialization: num runs in 2 seconds", - single_sqs == UseSingleSharedQuadState::YES - ? "_per_render_pass_shared_quad_state" - : "_per_quad_shared_quad_state", - test_name, count, "runs/s", true); - } - - static void RunSerializationTestParamTraits( - const std::string& test_name, - const viz::CompositorFrame& frame, - UseSingleSharedQuadState single_sqs) { - for (int i = 0; i < kNumWarmupRuns; ++i) { - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - IPC::ParamTraits<viz::CompositorFrame>::Write(&msg, frame); - } - - base::TimeTicks start = base::TimeTicks::Now(); - base::TimeTicks end = - start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); - base::TimeTicks now = start; - base::TimeDelta min_time; - size_t count = 0; - while (start < end) { - for (int i = 0; i < kTimeCheckInterval; ++i) { - IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); - IPC::ParamTraits<viz::CompositorFrame>::Write(&msg, frame); - now = base::TimeTicks::Now(); - // We don't count iterations after the end time. - if (now < end) - ++count; - } - - if (now - start < min_time || min_time.is_zero()) - min_time = now - start; - start = now; - } - - perf_test::PrintResult( - "ParamTraits serialization: min_frame_serialization_time", - single_sqs == UseSingleSharedQuadState::YES - ? "_per_render_pass_shared_quad_state" - : "_per_quad_shared_quad_state", - test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us", - true); - perf_test::PrintResult("ParamTraits serialization: num runs in 2 seconds", - single_sqs == UseSingleSharedQuadState::YES - ? "_per_render_pass_shared_quad_state" - : "_per_quad_shared_quad_state", - test_name, count, "runs/s", true); - } - static void RunDeserializationTestStructTraits( const std::string& test_name, - const viz::CompositorFrame& frame, + const CompositorFrame& frame, UseSingleSharedQuadState single_sqs) { - mojo::Message message = - viz::mojom::CompositorFrame::SerializeAsMessage(&frame); + mojo::Message message = mojom::CompositorFrame::SerializeAsMessage(&frame); for (int i = 0; i < kNumWarmupRuns; ++i) { - viz::CompositorFrame compositor_frame; - viz::mojom::CompositorFrame::Deserialize( + CompositorFrame compositor_frame; + mojom::CompositorFrame::Deserialize( message.payload(), message.payload_num_bytes(), &compositor_frame); } @@ -159,8 +56,8 @@ size_t count = 0; while (start < end) { for (int i = 0; i < kTimeCheckInterval; ++i) { - viz::CompositorFrame compositor_frame; - viz::mojom::CompositorFrame::Deserialize( + CompositorFrame compositor_frame; + mojom::CompositorFrame::Deserialize( message.payload(), message.payload_num_bytes(), &compositor_frame); now = base::TimeTicks::Now(); // We don't count iterations after the end time. @@ -190,11 +87,11 @@ static void RunSerializationTestStructTraits( const std::string& test_name, - const viz::CompositorFrame& frame, + const CompositorFrame& frame, UseSingleSharedQuadState single_sqs) { for (int i = 0; i < kNumWarmupRuns; ++i) { mojo::Message message = - viz::mojom::CompositorFrame::SerializeAsMessage(&frame); + mojom::CompositorFrame::SerializeAsMessage(&frame); } base::TimeTicks start = base::TimeTicks::Now(); @@ -206,7 +103,7 @@ while (start < end) { for (int i = 0; i < kTimeCheckInterval; ++i) { mojo::Message message = - viz::mojom::CompositorFrame::SerializeAsMessage(&frame); + mojom::CompositorFrame::SerializeAsMessage(&frame); now = base::TimeTicks::Now(); // We don't count iterations after the end time. if (now < end) @@ -233,12 +130,12 @@ } static void RunComplexCompositorFrameTest(const std::string& test_name) { - viz::CompositorFrame frame; - frame.metadata.begin_frame_ack = viz::BeginFrameAck(0, 1, true); + CompositorFrame frame; + frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, true); - std::vector<viz::TransferableResource>& resource_list = frame.resource_list; + std::vector<TransferableResource>& resource_list = frame.resource_list; for (uint32_t i = 0; i < 80; ++i) { - viz::TransferableResource arbitrary_resource; + TransferableResource arbitrary_resource; resource_list.push_back(arbitrary_resource); } @@ -279,27 +176,27 @@ SkBlendMode arbitrary_blend_mode1 = SkBlendMode::kScreen; SkBlendMode arbitrary_blend_mode2 = SkBlendMode::kLighten; SkBlendMode arbitrary_blend_mode3 = SkBlendMode::kOverlay; - viz::ResourceId arbitrary_resourceid1 = 55; - viz::ResourceId arbitrary_resourceid2 = 47; - viz::ResourceId arbitrary_resourceid3 = 23; - viz::ResourceId arbitrary_resourceid4 = 16; + ResourceId arbitrary_resourceid1 = 55; + ResourceId arbitrary_resourceid2 = 47; + ResourceId arbitrary_resourceid3 = 23; + ResourceId arbitrary_resourceid4 = 16; SkScalar arbitrary_sigma = SkFloatToScalar(2.0f); gfx::ColorSpace arbitrary_color_space = gfx::ColorSpace::CreateXYZD50(); int root_id = 14; - FilterOperations arbitrary_filters1; + cc::FilterOperations arbitrary_filters1; arbitrary_filters1.Append( - FilterOperation::CreateGrayscaleFilter(arbitrary_float1)); - arbitrary_filters1.Append( - FilterOperation::CreateReferenceFilter(sk_make_sp<BlurPaintFilter>( + cc::FilterOperation::CreateGrayscaleFilter(arbitrary_float1)); + arbitrary_filters1.Append(cc::FilterOperation::CreateReferenceFilter( + sk_make_sp<cc::BlurPaintFilter>( arbitrary_sigma, arbitrary_sigma, - BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr))); + cc::BlurPaintFilter::TileMode::kClampToBlack_TileMode, nullptr))); - FilterOperations arbitrary_filters2; + cc::FilterOperations arbitrary_filters2; arbitrary_filters2.Append( - FilterOperation::CreateBrightnessFilter(arbitrary_float2)); + cc::FilterOperation::CreateBrightnessFilter(arbitrary_float2)); - std::unique_ptr<viz::RenderPass> pass_in = viz::RenderPass::Create(); + std::unique_ptr<RenderPass> pass_in = RenderPass::Create(); pass_in->SetAll(root_id, arbitrary_rect1, arbitrary_rect2, arbitrary_matrix1, arbitrary_filters2, arbitrary_filters1, arbitrary_color_space, arbitrary_bool1, arbitrary_bool1, @@ -307,15 +204,14 @@ // Texture quads for (uint32_t i = 0; i < 10; ++i) { - viz::SharedQuadState* shared_state1_in = + SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState(); shared_state1_in->SetAll( arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1, arbitrary_rect2, arbitrary_bool1, arbitrary_bool1, arbitrary_float1, arbitrary_blend_mode1, arbitrary_context_id1); - auto* texture_in = - pass_in->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); + auto* texture_in = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>(); texture_in->SetAll(shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, arbitrary_bool1, arbitrary_resourceid1, arbitrary_size1, @@ -323,8 +219,7 @@ arbitrary_color, arbitrary_float_array, arbitrary_bool4, arbitrary_bool5, arbitrary_bool6); - auto* texture_in2 = - pass_in->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); + auto* texture_in2 = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>(); texture_in2->SetAll(shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, arbitrary_bool1, arbitrary_resourceid2, arbitrary_size1, @@ -332,8 +227,7 @@ arbitrary_color, arbitrary_float_array, arbitrary_bool4, arbitrary_bool5, arbitrary_bool6); - auto* texture_in3 = - pass_in->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); + auto* texture_in3 = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>(); texture_in3->SetAll(shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, arbitrary_bool1, arbitrary_resourceid3, arbitrary_size1, @@ -341,8 +235,7 @@ arbitrary_color, arbitrary_float_array, arbitrary_bool4, arbitrary_bool6, arbitrary_bool6); - auto* texture_in4 = - pass_in->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); + auto* texture_in4 = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>(); texture_in4->SetAll(shared_state1_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, arbitrary_bool1, arbitrary_resourceid4, arbitrary_size2, @@ -353,14 +246,14 @@ // Tiled quads for (uint32_t i = 0; i < 10; ++i) { - viz::SharedQuadState* shared_state2_in = + SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState(); shared_state2_in->SetAll( arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2, arbitrary_rect3, arbitrary_bool1, arbitrary_bool1, arbitrary_float2, arbitrary_blend_mode2, arbitrary_context_id2); for (uint32_t j = 0; j < 6; ++j) { - auto* tile_in = pass_in->CreateAndAppendDrawQuad<viz::TileDrawQuad>(); + auto* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>(); tile_in->SetAll(shared_state2_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1, @@ -371,7 +264,7 @@ // Solid color quads for (uint32_t i = 0; i < 5; ++i) { - viz::SharedQuadState* shared_state3_in = + SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState(); shared_state3_in->SetAll( arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3, arbitrary_rect1, @@ -379,7 +272,7 @@ arbitrary_blend_mode3, arbitrary_context_id3); for (uint32_t j = 0; j < 5; ++j) { auto* solidcolor_in = - pass_in->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); + pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); solidcolor_in->SetAll(shared_state3_in, arbitrary_rect3, arbitrary_rect2_inside_rect3, arbitrary_bool1, arbitrary_color, arbitrary_bool2); @@ -394,18 +287,17 @@ uint32_t num_quads, uint32_t num_passes, UseSingleSharedQuadState single_sqs) { - viz::CompositorFrame frame = viz::MakeEmptyCompositorFrame(); + CompositorFrame frame = MakeEmptyCompositorFrame(); for (uint32_t i = 0; i < num_passes; ++i) { - std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create(); + std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); render_pass->SetNew(1, gfx::Rect(20, 20), gfx::Rect(), gfx::Transform()); for (uint32_t j = 0; j < num_quads; ++j) { if (j == 0 || single_sqs == UseSingleSharedQuadState::NO) render_pass->CreateAndAppendSharedQuadState(); const gfx::Rect bounds(100, 100, 100, 100); const bool kForceAntiAliasingOff = true; - auto* quad = - render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); + auto* quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); quad->SetNew(render_pass->shared_quad_state_list.back(), bounds, bounds, SK_ColorRED, kForceAntiAliasingOff); } @@ -415,12 +307,10 @@ } static void RunTest(const std::string& test_name, - viz::CompositorFrame frame, + CompositorFrame frame, UseSingleSharedQuadState single_sqs) { RunSerializationTestStructTraits(test_name, frame, single_sqs); RunDeserializationTestStructTraits(test_name, frame, single_sqs); - RunSerializationTestParamTraits(test_name, frame, single_sqs); - RunDeserializationTestParamTraits(test_name, frame, single_sqs); } }; @@ -428,12 +318,12 @@ // list, 10 shared quad states with 4 texture quads each, 10 shared quad states // with 6 tiled quads each, and 5 shared quad states with 5 solid color quads // each. -TEST_F(CCSerializationPerfTest, DelegatedFrame_Complex) { +TEST_F(VizSerializationPerfTest, DelegatedFrame_Complex) { RunComplexCompositorFrameTest("DelegatedFrame_Complex"); } // Test for compositor frames with one render pass and 4000 quads. -TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_1_4000) { +TEST_F(VizSerializationPerfTest, DelegatedFrame_ManyQuads_1_4000) { // Case 1: One shared quad state for all quads in one render pass. RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1, UseSingleSharedQuadState::YES); @@ -443,7 +333,7 @@ } // Test for compositor frames with 5 render pass and each with 100 quads. -TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_5_100) { +TEST_F(VizSerializationPerfTest, DelegatedFrame_ManyRenderPasses_5_100) { // Case 1: One shared quad state for all quads in one render pass. RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5, UseSingleSharedQuadState::YES); @@ -453,7 +343,7 @@ } // Test for compositor frames with 10 render pass and each with 500 quads. -TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_10_500) { +TEST_F(VizSerializationPerfTest, DelegatedFrame_ManyRenderPasses_10_500) { // Case 1: One shared quad state for all quads in one render pass. RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10, UseSingleSharedQuadState::YES); @@ -463,4 +353,4 @@ } } // namespace -} // namespace cc +} // namespace viz
diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc index ce3e7291..70deee7 100644 --- a/sql/sqlite_features_unittest.cc +++ b/sql/sqlite_features_unittest.cc
@@ -173,13 +173,10 @@ " FROM flags WHERE id=1;")); ASSERT_TRUE(s.Step()); - // TODO(pwnall): Enable this check after upgrading to SQLite 3.23. - // EXPECT_TRUE(s.ColumnBool(0)) << " default TRUE at table creation time"; + EXPECT_TRUE(s.ColumnBool(0)) << " default TRUE at table creation time"; EXPECT_TRUE(!s.ColumnBool(1)) << " default FALSE at table creation time"; - // TODO(pwnall): Enable this check after upgrading to SQLite 3.23. - // EXPECT_TRUE(s.ColumnBool(2)) - // << " default TRUE added by altering the table"; + EXPECT_TRUE(s.ColumnBool(2)) << " default TRUE added by altering the table"; EXPECT_TRUE(!s.ColumnBool(3)) << " default FALSE added by altering the table"; }
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index 4cbcc31e..c380a484 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -1594,7 +1594,9 @@ "device_type": "sprout" } ], - "expiration": 14400 + "expiration": 14400, + "hard_timeout": 1800, + "shards": 3 }, "test": "net_unittests" }, @@ -2870,7 +2872,7 @@ } ], "expiration": 10800, - "hard_timeout": 1200, + "hard_timeout": 1800, "output_links": [ { "link": [ @@ -2881,7 +2883,7 @@ "name": "shard #${SHARD_INDEX} logcats" } ], - "shards": 2 + "shards": 3 }, "test": "net_unittests" }, @@ -4528,7 +4530,8 @@ { "device_type": "coho" } - ] + ], + "shards": 4 }, "test": "net_unittests" }, @@ -5396,7 +5399,9 @@ { "device_type": "gce_x86" } - ] + ], + "hard_timeout": 1800, + "shards": 3 }, "test": "net_unittests" },
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index a035329..a0720c1 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -1648,7 +1648,7 @@ "device_type": "hammerhead" } ], - "hard_timeout": 900, + "hard_timeout": 1800, "output_links": [ { "link": [ @@ -3246,7 +3246,7 @@ "device_type": "hammerhead" } ], - "hard_timeout": 900, + "hard_timeout": 1800, "output_links": [ { "link": [ @@ -9349,7 +9349,7 @@ "device_type": "bullhead" } ], - "hard_timeout": 960, + "hard_timeout": 1800, "output_links": [ { "link": [ @@ -10991,7 +10991,7 @@ "device_type": "bullhead" } ], - "hard_timeout": 960, + "hard_timeout": 1800, "output_links": [ { "link": [
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json index b4b4e26..a0f40f15 100644 --- a/testing/buildbot/chromium.clang.json +++ b/testing/buildbot/chromium.clang.json
@@ -4707,7 +4707,7 @@ "device_type": "hammerhead" } ], - "hard_timeout": 900, + "hard_timeout": 1800, "output_links": [ { "link": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 2993a7e..f96b686 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -2888,9 +2888,6 @@ "test": "ipc_tests" }, { - "args": [ - "--use-new-test-runner" - ], "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json index 90e2344..87b3e38 100644 --- a/testing/buildbot/chromium.win.json +++ b/testing/buildbot/chromium.win.json
@@ -2441,26 +2441,6 @@ } }, { - "args": [ - "--zero-tests-executed-ok" - ], - "experiment_percentage": 100, - "isolate_name": "webkit_layout_tests_exparchive", - "merge": { - "args": [ - "--verbose" - ], - "script": "//third_party/blink/tools/merge_web_test_results.py" - }, - "name": "webkit_layout_tests", - "only_retry_failed_tests": true, - "results_handler": "layout tests", - "swarming": { - "can_use_on_swarming_builders": true, - "shards": 12 - } - }, - { "isolate_name": "webkit_python_tests", "name": "webkit_python_tests", "swarming": {
diff --git a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter index a1ed905..31a230e 100644 --- a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter +++ b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
@@ -1,25 +1,25 @@ # https://crbug.com/828031 # Suites implicated in window_server deaths. -# -PDFExtensionTest.* -# -SubresourceFilterBrowserTest.* -# -DevToolsBeforeUnloadTest.* -# -PushMessagingBrowserTest.* -# -TaskManagerBrowserTest.* -# -AppBannerManagerBrowserTest.* -# -SpellingMenuObserverTest.* -# -PluginPowerSaverBrowserTest.* -# -SubresourceFilterPopupBrowserTest.* -# -FindInPageControllerTest.* -# -TestStatsDictionaryTest.* -# -PlatformAppWithFileBrowserTest.* -# -PermissionDialogTest.* -# -SecurityStateTabHelperTest.* -# -PrerenderBrowserTest.* -# -DomDistillerViewerSourceBrowserTest.* -# -PredictorBrowserTest.* -# -TabManagerTest.* -# -PasswordManagerBrowserTestBase.* +-PDFExtensionTest.* +-SubresourceFilterBrowserTest.* +-DevToolsBeforeUnloadTest.* +-PushMessagingBrowserTest.* +-TaskManagerBrowserTest.* +-AppBannerManagerBrowserTest.* +-SpellingMenuObserverTest.* +-PluginPowerSaverBrowserTest.* +-SubresourceFilterPopupBrowserTest.* +-FindInPageControllerTest.* +-TestStatsDictionaryTest.* +-PlatformAppWithFileBrowserTest.* +-PermissionDialogTest.* +-SecurityStateTabHelperTest.* +-PrerenderBrowserTest.* +-DomDistillerViewerSourceBrowserTest.* +-PredictorBrowserTest.* +-TabManagerTest.* +-PasswordManagerBrowserTestBase.* -DevToolsSanityTest.* -WebRtcBrowserTest.* -LoginPromptBrowserTest.*
diff --git a/testing/buildbot/filters/mash.ash_unittests.filter b/testing/buildbot/filters/mash.ash_unittests.filter index 68273f3..31852e1 100644 --- a/testing/buildbot/filters/mash.ash_unittests.filter +++ b/testing/buildbot/filters/mash.ash_unittests.filter
@@ -173,6 +173,9 @@ -NativeCursorManagerAshTest.SetDeviceScaleFactorAndRotation -NativeCursorManagerAshTest.UIScaleShouldNotChangeCursor +# TODO: NightLight and mirror mode in mash. https://crbug.com/836368 +-NightLightTest.TestNightLightWithDisplayConfigurationChanges + # TODO: Maybe due to dragging across displays. http://crbug.com/698888 -NormalPanelPopup/PanelWindowResizerTransientTest.PanelWithTransientChild/0
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter index 3f52029e..c980e7d 100644 --- a/testing/buildbot/filters/mash.browser_tests.filter +++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -77,10 +77,6 @@ # Need screenshot support. http://crbug.com/754899 -ChromeScreenshotGrabberBrowserTest.* -# ChromeBrowserMainExtraPartsAsh: Check failed: views::MusClient::Exists(). --ChromeMainTest.* --ProfilingBrowserTest.* - # Null immersive_fullscreen_controller_. -ChromeNativeAppWindowViewsAuraAshBrowserTest.* @@ -337,6 +333,8 @@ -GearMenu/FileManagerBrowserTest.Test/3 -GearMenu/FileManagerBrowserTest.Test/0 -CreateNewFolder/FileManagerBrowserTest.* +-FileDisplay/FileManagerBrowserTest.* +-OpenVideoFiles/FileManagerBrowserTest.* -TabindexFocusDownloads/FileManagerBrowserTestWithLegacyEventDispatch.Test/0 -TabindexFocusDownloads/FileManagerBrowserTestWithLegacyEventDispatch.Test/1 -DeclarativeNetRequestResourceTypeBrowserTest.Test1/1
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter index fb393b5..b68937c 100644 --- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter +++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -128,9 +128,6 @@ # http://crbug.com/783990 # Add support for http auth. -DownloadExtensionTest.DownloadExtensionTest_Download_AuthBasic_Fail -# BlockCrossdomainPromptForSubresources is flaky, see -# https://logs.chromium.org/v/?s=chromium%2Fbuildbucket%2Fcr-buildbucket.appspot.com%2F8952857968271661120%2F%2B%2Fsteps%2Fnetwork_service_browser_tests__with_patch_%2F0%2Flogs%2FLoginPromptBrowserTest.BlockCrossdomainPromptForSubresources%2F0 --LoginPromptBrowserTest.BlockCrossdomainPromptForSubresources -LoginPromptBrowserTest.NoLoginPromptForFavicon -LoginPromptBrowserTest.NoLoginPromptForXHRWithBadCredentials @@ -141,10 +138,6 @@ -IOThreadBrowserTestWithHangingPacRequest.Shutdown -ProxySettingsApiTest.ProxyEventsParseError -# http://crbug.com/783996 -# Add support for TLS client auth. --SSLUITestWithClientCert.TestWSSClientCert - # https://bugs.chromium.org/p/chromium/issues/detail?id=755309 -SSLUITest.SHA1PrefsCanEnable/0 -SSLUITest.SHA1PrefsCanEnable/1
diff --git a/testing/buildbot/filters/viz.content_browsertests.filter b/testing/buildbot/filters/viz.content_browsertests.filter index 5108e5e..a4fe91e 100644 --- a/testing/buildbot/filters/viz.content_browsertests.filter +++ b/testing/buildbot/filters/viz.content_browsertests.filter
@@ -1,8 +1,3 @@ -#### Dev Tools -# content::DevToolsProtocolTest::WaitForResponse times out -# http://crbug.com/784941 --CaptureScreenshotTest.* - #### GPU # GPU Shutdown https://crbug.com/781714 -WebRtcCaptureFromElementBrowserTest.CaptureFromCanvas2DHandlesContextLoss @@ -76,9 +71,6 @@ -GLAndSoftwareCompositing/CompositingRenderWidgetHostViewBrowserTestTabCapture.* -GLAndSoftwareCompositing/CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI.* -# GetSnapshotFromBrowser doesn't return snapshots http://crbug.com/785308 --SnapshotBrowserTest.* - #### VizProcessTransportFactory # No ContextProvider http://crbug.com/785268 -ImageTransportFactoryTearDownBrowserTest.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index ebbfbe3..374d5ace 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2001,14 +2001,6 @@ # chromium.fyi 'Fuchsia ARM64', ], - 'modifications': { - # chromium.fyi - 'Fuchsia': { - 'args': [ - '--use-new-test-runner', - ], - }, - }, }, 'message_center_unittests': { 'remove_from': [ @@ -2182,62 +2174,6 @@ 'Fuchsia x64', ], 'modifications': { - # chromium.android - 'KitKat Tablet Tester': { - 'swarming': { - 'hard_timeout': 1800, - }, - }, - 'Lollipop Phone Tester': { - 'swarming': { - 'hard_timeout': 1800, - }, - }, - 'Lollipop Tablet Tester': { - 'swarming': { - 'hard_timeout': 1800, - }, - }, - 'Marshmallow 64 bit Tester': { - 'swarming': { - 'hard_timeout': 960, - }, - }, - 'Marshmallow Phone Tester (rel)': { - 'swarming': { - 'hard_timeout': 960, - }, - }, - 'Marshmallow Tablet Tester': { - 'swarming': { - 'hard_timeout': 1800, - }, - }, - # chromium.android.fyi - 'Lollipop Low-end Tester': { - 'swarming': { - 'hard_timeout': 0, - 'shards': 1, - }, - }, - 'Nougat Phone Tester': { - 'swarming': { - 'hard_timeout': 1200, - 'shards': 2, - }, - }, - 'x64 Device Tester': { - 'swarming': { - 'hard_timeout': 0, - 'shards': 1, - }, - }, - 'x86 Cloud Tester': { - 'swarming': { - 'hard_timeout': 0, - 'shards': 1, - }, - }, # chromium.clang 'ToTLinuxASan': { # TODO(crbug.com/794372): net_unittests is slow under ASan. @@ -3328,6 +3264,7 @@ # chromium.win 'Win 7 Tests x64 (1)', 'Win10 Tests x64', + 'Win7 Tests (1)', ], 'modifications': { # chromium.fyi @@ -3562,10 +3499,6 @@ }, }, - 'Win7 Tests (1)': { - 'experiment_percentage': 100, - }, - # chromium.win 'Win7 Tests (dbg)(1)': { 'args': [
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 6afb88f1..a2d4cd7 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -456,7 +456,7 @@ 'midi_unittests': {}, 'net_unittests': { 'android_swarming': { - 'hard_timeout': 900, + 'hard_timeout': 1800, 'shards': 3, }, },
diff --git a/testing/libfuzzer/README.md b/testing/libfuzzer/README.md index df023d7..78a7a27 100644 --- a/testing/libfuzzer/README.md +++ b/testing/libfuzzer/README.md
@@ -29,8 +29,10 @@ * [Efficient Fuzzer Guide] explains how to measure fuzz target effectiveness and ways to improve it. * [Guide to libprotobuf-mutator] walks through the steps necessary to create a -fuzz target that libFuzzer gives mutated protobufs to as input (for developers -already familiar with libFuzzer). +fuzz target that expects a protobuf as input (instead of a byte stream). In +addition to fuzzing code that accepts protobufs, it can be used to fuzz +code that requires multiple mutated inputs, or to generate inputs defined by a +grammar. * [ClusterFuzz Integration] describes integration between ClusterFuzz and libFuzzer. * [Reproducing] contains information on how to reproduce bugs reported by
diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni index 8d72c6a..7fe926dc 100644 --- a/testing/libfuzzer/fuzzer_test.gni +++ b/testing/libfuzzer/fuzzer_test.gni
@@ -18,6 +18,7 @@ # - dict - a dictionary file for the fuzzer. # - libfuzzer_options - options for the fuzzer (e.g. -max_len or -timeout). # - seed_corpus - a directory with seed corpus. +# - skip_owners - if true, skips writing the owners file. # # If use_libfuzzer gn flag is defined, then proper fuzzer would be build. # Without use_libfuzzer or use_afl a unit-test style binary would be built on @@ -120,9 +121,10 @@ test_deps += [ ":" + config_name ] } - # Generate .owners file. + # Generate .owners file (if skip_owners is not true). # FIXME: Add support for sources parsing in deps attribute. - if (invoker.sources != []) { + if (invoker.sources != [] && + (!defined(invoker.skip_owners) || !invoker.skip_owners)) { owners_name = target_name + ".owners" action(owners_name) { script = "//testing/libfuzzer/gen_fuzzer_owners.py"
diff --git a/testing/libfuzzer/gen_fuzzer_owners.py b/testing/libfuzzer/gen_fuzzer_owners.py index c2b02de..605f080 100755 --- a/testing/libfuzzer/gen_fuzzer_owners.py +++ b/testing/libfuzzer/gen_fuzzer_owners.py
@@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python2.7 # # Copyright 2018 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -11,40 +11,89 @@ import argparse import os +import re import subprocess import sys +AUTHOR_REGEX = re.compile('author-mail <(.+)>') +THIRD_PARTY_SEARCH_STRING = 'third_party' + os.sep +OWNERS_FILENAME = 'OWNERS' -def GetOwnerForFuzzer(sources): - """Return owner given a list of sources as input.""" + +def GetAuthorFromGitBlame(blame_output): + """Return author from git blame output.""" + for line in blame_output.splitlines(): + m = AUTHOR_REGEX.match(line) + if m: + return m.group(1) + + return None + + +def GetOwnersIfThirdParty(source): + """Return owners using OWNERS file if in third_party.""" + match_index = source.find(THIRD_PARTY_SEARCH_STRING) + if match_index == -1: + # Not in third_party, skip. + return None + + match_index_with_library = source.find( + os.sep, match_index + len(THIRD_PARTY_SEARCH_STRING)) + if match_index_with_library == -1: + # Unable to determine library name, skip. + return None + + owners_file_path = os.path.join(source[:match_index_with_library], + OWNERS_FILENAME) + if not os.path.exists(owners_file_path): + return None + + return open(owners_file_path).read() + + +def GetOwnersForFuzzer(sources): + """Return owners given a list of sources as input.""" for source in sources: if not os.path.exists(source): continue + with open(source, 'r') as source_file_handle: source_content = source_file_handle.read() - if ('PROTO_FUZZER' in source_content or - 'LLVMFuzzerTestOneInput' in source_content): + + if ('LLVMFuzzerTestOneInput' in source_content or + 'PROTO_FUZZER' in source_content): # Found the fuzzer source (and not dependency of fuzzer). - return subprocess.check_output( - ['git', 'log', '--reverse', '--format=%ae', '-1', source]) + + is_git_file = bool(subprocess.check_output(['git', 'ls-files', source])) + if not is_git_file: + # File is not in working tree. Return owners for third_party. + return GetOwnersIfThirdParty(source) + + # git log --follow and --reverse don't work together and using just + # --follow is too slow. Make a best estimate with an assumption that + # the original author has authored line 1 which is usually the + # copyright line and does not change even with file rename / move. + blame_output = subprocess.check_output( + ['git', 'blame', '--porcelain', '-L1,1', source]) + return GetAuthorFromGitBlame(blame_output) return None def main(): - parser = argparse.ArgumentParser(description='Generate fuzzer owners.') + parser = argparse.ArgumentParser(description='Generate fuzzer owners file.') parser.add_argument('--owners', required=True) parser.add_argument('--sources', nargs='+') args = parser.parse_args() - owner = GetOwnerForFuzzer(args.sources) + owners = GetOwnersForFuzzer(args.sources) # Generate owners file. with open(args.owners, 'w') as owners_file: # If we found an owner, then write it to file. # Otherwise, leave empty file to keep ninja happy. - if owner: - owners_file.write(owner) + if owners: + owners_file.write(owners) if __name__ == '__main__':
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 24eb0096..33b943e 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1900,6 +1900,25 @@ ] } ], + "MseBufferByPts": [ + { + "platforms": [ + "android", + "chromeos", + "linux", + "mac", + "win" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "MseBufferByPts" + ] + } + ] + } + ], "NTPBreakingNewsPush": [ { "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore index 4ddbdc9..5e05272 100644 --- a/third_party/.gitignore +++ b/third_party/.gitignore
@@ -24,6 +24,7 @@ /apache-win32/modules/*.so /apache-win32/modules/*.dll /apk-patch-size-estimator/lib/ +/arcore-android-sdk /asan /auto/src /bazel/desugar/Desugar.jar
diff --git a/third_party/WebKit/.gitattributes b/third_party/WebKit/.gitattributes deleted file mode 100644 index 50bd5ebb..0000000 --- a/third_party/WebKit/.gitattributes +++ /dev/null
@@ -1,3 +0,0 @@ -# For best performance, place rules for deep paths in the parent directory. -# For example: instead of "Source/WebCore/inspector/Inspector.json -crlf" here, -# place "Inspector.json -crlf" in Source/WebCore/inspector/.gitattributes
diff --git a/third_party/WebKit/.gitignore b/third_party/WebKit/.gitignore deleted file mode 100644 index 6398492..0000000 --- a/third_party/WebKit/.gitignore +++ /dev/null
@@ -1,39 +0,0 @@ -*.Makefile -*.mk -*.mode* -*.pbxuser -*.perspective* -*.pyc -.DS_Store -.directory -autoinstall.cache.d - -# Ignore auto-generated files by VS2005 and VS2010. -*.ncb -*.opensdf -*.props -*.sdf -*.sln -*.suo -*.targets -*.vcproj -*.vcproj.*.user -*.vcxproj.user -*.vcxproj -*.vcxproj.filters -Source/core/make_core_generated.xml - -# Ignore Eclipse project files -.project -.cproject -/.settings - -# Ignore common tool auto-generated files. -.gdb_history -tags -*~ -.*.sw[a-p] - -# For best performance, place rules for deep paths in the parent directory. -# For example: instead of placing Source/WebKit/chromium/WebKit.xcodeproj here, -# place WebKit.xcodeproj in Source/WebKit/chromium/.gitignore
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=RootLayerScrolling b/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=RootLayerScrolling index 8084c8a..864042c4 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=RootLayerScrolling +++ b/third_party/WebKit/LayoutTests/FlagExpectations/disable-blink-features=RootLayerScrolling
@@ -2985,6 +2985,7 @@ crbug.com/773122 [ Win7 ] virtual/gpu/fast/canvas/canvas-lost-gpu-context.html [ Failure Pass ] # Sheriff failures 2017-10-13 +crbug.com/774437 paint/invalidation/selection/selection-partial-invalidation-between-blocks.html [ Pass Failure ] crbug.com/774463 [ Win7 Debug ] fast/events/autoscroll-should-not-stop-on-keypress.html [ Failure Pass ] crbug.com/774463 [ Win7 Debug ] virtual/mouseevent_fractional/fast/events/autoscroll-should-not-stop-on-keypress.html [ Failure Pass ] crbug.com/774688 [ Mac ] fast/spatial-navigation/snav-container-white-space.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG index aef755b..92f1ede39 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -99,7 +99,7 @@ crbug.com/591099 animations/interpolation/backdrop-filter-interpolation.html [ Timeout ] crbug.com/591099 animations/interpolation/line-height-interpolation.html [ Pass Timeout ] crbug.com/591099 animations/interpolation/svg-stroke-dasharray-interpolation.html [ Timeout ] -crbug.com/591099 animations/interpolation/webkit-clip-path-interpolation.html [ Pass Timeout ] +crbug.com/591099 animations/interpolation/webkit-clip-path-interpolation.html [ Timeout ] crbug.com/591099 animations/rotate-transform-equivalent.html [ Failure ] crbug.com/591099 animations/timing/timing-model.html [ Timeout ] crbug.com/714962 compositing/background-color/view-blending-base-background.html [ Failure ] @@ -117,12 +117,9 @@ crbug.com/591099 compositing/overflow/border-radius-above-composited-subframe.html [ Failure ] crbug.com/591099 compositing/overflow/nested-border-radius-clipping.html [ Failure ] crbug.com/591099 compositing/overflow/overflow-compositing-descendant.html [ Failure ] -crbug.com/591099 compositing/overflow/overflow-scroll-with-local-image-background.html [ Failure ] crbug.com/591099 compositing/overflow/scroll-ancestor-update.html [ Failure ] -crbug.com/591099 compositing/overflow/scrolling-content-clip-to-viewport.html [ Failure ] crbug.com/591099 compositing/overflow/universal-accelerated-overflow-scroll.html [ Pass Timeout ] crbug.com/591099 compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers.html [ Failure ] -crbug.com/591099 compositing/rtl/rtl-and-writing-mode-scrolling.html [ Failure ] crbug.com/591099 compositing/self-painting-layers.html [ Failure ] crbug.com/591099 compositing/squashing/add-remove-squashed-layers.html [ Failure ] crbug.com/591099 compositing/squashing/selection-repaint-with-gaps.html [ Failure ] @@ -140,7 +137,7 @@ crbug.com/591099 css2.1/t100801-c544-valgn-00-a-ag.html [ Failure ] crbug.com/591099 css2.1/t100801-c544-valgn-03-d-agi.html [ Failure ] crbug.com/591099 css2.1/t1202-counter-04-b.html [ Failure ] -crbug.com/591099 css2.1/t1202-counter-09-b.html [ Failure ] +crbug.com/591099 css2.1/t1202-counter-09-b.html [ Failure Pass ] crbug.com/591099 css2.1/t1202-counters-04-b.html [ Failure ] crbug.com/591099 css2.1/t1205-c564-list-img-00-b-g.html [ Failure ] crbug.com/591099 css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ] @@ -148,7 +145,6 @@ crbug.com/591099 css3/filters/effect-brightness.html [ Failure ] crbug.com/714962 css3/filters/effect-reference-zoom-hw.html [ Failure ] crbug.com/591099 css3/filters/multiple-references-id-mutate-crash-2.html [ Crash ] -crbug.com/591099 css3/flexbox/child-overflow.html [ Failure ] crbug.com/591099 css3/flexbox/flex-flow-border.html [ Failure ] crbug.com/591099 css3/flexbox/flex-flow-margins-auto-size.html [ Failure ] crbug.com/591099 css3/flexbox/flex-flow-padding.html [ Failure ] @@ -228,7 +224,7 @@ crbug.com/591099 editing/selection/selection-button-text.html [ Failure ] crbug.com/591099 editing/selection/shift-click.html [ Failure ] crbug.com/591099 editing/selection/skip-over-contenteditable.html [ Failure ] -crbug.com/591099 editing/selection/subpixel-positioned-selection.html [ Failure ] +crbug.com/591099 editing/selection/subpixel-positioned-selection.html [ Failure Pass ] crbug.com/591099 editing/selection/transformed-selection-rects.html [ Failure ] crbug.com/591099 editing/text-iterator/findString.html [ Timeout ] crbug.com/591099 external/wpt/2dcontext/drawing-images-to-the-canvas/drawimage_html_image_11.html [ Pass ] @@ -258,12 +254,8 @@ crbug.com/591099 external/wpt/acid/acid3/test.html [ Crash ] crbug.com/591099 external/wpt/compat/webkit-text-fill-color-property-005.html [ Pass ] crbug.com/591099 external/wpt/content-security-policy/inside-worker/dedicated-inheritance.html [ Failure ] -crbug.com/591099 external/wpt/content-security-policy/reporting/report-original-url.sub.html [ Pass Timeout ] -crbug.com/591099 external/wpt/content-security-policy/style-src/inline-style-attribute-blocked.sub.html [ Failure Pass ] crbug.com/591099 external/wpt/credential-management/federatedcredential-framed-get.sub.https.html [ Pass ] crbug.com/591099 external/wpt/credential-management/passwordcredential-framed-get.sub.https.html [ Pass ] -crbug.com/591099 external/wpt/css/CSS2/floats/floats-rule3-outside-left-002.xht [ Pass ] -crbug.com/591099 external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-001r.xht [ Failure Pass ] crbug.com/714962 external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Failure ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001e.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-insert-001h.xht [ Pass ] @@ -271,34 +263,25 @@ crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-nested-002.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-percents-001.xht [ Failure ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-remove-006.xht [ Pass ] -crbug.com/591099 external/wpt/css/CSS2/normal-flow/max-width-applies-to-006.xht [ Pass ] crbug.com/591099 external/wpt/css/CSS2/normal-flow/root-box-001.xht [ Failure ] crbug.com/591099 external/wpt/css/CSS2/text/white-space-mixed-003.xht [ Pass ] crbug.com/714962 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ] crbug.com/714962 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ] crbug.com/591099 external/wpt/css/css-backgrounds/box-shadow-syntax-001.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-cascade/important-prop.html [ Pass ] -crbug.com/591099 external/wpt/css/css-color/t422-rgba-onscreen-multiple-boxes-c.xht [ Pass ] crbug.com/591099 external/wpt/css/css-display/display-contents-details.html [ Crash ] crbug.com/591099 external/wpt/css/css-display/display-contents-dynamic-list-001-inline.html [ Failure ] crbug.com/591099 external/wpt/css/css-display/display-contents-dynamic-list-001-none.html [ Failure ] crbug.com/591099 external/wpt/css/css-display/display-contents-dynamic-table-001-inline.html [ Failure ] crbug.com/591099 external/wpt/css/css-display/display-contents-list-001.html [ Failure ] -crbug.com/591099 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-006.xht [ Pass ] crbug.com/591099 external/wpt/css/css-flexbox/percentage-heights-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-flexbox/percentage-heights-003.html [ Pass ] -crbug.com/591099 external/wpt/css/css-fonts/alternates-order.html [ Pass ] crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-1.html [ Pass ] crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-3.html [ Pass ] -crbug.com/591099 external/wpt/css/css-fonts/font-variant-alternates-12.html [ Pass ] -crbug.com/591099 external/wpt/css/css-fonts/font-variant-alternates-13.html [ Pass ] crbug.com/591099 external/wpt/css/css-fonts/font-variant-ligatures-11.html [ Pass ] crbug.com/591099 external/wpt/css/css-fonts/matching/fixed-stretch-style-over-weight.html [ Pass ] crbug.com/591099 external/wpt/css/css-fonts/matching/stretch-distance-over-weight-distance.html [ Pass ] crbug.com/591099 external/wpt/css/css-fonts/matching/style-ranges-over-weight-direction.html [ Pass ] crbug.com/591099 external/wpt/css/css-fonts/test_font_family_parsing.html [ Timeout ] -crbug.com/591099 external/wpt/css/css-fonts/test_font_feature_values_parsing.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-fonts/variations/font-stretch.html [ Failure Pass ] crbug.com/591099 external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-009.html [ Failure ] crbug.com/591099 external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-010.html [ Failure ] crbug.com/591099 external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-011.html [ Failure ] @@ -308,19 +291,7 @@ crbug.com/591099 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-010.html [ Failure ] crbug.com/591099 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-011.html [ Failure ] crbug.com/591099 external/wpt/css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-012.html [ Failure ] -crbug.com/591099 external/wpt/css/css-layout-api/layout-child-fixed.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-layout-api/perform-child-layout-fixed-inline-size-vrl.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-layout-api/position-fragment-vrl-ltr.https.html [ Failure Pass ] -crbug.com/824918 external/wpt/css/css-multicol/multicol-rule-dotted-000.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-multicol/multicol-span-float-001.xht [ Pass ] -crbug.com/591099 external/wpt/css/css-paint-api/background-image-multiple.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-paint-api/geometry-background-image-001.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-paint-api/geometry-border-image-002.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-paint-api/paint2d-rects.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-position/position-relative-table-tbody-top.html [ Pass ] -crbug.com/591099 external/wpt/css/css-position/position-relative-table-tfoot-left-absolute-child.html [ Pass ] -crbug.com/591099 external/wpt/css/css-position/position-relative-table-thead-left-absolute-child.html [ Pass ] -crbug.com/591099 external/wpt/css/css-position/position-sticky-overflow-padding.html [ Failure ] +crbug.com/824918 external/wpt/css/css-multicol/multicol-rule-dotted-000.xht [ Failure Pass ] crbug.com/591099 external/wpt/css/css-position/position-sticky-writing-modes.html [ Failure ] crbug.com/591099 external/wpt/css/css-rhythm/ [ Skip ] crbug.com/591099 external/wpt/css/css-scroll-anchoring/clipped-scrollers-skipped.html [ Failure ] @@ -330,15 +301,14 @@ crbug.com/714962 external/wpt/css/css-scroll-anchoring/wrapped-text.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-002.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-003.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-004.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-006.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-004.html [ Failure ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-006.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-007.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-box/shape-outside-box-008.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html [ Pass ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-002.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-003.html [ Failure ] @@ -365,10 +335,10 @@ crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-021.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-022.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-023.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-025.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-025.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-013.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-014.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-015.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-015.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-016.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-017.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-018.html [ Failure ] @@ -389,7 +359,7 @@ crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-015.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-016.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-017.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-018.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-018.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-019.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-020.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-021.html [ Failure ] @@ -405,9 +375,11 @@ crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-013.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-014.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-015.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-007.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html [ Failure ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html [ Failure ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-007.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-008.html [ Failure ] -crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-009.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-009.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-010.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-011.html [ Failure ] crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-012.html [ Failure ] @@ -440,16 +412,14 @@ crbug.com/591099 external/wpt/css/css-shapes/spec-examples/shape-outside-019.html [ Failure ] crbug.com/591099 external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-sizing/intrinsic-percent-non-replaced-003.html [ Failure ] -crbug.com/591099 external/wpt/css/css-tables/anonymous-table-ws-001.html [ Pass ] crbug.com/591099 external/wpt/css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html [ Failure ] crbug.com/591099 external/wpt/css/css-tables/height-distribution/td-different-subpixel-padding-in-same-row.html [ Failure ] -crbug.com/591099 external/wpt/css/css-tables/html5-table-formatting-fixed-layout-1.html [ Failure Pass ] crbug.com/591099 external/wpt/css/css-tables/table-model-fixup-2.html [ Failure ] crbug.com/591099 external/wpt/css/css-tables/width-distribution/td-with-subpixel-padding-vertical-rl.html [ Failure ] crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-color-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-left-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-left-002.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-right-001.xht [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-right-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-right-002.xht [ Failure ] crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-below-left-001.xht [ Failure ] crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-below-left-002.xht [ Failure ] @@ -470,7 +440,6 @@ crbug.com/591099 external/wpt/css/css-text/i18n/css3-text-line-break-jazh-142.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/i18n/css3-text-line-break-opclns-255.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/i18n/css3-text-line-break-opclns-259.html [ Pass ] -crbug.com/591099 external/wpt/css/css-text/letter-spacing/letter-spacing-control-chars-001.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-009.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-011.html [ Pass ] crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-002.html [ Pass ] @@ -487,23 +456,12 @@ crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-012.html [ Failure ] crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-016.html [ Failure ] crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-017.html [ Failure ] -crbug.com/591099 external/wpt/css/css-text/white-space/white-space-collapse-002.html [ Failure Pass ] crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Pass ] crbug.com/714962 external/wpt/css/css-transforms/transform-abspos-006.html [ Failure ] crbug.com/714962 external/wpt/css/css-transforms/transform-abspos-007.html [ Failure ] -crbug.com/591099 external/wpt/css/css-transforms/transform-display-003.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-transforms/transform-matrix-008.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-transforms/transform-origin-002.html [ Failure Pass ] -crbug.com/714962 external/wpt/css/css-transforms/transform-origin-006.html [ Failure ] -crbug.com/591099 external/wpt/css/css-transforms/transform-rotate-003.html [ Failure Pass ] +crbug.com/714962 external/wpt/css/css-transforms/transform-origin-006.html [ Failure Pass ] crbug.com/591099 external/wpt/css/css-transforms/transform-transformed-tr-percent-height-child.html [ Failure ] crbug.com/591099 external/wpt/css/css-transforms/transform3d-perspective-008.html [ Pass ] -crbug.com/591099 external/wpt/css/css-transforms/transform3d-rotatex-perspective-002.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-transitions/events-004.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/numeric-factory.tentative.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-typed-om/the-stylepropertymap/properties/quotes.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-typed-om/the-stylepropertymap/properties/stroke-opacity.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-typed-om/the-stylepropertymap/properties/text-justify.html [ Failure Pass ] crbug.com/591099 external/wpt/css/css-ui/text-overflow-010.html [ Pass ] crbug.com/591099 external/wpt/css/css-ui/text-overflow-012.html [ Failure ] crbug.com/591099 external/wpt/css/css-ui/text-overflow-013.html [ Failure ] @@ -511,8 +469,7 @@ crbug.com/591099 external/wpt/css/css-ui/text-overflow-015.html [ Failure ] crbug.com/591099 external/wpt/css/css-ui/text-overflow-022.html [ Failure ] crbug.com/591099 external/wpt/css/css-ui/text-overflow-027.html [ Failure ] -crbug.com/591099 external/wpt/css/css-ui/text-overflow-029.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-variables/variable-definition.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-ui/text-overflow-029.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-icb-vlr-003.xht [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-icb-vlr-005.xht [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-icb-vlr-011.xht [ Pass ] @@ -590,9 +547,8 @@ crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-220.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-224.xht [ Pass ] crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-228.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-writing-modes/available-size-001.html [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-writing-modes/available-size-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/available-size-002.html [ Pass ] -crbug.com/591099 external/wpt/css/css-writing-modes/available-size-005.html [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/available-size-010.html [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/available-size-011.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/available-size-012.html [ Failure ] @@ -601,15 +557,14 @@ crbug.com/591099 external/wpt/css/css-writing-modes/baseline-inline-non-replaced-004.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/block-flow-direction-vrl-009.xht [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/block-flow-direction-vrl-026.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/clearance-calculations-vrl-002.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/clearance-calculations-vrl-004.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/clearance-calculations-vrl-006.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/float-contiguous-vrl-002.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/float-contiguous-vrl-004.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/float-contiguous-vrl-006.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-writing-modes/float-contiguous-vrl-008.xht [ Failure Pass ] -crbug.com/591099 external/wpt/css/css-writing-modes/float-vlr-013.xht [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-writing-modes/float-contiguous-vrl-008.xht [ Failure ] +crbug.com/591099 external/wpt/css/css-writing-modes/float-vlr-013.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/float-vrl-002.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/float-vrl-004.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/float-vrl-006.xht [ Failure ] @@ -620,7 +575,7 @@ crbug.com/591099 external/wpt/css/css-writing-modes/margin-collapse-vrl-010.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/mongolian-orientation-002.html [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/ortho-htb-alongside-vrl-floats-002.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-writing-modes/ortho-htb-alongside-vrl-floats-010.xht [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-writing-modes/ortho-htb-alongside-vrl-floats-010.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-001a.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-001c.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-001e.html [ Failure ] @@ -639,9 +594,8 @@ crbug.com/591099 external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-001x.html [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/percent-padding-vrl-006.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vlr-001.xht [ Pass ] -crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-001.xht [ Pass ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-007.xht [ Failure ] -crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-008.xht [ Failure Pass ] +crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-008.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-009.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-011.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-012.xht [ Failure ] @@ -651,17 +605,16 @@ crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-023.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-024.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vrl-003.xht [ Failure ] -crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vrl-004.xht [ Failure Pass ] +crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vrl-004.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vrl-007.xht [ Failure ] crbug.com/714962 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vrl-008.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-vrl-in-htb-001.xht [ Pass ] crbug.com/591099 external/wpt/css/css-writing-modes/sizing-orthog-vrl-in-htb-013.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/text-baseline-vlr-007.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/text-baseline-vrl-006.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/text-combine-upright-decorations-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/text-combine-upright-layout-rules-001.html [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/text-orientation-016.xht [ Failure ] -crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-005.xht [ Failure Pass ] +crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-005.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-007.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-vlr-023.xht [ Failure ] crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-vlr-025.xht [ Failure ] @@ -673,17 +626,10 @@ crbug.com/591099 external/wpt/css/cssom-view/elementFromPoint-002.html [ Failure ] crbug.com/591099 external/wpt/css/cssom-view/elementFromPoint-003.html [ Failure ] crbug.com/591099 external/wpt/css/cssom/interfaces.html [ Pass Timeout ] -crbug.com/591099 external/wpt/css/cssom/serialize-variable-reference.html [ Failure Pass ] -crbug.com/591099 external/wpt/css/geometry/DOMPoint-001.html [ Failure Pass ] crbug.com/591099 external/wpt/css/geometry/interfaces.worker.html [ Pass ] crbug.com/591099 external/wpt/css/selectors/focus-within-004.html [ Pass ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/descriptor-suffix.html [ Pass ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-alphabetic.html [ Pass ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-cyclic.html [ Pass ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml [ Failure Pass ] +crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001b.html [ Pass ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html [ Pass ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-definite-sizes-002.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-definite-sizes-004.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass ] @@ -692,14 +638,11 @@ crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015.html [ Failure ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/emptyspan-1.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/percent-height-1.html [ Failure ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/remove-from-split-inline-6.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/split-inner-inline-2.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-break-inside-001.html [ Failure ] -crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-003.html [ Pass ] crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-007.html [ Failure ] -crbug.com/591099 external/wpt/dom/historical.html [ Failure Pass ] crbug.com/591099 external/wpt/dom/nodes/Element-classlist.html [ Timeout ] crbug.com/591099 external/wpt/dom/nodes/Element-matches.html [ Timeout ] crbug.com/591099 external/wpt/dom/nodes/Element-webkitMatchesSelector.html [ Timeout ] @@ -723,9 +666,9 @@ crbug.com/591099 external/wpt/editing/run/forecolor.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/formatblock.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/forwarddelete.html [ Timeout ] -crbug.com/591099 external/wpt/editing/run/hilitecolor.html [ Failure Timeout ] +crbug.com/591099 external/wpt/editing/run/hilitecolor.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/indent.html [ Timeout ] -crbug.com/591099 external/wpt/editing/run/inserthorizontalrule.html [ Failure Timeout ] +crbug.com/591099 external/wpt/editing/run/inserthorizontalrule.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/inserthtml.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/insertlinebreak.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/insertorderedlist.html [ Timeout ] @@ -734,7 +677,7 @@ crbug.com/591099 external/wpt/editing/run/insertunorderedlist.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/italic.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/justifycenter.html [ Timeout ] -crbug.com/591099 external/wpt/editing/run/justifyfull.html [ Failure Timeout ] +crbug.com/591099 external/wpt/editing/run/justifyfull.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/justifyleft.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/justifyright.html [ Timeout ] crbug.com/591099 external/wpt/editing/run/multitest.html [ Timeout ] @@ -837,25 +780,17 @@ crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-misc.html [ Timeout ] crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-href.html [ Timeout ] crbug.com/591099 external/wpt/encoding/textdecoder-fatal-single-byte.html [ Timeout ] -crbug.com/591099 external/wpt/fetch/api/abort/general.any.html [ Failure Pass ] -crbug.com/591099 external/wpt/fetch/api/response/response-idl.html [ Failure Pass ] +crbug.com/591099 external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Pass ] +crbug.com/591099 external/wpt/fetch/corb/preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html [ Pass ] +crbug.com/591099 external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Pass ] crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ] crbug.com/591099 external/wpt/html-media-capture/capture_audio_cancel-manual.html [ Failure ] crbug.com/591099 external/wpt/html-media-capture/capture_image_cancel-manual.html [ Failure ] crbug.com/591099 external/wpt/html-media-capture/capture_video_cancel-manual.html [ Failure ] -crbug.com/591099 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/012.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/browsing-the-web/unloading-documents/navigation-within-beforeunload.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/history/the-history-interface/traverse_the_history_3.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_3.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/browsers/the-window-object/window-indexed-properties-strict.html [ Failure Pass ] crbug.com/591099 external/wpt/html/browsers/windows/noreferrer-window-name.html [ Timeout ] crbug.com/591099 external/wpt/html/dom/documents/dom-tree-accessors/Document.currentScript.html [ Pass ] crbug.com/591099 external/wpt/html/dom/elements/the-innertext-idl-attribute/getter.html [ Failure ] crbug.com/591099 external/wpt/html/dom/interfaces.https.html [ Timeout ] -crbug.com/591099 external/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html [ Failure Pass ] crbug.com/591099 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html [ Timeout ] crbug.com/591099 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html [ Timeout ] crbug.com/591099 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html [ Timeout ] @@ -866,40 +801,22 @@ crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-auto.html [ Failure ] crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-embedded-sizing/svg-in-img-percentage.html [ Failure ] crbug.com/591099 external/wpt/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html [ Timeout ] -crbug.com/591099 external/wpt/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-pause.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-control.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-order.html [ Failure Pass ] crbug.com/591099 external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-non-snap-to-lines.html [ Failure ] -crbug.com/591099 external/wpt/html/semantics/embedded-content/the-img-element/current-pixel-density/basic.html [ Failure Pass ] crbug.com/591099 external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html [ Failure ] crbug.com/591099 external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html [ Failure ] crbug.com/591099 external/wpt/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html [ Failure ] crbug.com/591099 external/wpt/html/semantics/interactive-elements/the-dialog-element/centering.html [ Crash ] -crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/116.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/execution-timing/148.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/module/referrer-same-origin.sub.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/script-type-and-language-empty.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/semantics/selectors/pseudo-classes/link.html [ Failure Pass ] -crbug.com/591099 external/wpt/html/syntax/parsing/html5lib_tests11.html?run_type=write [ Failure Pass ] -crbug.com/591099 external/wpt/html/syntax/parsing/html5lib_tests11.html?run_type=write_single [ Pass ] crbug.com/591099 external/wpt/html/syntax/parsing/named-character-references.html [ Timeout ] -crbug.com/591099 external/wpt/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-1.htm [ Timeout ] +crbug.com/591099 external/wpt/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-1.htm [ Pass Timeout ] crbug.com/591099 external/wpt/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-4.htm [ Pass Timeout ] crbug.com/591099 external/wpt/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-7.htm [ Pass Timeout ] -crbug.com/591099 external/wpt/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html [ Failure Pass ] crbug.com/591099 external/wpt/http/basic-auth-cache-test.html [ Timeout ] crbug.com/591099 external/wpt/longtask-timing/longtask-in-sibling-iframe.html [ Pass Timeout ] -crbug.com/591099 external/wpt/media-capabilities/idlharness.html [ Failure Pass ] crbug.com/591099 external/wpt/media-source/mediasource-getvideoplaybackquality.html [ Timeout ] -crbug.com/591099 external/wpt/mimesniff/mime-types/parsing.any.html [ Failure Timeout ] -crbug.com/591099 external/wpt/mimesniff/mime-types/parsing.any.worker.html [ Failure Timeout ] -crbug.com/591099 external/wpt/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html [ Failure Pass ] -crbug.com/591099 external/wpt/notifications/event-onshow.html [ Failure Pass ] -crbug.com/591099 external/wpt/notifications/lang.html [ Failure Pass ] +crbug.com/591099 external/wpt/mimesniff/mime-types/parsing.any.html [ Timeout ] +crbug.com/591099 external/wpt/mimesniff/mime-types/parsing.any.worker.html [ Timeout ] crbug.com/591099 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.nocontext.worker.html [ Crash ] crbug.com/591099 external/wpt/offscreen-canvas/the-offscreen-canvas/offscreencanvas.getcontext.worker.html [ Pass ] -crbug.com/591099 external/wpt/offscreen-canvas/the-offscreen-canvas/size.attributes.parse.trailingjunk.html [ Failure Pass ] crbug.com/591099 external/wpt/payment-request/payment-allowed-by-feature-policy.https.sub.html [ Pass ] crbug.com/591099 external/wpt/payment-request/payment-disabled-by-feature-policy.https.sub.html [ Pass ] crbug.com/591099 external/wpt/pointerevents/pointerevent_attributes_hoverable_pointers-manual.html [ Timeout ] @@ -931,67 +848,39 @@ crbug.com/591099 external/wpt/selection/extend-20.html [ Timeout ] crbug.com/591099 external/wpt/selection/selectAllChildren.html [ Timeout ] crbug.com/591099 external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html [ Failure ] -crbug.com/591099 external/wpt/service-workers/service-worker/worker-interception-redirect.https.html [ Pass ] crbug.com/591099 external/wpt/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html [ Failure ] -crbug.com/591099 external/wpt/streams/readable-byte-streams/construct-byob-request.dedicatedworker.html [ Failure Pass ] crbug.com/591099 external/wpt/svg/interfaces.html [ Timeout ] crbug.com/591099 external/wpt/svg/linking/reftests/href-filter-element.html [ Failure ] -crbug.com/591099 external/wpt/svg/path/bearing/relative.svg [ Pass ] -crbug.com/591099 external/wpt/url/failure.html [ Failure Pass ] crbug.com/591099 external/wpt/url/url-setters.html [ Timeout ] crbug.com/591099 external/wpt/wasm/wasm_local_iframe_test.html [ Failure ] crbug.com/591099 external/wpt/web-animations/animation-model/animation-types/interpolation-per-property.html [ Timeout ] -crbug.com/591099 external/wpt/web-animations/interfaces/Animation/finished.html [ Failure Pass ] -crbug.com/591099 external/wpt/web-animations/timing-model/animations/reversing-an-animation.html [ Failure Pass ] crbug.com/591099 external/wpt/webaudio/idlharness.https.html [ Timeout ] crbug.com/591099 external/wpt/webmessaging/broadcastchannel/sandbox.html [ Failure ] -crbug.com/591099 external/wpt/webrtc/RTCConfiguration-bundlePolicy.html [ Failure Pass ] -crbug.com/591099 external/wpt/webrtc/RTCPeerConnection-constructor.html [ Failure Pass ] -crbug.com/591099 external/wpt/webrtc/RTCPeerConnection-createAnswer.html [ Failure Pass ] -crbug.com/591099 external/wpt/webrtc/RTCPeerConnection-createDataChannel.html [ Failure Pass ] crbug.com/591099 external/wpt/webrtc/interfaces.html [ Pass Timeout ] -crbug.com/591099 external/wpt/websockets/interfaces/WebSocket/close/close-nested.html?wss [ Failure Pass ] crbug.com/591099 external/wpt/webstorage/storage_setitem.html [ Pass Timeout ] crbug.com/591099 external/wpt/webvr/idlharness.https.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_completely_move_up.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/2_cues_overlapping_partially_move_up.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/bidi_ruby.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/cue_too_long.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/evil/9_cues_overlapping_completely.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/evil/9_cues_overlapping_completely_all_cues_have_same_timestamp.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/line_1_wrapped_cue_grow_downwards.html [ Pass ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/line_percent_and_integer_mixed_overlap.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/font_shorthand.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/bold_object/bold_background_shorthand.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/bold_object/bold_font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/bold_object/bold_font_shorthand.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/bold_object/bold_outline_properties.html [ Pass ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/bold_object/bold_text-shadow.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_font_shorthand.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_with_class_object_specific_selector.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/font_shorthand.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/italic_object/italic_animation_with_timestamp.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/italic_object/italic_font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/italic_object/italic_font_shorthand.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/text-decoration_overline.html [ Pass ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/underline_object/underline_font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/underline_object/underline_font_shorthand.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_font_properties.html [ Failure ] crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_font_shorthand.html [ Failure ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_outline_shorthand.html [ Pass ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_white-space_nowrap.html [ Pass ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/voice_object/voice_white-space_pre-wrap_wrapped.html [ Pass ] -crbug.com/591099 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/white-space_pre-line_wrapped.html [ Pass ] crbug.com/591099 external/wpt/workers/Worker_terminate_event_queue.htm [ Timeout ] -crbug.com/591099 external/wpt/workers/constructors/SharedWorker/null-arguments.html [ Failure Pass ] -crbug.com/591099 external/wpt/workers/interfaces.worker.html [ Failure Pass ] crbug.com/591099 external/wpt/xhr/send-authentication-prompt-2-manual.htm [ Failure ] -crbug.com/591099 external/wpt/xhr/send-content-type-charset.htm [ Failure Pass ] -crbug.com/591099 fast/animation/scroll-animations/scrolltimeline-currenttime.html [ Failure ] +crbug.com/591099 external/wpt/xhr/send-content-type-charset.htm [ Failure ] crbug.com/591099 fast/backgrounds/background-clip-text.html [ Failure ] crbug.com/591099 fast/backgrounds/background-leakage-transforms.html [ Failure ] crbug.com/591099 fast/backgrounds/border-radius-split-background-image.html [ Failure ] @@ -1023,7 +912,7 @@ crbug.com/591099 fast/block/margin-collapse/webkit-margin-collapse-siblings.html [ Failure ] crbug.com/591099 fast/block/over-constrained-auto-margin.html [ Failure ] crbug.com/591099 fast/block/positioning/abspos-auto-left-and-width-change-parent-margin-left.html [ Crash ] -crbug.com/591099 fast/block/positioning/auto/vertical-rl/007.html [ Failure ] +crbug.com/591099 fast/block/positioning/auto/vertical-rl/007.html [ Failure Pass ] crbug.com/591099 fast/block/positioning/child-of-fixed-pos-after-movement.html [ Crash ] crbug.com/591099 fast/block/positioning/complex-positioned-movement-inline-ancestor.html [ Failure ] crbug.com/591099 fast/block/positioning/differing-writing-modes-replaced.html [ Failure ] @@ -1112,7 +1001,7 @@ crbug.com/591099 fast/css/bidi-override-in-anonymous-block.html [ Failure ] crbug.com/591099 fast/css/case-transform.html [ Failure ] crbug.com/591099 fast/css/containment/size-and-layout-containment.html [ Failure ] -crbug.com/591099 fast/css/content-counter-010.htm [ Failure ] +crbug.com/591099 fast/css/content-counter-010.htm [ Failure Pass ] crbug.com/591099 fast/css/css-properties-position-relative-as-parent-fixed.html [ Failure ] crbug.com/714962 fast/css/first-letter-before-hit-test.html [ Failure ] crbug.com/714962 fast/css/first-letter-hit-test.html [ Failure ] @@ -1130,13 +1019,12 @@ crbug.com/591099 fast/css/getComputedStyle/getComputedStyle-margin-percentage.html [ Failure ] crbug.com/591099 fast/css/getComputedStyle/getComputedStyle-resolved-values.html [ Timeout ] crbug.com/714962 fast/css/hover-pseudo-element-quirks.html [ Failure ] -crbug.com/591099 fast/css/import_with_baseurl.html [ Failure Pass ] +crbug.com/591099 fast/css/import_with_baseurl.html [ Failure ] crbug.com/591099 fast/css/large-numbers.html [ Timeout ] crbug.com/591099 fast/css/margin-top-bottom-dynamic.html [ Failure ] crbug.com/591099 fast/css/negative-text-indent-in-inline-block.html [ Failure ] crbug.com/591099 fast/css/outline-narrowLine.html [ Failure ] crbug.com/714962 fast/css/outline-small-visual-overflow.html [ Failure ] -crbug.com/591099 fast/css/overflow-rtl-border-after.html [ Failure ] crbug.com/591099 fast/css/percent-min-width-img-src-change.html [ Failure ] crbug.com/591099 fast/css/percent-width-img-src-change.html [ Failure ] crbug.com/591099 fast/css/resize-corner-tracking.html [ Failure ] @@ -1218,7 +1106,6 @@ crbug.com/591099 fast/events/pointerevents/touch-capture.html [ Timeout ] crbug.com/591099 fast/events/select-element.html [ Timeout ] crbug.com/591099 fast/events/sequential-focus-navigation-starting-point.html [ Failure ] -crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-scroll.html [ Failure ] crbug.com/591099 fast/events/touch/compositor-touch-hit-rects.html [ Failure ] crbug.com/591099 fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ] crbug.com/591099 fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Pass ] @@ -1299,7 +1186,7 @@ crbug.com/824918 fast/multicol/inline-block-baseline.html [ Failure ] crbug.com/824918 fast/multicol/nested-one-line-in-inner.html [ Failure ] crbug.com/824918 fast/multicol/newmulticol/list-item.html [ Failure ] -crbug.com/591099 fast/multicol/positive-leading.html [ Failure ] +crbug.com/591099 fast/multicol/positive-leading.html [ Failure Pass ] crbug.com/824918 fast/multicol/span/overflow-on-multicol.html [ Failure ] crbug.com/824918 fast/multicol/span/summary-split.html [ Failure ] crbug.com/824918 fast/multicol/span/vertical-rl.html [ Failure ] @@ -1315,8 +1202,6 @@ crbug.com/824918 fast/multicol/vertical-rl/float-edge.html [ Failure ] crbug.com/824918 fast/multicol/vertical-rl/float-paginate.html [ Failure ] crbug.com/824918 fast/multicol/vertical-rl/unsplittable-inline-block.html [ Failure ] -crbug.com/591099 fast/overflow/007.html [ Failure ] -crbug.com/591099 fast/overflow/height-during-simplified-layout.html [ Failure ] crbug.com/591099 fast/overflow/image-selection-highlight.html [ Failure ] crbug.com/714962 fast/overflow/line-clamp-hides-trailing-anchor.html [ Failure ] crbug.com/591099 fast/overflow/line-clamp.html [ Failure ] @@ -1326,8 +1211,6 @@ crbug.com/591099 fast/overflow/overflow-update-transform.html [ Failure ] crbug.com/591099 fast/overflow/overflow-with-local-background-attachment.html [ Failure ] crbug.com/591099 fast/overflow/recompute-overflow-of-layout-root-container.html [ Failure ] -crbug.com/824918 fast/pagination/div-y-vertical-rl-ltr.html [ Failure ] -crbug.com/824918 fast/pagination/div-y-vertical-rl-rtl.html [ Failure ] crbug.com/591099 fast/pagination/modal-dialog.html [ Failure ] crbug.com/824918 fast/pagination/paged-x-column-gap.html [ Failure ] crbug.com/824918 fast/pagination/paged-y-to-paged-x.html [ Failure ] @@ -1339,7 +1222,7 @@ crbug.com/591099 fast/parser/entities-in-html.html [ Failure ] crbug.com/591099 fast/parser/entities-in-xhtml.xhtml [ Failure ] crbug.com/591099 fast/peerconnection/RTCRtpSender-setParameters-crash.html [ Pass ] -crbug.com/591099 fast/reflections/inline-crash.html [ Failure ] +crbug.com/591099 fast/reflections/inline-crash.html [ Failure Pass ] crbug.com/591099 fast/replaced/absolute-position-percentage-height.html [ Failure ] crbug.com/591099 fast/replaced/border-radius-clip.html [ Failure ] crbug.com/591099 fast/replaced/computed-image-width-with-percent-height-and-fixed-ancestor-vertical-lr.html [ Failure ] @@ -1459,7 +1342,7 @@ crbug.com/591099 fast/spatial-navigation/snav-fully-aligned-vertically.html [ Failure ] crbug.com/591099 fast/sub-pixel/computedstylemargin.html [ Failure ] crbug.com/591099 fast/sub-pixel/inline-block-with-padding.html [ Failure ] -crbug.com/591099 fast/sub-pixel/selection/selection-rect-in-sub-pixel-table.html [ Failure ] +crbug.com/591099 fast/sub-pixel/selection/selection-rect-in-sub-pixel-table.html [ Failure Pass ] crbug.com/591099 fast/sub-pixel/sub-pixel-border-2.html [ Failure ] crbug.com/591099 fast/table/032.html [ Failure ] crbug.com/591099 fast/table/background-gradient-border-collapsed.html [ Failure ] @@ -1479,11 +1362,8 @@ crbug.com/591099 fast/table/inline-table-margin-baseline.html [ Failure ] crbug.com/591099 fast/table/large-shrink-wrapped-width.html [ Failure ] crbug.com/591099 fast/table/padding-height-and-override-height.html [ Failure ] -crbug.com/591099 fast/table/percent-height-content-in-fixed-height-border-box-sized-cell-with-collapsed-border-on-table.html [ Failure Pass ] -crbug.com/591099 fast/table/percent-height-content-in-fixed-height-border-box-sized-cell-with-collapsed-border.html [ Failure Pass ] -crbug.com/591099 fast/table/percent-height-content-in-fixed-height-content-box-sized-cell.html [ Failure Pass ] crbug.com/591099 fast/table/percent-height-overflow-auto-content-in-cell.html [ Failure ] -crbug.com/591099 fast/table/percent-height-overflow-scroll-content-in-cell.html [ Failure Pass ] +crbug.com/591099 fast/table/percent-height-overflow-scroll-content-in-cell.html [ Failure ] crbug.com/591099 fast/table/percent-height-replaced-content-in-cell.html [ Failure ] crbug.com/591099 fast/table/percent-widths-stretch-vertical.html [ Failure ] crbug.com/591099 fast/table/split-table-section-before-anonymous-block-2.html [ Failure ] @@ -1552,9 +1432,9 @@ crbug.com/591099 fast/text-autosizing/wide-block.html [ Failure ] crbug.com/591099 fast/text-autosizing/wide-child.html [ Failure ] crbug.com/591099 fast/text-autosizing/wide-in-narrow-overflow-scroll.html [ Failure ] -crbug.com/591099 fast/text/complex-text-opacity.html [ Failure ] +crbug.com/591099 fast/text/complex-text-opacity.html [ Failure Pass ] crbug.com/591099 fast/text/container-align-with-inlines.html [ Failure ] -crbug.com/591099 fast/text/decorations-transformed.html [ Failure ] +crbug.com/591099 fast/text/decorations-transformed.html [ Failure Pass ] crbug.com/591099 fast/text/decorations-with-text-combine.html [ Failure ] crbug.com/714962 fast/text/ellipsis-in-justified-text.html [ Failure ] crbug.com/591099 fast/text/ellipsis-in-relative-inline-2.html [ Failure ] @@ -1575,7 +1455,7 @@ crbug.com/591099 fast/text/international/rtl-white-space-pre-wrap.html [ Failure ] crbug.com/796943 fast/text/international/shape-across-elements-simple.html [ Pass ] crbug.com/591099 fast/text/international/text-combine-image-test.html [ Failure ] -crbug.com/591099 fast/text/international/zerowidthjoiner.html [ Failure ] +crbug.com/591099 fast/text/international/zerowidthjoiner.html [ Failure Pass ] crbug.com/591099 fast/text/large-text-composed-char.html [ Timeout ] crbug.com/591099 fast/text/place-ellipsis-in-inline-block-adjacent-float-2.html [ Failure ] crbug.com/591099 fast/text/place-ellipsis-in-inline-block-adjacent-float.html [ Failure ] @@ -1707,6 +1587,7 @@ crbug.com/591099 http/tests/images/restyle-decode-error.html [ Failure ] crbug.com/783102 http/tests/incremental/frame-focus-before-load.html [ Timeout ] crbug.com/591099 http/tests/incremental/slow-utf8-text.pl [ Pass Timeout ] +crbug.com/591099 http/tests/inspector-protocol/side-effects/evaluate-embedder-side-effect-free-attributes.js [ Timeout ] crbug.com/591099 http/tests/loading/nested_bad_objects.php [ Failure ] crbug.com/591099 http/tests/loading/preload-picture-nested.html [ Failure ] crbug.com/591099 http/tests/loading/preload-picture-sizes-2x.html [ Failure ] @@ -1720,7 +1601,7 @@ crbug.com/591099 http/tests/security/contentSecurityPolicy/source-list-parsing-04.html [ Failure ] crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ] crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ] -crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash Pass ] +crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ] crbug.com/591099 http/tests/security/shape-image-cors-allow-origin.html [ Failure ] crbug.com/591099 http/tests/security/shape-image-cors-data-url.html [ Failure ] crbug.com/591099 http/tests/security/shape-image-cors-same-origin.html [ Failure ] @@ -1742,7 +1623,7 @@ crbug.com/591099 images/percent-height-image.html [ Failure ] crbug.com/591099 inspector-protocol/accessibility/accessibility-ignoredNodes.js [ Timeout ] crbug.com/714962 inspector-protocol/accessibility/accessibility-ignoredNodesModal.js [ Failure ] -crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-img-figure.js [ Timeout ] +crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-img-figure.js [ Pass Timeout ] crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-input-buttons.js [ Timeout ] crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-input.js [ Timeout ] crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-labelledby.js [ Timeout ] @@ -1753,7 +1634,7 @@ crbug.com/714962 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-viewport.js [ Failure ] crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot.js [ Timeout ] crbug.com/591099 inspector-protocol/input/dispatchTouchEvent.js [ Pass Timeout ] -crbug.com/714962 inspector-protocol/layers/paint-profiler.js [ Failure ] +crbug.com/714962 inspector-protocol/layers/paint-profiler.js [ Failure Pass ] crbug.com/714962 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Failure ] crbug.com/591099 inspector-protocol/timeline/page-frames.js [ Pass ] crbug.com/714962 intersection-observer/text-target.html [ Failure ] @@ -1770,7 +1651,6 @@ crbug.com/591099 media/video-zoom.html [ Failure ] crbug.com/591099 netinfo/estimate-multiple-frames.html [ Failure Pass ] crbug.com/591099 overflow/overflow-basic-002.html [ Pass ] -crbug.com/591099 overflow/overflow-position-003.html [ Failure ] crbug.com/591099 paint/inline/focus-ring-under-absolute-with-relative-continuation.html [ Failure ] crbug.com/591099 paint/invalidation/background/backgroundSizeRepaint.html [ Failure ] crbug.com/591099 paint/invalidation/block-layout-inline-children-replaced.html [ Failure ] @@ -1795,7 +1675,7 @@ crbug.com/591099 paint/invalidation/clip/repaint-tile-clipped.html [ Crash ] crbug.com/591099 paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting.html [ Failure ] crbug.com/714962 paint/invalidation/compositing/composited-inline-change-text-data-keep-geometry.html [ Crash ] -crbug.com/591099 paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure ] +crbug.com/591099 paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure Pass ] crbug.com/591099 paint/invalidation/compositing/iframe-inside-squashed-layer.html [ Failure ] crbug.com/591099 paint/invalidation/compositing/remove-squashed-layer-plus-move.html [ Failure ] crbug.com/714962 paint/invalidation/compositing/repaint-via-layout-offset.html [ Failure ] @@ -1879,8 +1759,6 @@ crbug.com/591099 paint/invalidation/overflow/line-overflow.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/negative-text-indent-with-overflow-hidden.html [ Failure ] crbug.com/591099 paint/invalidation/overflow/repaint-resized-overflow.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/vertical-overflow-parent.html [ Failure ] -crbug.com/591099 paint/invalidation/overflow/vertical-overflow-same.html [ Failure ] crbug.com/591099 paint/invalidation/overhanging-float-detach-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/position/absolute-margin-change-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/position/absolute-position-change-containing-block.html [ Failure ] @@ -1910,16 +1788,15 @@ crbug.com/591099 paint/invalidation/push-block-with-first-line.html [ Failure ] crbug.com/591099 paint/invalidation/quotes.html [ Failure ] crbug.com/591099 paint/invalidation/reflection/reflection-redraw.html [ Failure ] -crbug.com/591099 paint/invalidation/remove-block-after-layout.html [ Failure ] +crbug.com/591099 paint/invalidation/remove-block-after-layout.html [ Failure Pass ] crbug.com/591099 paint/invalidation/remove-inline-after-layout.html [ Failure ] crbug.com/591099 paint/invalidation/remove-inline-layer-after-layout.html [ Crash ] crbug.com/591099 paint/invalidation/repaint-across-writing-mode-boundary.html [ Failure ] crbug.com/591099 paint/invalidation/repaint-descandant-on-ancestor-layer-move.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/fixed-under-composited-absolute-scrolled.html [ Crash ] crbug.com/591099 paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled.html [ Crash ] -crbug.com/591099 paint/invalidation/scroll/invalidate-after-composited-scroll-of-window.html [ Failure ] +crbug.com/591099 paint/invalidation/scroll/invalidate-after-composited-scroll-of-window.html [ Failure Pass ] crbug.com/591099 paint/invalidation/scroll/line-in-scrolled-clipped-block.html [ Failure ] -crbug.com/591099 paint/invalidation/scroll/outline-change-in-scrollers.html [ Failure ] crbug.com/591099 paint/invalidation/scroll/overflow-scroll-delete.html [ Failure ] crbug.com/591099 paint/invalidation/selection/invalidation-rect-includes-newline-for-rtl.html [ Failure ] crbug.com/591099 paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-lr.html [ Failure ] @@ -1930,6 +1807,7 @@ crbug.com/591099 paint/invalidation/selection/japanese-rl-selection-repaint.html [ Failure ] crbug.com/591099 paint/invalidation/selection/selection-change-in-iframe-with-relative-parent.html [ Failure ] crbug.com/591099 paint/invalidation/selection/selection-clear.html [ Failure ] +crbug.com/591099 paint/invalidation/selection/selection-partial-invalidation-between-blocks.html [ Failure ] crbug.com/591099 paint/invalidation/selection/selection-rl.html [ Failure ] crbug.com/591099 paint/invalidation/selection/selection-within-composited-scroller.html [ Failure ] crbug.com/714962 paint/invalidation/selection/text-selection-rect-in-overflow-2.html [ Failure ] @@ -2003,7 +1881,6 @@ crbug.com/591099 paint/markers/first-letter.html [ Failure ] crbug.com/591099 paint/markers/marker-early-break-bug.html [ Failure ] crbug.com/591099 paint/overflow/background-mask-should-be-recorded-full.html [ Failure ] -crbug.com/591099 paint/overflow/composited-scroll-vertical-rl.html [ Failure ] crbug.com/591099 paint/pagination/pagination-change-clip-crash.html [ Failure ] crbug.com/591099 paint/selection/text-selection-drag-in-frame-scrolled.html [ Failure ] crbug.com/591099 paint/selection/text-selection-drag-in-frame.html [ Failure ] @@ -2014,7 +1891,7 @@ crbug.com/591099 paint/selection/text-selection-newline-across-blocks.html [ Failure ] crbug.com/591099 paint/selection/text-selection-newline-br.html [ Failure ] crbug.com/591099 paint/selection/text-selection-newline-clipped-by-overflow.html [ Failure ] -crbug.com/591099 paint/selection/text-selection-newline-mixed-ltr-rtl.html [ Failure ] +crbug.com/591099 paint/selection/text-selection-newline-mixed-ltr-rtl.html [ Failure Pass ] crbug.com/591099 paint/selection/text-selection-newline-rtl-double-linebreak.html [ Failure ] crbug.com/591099 paint/selection/text-selection-newline-rtl.html [ Failure ] crbug.com/591099 paint/selection/text-selection-newline-span-across-line.html [ Failure ] @@ -2156,7 +2033,7 @@ crbug.com/714962 virtual/incremental-shadow-dom/shadow-dom/focus-navigation-with-delegatesFocus.html [ Timeout ] crbug.com/591099 virtual/layout_ng/ [ Skip ] crbug.com/824918 virtual/layout_ng_experimental/ [ Skip ] -crbug.com/836297 virtual/layout_ng_experimental/printing/webgl-oversized-printing.html [ Pass Timeout Crash ] +crbug.com/836297 virtual/layout_ng_experimental/printing/webgl-oversized-printing.html [ Pass Timeout ] crbug.com/591099 virtual/modern-media-controls/media/controls/modern/tap-to-hide-controls.html [ Failure ] crbug.com/714962 virtual/mouseevent_fractional/fast/events/drag-in-frames.html [ Failure ] crbug.com/714962 virtual/mouseevent_fractional/fast/events/event-on-culled_inline.html [ Failure ] @@ -2169,14 +2046,13 @@ crbug.com/591099 virtual/mouseevent_fractional/fast/events/mouseevent-getModifierState.html [ Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/onclick-list-marker.html [ Failure ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/mouse-pointer-capture-transition-events.html [ Timeout ] -crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/mouse-pointer-capture.html [ Crash Pass Timeout ] +crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/mouse-pointer-capture.html [ Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/mouse-pointer-preventdefault.html [ Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/multi-pointer-preventdefault.html [ Pass Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/touch-capture-in-iframe.html [ Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/touch-capture.html [ Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/select-element.html [ Timeout ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/sequential-focus-navigation-starting-point.html [ Failure ] -crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects-scroll.html [ Failure ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects.html [ Failure ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ] crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Pass ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService index 86b741e..e69c92ba 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -41,12 +41,4 @@ # Cross-Origin Read Blocking is not implemented. crbug.com/792546 http/tests/inspector-protocol/network/block_cross_site_document_load.js [ Failure ] -crbug.com/816556 external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click-404.html [ Failure ] -crbug.com/829721 external/wpt/html/semantics/embedded-content/the-area-element/area-download-click.html [ Crash ] -crbug.com/829721 external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click.html [ Crash ] -crbug.com/829721 fast/dom/HTMLAnchorElement/anchor-download.html [ Crash ] -crbug.com/829721 fast/dom/HTMLAnchorElement/anchor-nodownload-set.html [ Crash ] -crbug.com/829721 fast/dom/HTMLAreaElement/area-download.html [ Crash ] -crbug.com/829721 http/tests/security/anchor-download-allow-blob.html [ Crash ] - crbug.com/812464 inspector-protocol/network/interception-file-url.js [ Pass Crash ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process index 9e4768a..b5d59bb 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process +++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -1,12 +1,6 @@ # These tests currently fail when they run with --site-per-process. # See https://crbug.com/477150. -# TODO(lukasza): Remove these once CORB is enabled by default. -crbug.com/802835 external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Pass ] -crbug.com/802835 external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html [ Pass ] -crbug.com/802835 external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Pass ] -crbug.com/802835 external/wpt/fetch/corb/preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html [ Pass ] - # https://crbug.com/794631: OOPIFs VS feature policy VS opaque origins? crbug.com/794631 virtual/unified-autoplay/external/wpt/feature-policy/autoplay-allowed-by-feature-policy-attribute.https.sub.html [ Failure ] crbug.com/794631 virtual/unified-autoplay/external/wpt/feature-policy/autoplay-allowed-by-feature-policy.https.sub.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 4266f2d..aee76803 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -241,7 +241,6 @@ crbug.com/722825 virtual/new-remote-playback-pipeline/media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Timeout Pass ] #### crbug.com/783229 overflow -crbug.com/724697 overflow/overflow-basic-002.html [ Failure ] crbug.com/724701 overflow/overflow-basic-004.html [ Failure ] ### external/wpt/css/css-tables/ @@ -353,7 +352,6 @@ ### virtual/layout_ng/overflow crbug.com/724701 virtual/layout_ng/overflow/overflow-basic-004.html [ Failure ] -crbug.com/728378 virtual/layout_ng/overflow/overflow-position-003.html [ Failure ] ### single pixel underline Mac failures crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/margin-collapse/001.html [ Failure ] @@ -1268,10 +1266,6 @@ crbug.com/736308 virtual/outofblink-cors/external/wpt/service-workers/service-worker/worker-in-sandboxed-iframe-by-csp-fetch-event.https.html [ Timeout ] # Failing tests in dictionary order. -crbug.com/802835 virtual/outofblink-cors/external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html [ Failure ] -crbug.com/802835 virtual/outofblink-cors/external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Failure ] -crbug.com/802835 virtual/outofblink-cors/external/wpt/fetch/corb/preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html [ Failure ] -crbug.com/802835 virtual/outofblink-cors/external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Failure ] crbug.com/834183 virtual/outofblink-cors/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html [ Failure Pass ] crbug.com/745327 virtual/outofblink-cors/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/registration-attribute.https.html [ Failure Pass ] crbug.com/736308 virtual/outofblink-cors/external/wpt/service-workers/service-worker/fetch-canvas-tainting-image-cache.https.html [ Failure ] @@ -3800,6 +3794,8 @@ crbug.com/773122 [ Win7 ] virtual/gpu/fast/canvas/canvas-lost-gpu-context.html [ Failure Pass ] # Sheriff failures 2017-10-13 +crbug.com/774437 paint/invalidation/selection/selection-partial-invalidation-between-blocks.html [ Pass Failure ] +crbug.com/774437 virtual/disable-spv175/paint/invalidation/selection/selection-partial-invalidation-between-blocks.html [ Skip ] crbug.com/774463 [ Win7 Debug ] fast/events/autoscroll-should-not-stop-on-keypress.html [ Failure Pass ] crbug.com/774463 [ Win7 Debug ] virtual/mouseevent_fractional/fast/events/autoscroll-should-not-stop-on-keypress.html [ Failure Pass ] crbug.com/774688 [ Mac ] fast/spatial-navigation/snav-container-white-space.html [ Pass Failure ] @@ -3973,12 +3969,6 @@ # TODO(senorblanco): rebaseline after Skia roll crbug.com/802896 virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill.html [ Failure Pass ] -# TODO(lukasza): Remove these once CORB is enabled by default. -crbug.com/802835 external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Failure ] -crbug.com/802835 external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html [ Failure ] -crbug.com/802835 external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Failure ] -crbug.com/802835 external/wpt/fetch/corb/preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html [ Failure ] - crbug.com/813704 http/tests/images/png-partial-load-as-document.html [ Failure Pass ] crbug.com/806645 [ Win7 Mac ] http/tests/devtools/elements/elements-panel-rewrite-href.js [ Failure Pass ] @@ -4135,7 +4125,6 @@ crbug.com/836241 [ Linux ] virtual/android/fullscreen/video-controls-timeline.html [ Pass Failure ] crbug.com/836242 [ Linux Mac Win ] fast/loader/reload-zero-byte-plugin.html [ Pass Failure ] crbug.com/836275 [ Linux Mac ] fast/spatial-navigation/snav-two-elements-one-line.html [ Pass Failure ] -crbug.com/836295 [ Win ] svg/animations/reinserting-svg-into-document.html [ Pass Failure ] # Sheriff 2018-04-25 crbug.com/836763 [ Win7 ] external/wpt/websockets/Create-protocols-repeated-case-insensitive.any.js [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/PRESUBMIT.py b/third_party/WebKit/LayoutTests/external/PRESUBMIT.py index 30bc647..c9d0e9fb 100644 --- a/third_party/WebKit/LayoutTests/external/PRESUBMIT.py +++ b/third_party/WebKit/LayoutTests/external/PRESUBMIT.py
@@ -40,10 +40,11 @@ args, stdout=input_api.subprocess.PIPE, stderr=input_api.subprocess.PIPE) - stdout, _ = proc.communicate() + stdout, stderr = proc.communicate() - if stdout: - return [output_api.PresubmitError(stdout)] + if proc.returncode != 0: + return [output_api.PresubmitError('wpt lint failed:', + long_text=stdout+stderr)] return []
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json index eee37e8..dad8ac15 100644 --- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json +++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -50125,6 +50125,30 @@ {} ] ], + "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html": [ + [ + "/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html", + [ + [ + "/css/css-shapes/shape-outside/supported-shapes/inset/reference/shape-outside-inset-010-ref.html", + "==" + ] + ], + {} + ] + ], + "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html": [ + [ + "/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html", + [ + [ + "/css/css-shapes/shape-outside/supported-shapes/inset/reference/shape-outside-inset-010-ref.html", + "==" + ] + ], + {} + ] + ], "css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-007.html": [ [ "/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-007.html", @@ -120280,11 +120304,6 @@ {} ] ], - "css/css-overflow/overflow-shorthand-001-expected.txt": [ - [ - {} - ] - ], "css/css-overflow/reference/input-scrollable-region-001-ref.html": [ [ {} @@ -132065,11 +132084,6 @@ {} ] ], - "css/cssom/index-002-expected.txt": [ - [ - {} - ] - ], "css/cssom/interfaces-expected.txt": [ [ {} @@ -132355,11 +132369,6 @@ {} ] ], - "css/geometry/DOMQuad-001-expected.txt": [ - [ - {} - ] - ], "css/geometry/DOMRectList-expected.txt": [ [ {} @@ -137095,6 +137104,11 @@ {} ] ], + "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt": [ + [ + {} + ] + ], "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html.headers": [ [ {} @@ -137585,11 +137599,6 @@ {} ] ], - "encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt": [ - [ - {} - ] - ], "encoding/legacy-mb-tchinese/big5/big5-enc-ascii.html.headers": [ [ {} @@ -138585,16 +138594,6 @@ {} ] ], - "fetch/api/basic/integrity.js": [ - [ - {} - ] - ], - "fetch/api/basic/mode-no-cors.js": [ - [ - {} - ] - ], "fetch/api/basic/request-upload.any-expected.txt": [ [ {} @@ -138605,21 +138604,6 @@ {} ] ], - "fetch/api/basic/response-url.js": [ - [ - {} - ] - ], - "fetch/api/basic/scheme-blob.js": [ - [ - {} - ] - ], - "fetch/api/basic/scheme-others.js": [ - [ - {} - ] - ], "fetch/api/cors/cors-expose-star.js": [ [ {} @@ -153355,6 +153339,31 @@ {} ] ], + "images/wpt-logo/wpt-logo-darkblue-bg.svg": [ + [ + {} + ] + ], + "images/wpt-logo/wpt-logo-darkblue.svg": [ + [ + {} + ] + ], + "images/wpt-logo/wpt-logo-lightblue-bg.svg": [ + [ + {} + ] + ], + "images/wpt-logo/wpt-logo-orange-bg.svg": [ + [ + {} + ] + ], + "images/wpt-logo/wpt-logo-white.svg": [ + [ + {} + ] + ], "images/yellow.png": [ [ {} @@ -163935,6 +163944,11 @@ {} ] ], + "webaudio/resources/audionodeoptions.js": [ + [ + {} + ] + ], "webaudio/resources/audioparam-testing.js": [ [ {} @@ -194152,14 +194166,6 @@ } ] ], - "encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html": [ - [ - "/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html", - { - "timeout": "long" - } - ] - ], "encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html": [ [ "/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html", @@ -195858,21 +195864,17 @@ {} ] ], - "fetch/api/basic/integrity-sharedworker.html": [ + "fetch/api/basic/integrity.sub.any.js": [ [ - "/fetch/api/basic/integrity-sharedworker.html", + "/fetch/api/basic/integrity.sub.any.html", {} - ] - ], - "fetch/api/basic/integrity-worker.html": [ + ], [ - "/fetch/api/basic/integrity-worker.html", + "/fetch/api/basic/integrity.sub.any.sharedworker.html", {} - ] - ], - "fetch/api/basic/integrity.html": [ + ], [ - "/fetch/api/basic/integrity.html", + "/fetch/api/basic/integrity.sub.any.worker.html", {} ] ], @@ -195888,15 +195890,13 @@ {} ] ], - "fetch/api/basic/mode-no-cors-worker.html": [ + "fetch/api/basic/mode-no-cors.sub.any.js": [ [ - "/fetch/api/basic/mode-no-cors-worker.html", + "/fetch/api/basic/mode-no-cors.sub.any.html", {} - ] - ], - "fetch/api/basic/mode-no-cors.html": [ + ], [ - "/fetch/api/basic/mode-no-cors.html", + "/fetch/api/basic/mode-no-cors.sub.any.worker.html", {} ] ], @@ -195986,15 +195986,13 @@ {} ] ], - "fetch/api/basic/response-url-worker.html": [ + "fetch/api/basic/response-url.sub.any.js": [ [ - "/fetch/api/basic/response-url-worker.html", + "/fetch/api/basic/response-url.sub.any.html", {} - ] - ], - "fetch/api/basic/response-url.html": [ + ], [ - "/fetch/api/basic/response-url.html", + "/fetch/api/basic/response-url.sub.any.worker.html", {} ] ], @@ -196008,15 +196006,13 @@ {} ] ], - "fetch/api/basic/scheme-blob-worker.html": [ + "fetch/api/basic/scheme-blob.sub.any.js": [ [ - "/fetch/api/basic/scheme-blob-worker.html", + "/fetch/api/basic/scheme-blob.sub.any.html", {} - ] - ], - "fetch/api/basic/scheme-blob.html": [ + ], [ - "/fetch/api/basic/scheme-blob.html", + "/fetch/api/basic/scheme-blob.sub.any.worker.html", {} ] ], @@ -196030,15 +196026,13 @@ {} ] ], - "fetch/api/basic/scheme-others-worker.html": [ + "fetch/api/basic/scheme-others.sub.any.js": [ [ - "/fetch/api/basic/scheme-others-worker.html", + "/fetch/api/basic/scheme-others.sub.any.html", {} - ] - ], - "fetch/api/basic/scheme-others.html": [ + ], [ - "/fetch/api/basic/scheme-others.html", + "/fetch/api/basic/scheme-others.sub.any.worker.html", {} ] ], @@ -235684,6 +235678,12 @@ {} ] ], + "webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html": [ + [ + "/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html", + {} + ] + ], "webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html": [ [ "/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html", @@ -235720,6 +235720,12 @@ {} ] ], + "webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html": [ + [ + "/webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html", + {} + ] + ], "webaudio/the-audio-api/the-audiodestinationnode-interface/idl-test.html": [ [ "/webaudio/the-audio-api/the-audiodestinationnode-interface/idl-test.html", @@ -236032,6 +236038,12 @@ {} ] ], + "webaudio/the-audio-api/the-biquadfilternode-interface/ctor-biquadfilter.html": [ + [ + "/webaudio/the-audio-api/the-biquadfilternode-interface/ctor-biquadfilter.html", + {} + ] + ], "webaudio/the-audio-api/the-biquadfilternode-interface/no-dezippering.html": [ [ "/webaudio/the-audio-api/the-biquadfilternode-interface/no-dezippering.html", @@ -236062,12 +236074,24 @@ {} ] ], + "webaudio/the-audio-api/the-channelmergernode-interface/ctor-channelmerger.html": [ + [ + "/webaudio/the-audio-api/the-channelmergernode-interface/ctor-channelmerger.html", + {} + ] + ], "webaudio/the-audio-api/the-channelsplitternode-interface/audiochannelsplitter.html": [ [ "/webaudio/the-audio-api/the-channelsplitternode-interface/audiochannelsplitter.html", {} ] ], + "webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html": [ + [ + "/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html", + {} + ] + ], "webaudio/the-audio-api/the-constantsourcenode-interface/constant-source-basic.html": [ [ "/webaudio/the-audio-api/the-constantsourcenode-interface/constant-source-basic.html", @@ -236086,6 +236110,12 @@ {} ] ], + "webaudio/the-audio-api/the-constantsourcenode-interface/ctor-constantsource.html": [ + [ + "/webaudio/the-audio-api/the-constantsourcenode-interface/ctor-constantsource.html", + {} + ] + ], "webaudio/the-audio-api/the-constantsourcenode-interface/test-constantsourcenode.html": [ [ "/webaudio/the-audio-api/the-constantsourcenode-interface/test-constantsourcenode.html", @@ -236134,6 +236164,18 @@ {} ] ], + "webaudio/the-audio-api/the-convolvernode-interface/ctor-convolver.html": [ + [ + "/webaudio/the-audio-api/the-convolvernode-interface/ctor-convolver.html", + {} + ] + ], + "webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html": [ + [ + "/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html", + {} + ] + ], "webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html": [ [ "/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html", @@ -236176,12 +236218,24 @@ {} ] ], + "webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html": [ + [ + "/webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html", + {} + ] + ], "webaudio/the-audio-api/the-dynamicscompressornode-interface/dynamicscompressor-basic.html": [ [ "/webaudio/the-audio-api/the-dynamicscompressornode-interface/dynamicscompressor-basic.html", {} ] ], + "webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html": [ + [ + "/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html", + {} + ] + ], "webaudio/the-audio-api/the-gainnode-interface/gain-basic.html": [ [ "/webaudio/the-audio-api/the-gainnode-interface/gain-basic.html", @@ -236206,6 +236260,12 @@ {} ] ], + "webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html": [ + [ + "/webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html", + {} + ] + ], "webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html": [ [ "/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html", @@ -236236,12 +236296,30 @@ {} ] ], + "webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html": [ + [ + "/webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html", + {} + ] + ], "webaudio/the-audio-api/the-offlineaudiocontext-interface/current-time-block-size.html": [ [ "/webaudio/the-audio-api/the-offlineaudiocontext-interface/current-time-block-size.html", {} ] ], + "webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html": [ + [ + "/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html", + {} + ] + ], + "webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html": [ + [ + "/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html", + {} + ] + ], "webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html": [ [ "/webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html", @@ -236314,6 +236392,12 @@ {} ] ], + "webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html": [ + [ + "/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html", + {} + ] + ], "webaudio/the-audio-api/the-stereopanner-interface/no-dezippering.html": [ [ "/webaudio/the-audio-api/the-stereopanner-interface/no-dezippering.html", @@ -236332,6 +236416,12 @@ {} ] ], + "webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html": [ + [ + "/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html", + {} + ] + ], "webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html": [ [ "/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html", @@ -253298,7 +253388,7 @@ "support" ], "FileAPI/historical.https.html": [ - "93903b4f58bc215ea2a67fc6d281593ec5ce79e9", + "e1cf63d05a0e401cc38a11d2b42efb9e8d315610", "testharness" ], "FileAPI/idlharness.html": [ @@ -253534,7 +253624,7 @@ "testharness" ], "IndexedDB/historical.html": [ - "0c4584e1b021a286445a2a9d3388ff011a1b933e", + "2a5eaca2bbe1d0c9495ab7c48560d611f60a007c", "testharness" ], "IndexedDB/idb-binary-key-detached.htm": [ @@ -295629,10 +295719,6 @@ "2fe3c96c09faaa25baa2d7e88215a2ccd47e9381", "testharness" ], - "css/css-overflow/overflow-shorthand-001-expected.txt": [ - "b0bf233e8b62346904b64efc631d997189a39853", - "support" - ], "css/css-overflow/overflow-shorthand-001.html": [ "a32d1b270f62b9d563ed397c2c4cd6e87b9405e1", "testharness" @@ -297130,7 +297216,7 @@ "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-010.html": [ - "69b7cd75faad315f1c87214d7f74cbebeba0f37d", + "6dcfc209b1e08c1a3f41036a301fa3e0a434b4af", "reftest" ], "css/css-shapes/shape-outside/shape-image/shape-image-011.html": [ @@ -297330,7 +297416,7 @@ "reftest" ], "css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-027.html": [ - "97237d7bc6fb98fc1cc0b27d493b2610d91b483b", + "87477b60302ae3f7e20c488b5a944d7b9169ba0d", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-028.html": [ @@ -297370,7 +297456,7 @@ "reftest" ], "css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-015.html": [ - "e6001d6ecc8565842fa1fe68ce280c06a17ee62a", + "121434db8bfc63877d10383c167ee3f2f8d65d7a", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-016.html": [ @@ -297378,7 +297464,7 @@ "reftest" ], "css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-017.html": [ - "ff109f0d61bd487df7834e8c56330a5c6f4bc52d", + "c16469402616b77d502ed4a65b623022a883c902", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-018.html": [ @@ -297426,27 +297512,35 @@ "support" ], "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-010.html": [ - "7878c4b4b0cd5b36e153d4fad565b37a1aeb746d", + "0a1a7449964d3dc18dbabb4da8777f7e1aedbf7e", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-011.html": [ - "bda8e62930c2f084005998d1e1679c5ff85f25a0", + "e98e70e094d84a2434d5d634caa482df9e6aeb07", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-012.html": [ - "5ef1e674163f358a2dbac6fd6175ff424e2a46ba", + "39ba9b59b93ada88d6655529a8af3d3d6d7c00be", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-013.html": [ - "2347978786094f1a6bb4a5a9a030c7a11cc85781", + "d53115c05401ffd274b55c6a95c9e4fd05011da1", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-014.html": [ - "124c822825bb26bec99543892f75ea0b11b0e74c", + "2ef7ecd7321bb6639490110e45118ec58ee2aa9c", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-015.html": [ - "23eab9e83a350804230169f4edc5e50a55716fe7", + "04a6a67837127ba47e757e5cc3a9ff91c1e01d9a", + "reftest" + ], + "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html": [ + "cafd680e210a6d677bde00ae4c5f9263c1e7b48e", + "reftest" + ], + "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html": [ + "67161f08b8aec3fe9bd94e14cbe865dd4c9d3664", "reftest" ], "css/css-shapes/shape-outside/supported-shapes/polygon/reference/shape-outside-polygon-007-ref.html": [ @@ -322445,10 +322539,6 @@ "02b135e62439d775d7e8de7ca94c831a8d00e077", "testharness" ], - "css/cssom/index-002-expected.txt": [ - "6998c032ac489ed7073fac09535e573bac6d0af2", - "support" - ], "css/cssom/inline-style-001.html": [ "4c58b6153eabe796749dcaf181e03d7dce2c9c07", "testharness" @@ -322506,7 +322596,7 @@ "testharness" ], "css/cssom/overflow-serialization.html": [ - "48ca70f4c20da103276e1b053ca0e4613fcc2819", + "85952fbd040de03f5082f28ba8116fd4827a1fc3", "testharness" ], "css/cssom/selectorSerialize.html": [ @@ -322562,7 +322652,7 @@ "support" ], "css/cssom/shorthand-values.html": [ - "8bfc0dff5da1859e17df31ceeca710d17a982109", + "9211674fd69ddfa32d3bafcf2bc967977eb3ec31", "testharness" ], "css/cssom/style-sheet-interfaces-001.html": [ @@ -322849,12 +322939,8 @@ "68b09f967ee8d221b11e949a32cc27a17082633b", "testharness" ], - "css/geometry/DOMQuad-001-expected.txt": [ - "d92f9b30522b42fe4e5c267a92380a01e74d3c6e", - "support" - ], "css/geometry/DOMQuad-001.html": [ - "1fe1fb785f99b90bae48f0f2d8eb46765c6f6fbb", + "cd82e18b88904531ffbfa88f11d90dfef571a30a", "testharness" ], "css/geometry/DOMQuad-002.html": [ @@ -322870,11 +322956,11 @@ "testharness" ], "css/geometry/DOMRectList-expected.txt": [ - "aba75d8e0261d4ea2cf89a2784fa406835090fdf", + "4d0fd62919aab862702f3a41a309c3108f4c27c9", "support" ], "css/geometry/DOMRectList.html": [ - "382b6c7ea9bc4c75d1b007b59ccdcb9c642f51ce", + "3b63015ab3dbe4b7ea4ac7ede67f6db603c0f65c", "testharness" ], "css/geometry/OWNERS": [ @@ -328754,11 +328840,11 @@ "testharness" ], "dom/historical-expected.txt": [ - "ac8961d766c33a78197a506ff406397279e77390", + "428a35a1e289a8bea86f1b5c37aeb145e67c4d9b", "support" ], "dom/historical.html": [ - "ccf3d9d2d8eb3e7353ecedd8a4d8ba232f9374ec", + "291d078c4424d0491aeab7524d350eab7304f989", "testharness" ], "dom/interface-objects.html": [ @@ -331005,6 +331091,10 @@ "3f9d6377755867c9b9b7d05ccaa88f459d0ca436", "support" ], + "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt": [ + "2b0e7b48d2afb396eb6ceaa62704a45db3f06eec", + "support" + ], "encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp.html": [ "d3751457c76d5dacd10a7dad1f22ceb7837c68a2", "testharness" @@ -331437,10 +331527,6 @@ "c79f9150befa2f7ce67d17069da7fb66f7f22ca0", "support" ], - "encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html": [ - "3a2d013060c95824579de848af715c1c8135c206", - "testharness" - ], "encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html.headers": [ "c79f9150befa2f7ce67d17069da7fb66f7f22ca0", "support" @@ -331669,10 +331755,6 @@ "895fb1caf07ea90bea9d34db70b0974f11d6c149", "support" ], - "encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt": [ - "00a9a6000f3e34e6ab860dd02ac877f469c9922c", - "support" - ], "encoding/legacy-mb-tchinese/big5/big5-enc-ascii.html": [ "d1de2bf7197001a451c5d055036340e3424ebed7", "testharness" @@ -333449,22 +333531,10 @@ "54181cd222c7a5428f9468f4777c273ecc553f4f", "testharness" ], - "fetch/api/basic/integrity-sharedworker.html": [ - "00d7eae5a324653caae19ab83bef76dd3503fb3b", + "fetch/api/basic/integrity.sub.any.js": [ + "7f25bf12a25919d07f2fd04fabaf8cd90f4cf77a", "testharness" ], - "fetch/api/basic/integrity-worker.html": [ - "1a0a6abd1c6bf8c4665a242d64f50bbbff4982f8", - "testharness" - ], - "fetch/api/basic/integrity.html": [ - "aa6d9159321e410d1e96c045f8f4d8700494beeb", - "testharness" - ], - "fetch/api/basic/integrity.js": [ - "59e8c5ebf0dfd2ab2432f28dcee5db82e9505cff", - "support" - ], "fetch/api/basic/keepalive.html": [ "1ecdcb478b65b258e79242ae17af79ba903db412", "testharness" @@ -333473,18 +333543,10 @@ "5e2e35f820982002b967d7444507a2bfc0d3aa34", "testharness" ], - "fetch/api/basic/mode-no-cors-worker.html": [ - "39992b8abf3ab30182dfbaa5816cc154a22ae0ad", + "fetch/api/basic/mode-no-cors.sub.any.js": [ + "5ccf57aa2feb7d11e4372ba71f81d6726875ae03", "testharness" ], - "fetch/api/basic/mode-no-cors.html": [ - "ff174cb1a76a63473028b72b3319602685689cb5", - "testharness" - ], - "fetch/api/basic/mode-no-cors.js": [ - "311ecadfe0c44430846b2f11facf53c1fafc6df0", - "support" - ], "fetch/api/basic/mode-same-origin.any.js": [ "1027de012d62a36a0df53bfadbfb8341a22558c0", "testharness" @@ -333529,50 +333591,26 @@ "32592b8263be16c7d4a61bd9bfcd235d18f654e2", "support" ], - "fetch/api/basic/response-url-worker.html": [ - "3d615668cb0122600af10d80cac4a7ebfe051528", + "fetch/api/basic/response-url.sub.any.js": [ + "f43c11ef52661046824b328112f6ab8826bed027", "testharness" ], - "fetch/api/basic/response-url.html": [ - "9e7c61ac685654e355174c960ba2084d17ff7f2e", - "testharness" - ], - "fetch/api/basic/response-url.js": [ - "444ef1e53b422f74a4bb322099d8b1147ed8f52b", - "support" - ], "fetch/api/basic/scheme-about.any.js": [ "68b11deaa95a691a8e0c76bdc9b7a07b5dea9868", "testharness" ], - "fetch/api/basic/scheme-blob-worker.html": [ - "adfea540164a4113484ea29007ac3738bb7456c4", + "fetch/api/basic/scheme-blob.sub.any.js": [ + "ec722257d9e93842751547b5a2a8cfff9da6d9de", "testharness" ], - "fetch/api/basic/scheme-blob.html": [ - "5835c0c3b108f05b61ef0db945f272264b377d6c", - "testharness" - ], - "fetch/api/basic/scheme-blob.js": [ - "ab29ee7bde800b3ffaade990d86816ea9b6cb2c7", - "support" - ], "fetch/api/basic/scheme-data.any.js": [ "d312396ed3c5a90004d46c3d630525cb3e8228e1", "testharness" ], - "fetch/api/basic/scheme-others-worker.html": [ - "4ea145afbbe3b349c227c19ac5612db94b10c61c", + "fetch/api/basic/scheme-others.sub.any.js": [ + "9b82765bb46386db2408260c14a511afb0ba662e", "testharness" ], - "fetch/api/basic/scheme-others.html": [ - "e7594d1119e1c937663b4bb0385b69c442f1c78f", - "testharness" - ], - "fetch/api/basic/scheme-others.js": [ - "e9f99ee8112a0e0983322aece2034461125a46b5", - "support" - ], "fetch/api/basic/stream-response.any.js": [ "b989131e8cf68e37c6d27397d801bfcc9c97b7ed", "testharness" @@ -355469,6 +355507,26 @@ "6a8595f419a6ec6bc13222f348ed42ad4c7b9783", "support" ], + "images/wpt-logo/wpt-logo-darkblue-bg.svg": [ + "d2f8fe86fa19cfeada7285eb9fdd1c7d8aeae500", + "support" + ], + "images/wpt-logo/wpt-logo-darkblue.svg": [ + "2cdd9e0004bfb27185f1d306b84b39e7b260bc06", + "support" + ], + "images/wpt-logo/wpt-logo-lightblue-bg.svg": [ + "79ba6641c99eacdedc2c9279ece8622944a37830", + "support" + ], + "images/wpt-logo/wpt-logo-orange-bg.svg": [ + "af4e23f30394794d87eb96dfa1b4e940c6b743f1", + "support" + ], + "images/wpt-logo/wpt-logo-white.svg": [ + "9c6494169ea074ede142c00b0e34f27df43007bd", + "support" + ], "images/yellow.png": [ "50eab1cff1bf2b1e68403ac724aa8043205901be", "support" @@ -382533,6 +382591,10 @@ "9ef52c13448d19b241b40a5c81f4a0480c05c5de", "support" ], + "webaudio/resources/audionodeoptions.js": [ + "d7712311bddd23e171e7e1f024aec0a565b08a13", + "support" + ], "webaudio/resources/audioparam-testing.js": [ "2855fbee30e629ea397166911b9bcdec74bd4fdf", "support" @@ -382597,6 +382659,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html": [ + "7e35ac29f00d39c84230535212c0b9ea081951d3", + "testharness" + ], "webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html": [ "e2320e33ef1df0155d5fcf536550e0e398b15407", "testharness" @@ -382629,6 +382695,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html": [ + "ce84d25460435564021a13dc9e26384bc30e9d96", + "testharness" + ], "webaudio/the-audio-api/the-audiocontext-interface/.gitkeep": [ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" @@ -382909,6 +382979,10 @@ "f24a473f695c2d10788ba7d50728259a08ed53c8", "testharness" ], + "webaudio/the-audio-api/the-biquadfilternode-interface/ctor-biquadfilter.html": [ + "475916d4aba810c017c385d98d51d353ad0fc561", + "testharness" + ], "webaudio/the-audio-api/the-biquadfilternode-interface/no-dezippering.html": [ "348376a643b765700342e9620e1346d432a28131", "testharness" @@ -382933,6 +383007,10 @@ "250c35e36dda600c30554024cbd500a37180292a", "testharness" ], + "webaudio/the-audio-api/the-channelmergernode-interface/ctor-channelmerger.html": [ + "72bfba4cbca1b4d3e7692ef236afb905f852fadd", + "testharness" + ], "webaudio/the-audio-api/the-channelsplitternode-interface/.gitkeep": [ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" @@ -382941,6 +383019,10 @@ "80cfd321970b37df7995d6e0d262a2c008e6e10c", "testharness" ], + "webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html": [ + "743fd6af6f67958f67f4a63cef432515518edf41", + "testharness" + ], "webaudio/the-audio-api/the-constantsourcenode-interface/constant-source-basic.html": [ "d60c7c7c6d9236f773199a213bef6b1103e02e2e", "testharness" @@ -382953,6 +383035,10 @@ "ce90b4d7ca5840abdc830d734df26028958fadd7", "testharness" ], + "webaudio/the-audio-api/the-constantsourcenode-interface/ctor-constantsource.html": [ + "1fa8892c98ce5f979a08294a838b3b91ce11a3a3", + "testharness" + ], "webaudio/the-audio-api/the-constantsourcenode-interface/test-constantsourcenode.html": [ "711b3f183d847e437a4c332f33054cc5a648fd22", "testharness" @@ -382989,10 +383075,18 @@ "f32f5acdf031b1a2b32bc37324b105d1df7c5fdb", "testharness" ], + "webaudio/the-audio-api/the-convolvernode-interface/ctor-convolver.html": [ + "70f49ae8525bd998d3b51bada7a296ba4cef03e5", + "testharness" + ], "webaudio/the-audio-api/the-delaynode-interface/.gitkeep": [ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html": [ + "473c5e05ab6ee4930de9e3a3ec47075af7e9650d", + "testharness" + ], "webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html": [ "c732635549f5eab61f8bdce05b27f0f3e8a3f6c2", "testharness" @@ -383025,6 +383119,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html": [ + "6338104e8199673ff7de6f41d310b79a2ee51f04", + "testharness" + ], "webaudio/the-audio-api/the-dynamicscompressornode-interface/dynamicscompressor-basic.html": [ "6ceaf50b8cacdfa246bc997f2c8e46aefd789659", "testharness" @@ -383033,6 +383131,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html": [ + "3c77f0ac735a224fa6d905f62585beba39643393", + "testharness" + ], "webaudio/the-audio-api/the-gainnode-interface/gain-basic.html": [ "70165c60c482d6507670af965756f639e7b3ba78", "testharness" @@ -383053,6 +383155,10 @@ "bf2de43e568c79b96fd5b0602e26346c483162a5", "testharness" ], + "webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html": [ + "3627d5b4a447a62de6c6a6f10556fa9f9dec1700", + "testharness" + ], "webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html": [ "176168861bc667b2b05312dbae48f76f7f90d791", "testharness" @@ -383089,6 +383195,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html": [ + "4264798e9e64d30c72f9f0577a9648efa2d0a50a", + "testharness" + ], "webaudio/the-audio-api/the-offlineaudiocontext-interface/current-time-block-size.html": [ "d0b1fefa6c0f75c0666cd5b7e8305099e9925d62", "testharness" @@ -383097,10 +383207,18 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html": [ + "53df9d36a96c61a6ce9215cbc2830783e26c91be", + "testharness" + ], "webaudio/the-audio-api/the-pannernode-interface/.gitkeep": [ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html": [ + "2b59d21a54b2216c1171a6ba2c7809291955a8af", + "testharness" + ], "webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html": [ "c1c94753ebcd1930e326d73c085e6c3197967cd5", "testharness" @@ -383157,6 +383275,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html": [ + "1908ffc477d8c16b81ab371f5b9dbca46cc16a83", + "testharness" + ], "webaudio/the-audio-api/the-stereopanner-interface/no-dezippering.html": [ "b3525a2c459125281196201216417c39030e79a8", "testharness" @@ -383173,6 +383295,10 @@ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "support" ], + "webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html": [ + "80b585554acf22f357264e3a024d2159fd184f13", + "testharness" + ], "webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html": [ "f88431616d6a8084a3434c1606e3543178d019fb", "testharness"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/FileAPI/historical.https.html b/third_party/WebKit/LayoutTests/external/wpt/FileAPI/historical.https.html index 9d78b56..877aed0 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/FileAPI/historical.https.html +++ b/third_party/WebKit/LayoutTests/external/wpt/FileAPI/historical.https.html
@@ -10,17 +10,20 @@ <body> <div id="log"></div> <script> - test(function() { - assert_false('toNativeLineEndings' in window); - }, '"toNativeLineEndings" should not be supported'); + var removedFromWindow = [ + 'toNativeLineEndings', + 'FileError', + 'FileException', + 'FileHandle', + 'FileRequest', + 'MutableFile', + ]; - test(function() { - assert_false('FileError' in window); - }, '"FileError" should not be supported'); - - test(function() { - assert_false('FileException' in window); - }, '"FileException" should not be supported'); + removedFromWindow.forEach(function(name) { + test(function() { + assert_false(name in window); + }, '"' + name + '" should not be supported'); + }); test(function() { var b = new Blob();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/historical.html b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/historical.html index 73d78c8..8e7097eb 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/historical.html +++ b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/historical.html
@@ -63,4 +63,18 @@ // Replaced circa May 2012 by a DOMString (later, IDBTransactionMode enum). assert_false('VERSION_CHANGE' in IDBTransaction); }, '"VERSION_CHANGE" should not be supported on IDBTransaction.'); + +// Gecko-proprietary interfaces. +var removedFromWindow = [ + 'IDBFileHandle', + 'IDBFileRequest', + 'IDBMutableFile', +]; + +removedFromWindow.forEach(function(name) { + test(function() { + assert_false(name in window); + }, '"' + name + '" should not be supported'); +}); + </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-overflow/overflow-shorthand-001-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-overflow/overflow-shorthand-001-expected.txt deleted file mode 100644 index 34705c8..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-overflow/overflow-shorthand-001-expected.txt +++ /dev/null
@@ -1,19 +0,0 @@ -This is a testharness.js-based test. -FAIL overflow: auto auto works assert_equals: expected "auto" but got "" -FAIL overflow: auto hidden works assert_equals: expected "auto hidden" but got "" -FAIL overflow: auto scroll works assert_equals: expected "auto scroll" but got "" -FAIL overflow: auto visible works assert_equals: expected "auto visible" but got "" -FAIL overflow: hidden auto works assert_equals: expected "hidden auto" but got "" -FAIL overflow: hidden hidden works assert_equals: expected "hidden" but got "" -FAIL overflow: hidden scroll works assert_equals: expected "hidden scroll" but got "" -FAIL overflow: hidden visible works assert_equals: expected "hidden visible" but got "" -FAIL overflow: scroll auto works assert_equals: expected "scroll auto" but got "" -FAIL overflow: scroll hidden works assert_equals: expected "scroll hidden" but got "" -FAIL overflow: scroll scroll works assert_equals: expected "scroll" but got "" -FAIL overflow: scroll visible works assert_equals: expected "scroll visible" but got "" -FAIL overflow: visible auto works assert_equals: expected "visible auto" but got "" -FAIL overflow: visible hidden works assert_equals: expected "visible hidden" but got "" -FAIL overflow: visible scroll works assert_equals: expected "visible scroll" but got "" -FAIL overflow: visible visible works assert_equals: expected "visible" but got "" -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-010.html index aedab213..df993be 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-010.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-010.html
@@ -40,7 +40,7 @@ height: 100px; shape-outside: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAALpJREFUeNrs0UEVABAURcHP5pcRSxpR9FHGhhycuQ3emxI9TnxQ7pxttfH6jhoCIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACAsQFQAQEiIAAERAgAgJEQAQEiIAAEZDPuwAAAP//AwCf+AWUylJrCQAAAABJRU5ErkJggg==); shape-margin: 5%; - shape-image-threshold: 0.25; + shape-image-threshold: 0.7; } .blue { width: 2px;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-027.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-027.html index 2141c12..b00f6c47 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-027.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-027.html
@@ -36,7 +36,7 @@ padding: 10px; border: 10px solid transparent; shape-margin: 15px; - shape-outside: margin-box circle(35% at 85px 75px); + shape-outside: margin-box circle(60px); } #line { position: absolute;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-015.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-015.html index 3502f8b..1a434a0 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-015.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-015.html
@@ -34,7 +34,7 @@ margin: 10px; padding: 10px; border: 10px solid transparent; - shape-outside: padding-box ellipse(closest-side at 75px 80px); + shape-outside: padding-box ellipse(closest-side closest-side at 75px 80px); } #line { position: absolute;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-017.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-017.html index 3cf6089f..fd9b816 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-017.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-017.html
@@ -36,7 +36,7 @@ padding: 10px; border: 10px solid transparent; shape-margin: 10px; - shape-outside: content-box ellipse(farthest-side); + shape-outside: content-box ellipse(farthest-side closest-side); } #line { position: absolute;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-010.html index 72147aaf..49627c8 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-010.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-010.html
@@ -19,8 +19,7 @@ #test-container { width: 200px; height: 200px; - font-family: Ahem; - font-size: 25px; + font: 25px/1 Ahem; background-color: red; color: green; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-011.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-011.html index 9a65184..96d2421 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-011.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-011.html
@@ -21,6 +21,7 @@ height: 200px; font-family: Ahem; font-size: 25px; + line-height: 1; background-color: red; color: green; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-012.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-012.html index d7988c2..b9dbe52 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-012.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-012.html
@@ -22,6 +22,7 @@ height: 200px; font-family: Ahem; font-size: 25px; + line-height: 1; background-color: red; color: green; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-013.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-013.html index 03e4baa..d49dd70 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-013.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-013.html
@@ -22,6 +22,7 @@ height: 200px; font-family: Ahem; font-size: 25px; + line-height: 1; background-color: red; color: green; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-014.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-014.html index bb791a5..30604af7 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-014.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-014.html
@@ -22,6 +22,7 @@ height: 200px; font-family: Ahem; font-size: 25px; + line-height: 1; background-color: red; color: green; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-015.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-015.html index b2e0e06..da54fb23 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-015.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-015.html
@@ -22,6 +22,7 @@ height: 200px; font-family: Ahem; font-size: 25px; + line-height: 1; background-color: red; color: green; }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html new file mode 100644 index 0000000..2e0d9ab --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: left float, inset, px units</title> + <link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-shapes-1/#funcdef-inset"> + <link rel="help" href="http://www.w3.org/TR/css-shapes-1/#shape-outside-property"> + <link rel="help" href="http://www.w3.org/TR/css-shapes-1/#shape-margin-property"> + <link rel="match" href="reference/shape-outside-inset-010-ref.html"/> + <meta name="flags" content="ahem" /> + <meta name="assert" content="The test verfies that text flows around a + right float with a shape-outside defined as + an inset rounded rectangle in px units with + a shape-margin."> + </head> + <style> + #container { + position: relative; + margin-left: 25px; + } + #test-container { + width: 200px; + height: 200px; + font: 25px/1 Ahem; + background-color: red; + color: green; + text-align: right; + } + #test-shape { + float: right; + width: 200px; + height: 200px; + background-color: green; + shape-margin: 10px; + shape-outside: inset(60px 10px 60px 110px round 20px); + } + #static-shape { + position: absolute; + left: 100px; + width: 100px; + height: 100px; + top: 50px; + background-color: green; + } + </style> + <body> + <p>The test passes if there is a green square and no red.</p> + <div id="container"> + <div id="test-container"> + <div id="test-shape"></div> + XXXXXXXX XXXXXXXX XXXX XXXX XXXX XXXX XXXXXXXX XXXXXXXX + </div> + <div id="static-shape"></div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html new file mode 100644 index 0000000..fe30d437 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: left float, inset, px units</title> + <link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-shapes-1/#funcdef-inset"> + <link rel="help" href="http://www.w3.org/TR/css-shapes-1/#shape-outside-property"> + <link rel="help" href="http://www.w3.org/TR/css-shapes-1/#shape-margin-property"> + <link rel="match" href="reference/shape-outside-inset-010-ref.html"/> + <meta name="flags" content="ahem" /> + <meta name="assert" content="The test verfies that text flows around a + right float with a shape-outside defined as + an inset irregular elliptically rounded + rectangle in px units with a shape-margin."> + </head> + <style> + #container { + position: relative; + margin-left: 25px; + } + #test-container { + width: 200px; + height: 200px; + font: 25px/1 Ahem; + background-color: red; + color: green; + text-align: right; + } + #test-shape { + float: right; + width: 200px; + height: 200px; + background-color: green; + shape-margin: 10px; + shape-outside: inset(60px 10px 60px 110px round 70px 0px 0px 10px / 10px 0px 0px 20px); + } + #static-shape { + position: absolute; + left: 100px; + width: 100px; + height: 100px; + top: 50px; + background-color: green; + } + </style> + <body> + <p>The test passes if there is a green square and no red.</p> + <div id="container"> + <div id="test-container"> + <div id="test-shape"></div> + XXXXXXXX XXXXXXXX XXXX XXXX XXXX XXXX XXXXXXXX XXXXXXXX + </div> + <div id="static-shape"></div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/index-002-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/index-002-expected.txt deleted file mode 100644 index aa2ca027e..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/index-002-expected.txt +++ /dev/null
@@ -1,22 +0,0 @@ -This is a testharness.js-based test. -PASS border is expected to be border: 1px; -PASS border is expected to be border: 1px solid red; -PASS border is expected to be border: 1px red; -PASS border is expected to be border: red; -FAIL border is expected to be border: 1px; (#2) assert_equals: expected "border: 1px;" but got "border-width: 1px; border-style: initial; border-color: initial;" -FAIL border is expected to be border-width: 1px 2px 3px 4px; assert_equals: expected "border-width: 1px 2px 3px 4px;" but got "border-width: 1px 2px 3px 4px; border-style: initial; border-color: initial;" -FAIL border is expected to be border-width: 2px 1px 1px; assert_equals: expected "border-width: 2px 1px 1px;" but got "border-width: 2px 1px 1px; border-style: initial; border-color: initial; border-image: initial;" -FAIL border is expected to be border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-width: 1px !important; assert_equals: expected "border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-width: 1px !important;" but got "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: initial; border-top: 1px !important;" -FAIL border is expected to be border-width: 1px; border-top-color: red; assert_equals: expected "border-width: 1px; border-top-color: red;" but got "border-width: 1px; border-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-color: red;" -PASS border is expected to be border: dotted; -PASS border is expected to be border-width: 1px; -FAIL overflow is expected to be overflow: scroll hidden; assert_equals: expected "overflow: scroll hidden;" but got "overflow-x: scroll; overflow-y: hidden;" -PASS overflow is expected to be overflow: scroll; -PASS outline is expected to be outline: blue dotted 2px; -PASS margin is expected to be margin: 1px 2px 3px 4px; -PASS list is expected to be list-style: circle inside; -PASS list is expected to be list-style-type: lower-alpha; -PASS font-family is expected to be font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold; -PASS padding is expected to be padding: 1px 2px 3px 4px; -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/overflow-serialization.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/overflow-serialization.html index 7e2d1767..136b8ab 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/overflow-serialization.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/overflow-serialization.html
@@ -2,7 +2,7 @@ <html> <head> <meta charset="utf-8"> - <title>CSSOM - Overlow property has different serialization than other shorthands.</title> + <title>CSSOM - Overflow shorthand serialization</title> <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-value"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -11,7 +11,7 @@ div { overflow: hidden; } div { overflow-x: initial; overflow-y: initial; } div { overflow-x: scroll; overflow-y: scroll; } - div { overflow-x: inherit; overflow-y: unset; } + div { overflow-x: scroll; overflow-y: hidden; } </style> <script> @@ -22,7 +22,7 @@ assert_equals(styleSheet.cssRules[1].style.cssText, "overflow: hidden;", "Single value overflow with non-CSS-wide keyword should serialize correctly."); assert_equals(styleSheet.cssRules[2].style.cssText, "overflow: initial;", "Overflow-x/y longhands with same CSS-wide keyword should serialize correctly."); assert_equals(styleSheet.cssRules[3].style.cssText, "overflow: scroll;", "Overflow-x/y longhands with same non-CSS-wide keyword should serialize correctly."); - assert_equals(styleSheet.cssRules[4].style.cssText, "overflow-x: inherit; overflow-y: unset;", "Overflow-x/y longhands with different keywords should serialize correctly."); + assert_equals(styleSheet.cssRules[4].style.cssText, "overflow: scroll hidden;", "Overflow-x/y longhands with different keywords should serialize correctly."); var div = document.createElement('div'); div.style.overflow = "inherit"; @@ -40,9 +40,9 @@ div.style.overflowY = "scroll"; assert_equals(div.style.overflow, "scroll", "Overflow-x/y longhands with same non-CSS-wide keyword should serialize correctly."); - div.style.overflowX = "inherit"; - div.style.overflowY = "unset"; - assert_equals(div.style.overflow, "", "Overflow-x/y longhands with different keywords shouldn't serialize."); + div.style.overflowX = "scroll"; + div.style.overflowY = "hidden"; + assert_equals(div.style.overflow, "scroll hidden", "Overflow-x/y longhands with different keywords should serialize correctly."); }); </script> </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/shorthand-values.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/shorthand-values.html index eb756b5..d8d7f53 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/shorthand-values.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/shorthand-values.html
@@ -32,7 +32,7 @@ 'border: 1px; border-top-color: red;': 'border-width: 1px; border-top-color: red;', 'border: solid; border-style: dotted': 'border: dotted;', 'border-width: 1px;': 'border-width: 1px;', - 'overflow-x: scroll; overflow-y: hidden;': 'overflow-x: scroll; overflow-y: hidden;', + 'overflow-x: scroll; overflow-y: hidden;': 'overflow: scroll hidden;', 'overflow-x: scroll; overflow-y: scroll;': 'overflow: scroll;', 'outline-width: 2px; outline-style: dotted; outline-color: blue;': 'outline: blue dotted 2px;', 'margin-top: 1px; margin-right: 2px; margin-bottom: 3px; margin-left: 4px;': 'margin: 1px 2px 3px 4px;',
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001-expected.txt deleted file mode 100644 index 0979e47..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001-expected.txt +++ /dev/null
@@ -1,38 +0,0 @@ -This is a testharness.js-based test. -PASS testConstructor0: points -PASS testConstructor0: bounds -PASS testConstructor1 -PASS testConstructor2 -PASS testConstructor3 -PASS testConstructor4 -FAIL testConstructor5: points assert_equals: Expected value for p2.x is 110 expected 110 but got 0 -FAIL testConstructor5: bounds assert_equals: Expected value for getBounds().x is 10 expected 10 but got 0 -FAIL testConstructor6: points assert_equals: Expected value for p2.x is -90 expected -90 but got 0 -FAIL testConstructor6: bounds assert_equals: Expected value for getBounds().x is -90 expected -90 but got 0 -FAIL testConstructor7: points assert_equals: Expected value for p2.x is NaN expected NaN but got 0 -FAIL testConstructor7: bounds assert_equals: Expected value for getBounds().width is NaN expected NaN but got Infinity -PASS testConstructor8: points -PASS testConstructor8: bounds -PASS testConstructor9: points -PASS testConstructor9: bounds -PASS testConstructor10: points -PASS testConstructor10: bounds -PASS testConstructor11: points -PASS testConstructor11: bounds -PASS testConstructor12: points -PASS testConstructor12: bounds -PASS testConstructor13: points -PASS testConstructor13: bounds -PASS testConstructor14: points -PASS testConstructor14: bounds -PASS testConstructor15 -PASS testConstructor16: points -PASS testConstructor16: bounds -PASS p1Top4Attributes0: points -PASS p1Top4Attributes0: bounds -PASS p1Top4Attributes1: points -FAIL p1Top4Attributes1: bounds assert_equals: Expected value for getBounds().x is 2 expected 2 but got 0 -PASS boundsAttribute0: points -PASS boundsAttribute0: bounds -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001.html index cfeaed7..4cafa02 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMQuad-001.html
@@ -44,31 +44,31 @@ },'testConstructor4'); checkDOMQuad( - function() { return new DOMQuad(new DOMRect(10, 20, 100, 200)); }, + function() { return DOMQuad.fromRect(new DOMRect(10, 20, 100, 200)); }, { p1: { x: 10, y: 20, z: 0, w: 1 }, p2: { x: 110, y: 20, z: 0, w: 1 }, p3: { x: 110, y: 220, z: 0, w: 1 }, p4: { x: 10, y: 220, z: 0, w: 1 }, bounds: { x: 10, y: 20, width: 100, height: 200 } }, - 'testConstructor5'); + 'fromRect() method on DOMQuad'); checkDOMQuad( - function() { return new DOMQuad(new DOMRect(10, 20, -100, -200)) }, + function() { return DOMQuad.fromRect(new DOMRect(10, 20, -100, -200)) }, { p1: { x: 10, y: 20, z: 0, w: 1 }, p2: { x: -90, y: 20, z: 0, w: 1 }, p3: { x: -90, y: -180, z: 0, w: 1 }, p4: { x: 10, y: -180, z: 0, w: 1 }, bounds: { x: -90, y: -180, width: 100, height: 200 } }, - 'testConstructor6'); + 'fromRect() method on DOMQuad with negatives'); checkDOMQuad( - function() { return new DOMQuad(new DOMRect(-Infinity, -Infinity, Infinity, Infinity)) }, + function() { return DOMQuad.fromRect(new DOMRect(-Infinity, -Infinity, Infinity, Infinity)) }, { p1: { x: -Infinity, y: -Infinity, z: 0, w: 1 }, p2: { x: NaN, y: -Infinity, z: 0, w: 1 }, p3: { x: NaN, y: NaN, z: 0, w: 1 }, p4: { x: -Infinity, y: NaN, z: 0, w: 1 }, bounds: { x: -Infinity, y: -Infinity, width: NaN, height: NaN } }, - 'testConstructor7'); + 'fromRect() method on DOMQuad with Infinity'); checkDOMQuad(function() { return new DOMQuad(new DOMRect()); }, initial, 'testConstructor8'); @@ -120,15 +120,9 @@ p2: { x: 2, y: 0, z: 0, w: 1 }, p3: { x: 2, y: 0, z: 0, w: 1 }, p4: { x: 2, y: 0, z: 0, w: 1 }, - bounds: { x: 2, y: 0, width: 0, height: 0 } }, + bounds: { x: 0, y: 0, width: 0, height: 0 } }, 'p1Top4Attributes1'); - checkDOMQuad(function() { - var q = new DOMQuad({}, {}, {}, {}); - q.bounds = new DOMRect(10, 10, 100, 100); - return q; - }, initial, 'boundsAttribute0'); - function checkDOMQuad(createQuad, exp, name) { test(function() { var q = createQuad();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList-expected.txt index 0d9e61c4..e95a14e 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList-expected.txt
@@ -1,6 +1,6 @@ This is a testharness.js-based test. -PASS DOMRectList [NoInterfaceObject] -FAIL DOMRectList [LegacyArrayClass] assert_true: expected true got false +FAIL DOMRectList is not [NoInterfaceObject] assert_true: expected true got false +PASS DOMRectList is not [LegacyArrayClass] PASS DOMRectList length PASS DOMRectList indexed getter PASS DOMRectList item()
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList.html b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList.html index f128a05..f3d050c 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList.html +++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/DOMRectList.html
@@ -10,12 +10,12 @@ }); test(() => { - assert_false('DOMRectList' in window); -}, 'DOMRectList [NoInterfaceObject]'); + assert_true('DOMRectList' in window); +}, 'DOMRectList is not [NoInterfaceObject]'); test(() => { - assert_true(domRectList instanceof Array); -}, 'DOMRectList [LegacyArrayClass]'); + assert_false(domRectList instanceof Array); +}, 'DOMRectList is not [LegacyArrayClass]'); test(() => { assert_equals(domRectList.length, 1);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/historical-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/historical-expected.txt index bf2c547..fdfba77 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/dom/historical-expected.txt +++ b/third_party/WebKit/LayoutTests/external/wpt/dom/historical-expected.txt
@@ -1,12 +1,14 @@ This is a testharness.js-based test. -Found 72 tests; 68 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 74 tests; 70 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS Historical DOM features must be removed: DOMConfiguration +PASS Historical DOM features must be removed: DOMCursor FAIL Historical DOM features must be removed: DOMError assert_equals: expected (undefined) undefined but got (function) function "function DOMError() { [native code] }" PASS Historical DOM features must be removed: DOMErrorHandler PASS Historical DOM features must be removed: DOMImplementationList PASS Historical DOM features must be removed: DOMImplementationSource PASS Historical DOM features must be removed: DOMLocator PASS Historical DOM features must be removed: DOMObject +PASS Historical DOM features must be removed: DOMRequest PASS Historical DOM features must be removed: DOMSettableTokenList PASS Historical DOM features must be removed: DOMUserData PASS Historical DOM features must be removed: Entity
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/historical.html b/third_party/WebKit/LayoutTests/external/wpt/dom/historical.html index 388366c..b45bebf 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/dom/historical.html +++ b/third_party/WebKit/LayoutTests/external/wpt/dom/historical.html
@@ -12,12 +12,14 @@ } var nukedInterfaces = [ "DOMConfiguration", + "DOMCursor", "DOMError", "DOMErrorHandler", "DOMImplementationList", "DOMImplementationSource", "DOMLocator", "DOMObject", + "DOMRequest", "DOMSettableTokenList", "DOMUserData", "Entity",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/platform/linux/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt rename to third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt
diff --git a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html b/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html deleted file mode 100644 index 1a79c6f..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html +++ /dev/null
@@ -1,181 +0,0 @@ -<!DOCTYPE html> -<html> <!-- DOESN'T WORK, NOT SURE WHY - THERE ARE 11 CHARACTERS PRODUCED, BUT ALL ARE UNASSIGNED CODE POINTS, SO SAFE TO IGNORE FOR NOW --> -<head> -<title>EUC-KR encoding errors (form, hangul)</title> -<meta charset="euc-kr"> <!-- test breaks if the server overrides this --> -<meta name="timeout" content="long"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="euckr_index.js"></script> -<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org"> -<link rel="help" href="https://encoding.spec.whatwg.org/#euc-kr"> -<meta name="assert" content="The browser produces percent-escaped character references when encoding bytes for a URL produced by a form when encoding hangul characters that are not in the euc-kr encoding."> -<style> - iframe { display:none } - form { display:none } -</style> -</head> -<body> -<div id="log"></div> -<script> -var tests = []; -var cplist = []; -var numTests = null; -var numFrames = 2; -var chunkSize = 400; -var numChunks = null; -var frames = null; -var frames = null; -var forms = null; -var seperator = ","; -var encodedSeperator = encodeURIComponent(","); -var currentChunkIndex = 0; - -function getByteSequence(cp) { - // uses the Encoding spec algorithm to derive a sequence of bytes for a character that can be encoded - // the result is either a percent-encoded value or null (if the character can't be encoded) - // cp: integer, a code point number - var cps = [cp]; - var out = ""; - - while (cps.length > 0) { - cp = cps.shift(); - if (cp >= 0x00 && cp <= 0x7f) { - // ASCII - out += "%" + cp.toString(16); - continue; - } - var ptr = indexcodepoints[cp]; - if (ptr == null) { - return null; - } - var lead = Math.floor(ptr / 190) + 0x81; - var trail = ptr % 190 + 0x41; - out += - "%" + - lead.toString(16).toUpperCase() + - "%" + - trail.toString(16).toUpperCase(); - } - return out; -} - -// set up a sparse array of all unicode codepoints listed in the index -// this will be used for lookup in getByteSequence -var indexcodepoints = []; // index is unicode cp, value is pointer -for (p = 0; p < euckr.length; p++) { - if (euckr[p] != null && indexcodepoints[euckr[p]] == null) { - indexcodepoints[euckr[p]] = p; - } -} - -setup(function() { - // set up a simple array of all unicode codepoints that are not encoded - var codepoints = []; - - for (i = 0xac00; i < 0xd7af; i++) { - result = getByteSequence(i); - if (!result) { - var item = {}; - codepoints.push(item); - item.cp = i; - item.expected = "%26%23" + item.cp + "%3B"; - item.desc = "hangul "; - } - } - - // convert the information into a simple array of objects that can be easily traversed - var currentChunk = []; - var currentTests = []; - cplist = [currentChunk]; - tests = [currentTests]; - for (i = 161; i < codepoints.length; i++) { - if (currentChunk.length == chunkSize) { - currentChunk = []; - cplist.push(currentChunk); - currentTests = []; - tests.push(currentTests); - } - var item = {}; - currentChunk.push(item); - item.cp = codepoints[i].cp; - item.expected = codepoints[i].expected; - item.desc = codepoints[i].desc; - currentTests.push( - async_test( - item.desc + - " U+" + - item.cp.toString(16).toUpperCase() + - " " + - String.fromCodePoint(item.cp) + - item.expected - ) - ); - } - - numChunks = cplist.length; - - for (var i = 0; i < numFrames; i++) { - var frame = document.createElement("iframe"); - frame.id = frame.name = "frame-" + i; - document.body.appendChild(frame); - var form = document.createElement("form"); - form.id = "form-" + i; - form.method = "GET"; - form.action = "/common/blank.html"; - form.acceptCharset = "euc-kr"; - form.target = frame.id; - var input = document.createElement("input"); - input.id = input.name = "input-" + i; - form.appendChild(input); - document.body.appendChild(form); - } - - addEventListener("load", function() { - frames = Array.prototype.slice.call( - document.getElementsByTagName("iframe") - ); - forms = Array.prototype.slice.call(document.getElementsByTagName("form")); - inputs = Array.prototype.slice.call(document.getElementsByTagName("input")); - for (var i = 0; i < Math.min(numFrames, numChunks); i++) { - runNext(i); - } - }); -}); - -function runNext(id) { - var i = currentChunkIndex; - currentChunkIndex += 1; - - var iframe = frames[id]; - var form = forms[id]; - var input = inputs[id]; - - input.value = cplist[i] - .map(function(x) { - return String.fromCodePoint(x.cp); - }) - .join(seperator); - form.submit(); - - iframe.onload = function() { - var url = iframe.contentWindow.location; - var query = url.search; - var result_string = query.substr(query.indexOf("=") + 1); - var results = result_string.split(encodedSeperator); - - for (var j = 0; j < cplist[i].length; j++) { - var t = tests[i][j]; - t.step(function() { - assert_equals(results[j], cplist[i][j].expected); // HERE'S THE TEST - }); - t.done(); - } - if (currentChunkIndex < numChunks) { - runNext(id); - } - }; -} -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt deleted file mode 100644 index 8772599b..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt +++ /dev/null
@@ -1,132 +0,0 @@ -This is a testharness.js-based test. -Found 127 tests; 123 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. -PASS big5 encoder: test for ASCII codepoint 0x0 \0 -PASS big5 encoder: test for ASCII codepoint 0x1 -PASS big5 encoder: test for ASCII codepoint 0x2 -PASS big5 encoder: test for ASCII codepoint 0x3 -PASS big5 encoder: test for ASCII codepoint 0x4 -PASS big5 encoder: test for ASCII codepoint 0x5 -PASS big5 encoder: test for ASCII codepoint 0x6 -PASS big5 encoder: test for ASCII codepoint 0x7 -PASS big5 encoder: test for ASCII codepoint 0x8 -FAIL big5 encoder: test for ASCII codepoint 0x9 assert_true: expected true got false -FAIL big5 encoder: test for ASCII codepoint 0xa - assert_true: expected true got false -PASS big5 encoder: test for ASCII codepoint 0xb -PASS big5 encoder: test for ASCII codepoint 0xc -FAIL big5 encoder: test for ASCII codepoint 0xd \r assert_true: expected true got false -PASS big5 encoder: test for ASCII codepoint 0xe -PASS big5 encoder: test for ASCII codepoint 0xf -PASS big5 encoder: test for ASCII codepoint 0x10 -PASS big5 encoder: test for ASCII codepoint 0x11 -PASS big5 encoder: test for ASCII codepoint 0x12 -PASS big5 encoder: test for ASCII codepoint 0x13 -PASS big5 encoder: test for ASCII codepoint 0x14 -PASS big5 encoder: test for ASCII codepoint 0x15 -PASS big5 encoder: test for ASCII codepoint 0x16 -PASS big5 encoder: test for ASCII codepoint 0x17 -PASS big5 encoder: test for ASCII codepoint 0x18 -PASS big5 encoder: test for ASCII codepoint 0x19 -PASS big5 encoder: test for ASCII codepoint 0x1a -PASS big5 encoder: test for ASCII codepoint 0x1b -PASS big5 encoder: test for ASCII codepoint 0x1c -PASS big5 encoder: test for ASCII codepoint 0x1d -PASS big5 encoder: test for ASCII codepoint 0x1e -PASS big5 encoder: test for ASCII codepoint 0x1f -PASS big5 encoder: test for ASCII codepoint 0x20 -PASS big5 encoder: test for ASCII codepoint 0x21 ! -PASS big5 encoder: test for ASCII codepoint 0x22 " -FAIL big5 encoder: test for ASCII codepoint 0x23 # assert_true: expected true got false -PASS big5 encoder: test for ASCII codepoint 0x24 $ -PASS big5 encoder: test for ASCII codepoint 0x25 % -PASS big5 encoder: test for ASCII codepoint 0x26 & -PASS big5 encoder: test for ASCII codepoint 0x27 ' -PASS big5 encoder: test for ASCII codepoint 0x28 ( -PASS big5 encoder: test for ASCII codepoint 0x29 ) -PASS big5 encoder: test for ASCII codepoint 0x2a * -PASS big5 encoder: test for ASCII codepoint 0x2b + -PASS big5 encoder: test for ASCII codepoint 0x2c , -PASS big5 encoder: test for ASCII codepoint 0x2d - -PASS big5 encoder: test for ASCII codepoint 0x2e . -PASS big5 encoder: test for ASCII codepoint 0x2f / -PASS big5 encoder: test for ASCII codepoint 0x30 0 -PASS big5 encoder: test for ASCII codepoint 0x31 1 -PASS big5 encoder: test for ASCII codepoint 0x32 2 -PASS big5 encoder: test for ASCII codepoint 0x33 3 -PASS big5 encoder: test for ASCII codepoint 0x34 4 -PASS big5 encoder: test for ASCII codepoint 0x35 5 -PASS big5 encoder: test for ASCII codepoint 0x36 6 -PASS big5 encoder: test for ASCII codepoint 0x37 7 -PASS big5 encoder: test for ASCII codepoint 0x38 8 -PASS big5 encoder: test for ASCII codepoint 0x39 9 -PASS big5 encoder: test for ASCII codepoint 0x3a : -PASS big5 encoder: test for ASCII codepoint 0x3b ; -PASS big5 encoder: test for ASCII codepoint 0x3c < -PASS big5 encoder: test for ASCII codepoint 0x3d = -PASS big5 encoder: test for ASCII codepoint 0x3e > -PASS big5 encoder: test for ASCII codepoint 0x3f ? -PASS big5 encoder: test for ASCII codepoint 0x40 @ -PASS big5 encoder: test for ASCII codepoint 0x41 A -PASS big5 encoder: test for ASCII codepoint 0x42 B -PASS big5 encoder: test for ASCII codepoint 0x43 C -PASS big5 encoder: test for ASCII codepoint 0x44 D -PASS big5 encoder: test for ASCII codepoint 0x45 E -PASS big5 encoder: test for ASCII codepoint 0x46 F -PASS big5 encoder: test for ASCII codepoint 0x47 G -PASS big5 encoder: test for ASCII codepoint 0x48 H -PASS big5 encoder: test for ASCII codepoint 0x49 I -PASS big5 encoder: test for ASCII codepoint 0x4a J -PASS big5 encoder: test for ASCII codepoint 0x4b K -PASS big5 encoder: test for ASCII codepoint 0x4c L -PASS big5 encoder: test for ASCII codepoint 0x4d M -PASS big5 encoder: test for ASCII codepoint 0x4e N -PASS big5 encoder: test for ASCII codepoint 0x4f O -PASS big5 encoder: test for ASCII codepoint 0x50 P -PASS big5 encoder: test for ASCII codepoint 0x51 Q -PASS big5 encoder: test for ASCII codepoint 0x52 R -PASS big5 encoder: test for ASCII codepoint 0x53 S -PASS big5 encoder: test for ASCII codepoint 0x54 T -PASS big5 encoder: test for ASCII codepoint 0x55 U -PASS big5 encoder: test for ASCII codepoint 0x56 V -PASS big5 encoder: test for ASCII codepoint 0x57 W -PASS big5 encoder: test for ASCII codepoint 0x58 X -PASS big5 encoder: test for ASCII codepoint 0x59 Y -PASS big5 encoder: test for ASCII codepoint 0x5a Z -PASS big5 encoder: test for ASCII codepoint 0x5b [ -PASS big5 encoder: test for ASCII codepoint 0x5c \ -PASS big5 encoder: test for ASCII codepoint 0x5d ] -PASS big5 encoder: test for ASCII codepoint 0x5e ^ -PASS big5 encoder: test for ASCII codepoint 0x5f _ -PASS big5 encoder: test for ASCII codepoint 0x60 ` -PASS big5 encoder: test for ASCII codepoint 0x61 a -PASS big5 encoder: test for ASCII codepoint 0x62 b -PASS big5 encoder: test for ASCII codepoint 0x63 c -PASS big5 encoder: test for ASCII codepoint 0x64 d -PASS big5 encoder: test for ASCII codepoint 0x65 e -PASS big5 encoder: test for ASCII codepoint 0x66 f -PASS big5 encoder: test for ASCII codepoint 0x67 g -PASS big5 encoder: test for ASCII codepoint 0x68 h -PASS big5 encoder: test for ASCII codepoint 0x69 i -PASS big5 encoder: test for ASCII codepoint 0x6a j -PASS big5 encoder: test for ASCII codepoint 0x6b k -PASS big5 encoder: test for ASCII codepoint 0x6c l -PASS big5 encoder: test for ASCII codepoint 0x6d m -PASS big5 encoder: test for ASCII codepoint 0x6e n -PASS big5 encoder: test for ASCII codepoint 0x6f o -PASS big5 encoder: test for ASCII codepoint 0x70 p -PASS big5 encoder: test for ASCII codepoint 0x71 q -PASS big5 encoder: test for ASCII codepoint 0x72 r -PASS big5 encoder: test for ASCII codepoint 0x73 s -PASS big5 encoder: test for ASCII codepoint 0x74 t -PASS big5 encoder: test for ASCII codepoint 0x75 u -PASS big5 encoder: test for ASCII codepoint 0x76 v -PASS big5 encoder: test for ASCII codepoint 0x77 w -PASS big5 encoder: test for ASCII codepoint 0x78 x -PASS big5 encoder: test for ASCII codepoint 0x79 y -PASS big5 encoder: test for ASCII codepoint 0x7a z -PASS big5 encoder: test for ASCII codepoint 0x7b { -PASS big5 encoder: test for ASCII codepoint 0x7c | -PASS big5 encoder: test for ASCII codepoint 0x7d } -PASS big5 encoder: test for ASCII codepoint 0x7e ~ -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity-sharedworker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity-sharedworker.html deleted file mode 100644 index fa90a60..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity-sharedworker.html +++ /dev/null
@@ -1,15 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch in sharedworker: integrity handling</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script> - fetch_tests_from_worker(new SharedWorker("integrity.js?pipe=sub")); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity-worker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity-worker.html deleted file mode 100644 index 9240bc6..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity-worker.html +++ /dev/null
@@ -1,16 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch in worker: integrity handling</title> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script> - fetch_tests_from_worker(new Worker("integrity.js?pipe=sub")); - </script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.html deleted file mode 100644 index 150c9b7..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.html +++ /dev/null
@@ -1,15 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch: integrity handling</title> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script src="../resources/utils.js"></script> - <script src="integrity.js?pipe=sub"></script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.sub.any.js similarity index 96% rename from third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.js rename to third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.sub.any.js index fb3f252..d487c37 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.js +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/integrity.sub.any.js
@@ -1,7 +1,5 @@ -if (this.document === undefined) { - importScripts("/resources/testharness.js"); - importScripts("../resources/utils.js"); -} +// META: global=sharedworker +// META: script=../resources/utils.js function integrity(desc, url, integrity, initRequestMode, shouldPass) { var fetchRequestInit = {'integrity': integrity}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors-worker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors-worker.html deleted file mode 100644 index 87376a130..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors-worker.html +++ /dev/null
@@ -1,17 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch in worker: no-cors mode and opaque filtering</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <meta name="help" href="https://fetch.spec.whatwg.org/#concept-filtered-response-opaque"> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script> - fetch_tests_from_worker(new Worker("mode-no-cors.js?pipe=sub")); - </script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.html deleted file mode 100644 index 7aee3790..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.html +++ /dev/null
@@ -1,16 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch: no-cors mode and opaque filtering</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <meta name="help" href="https://fetch.spec.whatwg.org/#concept-filtered-response-opaque"> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script src="../resources/utils.js"></script> - <script src="mode-no-cors.js?pipe=sub"></script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.sub.any.js similarity index 90% rename from third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.js rename to third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.sub.any.js index 53e8490..709eef5 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.js +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/mode-no-cors.sub.any.js
@@ -1,7 +1,4 @@ -if (this.document === undefined) { - importScripts("/resources/testharness.js"); - importScripts("../resources/utils.js"); -} +// META: script=../resources/utils.js function fetchNoCors(url, isOpaqueFiltered) { var urlQuery = "?pipe=header(x-is-filtered,value)"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url-worker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url-worker.html deleted file mode 100644 index 03374e0f..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url-worker.html +++ /dev/null
@@ -1,15 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch in worker: response url getter</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#response-class"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script> - fetch_tests_from_worker(new Worker("response-url.js?pipe=sub")); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.html deleted file mode 100644 index dfe9d96..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.html +++ /dev/null
@@ -1,13 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch: response url getter</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#response-class"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script src="response-url.js?pipe=sub"></script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.sub.any.js similarity index 85% rename from third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.js rename to third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.sub.any.js index 91b553aa..0d123c42 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.js +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/response-url.sub.any.js
@@ -1,7 +1,3 @@ -if (this.document === undefined) { - importScripts("/resources/testharness.js"); -} - function checkResponseURL(fetchedURL, expectedURL) { promise_test(function() { @@ -18,4 +14,3 @@ checkResponseURL(baseURL + "#ada", baseURL + "/"); done(); -
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob-worker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob-worker.html deleted file mode 100644 index 961ecbd5..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob-worker.html +++ /dev/null
@@ -1,17 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch in worker: blob scheme</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <meta name="help" href="https://fetch.spec.whatwg.org/#basic-fetch"> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script> - fetch_tests_from_worker(new Worker("scheme-blob.js?pipe=sub")); - </script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.html deleted file mode 100644 index 7787c37..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.html +++ /dev/null
@@ -1,16 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch: blob scheme</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <meta name="help" href="https://fetch.spec.whatwg.org/#basic-fetch"> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script src="../resources/utils.js"></script> - <script src="scheme-blob.js?pipe=sub"></script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.sub.any.js similarity index 92% rename from third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.js rename to third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.sub.any.js index 9bf73a69..fb1357e 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.js +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-blob.sub.any.js
@@ -1,7 +1,4 @@ -if (this.document === undefined) { - importScripts("/resources/testharness.js"); - importScripts("../resources/utils.js"); -} +// META: script=../resources/utils.js function checkFetchResponse(url, data, mime, size, desc) { promise_test(function(test) {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others-worker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others-worker.html deleted file mode 100644 index 397d925..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others-worker.html +++ /dev/null
@@ -1,17 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch in worker: urls with unsupported schemes</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <meta name="help" href="https://fetch.spec.whatwg.org/#basic-fetch"> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script> - fetch_tests_from_worker(new Worker("scheme-others.js?pipe=sub")); - </script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.html deleted file mode 100644 index dd37143b..0000000 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.html +++ /dev/null
@@ -1,16 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>Fetch: urls with unsupported schemes</title> - <meta name="help" href="https://fetch.spec.whatwg.org/#main-fetch"> - <meta name="help" href="https://fetch.spec.whatwg.org/#basic-fetch"> - <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - </head> - <body> - <script src="../resources/utils.js"></script> - <script src="scheme-others.js?pipe=sub"></script> - </body> -</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.sub.any.js similarity index 87% rename from third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.js rename to third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.sub.any.js index ce02ec1..5f9848ff 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.js +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/basic/scheme-others.sub.any.js
@@ -1,7 +1,4 @@ -if (this.document === undefined) { - importScripts("/resources/testharness.js"); - importScripts("../resources/utils.js"); -} +// META: script=../resources/utils.js function checkKoUrl(url, desc) { if (!desc)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/README.md b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/README.md index f16943eb..04c5b8a 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/README.md +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/README.md
@@ -26,18 +26,8 @@ is included in the official [Fetch spec](https://fetch.spec.whatwg.org/). Such tests may fail unless CORB is enabled. In practice this means that: -* Such tests will fail in default Chromium and have to be listed - in `third_party/WebKit/LayoutTests/TestExpectations` and associated - with https://crbug.com/802835. -* Such tests will pass in Chromium when either - 1) CORB is explicitly, manually enabled by passing extra cmdline flags to - `run-webkit-tests`: - `--additional-driver-flag=--enable-features=CrossSiteDocumentBlockingAlways` and - `--additional-expectations=third_party/WebKit/LayoutTests/FlagExpectations/site-per-process`. - 2) CORB is implicitly enabled via Site Isolation (e.g. in - `site_per_process_webkit_layout_tests` step on the test bots). The - expectations that the tests pass in this mode is controlled by the - `third_party/WebKit/LayoutTests/FlagExpectations/site-per-process` file. +* Such tests will pass in Chromium + (where CORB is enabled by default [since M68](https://crrev.com/553830)). * Such tests may fail in other browsers.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html index 4ed8f01..7ccc41b4 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html +++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/img-mime-types-coverage.tentative.sub.html
@@ -28,7 +28,7 @@ // - https://tools.ietf.org/html/rfc7303 "text/x-json", "text/json+blah", "application/json+blah", "text/xml+blah", "application/xml+blah", - "application/blahjson", "text/blahxml", + "application/blahjson", "text/blahxml"] var fails = [ // CORB-protected MIME-types - i.e. ones covered by:
diff --git a/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-darkblue-bg.svg b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-darkblue-bg.svg new file mode 100644 index 0000000..49f374c --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-darkblue-bg.svg
@@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 762 762"> +<rect x="0" y="0" fill="#003C56" width="762" height="762"/> +<rect x="183.9" y="511.4" fill="#FFFFFF" width="201.5" height="38"/> +<path fill="#FFFFFF" d="M504.9,549.4c37.3,0,67.6-30.3,67.6-67.6v-19h-38v19c0,16.3-13.3,29.6-29.6,29.6c-16.3,0-29.6-13.3-29.6-29.6 +V287.4h-38v56.5h-51.9v-92.6h112.4c25.5,0,46.3,20.8,46.3,46.3c0,20-13,37.8-31.8,44v39.1c40-6.9,69.8-42.1,69.8-83 +c0-46.5-37.8-84.3-84.3-84.3H366.4c-10.5,0-19,8.5-19,19V386l-46.4-79.4c-3.4-5.8-9.7-9.4-16.4-9.4c-6.7,0-13,3.6-16.4,9.4 +l-46.2,79.2V213.4h-38v242.7c0,8.6,5.8,16.1,14,18.3c8.3,2.2,17.1-1.4,21.4-8.8l65.2-111.8L350,465.7c3.4,5.8,9.7,9.4,16.4,9.4 +c1.7,0,3.3-0.2,5-0.7c8.3-2.2,14-9.8,14-18.3v-74.2h51.9v99.8C437.3,519.1,467.6,549.4,504.9,549.4z"/> +</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-darkblue.svg b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-darkblue.svg new file mode 100644 index 0000000..2db07da --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-darkblue.svg
@@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 398 340"> +<rect x="0" y="300" fill="#003C56" width="201.5" height="38"/> +<path fill="#003C56" d="M320.9,338c37.3,0,67.6-30.3,67.6-67.6v-19h-38v19c0,16.3-13.3,29.6-29.6,29.6c-16.3,0-29.6-13.3-29.6-29.6V76 +h-38v56.5h-51.9V40h112.4c25.5,0,46.3,20.8,46.3,46.3c0,20-13,37.8-31.8,44v39.1c40-6.9,69.8-42.1,69.8-83 +C398,39.8,360.2,2,313.8,2H182.4c-10.5,0-19,8.5-19,19v153.6l-46.4-79.4c-3.4-5.8-9.7-9.4-16.4-9.4c-6.7,0-13,3.6-16.4,9.4 +l-46.2,79.2V2h-38v242.7c0,8.6,5.8,16.1,14,18.3c8.3,2.2,17.1-1.4,21.4-8.8l65.2-111.8L166,254.3c3.4,5.8,9.7,9.4,16.4,9.4 +c1.7,0,3.3-0.2,5-0.7c8.3-2.2,14-9.8,14-18.3v-74.2h51.9v99.8C253.3,307.7,283.6,338,320.9,338z"/> +</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-lightblue-bg.svg b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-lightblue-bg.svg new file mode 100644 index 0000000..2f61672 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-lightblue-bg.svg
@@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 762 762"> +<rect x="0" y="0" fill="#78D9F4" width="762" height="762"/> +<rect x="183.9" y="511.4" fill="#003C56" width="201.5" height="38"/> +<path fill="#003C56" d="M504.9,549.4c37.3,0,67.6-30.3,67.6-67.6v-19h-38v19c0,16.3-13.3,29.6-29.6,29.6c-16.3,0-29.6-13.3-29.6-29.6 +V287.4h-38v56.5h-51.9v-92.6h112.4c25.5,0,46.3,20.8,46.3,46.3c0,20-13,37.8-31.8,44v39.1c40-6.9,69.8-42.1,69.8-83 +c0-46.5-37.8-84.3-84.3-84.3H366.4c-10.5,0-19,8.5-19,19V386l-46.4-79.4c-3.4-5.8-9.7-9.4-16.4-9.4c-6.7,0-13,3.6-16.4,9.4 +l-46.2,79.2V213.4h-38v242.7c0,8.6,5.8,16.1,14,18.3c8.3,2.2,17.1-1.4,21.4-8.8l65.2-111.8L350,465.7c3.4,5.8,9.7,9.4,16.4,9.4 +c1.7,0,3.3-0.2,5-0.7c8.3-2.2,14-9.8,14-18.3v-74.2h51.9v99.8C437.3,519.1,467.6,549.4,504.9,549.4z"/> +</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-orange-bg.svg b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-orange-bg.svg new file mode 100644 index 0000000..fde2c15 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-orange-bg.svg
@@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 762 762"> +<rect x="0" y="0" fill="#E86E43" width="762" height="762"/> +<rect x="183.9" y="511.4" fill="#FFFFFF" width="201.5" height="38"/> +<path fill="#FFFFFF" d="M504.9,549.4c37.3,0,67.6-30.3,67.6-67.6v-19h-38v19c0,16.3-13.3,29.6-29.6,29.6c-16.3,0-29.6-13.3-29.6-29.6 +V287.4h-38v56.5h-51.9v-92.6h112.4c25.5,0,46.3,20.8,46.3,46.3c0,20-13,37.8-31.8,44v39.1c40-6.9,69.8-42.1,69.8-83 +c0-46.5-37.8-84.3-84.3-84.3H366.4c-10.5,0-19,8.5-19,19V386l-46.4-79.4c-3.4-5.8-9.7-9.4-16.4-9.4c-6.7,0-13,3.6-16.4,9.4 +l-46.2,79.2V213.4h-38v242.7c0,8.6,5.8,16.1,14,18.3c8.3,2.2,17.1-1.4,21.4-8.8l65.2-111.8L350,465.7c3.4,5.8,9.7,9.4,16.4,9.4 +c1.7,0,3.3-0.2,5-0.7c8.3-2.2,14-9.8,14-18.3v-74.2h51.9v99.8C437.3,519.1,467.6,549.4,504.9,549.4z"/> +</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-white.svg b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-white.svg new file mode 100644 index 0000000..4cab7b5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/images/wpt-logo/wpt-logo-white.svg
@@ -0,0 +1,8 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 398 340"> +<rect x="0" y="300" fill="#FFFFFF" width="201.5" height="38"/> +<path fill="#FFFFFF" d="M320.9,338c37.3,0,67.6-30.3,67.6-67.6v-19h-38v19c0,16.3-13.3,29.6-29.6,29.6c-16.3,0-29.6-13.3-29.6-29.6V76 +h-38v56.5h-51.9V40h112.4c25.5,0,46.3,20.8,46.3,46.3c0,20-13,37.8-31.8,44v39.1c40-6.9,69.8-42.1,69.8-83 +C398,39.8,360.2,2,313.8,2H182.4c-10.5,0-19,8.5-19,19v153.6l-46.4-79.4c-3.4-5.8-9.7-9.4-16.4-9.4c-6.7,0-13,3.6-16.4,9.4 +l-46.2,79.2V2h-38v242.7c0,8.6,5.8,16.1,14,18.3c8.3,2.2,17.1-1.4,21.4-8.8l65.2-111.8L166,254.3c3.4,5.8,9.7,9.4,16.4,9.4 +c1.7,0,3.3-0.2,5-0.7c8.3-2.2,14-9.8,14-18.3v-74.2h51.9v99.8C253.3,307.7,283.6,338,320.9,338z"/> +</svg>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js b/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js index c5c0c98..fe72395 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
@@ -2166,7 +2166,7 @@ //@{ { var interfaceName = this.name; - var isPairIterator = member.idlType instanceof Array; + var isPairIterator = member.idlType.length === 2; test(function() { var descriptor = Object.getOwnPropertyDescriptor(self[interfaceName].prototype, Symbol.iterator);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js b/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js index ec0090c..39a467c3 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/testharness.js
@@ -13,7 +13,7 @@ /* Documentation: http://web-platform-tests.org/writing-tests/testharness-api.html * (../docs/_writing-tests/testharness-api.md) */ -(function () +(function (global_scope) { var debug = false; // default timeout is 10 seconds, test can override if needed @@ -48,9 +48,6 @@ * * // Should return the test harness timeout duration in milliseconds. * float test_timeout(); - * - * // Should return the global scope object. - * object global_scope(); * }; */ @@ -248,10 +245,6 @@ return settings.harness_timeout.normal; }; - WindowTestEnvironment.prototype.global_scope = function() { - return window; - }; - /* * Base TestEnvironment implementation for a generic web worker. * @@ -344,10 +337,6 @@ return null; }; - WorkerTestEnvironment.prototype.global_scope = function() { - return self; - }; - /* * Dedicated web workers. * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope @@ -463,23 +452,23 @@ }; function create_test_environment() { - if ('document' in self) { + if ('document' in global_scope) { return new WindowTestEnvironment(); } - if ('DedicatedWorkerGlobalScope' in self && - self instanceof DedicatedWorkerGlobalScope) { + if ('DedicatedWorkerGlobalScope' in global_scope && + global_scope instanceof DedicatedWorkerGlobalScope) { return new DedicatedWorkerTestEnvironment(); } - if ('SharedWorkerGlobalScope' in self && - self instanceof SharedWorkerGlobalScope) { + if ('SharedWorkerGlobalScope' in global_scope && + global_scope instanceof SharedWorkerGlobalScope) { return new SharedWorkerTestEnvironment(); } - if ('ServiceWorkerGlobalScope' in self && - self instanceof ServiceWorkerGlobalScope) { + if ('ServiceWorkerGlobalScope' in global_scope && + global_scope instanceof ServiceWorkerGlobalScope) { return new ServiceWorkerTestEnvironment(); } - if ('WorkerGlobalScope' in self && - self instanceof WorkerGlobalScope) { + if ('WorkerGlobalScope' in global_scope && + global_scope instanceof WorkerGlobalScope) { return new DedicatedWorkerTestEnvironment(); } @@ -489,13 +478,13 @@ var test_environment = create_test_environment(); function is_shared_worker(worker) { - return 'SharedWorker' in self && worker instanceof SharedWorker; + return 'SharedWorker' in global_scope && worker instanceof SharedWorker; } function is_service_worker(worker) { // The worker object may be from another execution context, // so do not use instanceof here. - return 'ServiceWorker' in self && + return 'ServiceWorker' in global_scope && Object.prototype.toString.call(worker) == '[object ServiceWorker]'; } @@ -2824,7 +2813,7 @@ function expose(object, name) { var components = name.split("."); - var target = test_environment.global_scope(); + var target = global_scope; for (var i = 0; i < components.length - 1; i++) { if (!(components[i] in target)) { target[components[i]] = {}; @@ -2846,7 +2835,7 @@ /** Returns the 'src' URL of the first <script> tag in the page to include the file 'testharness.js'. */ function get_script_url() { - if (!('document' in self)) { + if (!('document' in global_scope)) { return undefined; } @@ -2954,5 +2943,5 @@ test_environment.on_tests_ready(); -})(); +})(this); // vim: set expandtab shiftwidth=4 tabstop=4:
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/CHANGELOG.md b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/CHANGELOG.md index e6fa641..1596d71 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/CHANGELOG.md +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/CHANGELOG.md
@@ -1,5 +1,73 @@ # Change Log +## [v10.2.0](https://github.com/w3c/webidl2.js/tree/v10.2.0) (2018-01-30) +[Full Changelog](https://github.com/w3c/webidl2.js/compare/v10.1.0...v10.2.0) + +**Merged pull requests:** + +- Type on union idlType [\#135](https://github.com/w3c/webidl2.js/pull/135) ([saschanaz](https://github.com/saschanaz)) +- feat: add argument/return type [\#134](https://github.com/w3c/webidl2.js/pull/134) ([saschanaz](https://github.com/saschanaz)) +- feat: add dictionary/typedef-type [\#133](https://github.com/w3c/webidl2.js/pull/133) ([saschanaz](https://github.com/saschanaz)) +- feat: add const-type for idlTypes [\#132](https://github.com/w3c/webidl2.js/pull/132) ([saschanaz](https://github.com/saschanaz)) +- feat: add types on idlTypes [\#131](https://github.com/w3c/webidl2.js/pull/131) ([saschanaz](https://github.com/saschanaz)) +- Auto acquisition for parser result changes [\#130](https://github.com/w3c/webidl2.js/pull/130) ([saschanaz](https://github.com/saschanaz)) + +## [v10.1.0](https://github.com/w3c/webidl2.js/tree/v10.1.0) (2018-01-19) +[Full Changelog](https://github.com/w3c/webidl2.js/compare/v10.0.0...v10.1.0) + +**Closed issues:** + +- Support `raises` and `setraises` [\#128](https://github.com/w3c/webidl2.js/issues/128) +- Support `legacycaller` [\#127](https://github.com/w3c/webidl2.js/issues/127) +- Improve "No semicolon after enum" message [\#119](https://github.com/w3c/webidl2.js/issues/119) + +**Merged pull requests:** + +- Let error messages include the current definition name [\#129](https://github.com/w3c/webidl2.js/pull/129) ([saschanaz](https://github.com/saschanaz)) + +## [v10.0.0](https://github.com/w3c/webidl2.js/tree/v10.0.0) (2017-12-20) +[Full Changelog](https://github.com/w3c/webidl2.js/compare/v9.0.0...v10.0.0) + +**Closed issues:** + +- Always return an array for idlType, etc. [\#113](https://github.com/w3c/webidl2.js/issues/113) +- Maintain writer.js or not? [\#109](https://github.com/w3c/webidl2.js/issues/109) + +**Merged pull requests:** + +- Remove typeExtAttrs from docs [\#124](https://github.com/w3c/webidl2.js/pull/124) ([saschanaz](https://github.com/saschanaz)) +- Remove iterator documentation [\#123](https://github.com/w3c/webidl2.js/pull/123) ([saschanaz](https://github.com/saschanaz)) +- Maintain writer.js [\#122](https://github.com/w3c/webidl2.js/pull/122) ([saschanaz](https://github.com/saschanaz)) +- BREAKING CHANGE: remove deprecated iterator operation [\#121](https://github.com/w3c/webidl2.js/pull/121) ([saschanaz](https://github.com/saschanaz)) +- Use for-of on tests [\#120](https://github.com/w3c/webidl2.js/pull/120) ([saschanaz](https://github.com/saschanaz)) +- docs\(README\): iterables ildType is always array [\#118](https://github.com/w3c/webidl2.js/pull/118) ([marcoscaceres](https://github.com/marcoscaceres)) + +## [v9.0.0](https://github.com/w3c/webidl2.js/tree/v9.0.0) (2017-11-30) +[Full Changelog](https://github.com/w3c/webidl2.js/compare/v8.1.0...v9.0.0) + +**Closed issues:** + +- Code quality [\#116](https://github.com/w3c/webidl2.js/issues/116) +- Unable to parse HTMLAllCollection interface [\#114](https://github.com/w3c/webidl2.js/issues/114) +- Add support for mixin syntax [\#112](https://github.com/w3c/webidl2.js/issues/112) +- Whitespace issues [\#111](https://github.com/w3c/webidl2.js/issues/111) + +**Merged pull requests:** + +- Consistent array type for iterable.idlType [\#117](https://github.com/w3c/webidl2.js/pull/117) ([saschanaz](https://github.com/saschanaz)) +- Revert "chore: drop Node 6 support \(\#102\)" [\#115](https://github.com/w3c/webidl2.js/pull/115) ([TimothyGu](https://github.com/TimothyGu)) + +## [v8.1.0](https://github.com/w3c/webidl2.js/tree/v8.1.0) (2017-11-03) +[Full Changelog](https://github.com/w3c/webidl2.js/compare/v8.0.1...v8.1.0) + +**Closed issues:** + +- Extended Attributes `rhs` should always be there [\#96](https://github.com/w3c/webidl2.js/issues/96) + +**Merged pull requests:** + +- Always add rhs property [\#110](https://github.com/w3c/webidl2.js/pull/110) ([saschanaz](https://github.com/saschanaz)) + ## [v8.0.1](https://github.com/w3c/webidl2.js/tree/v8.0.1) (2017-11-03) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v8.0.0...v8.0.1) @@ -9,7 +77,7 @@ **Merged pull requests:** -- Remove m postfix from all\_ws\(\) [\#108](https://github.com/w3c/webidl2.js/pull/108) ([SaschaNaz](https://github.com/SaschaNaz)) +- Remove m postfix from all\_ws\(\) [\#108](https://github.com/w3c/webidl2.js/pull/108) ([saschanaz](https://github.com/saschanaz)) ## [v8.0.0](https://github.com/w3c/webidl2.js/tree/v8.0.0) (2017-11-03) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v7.0.0...v8.0.0) @@ -21,9 +89,9 @@ **Merged pull requests:** -- Support mixins + includes statements [\#105](https://github.com/w3c/webidl2.js/pull/105) ([SaschaNaz](https://github.com/SaschaNaz)) +- Support mixins + includes statements [\#105](https://github.com/w3c/webidl2.js/pull/105) ([saschanaz](https://github.com/saschanaz)) - chore: drop Node 6 support [\#102](https://github.com/w3c/webidl2.js/pull/102) ([marcoscaceres](https://github.com/marcoscaceres)) -- BREAKING CHANGE: drop creator support [\#101](https://github.com/w3c/webidl2.js/pull/101) ([SaschaNaz](https://github.com/SaschaNaz)) +- BREAKING CHANGE: drop creator support [\#101](https://github.com/w3c/webidl2.js/pull/101) ([saschanaz](https://github.com/saschanaz)) - Normalize some whitespace to pass wpt's lint [\#99](https://github.com/w3c/webidl2.js/pull/99) ([foolip](https://github.com/foolip)) ## [v7.0.0](https://github.com/w3c/webidl2.js/tree/v7.0.0) (2017-10-27) @@ -54,7 +122,7 @@ **Merged pull requests:** -- Use ES2015 syntax for tests [\#88](https://github.com/w3c/webidl2.js/pull/88) ([SaschaNaz](https://github.com/SaschaNaz)) +- Use ES2015 syntax for tests [\#88](https://github.com/w3c/webidl2.js/pull/88) ([saschanaz](https://github.com/saschanaz)) ## [v6.0.0](https://github.com/w3c/webidl2.js/tree/v6.0.0) (2017-10-17) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v5.0.0...v6.0.0) @@ -73,7 +141,7 @@ **Merged pull requests:** -- BREAKING CHANGE: Use ES2015 syntax [\#84](https://github.com/w3c/webidl2.js/pull/84) ([SaschaNaz](https://github.com/SaschaNaz)) +- BREAKING CHANGE: Use ES2015 syntax [\#84](https://github.com/w3c/webidl2.js/pull/84) ([saschanaz](https://github.com/saschanaz)) ## [v4.2.0](https://github.com/w3c/webidl2.js/tree/v4.2.0) (2017-10-16) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v4.1.0...v4.2.0) @@ -85,8 +153,8 @@ **Merged pull requests:** -- Check duplicated names [\#80](https://github.com/w3c/webidl2.js/pull/80) ([SaschaNaz](https://github.com/SaschaNaz)) -- Remove legacycaller [\#79](https://github.com/w3c/webidl2.js/pull/79) ([SaschaNaz](https://github.com/SaschaNaz)) +- Check duplicated names [\#80](https://github.com/w3c/webidl2.js/pull/80) ([saschanaz](https://github.com/saschanaz)) +- Remove legacycaller [\#79](https://github.com/w3c/webidl2.js/pull/79) ([saschanaz](https://github.com/saschanaz)) - Add "sequence" property to IDL Type AST definition [\#76](https://github.com/w3c/webidl2.js/pull/76) ([lerouche](https://github.com/lerouche)) ## [v4.1.0](https://github.com/w3c/webidl2.js/tree/v4.1.0) (2017-07-04) @@ -98,7 +166,7 @@ **Merged pull requests:** -- Support TypeWithExtendedAttributes on generics [\#75](https://github.com/w3c/webidl2.js/pull/75) ([SaschaNaz](https://github.com/SaschaNaz)) +- Support TypeWithExtendedAttributes on generics [\#75](https://github.com/w3c/webidl2.js/pull/75) ([saschanaz](https://github.com/saschanaz)) ## [v4.0.0](https://github.com/w3c/webidl2.js/tree/v4.0.0) (2017-06-27) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v3.0.2...v4.0.0) @@ -112,7 +180,7 @@ **Merged pull requests:** - BREAKING CHANGE: remove serializers \(closes \#73\) [\#74](https://github.com/w3c/webidl2.js/pull/74) ([marcoscaceres](https://github.com/marcoscaceres)) -- Add documentation for namespaces [\#70](https://github.com/w3c/webidl2.js/pull/70) ([SaschaNaz](https://github.com/SaschaNaz)) +- Add documentation for namespaces [\#70](https://github.com/w3c/webidl2.js/pull/70) ([saschanaz](https://github.com/saschanaz)) ## [v3.0.2](https://github.com/w3c/webidl2.js/tree/v3.0.2) (2017-05-29) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v3.0.1...v3.0.2) @@ -123,7 +191,7 @@ **Merged pull requests:** -- Test for latest LTS/stable node versions [\#69](https://github.com/w3c/webidl2.js/pull/69) ([SaschaNaz](https://github.com/SaschaNaz)) +- Test for latest LTS/stable node versions [\#69](https://github.com/w3c/webidl2.js/pull/69) ([saschanaz](https://github.com/saschanaz)) ## [v3.0.1](https://github.com/w3c/webidl2.js/tree/v3.0.1) (2017-05-18) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v2.4.0...v3.0.1) @@ -135,8 +203,8 @@ **Merged pull requests:** -- Fix whitespace error on parsing extended attributes [\#68](https://github.com/w3c/webidl2.js/pull/68) ([SaschaNaz](https://github.com/SaschaNaz)) -- Remove deprecated IDL arrays and exceptions [\#67](https://github.com/w3c/webidl2.js/pull/67) ([SaschaNaz](https://github.com/SaschaNaz)) +- Fix whitespace error on parsing extended attributes [\#68](https://github.com/w3c/webidl2.js/pull/68) ([saschanaz](https://github.com/saschanaz)) +- Remove deprecated IDL arrays and exceptions [\#67](https://github.com/w3c/webidl2.js/pull/67) ([saschanaz](https://github.com/saschanaz)) ## [v2.4.0](https://github.com/w3c/webidl2.js/tree/v2.4.0) (2017-04-12) [Full Changelog](https://github.com/w3c/webidl2.js/compare/v2.1.0...v2.4.0) @@ -157,7 +225,7 @@ - Update webidl2.js [\#63](https://github.com/w3c/webidl2.js/pull/63) ([tqeto](https://github.com/tqeto)) - Remove support for MapClass \(no longer valid in WebIDL\) [\#62](https://github.com/w3c/webidl2.js/pull/62) ([dontcallmedom](https://github.com/dontcallmedom)) - Add support for annotated types [\#61](https://github.com/w3c/webidl2.js/pull/61) ([dontcallmedom](https://github.com/dontcallmedom)) -- Support namespaces [\#58](https://github.com/w3c/webidl2.js/pull/58) ([SaschaNaz](https://github.com/SaschaNaz)) +- Support namespaces [\#58](https://github.com/w3c/webidl2.js/pull/58) ([saschanaz](https://github.com/saschanaz)) - Add support for records [\#57](https://github.com/w3c/webidl2.js/pull/57) ([TimothyGu](https://github.com/TimothyGu)) - Refactor [\#50](https://github.com/w3c/webidl2.js/pull/50) ([marcoscaceres](https://github.com/marcoscaceres)) - feat\(lib\): add AMD export support \(closes \#48\) [\#49](https://github.com/w3c/webidl2.js/pull/49) ([marcoscaceres](https://github.com/marcoscaceres))
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/README.md b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/README.md index 93cc78b..8791360d 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/README.md +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/README.md
@@ -310,7 +310,6 @@ ```JS { "type": "typedef", - "typeExtAttrs": [], "idlType": { "sequence": true, "generic": "sequence", @@ -336,8 +335,6 @@ * `name`: The typedef's name. * `idlType`: An [IDL Type](#idl-type) describing what typedef's type. * `extAttrs`: A list of [extended attributes](#extended-attributes). -* `typeExtAttrs`: A list of [extended attributes](#extended-attributes) that apply to the -type rather than to the typedef as a whole. ### Implements @@ -489,34 +486,6 @@ * `value`: The constant value as described by [Const Values](#default-and-const-values) * `extAttrs`: A list of [extended attributes](#extended-attributes). -### Iterator Member - -Iterator members look like this - -```JS -{ - "type": "iterator", - "getter": false, - "setter": false, - "deleter": false, - "static": false, - "stringifier": false, - "idlType": { - "sequence": false, - "generic": null, - "nullable": false, - "union": false, - "idlType": "Session2" - }, - "iteratorObject": "SessionIterator", - "extAttrs": [] -} -``` - -* `type`: Always "iterator". -* `iteratorObject`: The string on the right-hand side; absent if there isn't one. -* the rest: same as on [operations](#operation-member). - ### Arguments The arguments (e.g. for an operation) look like this: @@ -611,7 +580,7 @@ The fields are as follows: * `type`: Always one of "iterable", "legacyiterable", "maplike" or "setlike". -* `idlType`: An [IDL Type](#idl-type) (or an array of two types) representing the declared type arguments. +* `idlType`: An array with one or more [IDL Types](#idl-type) representing the declared type arguments. * `readonly`: Whether the maplike or setlike is declared as read only. * `extAttrs`: A list of [extended attributes](#extended-attributes).
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/webidl2.js b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/webidl2.js index 0c9a1fa..a7a61d9 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/webidl2.js +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/webidl2.js
@@ -1,33 +1,82 @@ "use strict"; (() => { + // These regular expressions use the sticky flag so they will only match at + // the current location (ie. the offset of lastIndex). + const tokenRe = { + // This expression uses a lookahead assertion to catch false matches + // against integers early. + "float": /-?(?=[0-9]*\.|[0-9]+[eE])(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y, + "integer": /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y, + "identifier": /[A-Z_a-z][0-9A-Z_a-z-]*/y, + "string": /"[^"]*"/y, + "whitespace": /[\t\n\r ]+/y, + "comment": /((\/(\/.*|\*([^*]|\*[^\/])*\*\/)[\t\n\r ]*)+)/y, + "other": /[^\t\n\r 0-9A-Z_a-z]/y + }; + + function attemptTokenMatch(str, type, re, lastIndex, tokens) { + re.lastIndex = lastIndex; + const result = re.exec(str); + if (result) { + tokens.push({ type, value: result[0] }); + return re.lastIndex; + } + return -1; + } + function tokenise(str) { const tokens = []; - const re = { - "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/, - "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/, - "identifier": /^[A-Z_a-z][0-9A-Z_a-z-]*/, - "string": /^"[^"]*"/, - "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/, - "other": /^[^\t\n\r 0-9A-Z_a-z]/ - }; - const types = ["float", "integer", "identifier", "string", "whitespace", "other"]; - while (str.length > 0) { - let matched = false; - for (var i in types) { - const type = types[i]; - str = str.replace(re[type], tok => { - tokens.push({ type, value: tok }); - matched = true; - return ""; - }); - if (matched) break; + let lastIndex = 0; + while (lastIndex < str.length) { + const nextChar = str.charAt(lastIndex); + let result = -1; + if (/[-0-9.]/.test(nextChar)) { + result = attemptTokenMatch(str, "float", tokenRe.float, lastIndex, + tokens); + if (result === -1) { + result = attemptTokenMatch(str, "integer", tokenRe.integer, lastIndex, + tokens); + } + if (result === -1) { + // '-' and '.' can also match "other". + result = attemptTokenMatch(str, "other", tokenRe.other, + lastIndex, tokens); + } + } else if (/[A-Z_a-z]/.test(nextChar)) { + result = attemptTokenMatch(str, "identifier", tokenRe.identifier, + lastIndex, tokens); + } else if (nextChar === '"') { + result = attemptTokenMatch(str, "string", tokenRe.string, + lastIndex, tokens); + if (result === -1) { + // '"' can also match "other". + result = attemptTokenMatch(str, "other", tokenRe.other, + lastIndex, tokens); + } + } else if (/[\t\n\r ]/.test(nextChar)) { + result = attemptTokenMatch(str, "whitespace", tokenRe.whitespace, + lastIndex, tokens); + } else if (nextChar === '/') { + // The parser expects comments to be labelled as "whitespace". + result = attemptTokenMatch(str, "whitespace", tokenRe.comment, + lastIndex, tokens); + if (result === -1) { + // '/' can also match "other". + result = attemptTokenMatch(str, "other", tokenRe.other, + lastIndex, tokens); + } + } else { + result = attemptTokenMatch(str, "other", tokenRe.other, + lastIndex, tokens); } - if (matched) continue; - throw new Error("Token stream not progressing"); + if (result === -1) { + throw new Error("Token stream not progressing"); + } + lastIndex = result; } return tokens; - }; + } class WebIDLParseError { constructor(str, line, input, tokens) { @@ -46,6 +95,7 @@ let line = 1; tokens = tokens.slice(); const names = new Map(); + let current = null; const FLOAT = "float"; const INT = "integer"; @@ -58,7 +108,7 @@ getter: false, setter: false, deleter: false, - "static": false, + static: false, stringifier: false }); @@ -70,8 +120,18 @@ tok += tokens[numTokens].value; numTokens++; } - throw new WebIDLParseError(str, line, tok, tokens.slice(0, maxTokens)); - }; + + let message; + if (current) { + message = `Got an error during or right after parsing \`${current.partial ? "partial " : ""}${current.type} ${current.name}\`: ${str}` + } + else { + // throwing before any valid definition + message = `Got an error before parsing any named definition: ${str}`; + } + + throw new WebIDLParseError(message, line, tok, tokens.slice(0, maxTokens)); + } function sanitize_name(name, type) { if (names.has(name)) { @@ -87,23 +147,34 @@ if (!tokens.length || tokens[0].type !== type) return; if (typeof value === "undefined" || tokens[0].value === value) { last_token = tokens.shift(); - if (type === ID) last_token.value = last_token.value.replace(/^_/, ""); + if (type === ID && last_token.value.startsWith('_')) + last_token.value = last_token.value.substring(1); return last_token; } - }; + } + + function count(str, char) { + let total = 0; + for (let i = str.indexOf(char); i !== -1; i = str.indexOf(char, i + 1)) { + ++total; + } + return total; + } function ws() { if (!tokens.length) return; if (tokens[0].type === "whitespace") { const t = tokens.shift(); - t.value.replace(/\n/g, m => { - line++; - return m; - }); + line += count(t.value, '\n'); return t; } - }; + } + const all_ws_re = { + "ws": /([\t\n\r ]+)/y, + "line-comment": /\/\/(.*)\r?\n?/y, + "multiline-comment": /\/\*((?:[^*]|\*[^/])*)\*\//y + }; function all_ws(store, pea) { // pea == post extended attribute, tpea = same for types const t = { type: "whitespace", value: "" }; while (true) { @@ -114,31 +185,30 @@ if (t.value.length > 0) { if (store) { let w = t.value; - const re = { - "ws": /^([\t\n\r ]+)/, - "line-comment": /^\/\/(.*)\r?\n?/, - "multiline-comment": /^\/\*((?:.|\n|\r)*?)\*\// - }; - const wsTypes = []; - for (var k in re) wsTypes.push(k); - while (w.length) { + let lastIndex = 0; + while (lastIndex < w.length) { let matched = false; - for (var i in wsTypes) { - const type = wsTypes[i]; - w = w.replace(re[type], (tok, m1) => { - store.push({ type: type + (pea ? ("-" + pea) : ""), value: m1 }); + // Servo doesn't support using "const" in this construction yet. + // See https://github.com/servo/servo/issues/20231. + // |type| can be made const once Servo supports it. + for (let type in all_ws_re) { + const re = all_ws_re[type]; + re.lastIndex = lastIndex; + const result = re.exec(w); + if (result) { + store.push({ type: type + (pea ? ("-" + pea) : ""), value: result[1] }); matched = true; - return ""; - }); - if (matched) break; + lastIndex = re.lastIndex; + break; + } } - if (matched) continue; - throw new Error("Surprising white space construct."); // this shouldn't happen + if (!matched) + throw new Error("Surprising white space construct."); // this shouldn't happen } } return t; } - }; + } function integer_type() { let ret = ""; @@ -153,7 +223,7 @@ return ret; } if (ret) error("Failed to parse integer type"); - }; + } function float_type() { let ret = ""; @@ -163,7 +233,7 @@ if (consume(ID, "float")) return ret + "float"; if (consume(ID, "double")) return ret + "double"; if (ret) error("Failed to parse float type"); - }; + } function primitive_type() { const num_type = integer_type() || float_type(); @@ -172,7 +242,7 @@ if (consume(ID, "boolean")) return "boolean"; if (consume(ID, "byte")) return "byte"; if (consume(ID, "octet")) return "octet"; - }; + } function const_value() { if (consume(ID, "true")) return { type: "boolean", value: true }; @@ -187,7 +257,7 @@ if (consume(ID, "Infinity")) return { type: "Infinity", negative: true }; else tokens.unshift(tok); } - }; + } function type_suffix(obj) { while (true) { @@ -197,11 +267,11 @@ obj.nullable = true; } else return; } - }; + } - function single_type() { + function single_type(typeName) { const prim = primitive_type(); - const ret = { sequence: false, generic: null, nullable: false, union: false }; + const ret = { type: typeName || null, sequence: false, generic: null, nullable: false, union: false }; let name; let value; if (prim) { @@ -219,7 +289,7 @@ const types = []; do { all_ws(); - types.push(type_with_extended_attributes() || error("Error parsing generic type " + value)); + types.push(type_with_extended_attributes(typeName) || error("Error parsing generic type " + value)); all_ws(); } while (consume(OTHER, ",")); @@ -248,12 +318,12 @@ type_suffix(ret); if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable"); return ret; - }; + } - function union_type() { + function union_type(typeName) { all_ws(); if (!consume(OTHER, "(")) return; - const ret = { sequence: false, generic: null, nullable: false, union: true, idlType: [] }; + const ret = { type: typeName || null, sequence: false, generic: null, nullable: false, union: true, idlType: [] }; const fst = type_with_extended_attributes() || error("Union type with no content"); ret.idlType.push(fst); while (true) { @@ -265,18 +335,18 @@ if (!consume(OTHER, ")")) error("Unterminated union type"); type_suffix(ret); return ret; - }; + } - function type() { - return single_type() || union_type(); - }; + function type(typeName) { + return single_type(typeName) || union_type(typeName); + } - function type_with_extended_attributes() { + function type_with_extended_attributes(typeName) { const extAttrs = extended_attrs(); - const ret = single_type() || union_type(); + const ret = single_type(typeName) || union_type(typeName); if (extAttrs.length && ret) ret.extAttrs = extAttrs; return ret; - }; + } function argument(store) { const ret = { optional: false, variadic: false }; @@ -287,7 +357,7 @@ ret.optional = true; all_ws(); } - ret.idlType = type_with_extended_attributes(); + ret.idlType = type_with_extended_attributes("argument-type"); if (!ret.idlType) { if (opt_token) tokens.unshift(opt_token); return; @@ -322,7 +392,7 @@ } } return ret; - }; + } function argument_list(store) { const ret = []; @@ -335,7 +405,7 @@ const nxt = argument(store ? ret : null) || error("Trailing comma in arguments list"); ret.push(nxt); } - }; + } function simple_extended_attr(store) { all_ws(); @@ -386,7 +456,7 @@ consume(OTHER, ")") || error("Unexpected token in extended attribute argument list"); } return ret; - }; + } // Note: we parse something simpler than the official syntax. It's all that ever // seems to be used @@ -406,7 +476,7 @@ all_ws(); consume(OTHER, "]") || error("No end of extended attribute"); return eas; - }; + } function default_() { all_ws(); @@ -420,11 +490,11 @@ return { type: "sequence", value: [] }; } else { const str = consume(STR) || error("No value for default"); - str.value = str.value.replace(/^"/, "").replace(/"$/, ""); + str.value = str.value.slice(1, -1); return str; } } - }; + } function const_(store) { all_ws(store, "pea"); @@ -436,7 +506,7 @@ typ = consume(ID) || error("No type for const"); typ = typ.value; } - ret.idlType = typ; + ret.idlType = { type: "const-type", idlType: typ }; all_ws(); if (consume(OTHER, "?")) { ret.nullable = true; @@ -453,7 +523,7 @@ all_ws(); consume(OTHER, ";") || error("Unterminated const"); return ret; - }; + } function inheritance() { all_ws(); @@ -462,7 +532,7 @@ const inh = consume(ID) || error("No type in inheritance"); return inh.value; } - }; + } function operation_rest(ret, store) { all_ws(); @@ -477,7 +547,7 @@ all_ws(); consume(OTHER, ";") || error("Unterminated operation"); return ret; - }; + } function callback(store) { all_ws(store, "pea"); @@ -486,12 +556,11 @@ all_ws(); const tok = consume(ID, "interface"); if (tok) { - ret = interface_rest(); - ret.type = "callback interface"; + ret = interface_rest(false, store, "callback interface"); return ret; } const name = consume(ID) || error("No name for callback"); - ret = { type: "callback", name: sanitize_name(name.value, "callback") }; + ret = current = { type: "callback", name: sanitize_name(name.value, "callback") }; all_ws(); consume(OTHER, "=") || error("No assignment in callback"); all_ws(); @@ -504,14 +573,14 @@ all_ws(); consume(OTHER, ";") || error("Unterminated callback"); return ret; - }; + } function attribute(store) { all_ws(store, "pea"); const grabbed = []; const ret = { type: "attribute", - "static": false, + static: false, stringifier: false, inherit: false, readonly: false @@ -519,7 +588,7 @@ const w = all_ws(); if (w) grabbed.push(w); if (consume(ID, "inherit")) { - if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit"); + if (ret.static || ret.stringifier) error("Cannot have a static or stringifier inherit"); ret.inherit = true; grabbed.push(last_token); const w = all_ws(); @@ -536,14 +605,14 @@ tokens = grabbed.concat(tokens); } return rest; - }; + } function attribute_rest(ret) { if (!consume(ID, "attribute")) { return; } all_ws(); - ret.idlType = type_with_extended_attributes() || error("No type in attribute"); + ret.idlType = type_with_extended_attributes("attribute-type") || error("No type in attribute"); if (ret.idlType.sequence) error("Attributes cannot accept sequence types"); if (ret.idlType.generic === "record") error("Attributes cannot accept record types"); all_ws(); @@ -552,17 +621,17 @@ all_ws(); consume(OTHER, ";") || error("Unterminated attribute"); return ret; - }; + } function return_type() { - const typ = type(); + const typ = type("return-type"); if (!typ) { if (consume(ID, "void")) { return "void"; } else error("No return type"); } return typ; - }; + } function operation(store) { all_ws(store, "pea"); @@ -582,24 +651,9 @@ } ret.idlType = return_type(); all_ws(); - if (consume(ID, "iterator")) { - all_ws(); - ret.type = "iterator"; - if (consume(ID, "object")) { - ret.iteratorObject = "object"; - } else if (consume(OTHER, "=")) { - all_ws(); - var name = consume(ID) || error("No right hand side in iterator"); - ret.iteratorObject = name.value; - } - all_ws(); - consume(OTHER, ";") || error("Unterminated iterator"); - return ret; - } else { - operation_rest(ret, store); - return ret; - } - }; + operation_rest(ret, store); + return ret; + } function static_member(store) { all_ws(store, "pea"); @@ -631,7 +685,7 @@ arr.push(name.value); } else break; } - }; + } function iterable_type() { if (consume(ID, "iterable")) return "iterable"; @@ -639,13 +693,13 @@ else if (consume(ID, "maplike")) return "maplike"; else if (consume(ID, "setlike")) return "setlike"; else return; - }; + } function readonly_iterable_type() { if (consume(ID, "maplike")) return "maplike"; else if (consume(ID, "setlike")) return "setlike"; else return; - }; + } function iterable(store) { all_ws(store, "pea"); @@ -672,17 +726,14 @@ delete ret.readonly; all_ws(); if (consume(OTHER, "<")) { - ret.idlType = type_with_extended_attributes() || error(`Error parsing ${ittype} declaration`); + ret.idlType = [type_with_extended_attributes()] || error(`Error parsing ${ittype} declaration`); all_ws(); if (secondTypeAllowed) { - let type2 = null; if (consume(OTHER, ",")) { all_ws(); - type2 = type_with_extended_attributes(); + ret.idlType.push(type_with_extended_attributes()); all_ws(); } - if (type2) - ret.idlType = [ret.idlType, type2]; else if (secondTypeRequired) error(`Missing second type argument in ${ittype} declaration`); } @@ -693,16 +744,16 @@ error(`Error parsing ${ittype} declaration`); return ret; - }; + } - function interface_rest(isPartial, store) { + function interface_rest(isPartial, store, typeName = "interface") { all_ws(); const name = consume(ID) || error("No name for interface"); const mems = []; - const ret = { - type: "interface", + const ret = current = { + type: typeName, name: isPartial ? name.value : sanitize_name(name.value, "interface"), - partial: false, + partial: isPartial, members: mems }; if (!isPartial) ret.inheritance = inheritance() || null; @@ -733,7 +784,7 @@ mem.extAttrs = ea; ret.members.push(mem); } - }; + } function mixin_rest(isPartial, store) { all_ws(); @@ -741,10 +792,10 @@ all_ws(); const name = consume(ID) || error("No name for interface mixin"); const mems = []; - const ret = { + const ret = current = { type: "interface mixin", name: isPartial ? name.value : sanitize_name(name.value, "interface mixin"), - partial: false, + partial: isPartial, members: mems }; all_ws(); @@ -787,7 +838,7 @@ all_ws(); const name = consume(ID) || error("No name for namespace"); const mems = []; - const ret = { + const ret = current = { type: "namespace", name: isPartial ? name.value : sanitize_name(name.value, "namespace"), partial: isPartial, @@ -817,7 +868,7 @@ const grabbed = []; const ret = { type: "attribute", - "static": false, + static: false, stringifier: false, inherit: false, readonly: false @@ -856,9 +907,8 @@ interface_(true, store) || namespace(true, store) || error("Partial doesn't apply to anything"); - thing.partial = true; return thing; - }; + } function dictionary(isPartial, store) { all_ws(isPartial ? null : store, "pea"); @@ -866,10 +916,10 @@ all_ws(); const name = consume(ID) || error("No name for dictionary"); const mems = []; - const ret = { + const ret = current = { type: "dictionary", name: isPartial ? name.value : sanitize_name(name.value, "dictionary"), - partial: false, + partial: isPartial, members: mems }; if (!isPartial) ret.inheritance = inheritance() || null; @@ -885,7 +935,7 @@ const ea = extended_attrs(store ? mems : null); all_ws(store ? mems : null, "pea"); const required = consume(ID, "required"); - const typ = type_with_extended_attributes() || error("No type for dictionary member"); + const typ = type_with_extended_attributes("dictionary-type") || error("No type for dictionary member"); all_ws(); const name = consume(ID) || error("No name for dictionary member"); const dflt = default_(); @@ -904,7 +954,7 @@ all_ws(); consume(OTHER, ";") || error("Unterminated dictionary member"); } - }; + } function enum_(store) { all_ws(store, "pea"); @@ -912,7 +962,7 @@ all_ws(); const name = consume(ID) || error("No name for enum"); const vals = []; - const ret = { + const ret = current = { type: "enum", name: sanitize_name(name.value, "enum"), values: vals @@ -928,7 +978,7 @@ return ret; } const val = consume(STR) || error("Unexpected value in enum"); - val.value = val.value.replace(/"/g, ""); + val.value = val.value.slice(1, -1); ret.values.push(val); all_ws(store ? vals : null); if (consume(OTHER, ",")) { @@ -939,7 +989,7 @@ saw_comma = false; } } - }; + } function typedef(store) { all_ws(store, "pea"); @@ -948,14 +998,15 @@ type: "typedef" }; all_ws(); - ret.idlType = type_with_extended_attributes() || error("No type in typedef"); + ret.idlType = type_with_extended_attributes("typedef-type") || error("No type in typedef"); all_ws(); const name = consume(ID) || error("No name in typedef"); ret.name = sanitize_name(name.value, "typedef"); + current = ret; all_ws(); consume(OTHER, ";") || error("Unterminated typedef"); return ret; - }; + } function implements_(store) { all_ws(store, "pea"); @@ -978,7 +1029,7 @@ tokens.unshift(w); tokens.unshift(target); } - }; + } function includes(store) { all_ws(store, "pea"); @@ -1001,7 +1052,7 @@ tokens.unshift(w); tokens.unshift(target); } - }; + } function definition(store) { return callback(store) || @@ -1013,7 +1064,7 @@ implements_(store) || includes(store) || namespace(false, store); - }; + } function definitions(store) { if (!tokens.length) return []; @@ -1029,11 +1080,11 @@ defs.push(def); } return defs; - }; + } const res = definitions(opt.ws); if (tokens.length) error("Unrecognised tokens"); return res; - }; + } const obj = { parse(str, opt) {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/writer.js b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/writer.js index 5e30e70..c00c0dd 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/writer.js +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/lib/writer.js
@@ -1,78 +1,71 @@ -(function() { +"use strict"; - var write = function(ast, opt) { - var curPea = "", - curTPea = "", - opt = opt || {}, - noop = function(str) { - return str; }, - optNames = "type".split(" "), - context = []; - for (var i = 0, n = optNames.length; i < n; i++) { - var o = optNames[i]; +(() => { + function write(ast, opt = {}) { + let curPea = ""; + let curTPea = ""; + const noop = str => str; + const optNames = "type".split(" "); + const context = []; + for (const o of optNames) { if (!opt[o]) opt[o] = noop; } - var literal = function(it) { + function literal(it) { return it.value; }; - var wsPea = function(it) { + function wsPea(it) { curPea += it.value; return ""; }; - var wsTPea = function(it) { + function wsTPea(it) { curTPea += it.value; return ""; }; - var lineComment = function(it) { - return "//" + it.value + "\n"; + function lineComment(it) { + return `//${it.value}\n`; }; - var multilineComment = function(it) { - return "/*" + it.value + "*/"; + function multilineComment(it) { + return `/*${it.value}*/`; }; - var type = function(it) { + function type(it) { if (typeof it === "string") return opt.type(it); // XXX should maintain some context - if (it.union) return "(" + it.idlType.map(type).join(" or ") + ")"; - var ret = ""; - if (it.generic) ret += it.generic + "<"; - else if (it.sequence) ret += "sequence<"; - if (Array.isArray(it.idlType)) ret += it.idlType.map(type).join(", "); - else ret += type(it.idlType); - if (it.array || it.generic === 'Array') { - for (var i = 0, n = it.nullableArray.length; i < n; i++) { - var val = it.nullableArray[i]; - if (val) ret += "?"; - ret += "[]"; - } + let ret = extended_attributes(it.extAttrs, curPea); + if (it.union) ret += `(${it.idlType.map(type).join(" or ")})`; + else { + if (it.generic) ret += `${it.generic}<`; + if (Array.isArray(it.idlType)) ret += it.idlType.map(type).join(", "); + else ret += type(it.idlType); + if (it.generic) ret += ">"; } - if (it.generic || it.sequence) ret += ">"; if (it.nullable) ret += "?"; return ret; }; - var const_value = function(it) { - var tp = it.type; + function const_value(it) { + const tp = it.type; if (tp === "boolean") return it.value ? "true" : "false"; else if (tp === "null") return "null"; else if (tp === "Infinity") return (it.negative ? "-" : "") + "Infinity"; else if (tp === "NaN") return "NaN"; else if (tp === "number") return it.value; - else return '"' + it.value + '"'; + else if (tp === "sequence") return "[]"; + else return `"${it.value}"`; }; - var argument = function(arg, pea) { - var ret = extended_attributes(arg.extAttrs, pea); + function argument(arg, pea) { + let ret = extended_attributes(arg.extAttrs, pea); if (arg.optional) ret += "optional "; ret += type(arg.idlType); if (arg.variadic) ret += "..."; - ret += " " + arg.name; - if (arg["default"]) ret += " = " + const_value(arg["default"]); + ret += ` ${arg.name}`; + if (arg["default"]) ret += ` = ${const_value(arg["default"])}`; return ret; }; - var args = function(its) { - var res = "", - pea = ""; - for (var i = 0, n = its.length; i < n; i++) { - var arg = its[i]; + function args(its) { + let res = ""; + let pea = ""; + for (let i = 0, n = its.length; i < n; i++) { + const arg = its[i]; if (arg.type === "ws") res += arg.value; else if (arg.type === "ws-pea") pea += arg.value; else { @@ -83,182 +76,198 @@ } return res; }; - var make_ext_at = function(it) { - if (it["arguments"] === null) return it.name; + function make_ext_at(it) { context.unshift(it); - var ret = it.name + "(" + (it["arguments"].length ? args(it["arguments"]) : "") + ")"; + let ret = it.name; + if (it.rhs) { + if (it.rhs.type === "identifier-list") ret += `=(${it.rhs.value.join(',')})`; + else ret += `=${it.rhs.value}`; + } + if (it.arguments) ret += `(${it["arguments"].length ? args(it["arguments"]) : ""})`; context.shift(); // XXX need to add more contexts, but not more than needed for ReSpec return ret; }; - var extended_attributes = function(eats, pea) { + function extended_attributes(eats, pea) { if (!eats || !eats.length) return ""; - return "[" + eats.map(make_ext_at).join(", ") + "]" + pea; + return `[${eats.map(make_ext_at).join(", ")}]${pea}`; }; - var modifiers = "getter setter creator deleter legacycaller stringifier static".split(" "); - var operation = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + const modifiers = "getter setter creator deleter legacycaller stringifier static".split(" "); + function operation(it) { + let ret = extended_attributes(it.extAttrs, curPea); curPea = ""; if (it.stringifier && !it.idlType) return "stringifier;"; - for (var i = 0, n = modifiers.length; i < n; i++) { - var mod = modifiers[i]; + for (const mod of modifiers) { if (it[mod]) ret += mod + " "; } ret += type(it.idlType) + " "; if (it.name) ret += it.name; - ret += "(" + args(it["arguments"]) + ");"; + ret += `(${args(it["arguments"])});`; return ret; }; - var attribute = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function attribute(it) { + let ret = extended_attributes(it.extAttrs, curPea); curPea = ""; if (it["static"]) ret += "static "; if (it.stringifier) ret += "stringifier "; if (it.readonly) ret += "readonly "; if (it.inherit) ret += "inherit "; - ret += "attribute " + type(it.idlType) + " " + it.name + ";"; + ret += `attribute ${type(it.idlType)} ${it.name};`; return ret; }; - var interface_ = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function interface_(it) { + let ret = extended_attributes(it.extAttrs, curPea); curPea = ""; if (it.partial) ret += "partial "; - ret += "interface " + it.name + " "; - if (it.inheritance) ret += ": " + it.inheritance + " "; - ret += "{" + iterate(it.members) + "};"; + ret += `interface ${it.name} `; + if (it.inheritance) ret += `: ${it.inheritance} `; + ret += `{${iterate(it.members)}};`; return ret; }; - var dictionary = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function interface_mixin(it) { + let ret = extended_attributes(it.extAttrs, curPea); curPea = ""; if (it.partial) ret += "partial "; - ret += "dictionary " + it.name + " "; - ret += "{" + iterate(it.members) + "};"; + ret += `interface mixin ${it.name} `; + ret += `{${iterate(it.members)}};`; + return ret; + } + + function namespace(it) { + let ret = extended_attributes(it.extAttrs, curPea); + curPea = ""; + if (it.partial) ret += "partial "; + ret += `namespace ${it.name} `; + ret += `{${iterate(it.members)}};`; + return ret; + } + + function dictionary(it) { + let ret = extended_attributes(it.extAttrs, curPea); + curPea = ""; + if (it.partial) ret += "partial "; + ret += `dictionary ${it.name} `; + if (it.inheritance) ret += `: ${it.inheritance} `; + ret += `{${iterate(it.members)}};`; return ret; }; - var field = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function field(it) { + let ret = extended_attributes(it.extAttrs, curPea); curPea = ""; if (it.required) ret += "required "; - ret += type(it.idlType) + " " + it.name; - if (it["default"]) ret += " = " + const_value(it["default"]); + ret += `${type(it.idlType)} ${it.name}`; + if (it["default"]) ret += ` = ${const_value(it["default"])}`; ret += ";"; return ret; }; - var exception = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function const_(it) { + const ret = extended_attributes(it.extAttrs, curPea); curPea = ""; - ret += "exception " + it.name + " "; - if (it.inheritance) ret += ": " + it.inheritance + " "; - ret += "{" + iterate(it.members) + "};"; - return ret; + return `${ret}const ${type(it.idlType)}${it.nullable ? "?" : ""} ${it.name} = ${const_value(it.value)};`; }; - var const_ = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function typedef(it) { + let ret = extended_attributes(it.extAttrs, curPea); curPea = ""; - return ret + "const " + type(it.idlType) + " " + it.name + " = " + const_value(it.value) + ";"; - }; - var typedef = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); - curPea = ""; - ret += "typedef " + extended_attributes(it.typeExtAttrs, curTPea); + ret += `typedef ${extended_attributes(it.typeExtAttrs, curTPea)}`; curTPea = ""; - return ret + type(it.idlType) + " " + it.name + ";"; + return `${ret}${type(it.idlType)} ${it.name};`; }; - var implements_ = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function implements_(it) { + const ret = extended_attributes(it.extAttrs, curPea); curPea = ""; - return ret + it.target + " implements " + it["implements"] + ";"; + return `${ret}${it.target} implements ${it["implements"]};`; }; - var callback = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function includes(it) { + const ret = extended_attributes(it.extAttrs, curPea); curPea = ""; - return ret + "callback " + it.name + " = " + type(it.idlType) + - "(" + args(it["arguments"]) + ");"; + return `${ret}${it.target} includes ${it.includes};`; }; - var enum_ = function(it) { - var ret = extended_attributes(it.extAttrs, curPea); + function callback(it) { + const ret = extended_attributes(it.extAttrs, curPea); curPea = ""; - ret += "enum " + it.name + " {"; - for (var i = 0, n = it.values.length; i < n; i++) { - var v = it.values[i]; - if (typeof v === "string") ret += '"' + v + '"'; + return `${ret}callback ${it.name} = ${type(it.idlType)}(${args(it["arguments"])});`; + }; + function enum_(it) { + let ret = extended_attributes(it.extAttrs, curPea); + curPea = ""; + ret += `enum ${it.name} {`; + for (const v of it.values) { + if (v.type === "string") ret += `"${v.value}"`; else if (v.type === "ws") ret += v.value; else if (v.type === ",") ret += ","; } return ret + "};"; }; - var iterable = function(it) { - return "iterable<" + (it.idlType instanceof Array ? it.idlType.map(type).join(", ") : type(it.idlType)) + ">;"; + function iterable(it) { + return `iterable<${Array.isArray(it.idlType) ? it.idlType.map(type).join(", ") : type(it.idlType)}>;`; }; - var legacyiterable = function(it) { - return "legacyiterable<" + (it.idlType instanceof Array ? it.idlType.map(type).join(", ") : type(it.idlType)) + ">;"; + function legacyiterable(it) { + return `legacyiterable<${Array.isArray(it.idlType) ? it.idlType.map(type).join(", ") : type(it.idlType)}>;`; }; - var maplike = function(it) { - return (it.readonly ? "readonly " : "") + "maplike<" + - it.idlType.map(type).join(", ") + ">;"; + function maplike(it) { + return `${it.readonly ? "readonly " : ""}maplike<${it.idlType.map(type).join(", ")}>;`; }; - var setlike = function(it) { - return (it.readonly ? "readonly " : "") + "setlike<" + - type(it.idlType) + ">;"; + function setlike(it) { + return `${it.readonly ? "readonly " : ""}setlike<${type(it.idlType[0])}>;`; }; - var callbackInterface = function(it) { - return 'callback ' + interface_(it); + function callbackInterface(it) { + return `callback ${interface_(it)}`; }; - var table = { + const table = { ws: literal, "ws-pea": wsPea, "ws-tpea": wsTPea, "line-comment": lineComment, "multiline-comment": multilineComment, - "interface": interface_, - operation: operation, - attribute: attribute, - dictionary: dictionary, - field: field, - exception: exception, - "const": const_, - typedef: typedef, - "implements": implements_, - callback: callback, - "enum": enum_, - iterable: iterable, - legacyiterable: legacyiterable, - maplike: maplike, - setlike: setlike, + interface: interface_, + "interface mixin": interface_mixin, + namespace, + operation, + attribute, + dictionary, + field, + const: const_, + typedef, + implements: implements_, + includes, + callback, + enum: enum_, + iterable, + legacyiterable, + maplike, + setlike, "callback interface": callbackInterface }; - var dispatch = function(it) { + function dispatch(it) { + const dispatcher = table[it.type]; + if (!dispatcher) { + throw new Error(`Type "${it.type}" is unsupported`) + } return table[it.type](it); }; - var iterate = function(things) { + function iterate(things) { if (!things) return; - var ret = ""; - for (var i = 0, n = things.length; i < n; i++) ret += dispatch(things[i]); + let ret = ""; + for (const thing of things) ret += dispatch(thing); return ret; }; return iterate(ast); }; - var obj = { - write: function(ast, opt) { - if (!opt) opt = {}; - return write(ast, opt); - } + const obj = { + write }; if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = obj; } else if (typeof define === 'function' && define.amd) { - define([], function() { - return obj; - }); + define([], () => obj); } else { (self || window).WebIDL2Writer = obj; } -}()); +})();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package-lock.json b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package-lock.json index 5352871..a6f529d6 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package-lock.json +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package-lock.json
@@ -1,25 +1,52 @@ { "name": "webidl2", - "version": "4.2.0", + "version": "10.2.1", "lockfileVersion": 1, + "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz", + "integrity": "sha512-eVXQSbu/RimU6OKcK2/gDJVTFcxXJI4sHbIqw2mhwMZeQ2as/8AhS9DGkEDoHMBBNJZ5B0US63lF56x+KDcxiA==", + "dev": true, + "requires": { + "@babel/highlight": "7.0.0-beta.40" + } + }, + "@babel/highlight": { + "version": "7.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.40.tgz", + "integrity": "sha512-mOhhTrzieV6VO7odgzFGFapiwRK0ei8RZRhfzHhb6cpX3QM8XXuCLXWjN8qBB7JReDdUR80V3LFfFrGUYevhNg==", + "dev": true, + "requires": { + "chalk": "2.3.2", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, "ansi-regex": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } }, "arr-flatten": { "version": "1.1.0", @@ -40,34 +67,51 @@ "dev": true }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } }, "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "chalk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", - "dev": true + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.3.0" + } }, "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } }, "color-name": { "version": "1.1.3", @@ -76,9 +120,9 @@ "dev": true }, "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true }, "concat-map": { @@ -88,15 +132,24 @@ "dev": true }, "debug": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", - "integrity": "sha1-vFlryr52F/Edn6FTYe3tVgi4SZs=", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } }, "diff": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", - "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diff-match-patch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.0.tgz", + "integrity": "sha1-HMPIOkkNZ/ldkeOfatHy4Ia2MEg=", "dev": true }, "escape-string-regexp": { @@ -105,37 +158,52 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } }, "expand-range": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true + "dev": true, + "requires": { + "fill-range": "2.2.3" + } }, "expect": { - "version": "21.2.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-21.2.1.tgz", - "integrity": "sha512-orfQQqFRTX0jH7znRIGi8ZMR8kTNpXklTTz8+HGTpmTKZo3Occ6JNB5FXMb8cRuiiC/GyDqsr30zUa66ACYlYw==", + "version": "22.4.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-22.4.0.tgz", + "integrity": "sha512-Fiy862jT3qc70hwIHwwCBNISmaqBrfWKKrtqyMJ6iwZr+6KXtcnHojZFtd63TPRvRl8EQTJ+YXYy2lK6/6u+Hw==", "dev": true, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true - } + "requires": { + "ansi-styles": "3.2.1", + "jest-diff": "22.4.0", + "jest-get-type": "22.1.0", + "jest-matcher-utils": "22.4.0", + "jest-message-util": "22.4.0", + "jest-regex-util": "22.1.0" } }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } }, "filename-regex": { "version": "2.0.1", @@ -147,7 +215,14 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } }, "for-in": { "version": "1.0.2", @@ -159,7 +234,10 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true + "dev": true, + "requires": { + "for-in": "1.0.2" + } }, "fs.realpath": { "version": "1.0.0", @@ -168,52 +246,65 @@ "dev": true }, "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", - "dev": true + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "glob-base": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } }, "glob-parent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "dev": true, + "requires": { + "is-glob": "2.0.1" + } }, "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", - "dev": true - }, - "has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", "dev": true }, "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } }, "inherits": { "version": "2.0.3", @@ -222,9 +313,9 @@ "dev": true }, "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-dotfile": { @@ -237,7 +328,10 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } }, "is-extendable": { "version": "0.1.1", @@ -255,13 +349,19 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true + "dev": true, + "requires": { + "kind-of": "3.2.2" + } }, "is-posix-bracket": { "version": "0.1.1", @@ -285,199 +385,113 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true + "dev": true, + "requires": { + "isarray": "1.0.0" + } }, "jest-diff": { - "version": "21.2.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-21.2.1.tgz", - "integrity": "sha512-E5fu6r7PvvPr5qAWE1RaUwIh/k6Zx/3OOkZ4rk5dBJkEWRrUuSgbMt2EO8IUTPTd6DOqU3LW6uTIwX5FRvXoFA==", + "version": "22.4.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.0.tgz", + "integrity": "sha512-+/t20WmnkOkB8MOaGaPziI8zWKxquMvYw4Ub+wOzi7AUhmpFXz43buWSxVoZo4J5RnCozpGbX3/FssjJ5KV9Nw==", "dev": true, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true - } + "requires": { + "chalk": "2.3.2", + "diff": "3.5.0", + "jest-get-type": "22.1.0", + "pretty-format": "22.4.0" } }, "jest-get-type": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-21.2.0.tgz", - "integrity": "sha512-y2fFw3C+D0yjNSDp7ab1kcd6NUYfy3waPTlD8yWkAtiocJdBRQqNoRqVfMNxgj+IjT0V5cBIHJO0z9vuSSZ43Q==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.1.0.tgz", + "integrity": "sha512-nD97IVOlNP6fjIN5i7j5XRH+hFsHL7VlauBbzRvueaaUe70uohrkz7pL/N8lx/IAwZRTJ//wOdVgh85OgM7g3w==", "dev": true }, "jest-matcher-utils": { - "version": "21.2.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-21.2.1.tgz", - "integrity": "sha512-kn56My+sekD43dwQPrXBl9Zn9tAqwoy25xxe7/iY4u+mG8P3ALj5IK7MLHZ4Mi3xW7uWVCjGY8cm4PqgbsqMCg==", + "version": "22.4.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.0.tgz", + "integrity": "sha512-03m3issxUXpWMwDYTfmL8hRNewUB0yCRTeXPm+eq058rZxLHD9f5NtSSO98CWHqe4UyISIxd9Ao9iDVjHWd2qg==", "dev": true, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true - } + "requires": { + "chalk": "2.3.2", + "jest-get-type": "22.1.0", + "pretty-format": "22.4.0" } }, "jest-message-util": { - "version": "21.2.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-21.2.1.tgz", - "integrity": "sha512-EbC1X2n0t9IdeMECJn2BOg7buOGivCvVNjqKMXTzQOu7uIfLml+keUfCALDh8o4rbtndIeyGU8/BKfoTr/LVDQ==", + "version": "22.4.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.0.tgz", + "integrity": "sha512-eyCJB0T3hrlpFF2FqQoIB093OulP+1qvATQmD3IOgJgMGqPL6eYw8TbC5P/VCWPqKhGL51xvjIIhow5eZ2wHFw==", "dev": true, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true - } + "requires": { + "@babel/code-frame": "7.0.0-beta.40", + "chalk": "2.3.2", + "micromatch": "2.3.11", + "slash": "1.0.0", + "stack-utils": "1.0.1" } }, "jest-regex-util": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-21.2.0.tgz", - "integrity": "sha512-BKQ1F83EQy0d9Jen/mcVX7D+lUt2tthhK/2gDWRgLDJRNOdRgSp1iVqFxP8EN1ARuypvDflRfPzYT8fQnoBQFQ==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.1.0.tgz", + "integrity": "sha512-on0LqVS6Xeh69sw3d1RukVnur+lVOl3zkmb0Q54FHj9wHoq6dbtWqb3TSlnVUyx36hqjJhjgs/QLqs07Bzu72Q==", "dev": true }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "jsondiffpatch": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.2.4.tgz", - "integrity": "sha1-1LbFOz/H2htLkcHCrsi5MrdRHVw=", - "dev": true + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.3.5.tgz", + "integrity": "sha512-v7eaGLDMCHXH+fsIaZhptEUJmS8EJpunq7IM4cc4vIT/kSRAkaZ6ZF4ebiNcyUelL0znbvj6o2B5Gh9v7Og0BQ==", + "dev": true, + "requires": { + "chalk": "2.3.2", + "diff-match-patch": "1.0.0" + } }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basecreate": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash.create": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", - "dev": true - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } }, "minimist": { "version": "0.0.8", @@ -489,51 +503,91 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true + "dev": true, + "requires": { + "minimist": "0.0.8" + } }, "mocha": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.4.1.tgz", - "integrity": "sha1-o4ArSqOBk0yss43nDPdxYh2o+a8=", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.4.tgz", + "integrity": "sha512-nMOpAPFosU1B4Ix1jdhx5e3q7XO55ic5a8cgYvW27CequcEY+BabS0kUVL1Cw1V5PuVHZWeNRWFLmEPexo79VA==", "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, "dependencies": { - "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } } } }, "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true + "dev": true, + "requires": { + "wrappy": "1.0.2" + } }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } }, "path-is-absolute": { "version": "1.0.1", @@ -548,23 +602,13 @@ "dev": true }, "pretty-format": { - "version": "21.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-21.2.1.tgz", - "integrity": "sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A==", + "version": "22.4.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.0.tgz", + "integrity": "sha512-pvCxP2iODIIk9adXlo4S3GRj0BrJiil68kByAa1PrgG97c1tClh9dLMgp3Z6cHFZrclaABt0UH8PIhwHuFLqYA==", "dev": true, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true - } + "requires": { + "ansi-regex": "3.0.0", + "ansi-styles": "3.2.1" } }, "randomatic": { @@ -572,18 +616,28 @@ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, "dependencies": { "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, + "requires": { + "kind-of": "3.2.2" + }, "dependencies": { "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } } } }, @@ -591,7 +645,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } } } }, @@ -599,7 +656,10 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } }, "remove-trailing-separator": { "version": "1.1.0", @@ -625,17 +685,20 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, - "strip-ansi": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=", "dev": true }, "supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", - "dev": true + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } }, "wrappy": { "version": "1.0.2",
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package.json b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package.json index d7b8a42..ab282d1 100644 --- a/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package.json +++ b/third_party/WebKit/LayoutTests/external/wpt/resources/webidl2/package.json
@@ -1,22 +1,24 @@ { "name": "webidl2", "description": "A WebIDL Parser", - "version": "8.1.0", + "version": "10.2.1", "contributors": [ "Robin Berjon <robin@berjon.com> (https://berjon.com)", "Marcos Caceres <marcos@marcosc.com> (https://marcosc.com)", - "Kagami Sascha Rosylight <saschaplas@outlook.com>" + "Kagami Sascha Rosylight <saschaplas@outlook.com>", + "Timothy Gu <timothygu99@gmail.com>" ], "license": "W3C", "dependencies": {}, "devDependencies": { - "expect": "21.2.1", - "jsondiffpatch": "0.2.5", - "mocha": "4.0.1" + "expect": "22.4.0", + "jsondiffpatch": "0.3.5", + "mocha": "5.0.4" }, "scripts": { - "test": "mocha" + "test": "mocha", + "acquire": "node test/util/acquire.js" }, - "repository": "git://github.com/darobin/webidl2.js", + "repository": "git://github.com/w3c/webidl2.js", "main": "index" }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webaudio/resources/audionodeoptions.js b/third_party/WebKit/LayoutTests/external/wpt/webaudio/resources/audionodeoptions.js new file mode 100644 index 0000000..293e8e0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/resources/audionodeoptions.js
@@ -0,0 +1,251 @@ +// Test that constructor for the node with name |nodeName| handles the +// various possible values for channelCount, channelCountMode, and +// channelInterpretation. + +// The |should| parameter is the test function from new |Audit|. +function testAudioNodeOptions(should, context, nodeName, expectedNodeOptions) { + if (expectedNodeOptions === undefined) + expectedNodeOptions = {}; + let node; + + // Test that we can set channelCount and that errors are thrown for + // invalid values + let testChannelCount = 17; + if (expectedNodeOptions.channelCount) { + testChannelCount = expectedNodeOptions.channelCount.value; + } + should( + () => { + node = new window[nodeName]( + context, Object.assign({}, expectedNodeOptions.additionalOptions, { + channelCount: testChannelCount + })); + }, + 'new ' + nodeName + '(c, {channelCount: ' + testChannelCount + '}}') + .notThrow(); + should(node.channelCount, 'node.channelCount').beEqualTo(testChannelCount); + + if (expectedNodeOptions.channelCount && + expectedNodeOptions.channelCount.isFixed) { + // The channel count is fixed. Verify that we throw an error if + // we try to change it. Arbitrarily set the count to be one more + // than the expected value. + testChannelCount = expectedNodeOptions.channelCount.value + 1; + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionalOptions, + {channelCount: testChannelCount})); + }, + 'new ' + nodeName + '(c, {channelCount: ' + testChannelCount + '}}') + .throw(expectedNodeOptions.channelCount.errorType || 'TypeError'); + } else { + // The channel count is not fixed. Try to set the count to invalid + // values and make sure an error is thrown. + let errorType = 'NotSupportedError'; + + [0, 99].forEach(testValue => { + should(() => { + node = new window[nodeName]( + context, Object.assign({}, expectedNodeOptions.additionalOptions, { + channelCount: testValue + })); + }, `new ${nodeName}(c, {channelCount: ${testValue}})`).throw(errorType); + }); + } + + // Test channelCountMode + let testChannelCountMode = 'max'; + if (expectedNodeOptions.channelCountMode) { + testChannelCountMode = expectedNodeOptions.channelCountMode.value; + } + should( + () => { + node = new window[nodeName]( + context, Object.assign({}, expectedNodeOptions.additionalOptions, { + channelCountMode: testChannelCountMode + })); + }, + 'new ' + nodeName + '(c, {channelCountMode: "' + testChannelCountMode + + '"}') + .notThrow(); + should(node.channelCountMode, 'node.channelCountMode') + .beEqualTo(testChannelCountMode); + + if (expectedNodeOptions.channelCountMode && + expectedNodeOptions.channelCountMode.isFixed) { + // Channel count mode is fixed. Test setting to something else throws. + ['max', 'clamped-max', 'explicit'].forEach(testValue => { + if (testValue !== expectedNodeOptions.channelCountMode.value) { + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionalOptions, + {channelCountMode: testValue})); + }, + `new ${nodeName}(c, {channelCountMode: "${testValue}"})`) + .throw(expectedNodeOptions.channelCountMode.errorType); + } + }); + } else { + // Mode is not fixed. Verify that we can set the mode to all valid + // values, and that we throw for invalid values. + + let testValues = ['max', 'clamped-max', 'explicit']; + + testValues.forEach(testValue => { + should(() => { + node = new window[nodeName]( + context, Object.assign({}, expectedNodeOptions.additionalOptions, { + channelCountMode: testValue + })); + }, `new ${nodeName}(c, {channelCountMode: "${testValue}"})`).notThrow(); + should( + node.channelCountMode, 'node.channelCountMode after valid setter') + .beEqualTo(testValue); + + }); + + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionalOptions, + {channelCountMode: 'foobar'})); + }, + 'new ' + nodeName + '(c, {channelCountMode: "foobar"}') + .throw('TypeError'); + should(node.channelCountMode, 'node.channelCountMode after invalid setter') + .beEqualTo(testValues[testValues.length - 1]); + } + + // Test channelInterpretation + if (expectedNodeOptions.channelInterpretation && + expectedNodeOptions.channelInterpretation.isFixed) { + // The channel interpretation is fixed. Verify that we throw an + // error if we try to change it. + ['speakers', 'discrete'].forEach(testValue => { + if (testValue !== expectedNodeOptions.channelInterpretation.value) { + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionOptions, + {channelInterpretation: testValue})); + }, + `new ${nodeName}(c, {channelInterpretation: "${testValue}"})`) + .throw(expectedNodeOptions.channelInterpretation.errorType); + } + }); + } else { + // Channel interpretation is not fixed. Verify that we can set it + // to all possible values. + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionalOptions, + {channelInterpretation: 'speakers'})); + }, + 'new ' + nodeName + '(c, {channelInterpretation: "speakers"})') + .notThrow(); + should(node.channelInterpretation, 'node.channelInterpretation') + .beEqualTo('speakers'); + + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionalOptions, + {channelInterpretation: 'discrete'})); + }, + 'new ' + nodeName + '(c, {channelInterpretation: "discrete"})') + .notThrow(); + should(node.channelInterpretation, 'node.channelInterpretation') + .beEqualTo('discrete'); + + should( + () => { + node = new window[nodeName]( + context, + Object.assign( + {}, expectedNodeOptions.additionalOptions, + {channelInterpretation: 'foobar'})); + }, + 'new ' + nodeName + '(c, {channelInterpretation: "foobar"})') + .throw('TypeError'); + should( + node.channelInterpretation, + 'node.channelInterpretation after invalid setter') + .beEqualTo('discrete'); + } +} + +function initializeContext(should) { + let c; + should(() => { + c = new OfflineAudioContext(1, 1, 48000); + }, 'context = new OfflineAudioContext(...)').notThrow(); + + return c; +} + +function testInvalidConstructor(should, name, context) { + should(() => { + new window[name](); + }, 'new ' + name + '()').throw('TypeError'); + should(() => { + new window[name](1); + }, 'new ' + name + '(1)').throw('TypeError'); + should(() => { + new window[name](context, 42); + }, 'new ' + name + '(context, 42)').throw('TypeError'); +} + +function testDefaultConstructor(should, name, context, options) { + let node; + + let message = options.prefix + ' = new ' + name + '(context'; + if (options.constructorOptions) + message += ', ' + JSON.stringify(options.constructorOptions); + message += ')' + + should(() => { + node = new window[name](context, options.constructorOptions); + }, message).notThrow(); + + should(node instanceof window[name], options.prefix + ' instanceof ' + name) + .beEqualTo(true); + should(node.numberOfInputs, options.prefix + '.numberOfInputs') + .beEqualTo(options.numberOfInputs); + should(node.numberOfOutputs, options.prefix + '.numberOfOutputs') + .beEqualTo(options.numberOfOutputs); + should(node.channelCount, options.prefix + '.channelCount') + .beEqualTo(options.channelCount); + should(node.channelCountMode, options.prefix + '.channelCountMode') + .beEqualTo(options.channelCountMode); + should(node.channelInterpretation, options.prefix + '.channelInterpretation') + .beEqualTo(options.channelInterpretation); + + return node; +} + +function testDefaultAttributes(should, node, prefix, items) { + items.forEach((item) => { + let attr = node[item.name]; + if (attr instanceof AudioParam) { + should(attr.value, prefix + '.' + item.name + '.value') + .beEqualTo(item.value); + } else { + should(attr, prefix + '.' + item.name).beEqualTo(item.value); + } + }); +}
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/ctor-analyser.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html similarity index 94% rename from third_party/WebKit/LayoutTests/webaudio/Analyser/ctor-analyser.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html index a69231c..2112edeef 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Analyser/ctor-analyser.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html
@@ -4,11 +4,11 @@ <title> Test Constructor: AnalyserNode </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/ctor-audiobuffersource.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html similarity index 91% rename from third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/ctor-audiobuffersource.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html index 7ab4712..c1c3203 100644 --- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/ctor-audiobuffersource.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html
@@ -4,11 +4,11 @@ <title> Test Constructor: AudioBufferSource </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/ctor-biquadfilter.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/ctor-biquadfilter.html similarity index 88% rename from third_party/WebKit/LayoutTests/webaudio/BiquadFilter/ctor-biquadfilter.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/ctor-biquadfilter.html index ab7ac2f..e63479f9 100644 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/ctor-biquadfilter.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/ctor-biquadfilter.html
@@ -4,11 +4,11 @@ <title> Test Constructor: BiquadFilter </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/ChannelMerger/ctor-channelmerger.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/ctor-channelmerger.html similarity index 90% rename from third_party/WebKit/LayoutTests/webaudio/ChannelMerger/ctor-channelmerger.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/ctor-channelmerger.html index 2142b1e9d..115bd994 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ChannelMerger/ctor-channelmerger.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/ctor-channelmerger.html
@@ -4,11 +4,11 @@ <title> Test Constructor: ChannelMerger </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/ChannelSplitter/ctor-channelsplitter.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html similarity index 90% rename from third_party/WebKit/LayoutTests/webaudio/ChannelSplitter/ctor-channelsplitter.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html index df64bc8..7fa9d6f 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ChannelSplitter/ctor-channelsplitter.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html
@@ -4,11 +4,11 @@ <title> Test Constructor: ChannelSplitter </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/ConstantSource/ctor-constantsource.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-constantsourcenode-interface/ctor-constantsource.html similarity index 78% rename from third_party/WebKit/LayoutTests/webaudio/ConstantSource/ctor-constantsource.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-constantsourcenode-interface/ctor-constantsource.html index 3a3524f..ea4a65e1 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ConstantSource/ctor-constantsource.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-constantsourcenode-interface/ctor-constantsource.html
@@ -4,11 +4,11 @@ <title> Test Constructor: ConstantSource </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/Convolver/ctor-convolver.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/ctor-convolver.html similarity index 91% rename from third_party/WebKit/LayoutTests/webaudio/Convolver/ctor-convolver.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/ctor-convolver.html index e445fab..cf81833 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Convolver/ctor-convolver.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/ctor-convolver.html
@@ -4,11 +4,11 @@ <title> Test Constructor: Convolver </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/Delay/ctor-delay.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html similarity index 85% rename from third_party/WebKit/LayoutTests/webaudio/Delay/ctor-delay.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html index f061ede..e7ccefc6 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Delay/ctor-delay.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html
@@ -4,11 +4,11 @@ <title> Test Constructor: Delay </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/DynamicsCompressor/ctor-dynamicscompressor.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html similarity index 94% rename from third_party/WebKit/LayoutTests/webaudio/DynamicsCompressor/ctor-dynamicscompressor.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html index 1b32303..799c1872 100644 --- a/third_party/WebKit/LayoutTests/webaudio/DynamicsCompressor/ctor-dynamicscompressor.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html
@@ -4,11 +4,11 @@ <title> Test Constructor: DynamicsCompressor </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/Gain/ctor-gain.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html similarity index 86% rename from third_party/WebKit/LayoutTests/webaudio/Gain/ctor-gain.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html index 86a1433..dec273e 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Gain/ctor-gain.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html
@@ -4,11 +4,11 @@ <title> Test Constructor: Gain </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/IIRFilter/ctor-iirfilter.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html similarity index 92% rename from third_party/WebKit/LayoutTests/webaudio/IIRFilter/ctor-iirfilter.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html index ccf28a9..bb89512 100644 --- a/third_party/WebKit/LayoutTests/webaudio/IIRFilter/ctor-iirfilter.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html
@@ -4,11 +4,11 @@ <title> Test Constructor: IIRFilter </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/OfflineAudioContext/ctor-offlineaudiocontext.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html similarity index 95% rename from third_party/WebKit/LayoutTests/webaudio/OfflineAudioContext/ctor-offlineaudiocontext.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html index b4454c7f..79aafe7 100644 --- a/third_party/WebKit/LayoutTests/webaudio/OfflineAudioContext/ctor-offlineaudiocontext.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html
@@ -2,11 +2,11 @@ <html> <head> <title>Test Constructor: OfflineAudioContext</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/Oscillator/ctor-oscillator.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html similarity index 90% rename from third_party/WebKit/LayoutTests/webaudio/Oscillator/ctor-oscillator.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html index 47941697..aaf77ae 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Oscillator/ctor-oscillator.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html
@@ -4,11 +4,11 @@ <title> Test Constructor: Oscillator </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/Panner/ctor-panner.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html similarity index 96% rename from third_party/WebKit/LayoutTests/webaudio/Panner/ctor-panner.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html index db00203b..48b368d 100644 --- a/third_party/WebKit/LayoutTests/webaudio/Panner/ctor-panner.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html
@@ -4,11 +4,11 @@ <title> Test Constructor: Panner </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/StereoPanner/ctor-stereopanner.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html similarity index 92% rename from third_party/WebKit/LayoutTests/webaudio/StereoPanner/ctor-stereopanner.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html index f124a4b..9de58cf 100644 --- a/third_party/WebKit/LayoutTests/webaudio/StereoPanner/ctor-stereopanner.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html
@@ -4,11 +4,11 @@ <title> Test Constructor: StereoPanner </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/WaveShaper/ctor-waveshaper.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html similarity index 86% rename from third_party/WebKit/LayoutTests/webaudio/WaveShaper/ctor-waveshaper.html rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html index 17f3fbe..7aa33ca5 100644 --- a/third_party/WebKit/LayoutTests/webaudio/WaveShaper/ctor-waveshaper.html +++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html
@@ -4,11 +4,11 @@ <title> Test Constructor: WaveShaper </title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/audionodeoptions.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audionodeoptions.js"></script> </head> <body> <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/fast/css/cssText-shorthand.html b/third_party/WebKit/LayoutTests/fast/css/cssText-shorthand.html index fef9b6c..658b889a 100644 --- a/third_party/WebKit/LayoutTests/fast/css/cssText-shorthand.html +++ b/third_party/WebKit/LayoutTests/fast/css/cssText-shorthand.html
@@ -4,39 +4,15 @@ <script> var tests = [ - ['border: 1px; border-top: 1px;', 'border: 1px;'], - ['border: 1px solid red;', 'border: 1px solid red;'], - ['border: 1px red;', 'border: 1px red;'], - ['border: red;', 'border: red;'], - ['border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px;', - 'border-color: initial; border-style: initial; border-width: 1px;'], - ['border-top: 1px; border-right: 2px; border-bottom: 3px; border-left: 4px;', 'border-color: initial; border-style: initial; border-width: 1px 2px 3px 4px;'], - ['border: 1px; border-top: 2px;', 'border-color: initial; border-style: initial; border-width: 2px 1px 1px; border-image: initial;'], - ['border: 1px; border-top: 1px !important;', - 'border-right: 1px; border-bottom: 1px; border-left: 1px; border-top: 1px !important; border-image: initial;'], - - ['border: 1px; border-top-color: red;', - 'border-bottom-color: initial; border-image: initial; border-left-color: initial; border-right-color: initial; border-style: initial; border-top-color: red; border-width: 1px;'], - ['border: solid; border-style: dotted', 'border: dotted;'], - ['border-width: 1px;', 'border-width: 1px;'], - ['-webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 2px;', 'border-spacing: 1px 2px;'], // We don't use shorthand for font-family, etc... for compatibility reasons ['font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold;', 'font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold;'], - ['list-style-type: circle; list-style-position: inside; list-style-image: initial;', 'list-style: circle inside;'], - ['margin-top: 1px; margin-right: 2px; margin-bottom: 3px; margin-left: 4px;', 'margin: 1px 2px 3px 4px;'], ['margin: 1px 2px; margin-top: var(--x);', 'margin-right: 2px; margin-bottom: 1px; margin-left: 2px; margin-top: var(--x);'], - ['outline-width: 2px; outline-style: dotted; outline-color: blue;', 'outline: blue dotted 2px;'], - ['overflow-x: scroll; overflow-y: hidden;', 'overflow-x: scroll; overflow-y: hidden;'], - ['overflow-x: scroll; overflow-y: scroll;', 'overflow: scroll;'], - ['padding-top: 1px; padding-right: 2px; padding-bottom: 3px; padding-left: 4px;', 'padding: 1px 2px 3px 4px;'], ['padding: initial; padding-top: initial !important', 'padding-top: initial !important; padding-right: initial; padding-bottom: initial; padding-left: initial;'], - - ['list-style-type: lower-alpha;', 'list-style-type: lower-alpha;'] ]; function normalizeCssText(text) { return text.trim().split(/;\s*/).sort().slice(1).join("; "); }
diff --git a/third_party/WebKit/LayoutTests/fast/css/different-overflow-x-and-y.html b/third_party/WebKit/LayoutTests/fast/css/different-overflow-x-and-y.html deleted file mode 100644 index a4115cd3..0000000 --- a/third_party/WebKit/LayoutTests/fast/css/different-overflow-x-and-y.html +++ /dev/null
@@ -1,10 +0,0 @@ -<!DOCTYPE html> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<div id="target" style="overflow-x: hidden; overflow-y: scroll"></div> -<script> -var element = document.getElementById('target'); -test(function() { - assert_equals("", window.getComputedStyle(element).overflow); -}, 'Overflow is empty string if overflowX != overflowY') -</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation-errors-2.html b/third_party/WebKit/LayoutTests/fast/css/invalidation-errors-2.html index 34a7846a..938e6f8 100644 --- a/third_party/WebKit/LayoutTests/fast/css/invalidation-errors-2.html +++ b/third_party/WebKit/LayoutTests/fast/css/invalidation-errors-2.html
@@ -60,7 +60,7 @@ outline: red solid auto; overflow: hidden; - overflow: visible scroll; + overflow: visible blue; } #Content { display: inline;
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation-errors.html b/third_party/WebKit/LayoutTests/fast/css/invalidation-errors.html index 055d8597..fd3c7d1f 100644 --- a/third_party/WebKit/LayoutTests/fast/css/invalidation-errors.html +++ b/third_party/WebKit/LayoutTests/fast/css/invalidation-errors.html
@@ -60,7 +60,7 @@ outline: red solid garbage; overflow: hidden; - overflow: visible scroll; + overflow: visible blue; } #Content { display: inline;
diff --git a/third_party/WebKit/LayoutTests/fast/css/overflow-property-expected.txt b/third_party/WebKit/LayoutTests/fast/css/overflow-property-expected.txt index 83feaabb..77443572 100644 --- a/third_party/WebKit/LayoutTests/fast/css/overflow-property-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/overflow-property-expected.txt
@@ -3,13 +3,13 @@ PASS test0.style.overflow is "auto" PASS test0.style.overflowX is "auto" PASS test0.style.overflowY is "auto" -PASS test1.style.overflow is "" -PASS test1.style.overflowX is "" -PASS test1.style.overflowY is "" +PASS test1.style.overflow is "auto" +PASS test1.style.overflowX is "auto" +PASS test1.style.overflowY is "auto" PASS test2.style.overflow is "scroll" PASS test2.style.overflowX is "scroll" PASS test2.style.overflowY is "scroll" -PASS test3.style.overflow is "" +PASS test3.style.overflow is "overlay hidden" PASS test3.style.overflowX is "overlay" PASS test3.style.overflowY is "hidden" PASS test4.style.overflow is ""
diff --git a/third_party/WebKit/LayoutTests/fast/css/overflow-property.html b/third_party/WebKit/LayoutTests/fast/css/overflow-property.html index d94717d..d2e1ce5 100644 --- a/third_party/WebKit/LayoutTests/fast/css/overflow-property.html +++ b/third_party/WebKit/LayoutTests/fast/css/overflow-property.html
@@ -39,13 +39,13 @@ shouldBeEqualToString("test0.style.overflow", "auto"); shouldBeEqualToString("test0.style.overflowX", "auto"); shouldBeEqualToString("test0.style.overflowY", "auto"); - shouldBeEqualToString("test1.style.overflow", ""); - shouldBeEqualToString("test1.style.overflowX", ""); - shouldBeEqualToString("test1.style.overflowY", ""); + shouldBeEqualToString("test1.style.overflow", "auto"); + shouldBeEqualToString("test1.style.overflowX", "auto"); + shouldBeEqualToString("test1.style.overflowY", "auto"); shouldBeEqualToString("test2.style.overflow", "scroll"); shouldBeEqualToString("test2.style.overflowX", "scroll"); shouldBeEqualToString("test2.style.overflowY", "scroll"); - shouldBeEqualToString("test3.style.overflow", ""); + shouldBeEqualToString("test3.style.overflow", "overlay hidden"); shouldBeEqualToString("test3.style.overflowX", "overlay"); shouldBeEqualToString("test3.style.overflowY", "hidden"); shouldBeEqualToString("test4.style.overflow", "");
diff --git a/third_party/WebKit/LayoutTests/fast/forms/range/slider-horizontal-to-vertical-expected.html b/third_party/WebKit/LayoutTests/fast/forms/range/slider-horizontal-to-vertical-expected.html new file mode 100644 index 0000000..9aa155b --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/forms/range/slider-horizontal-to-vertical-expected.html
@@ -0,0 +1,3 @@ +<!DOCTYPE html> +<p>You should see a vertical slider with a matching thumb below.</p> +<input type="range" style="-webkit-appearance:slider-vertical">
diff --git a/third_party/WebKit/LayoutTests/fast/forms/range/slider-horizontal-to-vertical.html b/third_party/WebKit/LayoutTests/fast/forms/range/slider-horizontal-to-vertical.html new file mode 100644 index 0000000..ec626faa --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/forms/range/slider-horizontal-to-vertical.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<style> + .horizontal { -webkit-appearance: slider-horizontal } + .vertical { -webkit-appearance: slider-vertical } +</style> +<p>You should see a vertical slider with a matching thumb below.</p> +<input id="range" type="range" class="horizontal"> +<script> + document.body.offsetTop; + range.className = "vertical"; +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/frames/appended-iframes-layout-expected.html b/third_party/WebKit/LayoutTests/fast/frames/appended-iframes-layout-expected.html new file mode 100644 index 0000000..43694ce1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/frames/appended-iframes-layout-expected.html
@@ -0,0 +1,9 @@ +<!doctype html> +<html> +<body> + <div> + <iframe src="about:blank"></iframe> + <iframe src="data:text/plain;utf-8,hello"></iframe> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/frames/appended-iframes-layout.html b/third_party/WebKit/LayoutTests/fast/frames/appended-iframes-layout.html new file mode 100644 index 0000000..6bba487d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/frames/appended-iframes-layout.html
@@ -0,0 +1,18 @@ +<!DOCTYPE html> +<link rel="appended-iframes-layout-expected.html"> +<!-- The tests creates two iframes in a template and instantiates in script. --> +<html> +<body> + <template> + <div> + <iframe src="about:blank"></iframe> + <iframe src="data:text/plain;utf-8,hello"></iframe> + </div> + </template> + + <script> + var t = document.importNode(document.querySelector('template').content, true); + document.body.appendChild(t); + </script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html b/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html index 764ac2b..c213c52a7 100644 --- a/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html +++ b/third_party/WebKit/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html
@@ -114,135 +114,137 @@ eventSender.gestureScrollEnd(0, 0); } -if ('ScrollState' in window) { - test(function() { - reset(); +addEventListener('load', () => { + if ('ScrollState' in window) { + test(function() { + reset(); - // Scroll five times, with three scrollable elements. - var cScrollTop = [85, 100, 100, 100, 100]; - var aScrollTop = [0, 0, 65, 100, 100]; - var scrollingElementScrollTop = [0, 0, 0, 0, 45]; + // Scroll five times, with three scrollable elements. + var cScrollTop = [85, 100, 100, 100, 100]; + var aScrollTop = [0, 0, 65, 100, 100]; + var scrollingElementScrollTop = [0, 0, 0, 0, 45]; - for (var i = 0; i < deltas.length; ++i) { - applyDelta(deltas[i]); - assert_equals(a.scrollTop, aScrollTop[i], "For id 'a' on step " + i); - assert_equals(c.scrollTop, cScrollTop[i], "For id 'c' on step " + i); - assert_equals(document.scrollingElement.scrollTop, scrollingElementScrollTop[i], "For scrollingElement on step " + i); - } - }, "Scroll offsets are modified correctly."); + for (var i = 0; i < deltas.length; ++i) { + applyDelta(deltas[i]); + assert_equals(a.scrollTop, aScrollTop[i], "For id 'a' on step " + i); + assert_equals(c.scrollTop, cScrollTop[i], "For id 'c' on step " + i); + assert_equals(document.scrollingElement.scrollTop, scrollingElementScrollTop[i], "For scrollingElement on step " + i); + } + }, "Scroll offsets are modified correctly."); - test(function() { - reset(); + test(function() { + reset(); - // Scroll five times, with five elements. - var unapplied = [ - // d, the innermost element, never applies any scroll. - [85, 75, 65, 55, 45], - // c applies the first two scrolls, and then hits its scroll extents. - [0, 0, 65, 55, 45], - // b doesn't scroll, and so leaves the same deltas unapplied as c. - [65, 55, 45], - // a hits its scroll extent on the second last step. - [0, 0, 45], - // The scrollingElement performs the frame scroll. - [0]]; + // Scroll five times, with five elements. + var unapplied = [ + // d, the innermost element, never applies any scroll. + [85, 75, 65, 55, 45], + // c applies the first two scrolls, and then hits its scroll extents. + [0, 0, 65, 55, 45], + // b doesn't scroll, and so leaves the same deltas unapplied as c. + [65, 55, 45], + // a hits its scroll extent on the second last step. + [0, 0, 45], + // The scrollingElement performs the frame scroll. + [0]]; - for (var i = 0; i < deltas.length; ++i) - applyDelta(deltas[i]); + for (var i = 0; i < deltas.length; ++i) + applyDelta(deltas[i]); - for (var i = 0; i < elements.length; ++i) { - var el = elements[i]; - // Every element sees the same deltas being distributed. - assert_array_equals(el.distributedDeltaY, deltas, "distributed delta for " + el.id + ":"); - assert_array_equals(el.unappliedDeltaY, unapplied[i], "unapplied delta for " + el.id + ":"); - } + for (var i = 0; i < elements.length; ++i) { + var el = elements[i]; + // Every element sees the same deltas being distributed. + assert_array_equals(el.distributedDeltaY, deltas, "distributed delta for " + el.id + ":"); + assert_array_equals(el.unappliedDeltaY, unapplied[i], "unapplied delta for " + el.id + ":"); + } - // Ensure that the document leaves scroll unapplied when appropriate. - var documentUnapplied = document.scrollingElement.unappliedDeltaY; - applyDelta(4000); - assert_equals(documentUnapplied[documentUnapplied.length - 1], 0); - applyDelta(4000); - assert_equals(documentUnapplied[documentUnapplied.length - 1], 4000); - }, "Correct amount of delta is consumed."); + // Ensure that the document leaves scroll unapplied when appropriate. + var documentUnapplied = document.scrollingElement.unappliedDeltaY; + applyDelta(4000); + assert_equals(documentUnapplied[documentUnapplied.length - 1], 0); + applyDelta(4000); + assert_equals(documentUnapplied[documentUnapplied.length - 1], 4000); + }, "Correct amount of delta is consumed."); - test(function() { - reset(); + test(function() { + reset(); - // Scroll five times, with three scrollable elements. - var cScrollTop = [85, 100, 100, 100, 100]; - var aScrollTop = [0, 0, 65, 100, 100]; - var scrollingElementScrollTop = [0, 0, 0, 0, 45]; - for (var i = 0; i < deltas.length; ++i) { - applyDelta(deltas[i]); - assert_equals(c.scrollTop, cScrollTop[i], "For id 'c' on step " + i); - assert_equals(a.scrollTop, aScrollTop[i], "For id 'a' on step " + i); - assert_equals(document.scrollingElement.scrollTop, scrollingElementScrollTop[i], "For scrollingElement on step " + i); - } - }, "Scroll propagation behaves correctly."); + // Scroll five times, with three scrollable elements. + var cScrollTop = [85, 100, 100, 100, 100]; + var aScrollTop = [0, 0, 65, 100, 100]; + var scrollingElementScrollTop = [0, 0, 0, 0, 45]; + for (var i = 0; i < deltas.length; ++i) { + applyDelta(deltas[i]); + assert_equals(c.scrollTop, cScrollTop[i], "For id 'c' on step " + i); + assert_equals(a.scrollTop, aScrollTop[i], "For id 'a' on step " + i); + assert_equals(document.scrollingElement.scrollTop, scrollingElementScrollTop[i], "For scrollingElement on step " + i); + } + }, "Scroll propagation behaves correctly."); - test(function() { - reset(); + test(function() { + reset(); - for (var i = 0; i < deltas.length; ++i) - applyDelta(deltas[i]); + for (var i = 0; i < deltas.length; ++i) + applyDelta(deltas[i]); - for (var i = 0; i < elements.length; ++i) { - assert_equals(elements[i].numberOfScrollBegins, deltas.length, "Incorrect number of begin events for " + elements[i].id); - assert_equals(elements[i].numberOfScrollEnds, deltas.length, "Incorrect number of end events for " + elements[i].id); - } - }, "Correct number of scroll begin and end events observed."); + for (var i = 0; i < elements.length; ++i) { + assert_equals(elements[i].numberOfScrollBegins, deltas.length, "Incorrect number of begin events for " + elements[i].id); + assert_equals(elements[i].numberOfScrollEnds, deltas.length, "Incorrect number of end events for " + elements[i].id); + } + }, "Correct number of scroll begin and end events observed."); - { - // NOTE - this async test needs to be run last, as it shares state with the - // other tests. If other tests are run after it, they'll modify the state - // while this test is still running. - var flingTest = async_test("Touchscreen fling doesn't propagate."); - reset(); + { + // NOTE - this async test needs to be run last, as it shares state with the + // other tests. If other tests are run after it, they'll modify the state + // while this test is still running. + var flingTest = async_test("Touchscreen fling doesn't propagate."); + reset(); - function assertScrollTops(cTop, aTop, scrollingElementTop, step) { - assert_equals(c.scrollTop, cTop, "For id 'c' on step " + step); - assert_equals(a.scrollTop, aTop, "For id 'a' on step " + step); - assert_equals(document.scrollingElement.scrollTop, scrollingElementTop, "For scrollingElement on step " + step); - }; + function assertScrollTops(cTop, aTop, scrollingElementTop, step) { + assert_equals(c.scrollTop, cTop, "For id 'c' on step " + step); + assert_equals(a.scrollTop, aTop, "For id 'a' on step " + step); + assert_equals(document.scrollingElement.scrollTop, scrollingElementTop, "For scrollingElement on step " + step); + }; - assertScrollTops(0, 0, 0, 0); + assertScrollTops(0, 0, 0, 0); - var frame_actions = [ - function() { - // Touchscreen GFS arrives when there is an ongoing scroll. - eventSender.gestureScrollBegin(10, 10); - eventSender.gestureFlingStart(10, 10, -1000000, -1000000, "touchscreen"); - }, - flingTest.step_func(function() { - assertScrollTops(100, 0, 0, 1); - }), - flingTest.step_func(function() { - assertScrollTops(100, 0, 0, 2); - }), - flingTest.step_func(function() { - assertScrollTops(100, 0, 0, 3); - }), - flingTest.step_func(function() { - assertScrollTops(100, 0, 0, 4); - flingTest.done(); - }) - ] + var frame_actions = [ + function() { + // Touchscreen GFS arrives when there is an ongoing scroll. + eventSender.gestureScrollBegin(10, 10); + eventSender.gestureFlingStart(10, 10, -1000000, -1000000, "touchscreen"); + }, + flingTest.step_func(function() { + assertScrollTops(100, 0, 0, 1); + }), + flingTest.step_func(function() { + assertScrollTops(100, 0, 0, 2); + }), + flingTest.step_func(function() { + assertScrollTops(100, 0, 0, 3); + }), + flingTest.step_func(function() { + assertScrollTops(100, 0, 0, 4); + flingTest.done(); + }) + ] - function executeFrameActions(frame_actions) { - var frame = 0; - function raf() { - frame_actions[frame](); - frame++; - if (frame >= frame_actions.length) - return; + function executeFrameActions(frame_actions) { + var frame = 0; + function raf() { + frame_actions[frame](); + frame++; + if (frame >= frame_actions.length) + return; + window.requestAnimationFrame(raf); + } window.requestAnimationFrame(raf); } - window.requestAnimationFrame(raf); - } - executeFrameActions(frame_actions); + executeFrameActions(frame_actions); + } } -} +}); </script> </body>
diff --git a/third_party/WebKit/LayoutTests/fast/scrolling/keyboard-scroll-before-layout.html b/third_party/WebKit/LayoutTests/fast/scrolling/keyboard-scroll-before-layout.html new file mode 100644 index 0000000..0fa8fa31 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/scrolling/keyboard-scroll-before-layout.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html hidden=""> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script> + test(() => { + eventSender.keyDown(' ', []); + }, "Test that keyboard scroll without a layout tree doesn't crash"); +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/compositing/overflow/parent-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/compositing/overflow/parent-overflow-expected.txt deleted file mode 100644 index 8255bbc0..0000000 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/compositing/overflow/parent-overflow-expected.txt +++ /dev/null
@@ -1,26 +0,0 @@ -layer at (0,0) size 800x600 - LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x227 - LayoutNGBlockFlow {HTML} at (0,0) size 800x227 - LayoutNGBlockFlow {BODY} at (8,16) size 784x203 - LayoutNGBlockFlow {P} at (0,0) size 784x20 - LayoutText {#text} at (0,0) size 362x19 - text run at (0,0) width 362: "Gray box should be clipped by black border in each case." - LayoutNGBlockFlow (anonymous) at (0,36) size 784x167 - LayoutText {#text} at (162,147) size 4x19 - text run at (162,147) width 4: " " - LayoutText {#text} at (328,147) size 4x19 - text run at (328,147) width 4: " " - LayoutText {#text} at (0,0) size 0x0 -layer at (28,72) size 122x122 clip at (29,73) size 120x120 scrollHeight 310 - LayoutNGBlockFlow {DIV} at (20,20) size 122x122 [border: (1px solid #000000)] -layer at (39,83) size 100x300 backgroundClip at (29,73) size 120x120 clip at (29,73) size 120x120 - LayoutNGBlockFlow {DIV} at (11,11) size 100x300 [bgcolor=#808080] -layer at (194,72) size 122x122 clip at (195,73) size 120x120 scrollHeight 310 - LayoutNGBlockFlow (relative positioned) {DIV} at (186,20) size 122x122 [border: (1px solid #000000)] -layer at (205,83) size 100x300 backgroundClip at (195,73) size 120x120 clip at (195,73) size 120x120 - LayoutNGBlockFlow {DIV} at (11,11) size 100x300 [bgcolor=#808080] -layer at (360,72) size 122x122 clip at (361,73) size 120x120 scrollHeight 310 - LayoutNGBlockFlow (relative positioned) {DIV} at (352,20) size 122x122 [border: (1px solid #000000)] -layer at (371,83) size 100x300 backgroundClip at (361,73) size 120x120 clip at (361,73) size 120x120 - LayoutNGBlockFlow {DIV} at (11,11) size 100x300 [bgcolor=#808080]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/alt-text-wrapping-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/alt-text-wrapping-expected.txt index c974b4d..d3d3ec5d 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/alt-text-wrapping-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/images/alt-text-wrapping-expected.txt
@@ -5,7 +5,7 @@ LayoutNGBlockFlow {BODY} at (8,8) size 784x584 LayoutNGBlockFlow {IMG} at (0,0) size 100x100 LayoutText {#text} at (0,0) size 0x0 -layer at (8,8) size 100x100 clip at (9,9) size 98x98 scrollHeight 101 +layer at (8,8) size 100x100 clip at (9,9) size 98x98 scrollHeight 102 LayoutNGBlockFlow {SPAN} at (0,0) size 100x100 [border: (1px solid #C0C0C0)] LayoutImage (floating) {IMG} at (2,2) size 16x16 LayoutNGBlockFlow (anonymous) at (2,2) size 96x100
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/alt-text-wrapping-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/alt-text-wrapping-expected.txt index c974b4d..d3d3ec5d 100644 --- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/alt-text-wrapping-expected.txt +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu-rasterization/images/alt-text-wrapping-expected.txt
@@ -5,7 +5,7 @@ LayoutNGBlockFlow {BODY} at (8,8) size 784x584 LayoutNGBlockFlow {IMG} at (0,0) size 100x100 LayoutText {#text} at (0,0) size 0x0 -layer at (8,8) size 100x100 clip at (9,9) size 98x98 scrollHeight 101 +layer at (8,8) size 100x100 clip at (9,9) size 98x98 scrollHeight 102 LayoutNGBlockFlow {SPAN} at (0,0) size 100x100 [border: (1px solid #C0C0C0)] LayoutImage (floating) {IMG} at (2,2) size 16x16 LayoutNGBlockFlow (anonymous) at (2,2) size 96x100
diff --git a/third_party/WebKit/LayoutTests/media/controls/modern/overlay-play-button-tap-to-hide.html b/third_party/WebKit/LayoutTests/media/controls/modern/overlay-play-button-tap-to-hide.html new file mode 100644 index 0000000..f9191ad --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/controls/modern/overlay-play-button-tap-to-hide.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>Hide the overlay play button on play, show it on tap</title> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../media-controls.js"></script> +<body> +<video id=video width=400 controls preload=metadata></video> +<script> +async_test((t) => { + const video = document.getElementById('video'); + const button = mediaControlsOverlayPlayButtonInternal(video); + const controls = mediaControls(video); + const overlay = mediaControlsOverlayPlayButton(video); + + // Make sure the overlay button is not hidden. + assert_false(overlay.classList.contains('hidden')); + + // Wait until we have loaded the video and tap on the button. + video.addEventListener('canplay', t.step_func(() => { + singleTapOnControl(button); + }), { once: true }); + + // When we start playing make sure the overlay button is hidden. + video.addEventListener('play', t.step_func(() => { + assert_true(overlay.classList.contains('hidden')); + + // Now tap anywhere on the controls and make sure the button + // unhides itself. + singleTapOnControl(controls, t.step_func(() => { + overlay.addEventListener('transitionend', t.step_func_done(() => { + assert_false(overlay.classList.contains('hidden')); + })); + })); + }), { once: true }); + + video.src = '../../content/test.webm'; +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/overflow/overflow-basic-002.html b/third_party/WebKit/LayoutTests/overflow/overflow-basic-002.html index 4b7bf87..ad5f42f 100644 --- a/third_party/WebKit/LayoutTests/overflow/overflow-basic-002.html +++ b/third_party/WebKit/LayoutTests/overflow/overflow-basic-002.html
@@ -7,7 +7,7 @@ border: 1px solid black; width: 200px; height: 100px; - padding: 50px; + padding: 5px 10px 15px 20px; } #target { width: 300px; @@ -27,17 +27,19 @@ test(function() { var containerStyle = window.getComputedStyle(container); assert_equals(container.scrollHeight, target.offsetHeight + - parseInt(containerStyle.paddingTop)); + parseInt(containerStyle.paddingTop) + + parseInt(containerStyle.paddingBottom)); assert_equals(container.scrollWidth, target.offsetWidth + - parseInt(containerStyle.paddingTop)); + parseInt(containerStyle.paddingLeft)); }, "overflow with padding"); test(function() { target.style.height = "400px"; var containerStyle = window.getComputedStyle(container); assert_equals(container.scrollHeight, target.offsetHeight + - parseInt(containerStyle.paddingTop)); + parseInt(containerStyle.paddingTop) + + parseInt(containerStyle.paddingBottom)); assert_equals(container.scrollWidth, target.offsetWidth + - parseInt(containerStyle.paddingTop)); + parseInt(containerStyle.paddingLeft)); }, "overflow with paddding, after resize"); </script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-partial-invalidation-between-blocks.html b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-partial-invalidation-between-blocks.html index 0c27864c..00926562 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-partial-invalidation-between-blocks.html +++ b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-partial-invalidation-between-blocks.html
@@ -14,15 +14,13 @@ if (window.testRunner) testRunner.waitUntilDone(); +document.execCommand("selectAll"); + function repaintTest() { var selection = window.getSelection(); var firstCell = document.getElementById("firstCell"); selection.setBaseAndExtent(firstCell, 0, firstCell, 1); finishRepaintTest(); } - -onload = () => { - document.execCommand("selectAll"); - runAfterLayoutAndPaint(runRepaintAndPixelTest); -}; +runAfterLayoutAndPaint(runRepaintAndPixelTest); </script>
diff --git a/third_party/WebKit/LayoutTests/platform/android/external/wpt/dom/historical-expected.txt b/third_party/WebKit/LayoutTests/platform/android/external/wpt/dom/historical-expected.txt new file mode 100644 index 0000000..bf2c547 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/external/wpt/dom/historical-expected.txt
@@ -0,0 +1,76 @@ +This is a testharness.js-based test. +Found 72 tests; 68 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +PASS Historical DOM features must be removed: DOMConfiguration +FAIL Historical DOM features must be removed: DOMError assert_equals: expected (undefined) undefined but got (function) function "function DOMError() { [native code] }" +PASS Historical DOM features must be removed: DOMErrorHandler +PASS Historical DOM features must be removed: DOMImplementationList +PASS Historical DOM features must be removed: DOMImplementationSource +PASS Historical DOM features must be removed: DOMLocator +PASS Historical DOM features must be removed: DOMObject +PASS Historical DOM features must be removed: DOMSettableTokenList +PASS Historical DOM features must be removed: DOMUserData +PASS Historical DOM features must be removed: Entity +PASS Historical DOM features must be removed: EntityReference +PASS Historical DOM features must be removed: EventException +PASS Historical DOM features must be removed: NameList +PASS Historical DOM features must be removed: Notation +PASS Historical DOM features must be removed: TypeInfo +PASS Historical DOM features must be removed: UserDataHandler +PASS Historical DOM features must be removed: RangeException +PASS Historical DOM features must be removed: createEntityReference +FAIL Historical DOM features must be removed: xmlEncoding assert_equals: expected (undefined) undefined but got (object) null +FAIL Historical DOM features must be removed: xmlStandalone assert_equals: expected (undefined) undefined but got (boolean) false +FAIL Historical DOM features must be removed: xmlVersion assert_equals: expected (undefined) undefined but got (object) null +PASS Historical DOM features must be removed: strictErrorChecking +PASS Historical DOM features must be removed: domConfig +PASS Historical DOM features must be removed: normalizeDocument +PASS Historical DOM features must be removed: renameNode +PASS Historical DOM features must be removed: defaultCharset +PASS Historical DOM features must be removed: height +PASS Historical DOM features must be removed: width +PASS Historical DOM features must be removed: commands +PASS Historical DOM features must be removed: cssElementMap +PASS Historical DOM features must be removed: async +PASS document.load +PASS DOMImplementation.getFeature() must be nuked. +PASS Historical DOM features must be removed: schemaTypeInfo +PASS Historical DOM features must be removed: setIdAttribute +PASS Historical DOM features must be removed: setIdAttributeNS +PASS Historical DOM features must be removed: setIdAttributeNode +PASS Attr member must be nuked: schemaTypeInfo +PASS Attr member must be nuked: isId +PASS DocumentType member must be nuked: entities +PASS DocumentType member must be nuked: notations +PASS DocumentType member must be nuked: internalSubset +PASS Text member must be nuked: isElementContentWhitespace +PASS Text member must be nuked: replaceWholeText +PASS Node member must be nuked: hasAttributes +PASS Node member must be nuked: attributes +PASS Node member must be nuked: namespaceURI +PASS Node member must be nuked: prefix +PASS Node member must be nuked: localName +PASS Node member must be nuked: isSupported +PASS Node member must be nuked: getFeature +PASS Node member must be nuked: getUserData +PASS Node member must be nuked: setUserData +PASS Node member must be nuked: rootNode +PASS Window member must be nuked: attachEvent +PASS Event should not have this constant: MOUSEDOWN +PASS Event should not have this constant: MOUSEUP +PASS Event should not have this constant: MOUSEOVER +PASS Event should not have this constant: MOUSEOUT +PASS Event should not have this constant: MOUSEMOVE +PASS Event should not have this constant: MOUSEDRAG +PASS Event should not have this constant: CLICK +PASS Event should not have this constant: DBLCLICK +PASS Event should not have this constant: KEYDOWN +PASS Event should not have this constant: KEYUP +PASS Event should not have this constant: KEYPRESS +PASS Event should not have this constant: DRAGDROP +PASS Event should not have this constant: FOCUS +PASS Event should not have this constant: BLUR +PASS Event should not have this constant: SELECT +PASS Event should not have this constant: CHANGE +PASS Event.prototype should not have this property: getPreventDefault +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/platform/fuchsia/external/wpt/dom/historical-expected.txt b/third_party/WebKit/LayoutTests/platform/fuchsia/external/wpt/dom/historical-expected.txt new file mode 100644 index 0000000..bf2c547 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/fuchsia/external/wpt/dom/historical-expected.txt
@@ -0,0 +1,76 @@ +This is a testharness.js-based test. +Found 72 tests; 68 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +PASS Historical DOM features must be removed: DOMConfiguration +FAIL Historical DOM features must be removed: DOMError assert_equals: expected (undefined) undefined but got (function) function "function DOMError() { [native code] }" +PASS Historical DOM features must be removed: DOMErrorHandler +PASS Historical DOM features must be removed: DOMImplementationList +PASS Historical DOM features must be removed: DOMImplementationSource +PASS Historical DOM features must be removed: DOMLocator +PASS Historical DOM features must be removed: DOMObject +PASS Historical DOM features must be removed: DOMSettableTokenList +PASS Historical DOM features must be removed: DOMUserData +PASS Historical DOM features must be removed: Entity +PASS Historical DOM features must be removed: EntityReference +PASS Historical DOM features must be removed: EventException +PASS Historical DOM features must be removed: NameList +PASS Historical DOM features must be removed: Notation +PASS Historical DOM features must be removed: TypeInfo +PASS Historical DOM features must be removed: UserDataHandler +PASS Historical DOM features must be removed: RangeException +PASS Historical DOM features must be removed: createEntityReference +FAIL Historical DOM features must be removed: xmlEncoding assert_equals: expected (undefined) undefined but got (object) null +FAIL Historical DOM features must be removed: xmlStandalone assert_equals: expected (undefined) undefined but got (boolean) false +FAIL Historical DOM features must be removed: xmlVersion assert_equals: expected (undefined) undefined but got (object) null +PASS Historical DOM features must be removed: strictErrorChecking +PASS Historical DOM features must be removed: domConfig +PASS Historical DOM features must be removed: normalizeDocument +PASS Historical DOM features must be removed: renameNode +PASS Historical DOM features must be removed: defaultCharset +PASS Historical DOM features must be removed: height +PASS Historical DOM features must be removed: width +PASS Historical DOM features must be removed: commands +PASS Historical DOM features must be removed: cssElementMap +PASS Historical DOM features must be removed: async +PASS document.load +PASS DOMImplementation.getFeature() must be nuked. +PASS Historical DOM features must be removed: schemaTypeInfo +PASS Historical DOM features must be removed: setIdAttribute +PASS Historical DOM features must be removed: setIdAttributeNS +PASS Historical DOM features must be removed: setIdAttributeNode +PASS Attr member must be nuked: schemaTypeInfo +PASS Attr member must be nuked: isId +PASS DocumentType member must be nuked: entities +PASS DocumentType member must be nuked: notations +PASS DocumentType member must be nuked: internalSubset +PASS Text member must be nuked: isElementContentWhitespace +PASS Text member must be nuked: replaceWholeText +PASS Node member must be nuked: hasAttributes +PASS Node member must be nuked: attributes +PASS Node member must be nuked: namespaceURI +PASS Node member must be nuked: prefix +PASS Node member must be nuked: localName +PASS Node member must be nuked: isSupported +PASS Node member must be nuked: getFeature +PASS Node member must be nuked: getUserData +PASS Node member must be nuked: setUserData +PASS Node member must be nuked: rootNode +PASS Window member must be nuked: attachEvent +PASS Event should not have this constant: MOUSEDOWN +PASS Event should not have this constant: MOUSEUP +PASS Event should not have this constant: MOUSEOVER +PASS Event should not have this constant: MOUSEOUT +PASS Event should not have this constant: MOUSEMOVE +PASS Event should not have this constant: MOUSEDRAG +PASS Event should not have this constant: CLICK +PASS Event should not have this constant: DBLCLICK +PASS Event should not have this constant: KEYDOWN +PASS Event should not have this constant: KEYUP +PASS Event should not have this constant: KEYPRESS +PASS Event should not have this constant: DRAGDROP +PASS Event should not have this constant: FOCUS +PASS Event should not have this constant: BLUR +PASS Event should not have this constant: SELECT +PASS Event should not have this constant: CHANGE +PASS Event.prototype should not have this property: getPreventDefault +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt deleted file mode 100644 index 6a8a733a..0000000 --- a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt +++ /dev/null
@@ -1,126 +0,0 @@ -This is a testharness.js-based test. -PASS big5 encoder: test for ASCII codepoint 0x0 \0 -PASS big5 encoder: test for ASCII codepoint 0x1 -PASS big5 encoder: test for ASCII codepoint 0x2 -PASS big5 encoder: test for ASCII codepoint 0x3 -PASS big5 encoder: test for ASCII codepoint 0x4 -PASS big5 encoder: test for ASCII codepoint 0x5 -PASS big5 encoder: test for ASCII codepoint 0x6 -PASS big5 encoder: test for ASCII codepoint 0x7 -PASS big5 encoder: test for ASCII codepoint 0x8 -PASS big5 encoder: test for ASCII codepoint 0xb -PASS big5 encoder: test for ASCII codepoint 0xc -PASS big5 encoder: test for ASCII codepoint 0xe -PASS big5 encoder: test for ASCII codepoint 0xf -PASS big5 encoder: test for ASCII codepoint 0x10 -PASS big5 encoder: test for ASCII codepoint 0x11 -PASS big5 encoder: test for ASCII codepoint 0x12 -PASS big5 encoder: test for ASCII codepoint 0x13 -PASS big5 encoder: test for ASCII codepoint 0x14 -PASS big5 encoder: test for ASCII codepoint 0x15 -PASS big5 encoder: test for ASCII codepoint 0x16 -PASS big5 encoder: test for ASCII codepoint 0x17 -PASS big5 encoder: test for ASCII codepoint 0x18 -PASS big5 encoder: test for ASCII codepoint 0x19 -PASS big5 encoder: test for ASCII codepoint 0x1a -PASS big5 encoder: test for ASCII codepoint 0x1b -PASS big5 encoder: test for ASCII codepoint 0x1c -PASS big5 encoder: test for ASCII codepoint 0x1d -PASS big5 encoder: test for ASCII codepoint 0x1e -PASS big5 encoder: test for ASCII codepoint 0x1f -PASS big5 encoder: test for ASCII codepoint 0x20 -PASS big5 encoder: test for ASCII codepoint 0x21 ! -PASS big5 encoder: test for ASCII codepoint 0x22 " -PASS big5 encoder: test for ASCII codepoint 0x24 $ -PASS big5 encoder: test for ASCII codepoint 0x25 % -PASS big5 encoder: test for ASCII codepoint 0x26 & -PASS big5 encoder: test for ASCII codepoint 0x27 ' -PASS big5 encoder: test for ASCII codepoint 0x28 ( -PASS big5 encoder: test for ASCII codepoint 0x29 ) -PASS big5 encoder: test for ASCII codepoint 0x2a * -PASS big5 encoder: test for ASCII codepoint 0x2b + -PASS big5 encoder: test for ASCII codepoint 0x2c , -PASS big5 encoder: test for ASCII codepoint 0x2d - -PASS big5 encoder: test for ASCII codepoint 0x2e . -PASS big5 encoder: test for ASCII codepoint 0x2f / -PASS big5 encoder: test for ASCII codepoint 0x30 0 -PASS big5 encoder: test for ASCII codepoint 0x31 1 -PASS big5 encoder: test for ASCII codepoint 0x32 2 -PASS big5 encoder: test for ASCII codepoint 0x33 3 -PASS big5 encoder: test for ASCII codepoint 0x34 4 -PASS big5 encoder: test for ASCII codepoint 0x35 5 -PASS big5 encoder: test for ASCII codepoint 0x36 6 -PASS big5 encoder: test for ASCII codepoint 0x37 7 -PASS big5 encoder: test for ASCII codepoint 0x38 8 -PASS big5 encoder: test for ASCII codepoint 0x39 9 -PASS big5 encoder: test for ASCII codepoint 0x3a : -PASS big5 encoder: test for ASCII codepoint 0x3b ; -PASS big5 encoder: test for ASCII codepoint 0x3c < -PASS big5 encoder: test for ASCII codepoint 0x3d = -PASS big5 encoder: test for ASCII codepoint 0x3e > -PASS big5 encoder: test for ASCII codepoint 0x3f ? -PASS big5 encoder: test for ASCII codepoint 0x40 @ -PASS big5 encoder: test for ASCII codepoint 0x41 A -PASS big5 encoder: test for ASCII codepoint 0x42 B -PASS big5 encoder: test for ASCII codepoint 0x43 C -PASS big5 encoder: test for ASCII codepoint 0x44 D -PASS big5 encoder: test for ASCII codepoint 0x45 E -PASS big5 encoder: test for ASCII codepoint 0x46 F -PASS big5 encoder: test for ASCII codepoint 0x47 G -PASS big5 encoder: test for ASCII codepoint 0x48 H -PASS big5 encoder: test for ASCII codepoint 0x49 I -PASS big5 encoder: test for ASCII codepoint 0x4a J -PASS big5 encoder: test for ASCII codepoint 0x4b K -PASS big5 encoder: test for ASCII codepoint 0x4c L -PASS big5 encoder: test for ASCII codepoint 0x4d M -PASS big5 encoder: test for ASCII codepoint 0x4e N -PASS big5 encoder: test for ASCII codepoint 0x4f O -PASS big5 encoder: test for ASCII codepoint 0x50 P -PASS big5 encoder: test for ASCII codepoint 0x51 Q -PASS big5 encoder: test for ASCII codepoint 0x52 R -PASS big5 encoder: test for ASCII codepoint 0x53 S -PASS big5 encoder: test for ASCII codepoint 0x54 T -PASS big5 encoder: test for ASCII codepoint 0x55 U -PASS big5 encoder: test for ASCII codepoint 0x56 V -PASS big5 encoder: test for ASCII codepoint 0x57 W -PASS big5 encoder: test for ASCII codepoint 0x58 X -PASS big5 encoder: test for ASCII codepoint 0x59 Y -PASS big5 encoder: test for ASCII codepoint 0x5a Z -PASS big5 encoder: test for ASCII codepoint 0x5b [ -PASS big5 encoder: test for ASCII codepoint 0x5c \ -PASS big5 encoder: test for ASCII codepoint 0x5d ] -PASS big5 encoder: test for ASCII codepoint 0x5e ^ -PASS big5 encoder: test for ASCII codepoint 0x5f _ -PASS big5 encoder: test for ASCII codepoint 0x60 ` -PASS big5 encoder: test for ASCII codepoint 0x61 a -PASS big5 encoder: test for ASCII codepoint 0x62 b -PASS big5 encoder: test for ASCII codepoint 0x63 c -PASS big5 encoder: test for ASCII codepoint 0x64 d -PASS big5 encoder: test for ASCII codepoint 0x65 e -PASS big5 encoder: test for ASCII codepoint 0x66 f -PASS big5 encoder: test for ASCII codepoint 0x67 g -PASS big5 encoder: test for ASCII codepoint 0x68 h -PASS big5 encoder: test for ASCII codepoint 0x69 i -PASS big5 encoder: test for ASCII codepoint 0x6a j -PASS big5 encoder: test for ASCII codepoint 0x6b k -PASS big5 encoder: test for ASCII codepoint 0x6c l -PASS big5 encoder: test for ASCII codepoint 0x6d m -PASS big5 encoder: test for ASCII codepoint 0x6e n -PASS big5 encoder: test for ASCII codepoint 0x6f o -PASS big5 encoder: test for ASCII codepoint 0x70 p -PASS big5 encoder: test for ASCII codepoint 0x71 q -PASS big5 encoder: test for ASCII codepoint 0x72 r -PASS big5 encoder: test for ASCII codepoint 0x73 s -PASS big5 encoder: test for ASCII codepoint 0x74 t -PASS big5 encoder: test for ASCII codepoint 0x75 u -PASS big5 encoder: test for ASCII codepoint 0x76 v -PASS big5 encoder: test for ASCII codepoint 0x77 w -PASS big5 encoder: test for ASCII codepoint 0x78 x -PASS big5 encoder: test for ASCII codepoint 0x79 y -PASS big5 encoder: test for ASCII codepoint 0x7a z -PASS big5 encoder: test for ASCII codepoint 0x7b { -PASS big5 encoder: test for ASCII codepoint 0x7c | -PASS big5 encoder: test for ASCII codepoint 0x7d } -PASS big5 encoder: test for ASCII codepoint 0x7e ~ -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png index 95d9b53..2c4b5c6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt index 3a0b9e1..c39252c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt
@@ -18,12 +18,12 @@ "backgroundColor": "#FFFFFF", "paintInvalidations": [ { - "object": "InlineTextBox 'Second cell'", - "rect": [11, 35, 74, 19], + "object": "LayoutText #text", + "rect": [11, 35, 73, 19], "reason": "selection" }, { - "object": "InlineTextBox 'First cell'", + "object": "LayoutText #text", "rect": [11, 11, 58, 19], "reason": "geometry" } @@ -32,6 +32,22 @@ ], "objectPaintInvalidations": [ { + "object": "LayoutBlockFlow HTML", + "reason": "selection" + }, + { + "object": "LayoutBlockFlow BODY", + "reason": "selection" + }, + { + "object": "LayoutTable TABLE", + "reason": "selection" + }, + { + "object": "LayoutTableCell TD id='firstCell'", + "reason": "selection" + }, + { "object": "LayoutText #text", "reason": "geometry" }, @@ -40,6 +56,10 @@ "reason": "geometry" }, { + "object": "LayoutTableCell TD id='secondCell'", + "reason": "selection" + }, + { "object": "LayoutText #text", "reason": "selection" },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spv175/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spv175/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt deleted file mode 100644 index 9373e28..0000000 --- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spv175/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt +++ /dev/null
@@ -1,52 +0,0 @@ -{ - "layers": [ - { - "name": "LayoutView #document", - "bounds": [800, 600], - "drawsContent": false, - "backgroundColor": "#FFFFFF" - }, - { - "name": "Scrolling Layer", - "bounds": [800, 600], - "drawsContent": false - }, - { - "name": "Scrolling Contents Layer", - "bounds": [800, 600], - "contentsOpaque": true, - "backgroundColor": "#FFFFFF", - "paintInvalidations": [ - { - "object": "LayoutText #text", - "rect": [11, 35, 73, 19], - "reason": "selection" - }, - { - "object": "LayoutText #text", - "rect": [11, 11, 58, 19], - "reason": "geometry" - } - ] - } - ], - "objectPaintInvalidations": [ - { - "object": "LayoutText #text", - "reason": "geometry" - }, - { - "object": "InlineTextBox 'First cell'", - "reason": "geometry" - }, - { - "object": "LayoutText #text", - "reason": "selection" - }, - { - "object": "InlineTextBox 'Second cell'", - "reason": "selection" - } - ] -} -
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/external/wpt/html/semantics/scripting-1/the-script-element/module/crossorigin-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/external/wpt/html/semantics/scripting-1/the-script-element/module/crossorigin-expected.txt deleted file mode 100644 index 27f90ec4..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/external/wpt/html/semantics/scripting-1/the-script-element/module/crossorigin-expected.txt +++ /dev/null
@@ -1,11 +0,0 @@ -This is a testharness.js-based test. -PASS Root module, Error in CORS-same-origin script -PASS Root module, Blocked script download, missing CORS ACAO header -PASS Root module, Blocked script download, crossorigin attribute with missing CORS ACAO header -PASS Root module, Blocked script download, mismatched CORS ACAO header -PASS Imported module, Error in CORS-same-origin script -FAIL Imported module, Blocked script download, missing CORS ACAO header assert_equals: Unexpected _log value expected (string) "error" but got (undefined) undefined -PASS Imported module, Blocked script download, crossorigin attribute with missing CORS ACAO header -PASS Imported module, Blocked script download, mismatched CORS ACAO header -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt deleted file mode 100644 index 2fd0703..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/encoding/legacy-mb-japanese/iso-2022-jp/iso2022jp-encode-form-csiso2022jp-expected.txt +++ /dev/null
@@ -1,7333 +0,0 @@ -This is a testharness.js-based test. -Found 7329 tests; 6956 PASS, 373 FAIL, 0 TIMEOUT, 0 NOTRUN. -PASS U+A5 ¥ %1B%28%4A%5C -PASS U+A7 § %1B%24%42%21%78 -PASS U+A8 ¨ %1B%24%42%21%2F -PASS U+B0 ° %1B%24%42%21%6B -PASS U+B1 ± %1B%24%42%21%5E -PASS U+B4 ´ %1B%24%42%21%2D -PASS U+B6 ¶ %1B%24%42%22%79 -PASS U+D7 × %1B%24%42%21%5F -PASS U+F7 ÷ %1B%24%42%21%60 -PASS U+391 Α %1B%24%42%26%21 -PASS U+392 Β %1B%24%42%26%22 -PASS U+393 Γ %1B%24%42%26%23 -PASS U+394 Δ %1B%24%42%26%24 -PASS U+395 Ε %1B%24%42%26%25 -PASS U+396 Ζ %1B%24%42%26%26 -PASS U+397 Η %1B%24%42%26%27 -PASS U+398 Θ %1B%24%42%26%28 -PASS U+399 Ι %1B%24%42%26%29 -PASS U+39A Κ %1B%24%42%26%2A -PASS U+39B Λ %1B%24%42%26%2B -PASS U+39C Μ %1B%24%42%26%2C -PASS U+39D Ν %1B%24%42%26%2D -PASS U+39E Ξ %1B%24%42%26%2E -PASS U+39F Ο %1B%24%42%26%2F -PASS U+3A0 Π %1B%24%42%26%30 -PASS U+3A1 Ρ %1B%24%42%26%31 -PASS U+3A3 Σ %1B%24%42%26%32 -PASS U+3A4 Τ %1B%24%42%26%33 -PASS U+3A5 Υ %1B%24%42%26%34 -PASS U+3A6 Φ %1B%24%42%26%35 -PASS U+3A7 Χ %1B%24%42%26%36 -PASS U+3A8 Ψ %1B%24%42%26%37 -PASS U+3A9 Ω %1B%24%42%26%38 -PASS U+3B1 α %1B%24%42%26%41 -PASS U+3B2 β %1B%24%42%26%42 -PASS U+3B3 γ %1B%24%42%26%43 -PASS U+3B4 δ %1B%24%42%26%44 -PASS U+3B5 ε %1B%24%42%26%45 -PASS U+3B6 ζ %1B%24%42%26%46 -PASS U+3B7 η %1B%24%42%26%47 -PASS U+3B8 θ %1B%24%42%26%48 -PASS U+3B9 ι %1B%24%42%26%49 -PASS U+3BA κ %1B%24%42%26%4A -PASS U+3BB λ %1B%24%42%26%4B -PASS U+3BC μ %1B%24%42%26%4C -PASS U+3BD ν %1B%24%42%26%4D -PASS U+3BE ξ %1B%24%42%26%4E -PASS U+3BF ο %1B%24%42%26%4F -PASS U+3C0 π %1B%24%42%26%50 -PASS U+3C1 ρ %1B%24%42%26%51 -PASS U+3C3 σ %1B%24%42%26%52 -PASS U+3C4 τ %1B%24%42%26%53 -PASS U+3C5 υ %1B%24%42%26%54 -PASS U+3C6 φ %1B%24%42%26%55 -PASS U+3C7 χ %1B%24%42%26%56 -PASS U+3C8 ψ %1B%24%42%26%57 -PASS U+3C9 ω %1B%24%42%26%58 -PASS U+401 Ё %1B%24%42%27%27 -PASS U+410 А %1B%24%42%27%21 -PASS U+411 Б %1B%24%42%27%22 -PASS U+412 В %1B%24%42%27%23 -PASS U+413 Г %1B%24%42%27%24 -PASS U+414 Д %1B%24%42%27%25 -PASS U+415 Е %1B%24%42%27%26 -PASS U+416 Ж %1B%24%42%27%28 -PASS U+417 З %1B%24%42%27%29 -PASS U+418 И %1B%24%42%27%2A -PASS U+419 Й %1B%24%42%27%2B -PASS U+41A К %1B%24%42%27%2C -PASS U+41B Л %1B%24%42%27%2D -PASS U+41C М %1B%24%42%27%2E -PASS U+41D Н %1B%24%42%27%2F -PASS U+41E О %1B%24%42%27%30 -PASS U+41F П %1B%24%42%27%31 -PASS U+420 Р %1B%24%42%27%32 -PASS U+421 С %1B%24%42%27%33 -PASS U+422 Т %1B%24%42%27%34 -PASS U+423 У %1B%24%42%27%35 -PASS U+424 Ф %1B%24%42%27%36 -PASS U+425 Х %1B%24%42%27%37 -PASS U+426 Ц %1B%24%42%27%38 -PASS U+427 Ч %1B%24%42%27%39 -PASS U+428 Ш %1B%24%42%27%3A -PASS U+429 Щ %1B%24%42%27%3B -PASS U+42A Ъ %1B%24%42%27%3C -PASS U+42B Ы %1B%24%42%27%3D -PASS U+42C Ь %1B%24%42%27%3E -PASS U+42D Э %1B%24%42%27%3F -PASS U+42E Ю %1B%24%42%27%40 -PASS U+42F Я %1B%24%42%27%41 -PASS U+430 а %1B%24%42%27%51 -PASS U+431 б %1B%24%42%27%52 -PASS U+432 в %1B%24%42%27%53 -PASS U+433 г %1B%24%42%27%54 -PASS U+434 д %1B%24%42%27%55 -PASS U+435 е %1B%24%42%27%56 -PASS U+436 ж %1B%24%42%27%58 -PASS U+437 з %1B%24%42%27%59 -PASS U+438 и %1B%24%42%27%5A -PASS U+439 й %1B%24%42%27%5B -PASS U+43A к %1B%24%42%27%5C -PASS U+43B л %1B%24%42%27%5D -PASS U+43C м %1B%24%42%27%5E -PASS U+43D н %1B%24%42%27%5F -PASS U+43E о %1B%24%42%27%60 -PASS U+43F п %1B%24%42%27%61 -PASS U+440 р %1B%24%42%27%62 -PASS U+441 с %1B%24%42%27%63 -PASS U+442 т %1B%24%42%27%64 -PASS U+443 у %1B%24%42%27%65 -PASS U+444 ф %1B%24%42%27%66 -PASS U+445 х %1B%24%42%27%67 -PASS U+446 ц %1B%24%42%27%68 -PASS U+447 ч %1B%24%42%27%69 -PASS U+448 ш %1B%24%42%27%6A -PASS U+449 щ %1B%24%42%27%6B -PASS U+44A ъ %1B%24%42%27%6C -PASS U+44B ы %1B%24%42%27%6D -PASS U+44C ь %1B%24%42%27%6E -PASS U+44D э %1B%24%42%27%6F -PASS U+44E ю %1B%24%42%27%70 -PASS U+44F я %1B%24%42%27%71 -PASS U+451 ё %1B%24%42%27%57 -PASS U+2010 ‐ %1B%24%42%21%3E -PASS U+2015 ― %1B%24%42%21%3D -PASS U+2018 ‘ %1B%24%42%21%46 -PASS U+2019 ’ %1B%24%42%21%47 -PASS U+201C “ %1B%24%42%21%48 -PASS U+201D ” %1B%24%42%21%49 -PASS U+2020 † %1B%24%42%22%77 -PASS U+2021 ‡ %1B%24%42%22%78 -PASS U+2025 ‥ %1B%24%42%21%45 -PASS U+2026 … %1B%24%42%21%44 -PASS U+2030 ‰ %1B%24%42%22%73 -PASS U+2032 ′ %1B%24%42%21%6C -PASS U+2033 ″ %1B%24%42%21%6D -PASS U+203B ※ %1B%24%42%22%28 -PASS U+203E ‾ %1B%28%4A%7E -PASS U+2103 ℃ %1B%24%42%21%6E -PASS U+2116 № %1B%24%42%2D%62 -PASS U+2121 ℡ %1B%24%42%2D%64 -PASS U+212B Å %1B%24%42%22%72 -PASS U+2160 Ⅰ %1B%24%42%2D%35 -PASS U+2161 Ⅱ %1B%24%42%2D%36 -PASS U+2162 Ⅲ %1B%24%42%2D%37 -PASS U+2163 Ⅳ %1B%24%42%2D%38 -PASS U+2164 Ⅴ %1B%24%42%2D%39 -PASS U+2165 Ⅵ %1B%24%42%2D%3A -PASS U+2166 Ⅶ %1B%24%42%2D%3B -PASS U+2167 Ⅷ %1B%24%42%2D%3C -PASS U+2168 Ⅸ %1B%24%42%2D%3D -PASS U+2169 Ⅹ %1B%24%42%2D%3E -FAIL U+2170 ⅰ %1B%24%42%7C%71 assert_equals: expected "%1B%24%42%7C%71" but got "%26%23%38%35%36%30%3B" -FAIL U+2171 ⅱ %1B%24%42%7C%72 assert_equals: expected "%1B%24%42%7C%72" but got "%26%23%38%35%36%31%3B" -FAIL U+2172 ⅲ %1B%24%42%7C%73 assert_equals: expected "%1B%24%42%7C%73" but got "%26%23%38%35%36%32%3B" -FAIL U+2173 ⅳ %1B%24%42%7C%74 assert_equals: expected "%1B%24%42%7C%74" but got "%26%23%38%35%36%33%3B" -FAIL U+2174 ⅴ %1B%24%42%7C%75 assert_equals: expected "%1B%24%42%7C%75" but got "%26%23%38%35%36%34%3B" -FAIL U+2175 ⅵ %1B%24%42%7C%76 assert_equals: expected "%1B%24%42%7C%76" but got "%26%23%38%35%36%35%3B" -FAIL U+2176 ⅶ %1B%24%42%7C%77 assert_equals: expected "%1B%24%42%7C%77" but got "%26%23%38%35%36%36%3B" -FAIL U+2177 ⅷ %1B%24%42%7C%78 assert_equals: expected "%1B%24%42%7C%78" but got "%26%23%38%35%36%37%3B" -FAIL U+2178 ⅸ %1B%24%42%7C%79 assert_equals: expected "%1B%24%42%7C%79" but got "%26%23%38%35%36%38%3B" -FAIL U+2179 ⅹ %1B%24%42%7C%7A assert_equals: expected "%1B%24%42%7C%7A" but got "%26%23%38%35%36%39%3B" -PASS U+2190 ← %1B%24%42%22%2B -PASS U+2191 ↑ %1B%24%42%22%2C -PASS U+2192 → %1B%24%42%22%2A -PASS U+2193 ↓ %1B%24%42%22%2D -PASS U+21D2 ⇒ %1B%24%42%22%4D -PASS U+21D4 ⇔ %1B%24%42%22%4E -PASS U+2200 ∀ %1B%24%42%22%4F -PASS U+2202 ∂ %1B%24%42%22%5F -PASS U+2203 ∃ %1B%24%42%22%50 -PASS U+2207 ∇ %1B%24%42%22%60 -PASS U+2208 ∈ %1B%24%42%22%3A -PASS U+220B ∋ %1B%24%42%22%3B -PASS U+2211 ∑ %1B%24%42%2D%74 -PASS U+2212 − %1B%24%42%21%5D -PASS U+221A √ %1B%24%42%22%65 -PASS U+221D ∝ %1B%24%42%22%67 -PASS U+221E ∞ %1B%24%42%21%67 -PASS U+221F ∟ %1B%24%42%2D%78 -PASS U+2220 ∠ %1B%24%42%22%5C -PASS U+2225 ∥ %1B%24%42%21%42 -PASS U+2227 ∧ %1B%24%42%22%4A -PASS U+2228 ∨ %1B%24%42%22%4B -PASS U+2229 ∩ %1B%24%42%22%41 -PASS U+222A ∪ %1B%24%42%22%40 -PASS U+222B ∫ %1B%24%42%22%69 -PASS U+222C ∬ %1B%24%42%22%6A -PASS U+222E ∮ %1B%24%42%2D%73 -PASS U+2234 ∴ %1B%24%42%21%68 -PASS U+2235 ∵ %1B%24%42%22%68 -PASS U+223D ∽ %1B%24%42%22%66 -PASS U+2252 ≒ %1B%24%42%22%62 -PASS U+2260 ≠ %1B%24%42%21%62 -PASS U+2261 ≡ %1B%24%42%22%61 -PASS U+2266 ≦ %1B%24%42%21%65 -PASS U+2267 ≧ %1B%24%42%21%66 -PASS U+226A ≪ %1B%24%42%22%63 -PASS U+226B ≫ %1B%24%42%22%64 -PASS U+2282 ⊂ %1B%24%42%22%3E -PASS U+2283 ⊃ %1B%24%42%22%3F -PASS U+2286 ⊆ %1B%24%42%22%3C -PASS U+2287 ⊇ %1B%24%42%22%3D -PASS U+22A5 ⊥ %1B%24%42%22%5D -PASS U+22BF ⊿ %1B%24%42%2D%79 -PASS U+2312 ⌒ %1B%24%42%22%5E -PASS U+2460 ① %1B%24%42%2D%21 -PASS U+2461 ② %1B%24%42%2D%22 -PASS U+2462 ③ %1B%24%42%2D%23 -PASS U+2463 ④ %1B%24%42%2D%24 -PASS U+2464 ⑤ %1B%24%42%2D%25 -PASS U+2465 ⑥ %1B%24%42%2D%26 -PASS U+2466 ⑦ %1B%24%42%2D%27 -PASS U+2467 ⑧ %1B%24%42%2D%28 -PASS U+2468 ⑨ %1B%24%42%2D%29 -PASS U+2469 ⑩ %1B%24%42%2D%2A -PASS U+246A ⑪ %1B%24%42%2D%2B -PASS U+246B ⑫ %1B%24%42%2D%2C -PASS U+246C ⑬ %1B%24%42%2D%2D -PASS U+246D ⑭ %1B%24%42%2D%2E -PASS U+246E ⑮ %1B%24%42%2D%2F -PASS U+246F ⑯ %1B%24%42%2D%30 -PASS U+2470 ⑰ %1B%24%42%2D%31 -PASS U+2471 ⑱ %1B%24%42%2D%32 -PASS U+2472 ⑲ %1B%24%42%2D%33 -PASS U+2473 ⑳ %1B%24%42%2D%34 -PASS U+2500 ─ %1B%24%42%28%21 -PASS U+2501 ━ %1B%24%42%28%2C -PASS U+2502 │ %1B%24%42%28%22 -PASS U+2503 ┃ %1B%24%42%28%2D -PASS U+250C ┌ %1B%24%42%28%23 -PASS U+250F ┏ %1B%24%42%28%2E -PASS U+2510 ┐ %1B%24%42%28%24 -PASS U+2513 ┓ %1B%24%42%28%2F -PASS U+2514 └ %1B%24%42%28%26 -PASS U+2517 ┗ %1B%24%42%28%31 -PASS U+2518 ┘ %1B%24%42%28%25 -PASS U+251B ┛ %1B%24%42%28%30 -PASS U+251C ├ %1B%24%42%28%27 -PASS U+251D ┝ %1B%24%42%28%3C -PASS U+2520 ┠ %1B%24%42%28%37 -PASS U+2523 ┣ %1B%24%42%28%32 -PASS U+2524 ┤ %1B%24%42%28%29 -PASS U+2525 ┥ %1B%24%42%28%3E -PASS U+2528 ┨ %1B%24%42%28%39 -PASS U+252B ┫ %1B%24%42%28%34 -PASS U+252C ┬ %1B%24%42%28%28 -PASS U+252F ┯ %1B%24%42%28%38 -PASS U+2530 ┰ %1B%24%42%28%3D -PASS U+2533 ┳ %1B%24%42%28%33 -PASS U+2534 ┴ %1B%24%42%28%2A -PASS U+2537 ┷ %1B%24%42%28%3A -PASS U+2538 ┸ %1B%24%42%28%3F -PASS U+253B ┻ %1B%24%42%28%35 -PASS U+253C ┼ %1B%24%42%28%2B -PASS U+253F ┿ %1B%24%42%28%3B -PASS U+2542 ╂ %1B%24%42%28%40 -PASS U+254B ╋ %1B%24%42%28%36 -PASS U+25A0 ■ %1B%24%42%22%23 -PASS U+25A1 □ %1B%24%42%22%22 -PASS U+25B2 ▲ %1B%24%42%22%25 -PASS U+25B3 △ %1B%24%42%22%24 -PASS U+25BC ▼ %1B%24%42%22%27 -PASS U+25BD ▽ %1B%24%42%22%26 -PASS U+25C6 ◆ %1B%24%42%22%21 -PASS U+25C7 ◇ %1B%24%42%21%7E -PASS U+25CB ○ %1B%24%42%21%7B -PASS U+25CE ◎ %1B%24%42%21%7D -PASS U+25CF ● %1B%24%42%21%7C -PASS U+25EF ◯ %1B%24%42%22%7E -PASS U+2605 ★ %1B%24%42%21%7A -PASS U+2606 ☆ %1B%24%42%21%79 -PASS U+2640 ♀ %1B%24%42%21%6A -PASS U+2642 ♂ %1B%24%42%21%69 -PASS U+266A ♪ %1B%24%42%22%76 -PASS U+266D ♭ %1B%24%42%22%75 -PASS U+266F ♯ %1B%24%42%22%74 -PASS U+3000 %1B%24%42%21%21 -PASS U+3001 、 %1B%24%42%21%22 -PASS U+3002 。 %1B%24%42%21%23 -PASS U+3003 〃 %1B%24%42%21%37 -PASS U+3005 々 %1B%24%42%21%39 -PASS U+3006 〆 %1B%24%42%21%3A -PASS U+3007 〇 %1B%24%42%21%3B -PASS U+3008 〈 %1B%24%42%21%52 -PASS U+3009 〉 %1B%24%42%21%53 -PASS U+300A 《 %1B%24%42%21%54 -PASS U+300B 》 %1B%24%42%21%55 -PASS U+300C 「 %1B%24%42%21%56 -PASS U+300D 」 %1B%24%42%21%57 -PASS U+300E 『 %1B%24%42%21%58 -PASS U+300F 』 %1B%24%42%21%59 -PASS U+3010 【 %1B%24%42%21%5A -PASS U+3011 】 %1B%24%42%21%5B -PASS U+3012 〒 %1B%24%42%22%29 -PASS U+3013 〓 %1B%24%42%22%2E -PASS U+3014 〔 %1B%24%42%21%4C -PASS U+3015 〕 %1B%24%42%21%4D -PASS U+301D 〝 %1B%24%42%2D%60 -PASS U+301F 〟 %1B%24%42%2D%61 -PASS U+3041 ぁ %1B%24%42%24%21 -PASS U+3042 あ %1B%24%42%24%22 -PASS U+3043 ぃ %1B%24%42%24%23 -PASS U+3044 い %1B%24%42%24%24 -PASS U+3045 ぅ %1B%24%42%24%25 -PASS U+3046 う %1B%24%42%24%26 -PASS U+3047 ぇ %1B%24%42%24%27 -PASS U+3048 え %1B%24%42%24%28 -PASS U+3049 ぉ %1B%24%42%24%29 -PASS U+304A お %1B%24%42%24%2A -PASS U+304B か %1B%24%42%24%2B -PASS U+304C が %1B%24%42%24%2C -PASS U+304D き %1B%24%42%24%2D -PASS U+304E ぎ %1B%24%42%24%2E -PASS U+304F く %1B%24%42%24%2F -PASS U+3050 ぐ %1B%24%42%24%30 -PASS U+3051 け %1B%24%42%24%31 -PASS U+3052 げ %1B%24%42%24%32 -PASS U+3053 こ %1B%24%42%24%33 -PASS U+3054 ご %1B%24%42%24%34 -PASS U+3055 さ %1B%24%42%24%35 -PASS U+3056 ざ %1B%24%42%24%36 -PASS U+3057 し %1B%24%42%24%37 -PASS U+3058 じ %1B%24%42%24%38 -PASS U+3059 す %1B%24%42%24%39 -PASS U+305A ず %1B%24%42%24%3A -PASS U+305B せ %1B%24%42%24%3B -PASS U+305C ぜ %1B%24%42%24%3C -PASS U+305D そ %1B%24%42%24%3D -PASS U+305E ぞ %1B%24%42%24%3E -PASS U+305F た %1B%24%42%24%3F -PASS U+3060 だ %1B%24%42%24%40 -PASS U+3061 ち %1B%24%42%24%41 -PASS U+3062 ぢ %1B%24%42%24%42 -PASS U+3063 っ %1B%24%42%24%43 -PASS U+3064 つ %1B%24%42%24%44 -PASS U+3065 づ %1B%24%42%24%45 -PASS U+3066 て %1B%24%42%24%46 -PASS U+3067 で %1B%24%42%24%47 -PASS U+3068 と %1B%24%42%24%48 -PASS U+3069 ど %1B%24%42%24%49 -PASS U+306A な %1B%24%42%24%4A -PASS U+306B に %1B%24%42%24%4B -PASS U+306C ぬ %1B%24%42%24%4C -PASS U+306D ね %1B%24%42%24%4D -PASS U+306E の %1B%24%42%24%4E -PASS U+306F は %1B%24%42%24%4F -PASS U+3070 ば %1B%24%42%24%50 -PASS U+3071 ぱ %1B%24%42%24%51 -PASS U+3072 ひ %1B%24%42%24%52 -PASS U+3073 び %1B%24%42%24%53 -PASS U+3074 ぴ %1B%24%42%24%54 -PASS U+3075 ふ %1B%24%42%24%55 -PASS U+3076 ぶ %1B%24%42%24%56 -PASS U+3077 ぷ %1B%24%42%24%57 -PASS U+3078 へ %1B%24%42%24%58 -PASS U+3079 べ %1B%24%42%24%59 -PASS U+307A ぺ %1B%24%42%24%5A -PASS U+307B ほ %1B%24%42%24%5B -PASS U+307C ぼ %1B%24%42%24%5C -PASS U+307D ぽ %1B%24%42%24%5D -PASS U+307E ま %1B%24%42%24%5E -PASS U+307F み %1B%24%42%24%5F -PASS U+3080 む %1B%24%42%24%60 -PASS U+3081 め %1B%24%42%24%61 -PASS U+3082 も %1B%24%42%24%62 -PASS U+3083 ゃ %1B%24%42%24%63 -PASS U+3084 や %1B%24%42%24%64 -PASS U+3085 ゅ %1B%24%42%24%65 -PASS U+3086 ゆ %1B%24%42%24%66 -PASS U+3087 ょ %1B%24%42%24%67 -PASS U+3088 よ %1B%24%42%24%68 -PASS U+3089 ら %1B%24%42%24%69 -PASS U+308A り %1B%24%42%24%6A -PASS U+308B る %1B%24%42%24%6B -PASS U+308C れ %1B%24%42%24%6C -PASS U+308D ろ %1B%24%42%24%6D -PASS U+308E ゎ %1B%24%42%24%6E -PASS U+308F わ %1B%24%42%24%6F -PASS U+3090 ゐ %1B%24%42%24%70 -PASS U+3091 ゑ %1B%24%42%24%71 -PASS U+3092 を %1B%24%42%24%72 -PASS U+3093 ん %1B%24%42%24%73 -PASS U+309B ゛ %1B%24%42%21%2B -PASS U+309C ゜ %1B%24%42%21%2C -PASS U+309D ゝ %1B%24%42%21%35 -PASS U+309E ゞ %1B%24%42%21%36 -PASS U+30A1 ァ %1B%24%42%25%21 -PASS U+30A2 ア %1B%24%42%25%22 -PASS U+30A3 ィ %1B%24%42%25%23 -PASS U+30A4 イ %1B%24%42%25%24 -PASS U+30A5 ゥ %1B%24%42%25%25 -PASS U+30A6 ウ %1B%24%42%25%26 -PASS U+30A7 ェ %1B%24%42%25%27 -PASS U+30A8 エ %1B%24%42%25%28 -PASS U+30A9 ォ %1B%24%42%25%29 -PASS U+30AA オ %1B%24%42%25%2A -PASS U+30AB カ %1B%24%42%25%2B -PASS U+30AC ガ %1B%24%42%25%2C -PASS U+30AD キ %1B%24%42%25%2D -PASS U+30AE ギ %1B%24%42%25%2E -PASS U+30AF ク %1B%24%42%25%2F -PASS U+30B0 グ %1B%24%42%25%30 -PASS U+30B1 ケ %1B%24%42%25%31 -PASS U+30B2 ゲ %1B%24%42%25%32 -PASS U+30B3 コ %1B%24%42%25%33 -PASS U+30B4 ゴ %1B%24%42%25%34 -PASS U+30B5 サ %1B%24%42%25%35 -PASS U+30B6 ザ %1B%24%42%25%36 -PASS U+30B7 シ %1B%24%42%25%37 -PASS U+30B8 ジ %1B%24%42%25%38 -PASS U+30B9 ス %1B%24%42%25%39 -PASS U+30BA ズ %1B%24%42%25%3A -PASS U+30BB セ %1B%24%42%25%3B -PASS U+30BC ゼ %1B%24%42%25%3C -PASS U+30BD ソ %1B%24%42%25%3D -PASS U+30BE ゾ %1B%24%42%25%3E -PASS U+30BF タ %1B%24%42%25%3F -PASS U+30C0 ダ %1B%24%42%25%40 -PASS U+30C1 チ %1B%24%42%25%41 -PASS U+30C2 ヂ %1B%24%42%25%42 -PASS U+30C3 ッ %1B%24%42%25%43 -PASS U+30C4 ツ %1B%24%42%25%44 -PASS U+30C5 ヅ %1B%24%42%25%45 -PASS U+30C6 テ %1B%24%42%25%46 -PASS U+30C7 デ %1B%24%42%25%47 -PASS U+30C8 ト %1B%24%42%25%48 -PASS U+30C9 ド %1B%24%42%25%49 -PASS U+30CA ナ %1B%24%42%25%4A -PASS U+30CB ニ %1B%24%42%25%4B -PASS U+30CC ヌ %1B%24%42%25%4C -PASS U+30CD ネ %1B%24%42%25%4D -PASS U+30CE ノ %1B%24%42%25%4E -PASS U+30CF ハ %1B%24%42%25%4F -PASS U+30D0 バ %1B%24%42%25%50 -PASS U+30D1 パ %1B%24%42%25%51 -PASS U+30D2 ヒ %1B%24%42%25%52 -PASS U+30D3 ビ %1B%24%42%25%53 -PASS U+30D4 ピ %1B%24%42%25%54 -PASS U+30D5 フ %1B%24%42%25%55 -PASS U+30D6 ブ %1B%24%42%25%56 -PASS U+30D7 プ %1B%24%42%25%57 -PASS U+30D8 ヘ %1B%24%42%25%58 -PASS U+30D9 ベ %1B%24%42%25%59 -PASS U+30DA ペ %1B%24%42%25%5A -PASS U+30DB ホ %1B%24%42%25%5B -PASS U+30DC ボ %1B%24%42%25%5C -PASS U+30DD ポ %1B%24%42%25%5D -PASS U+30DE マ %1B%24%42%25%5E -PASS U+30DF ミ %1B%24%42%25%5F -PASS U+30E0 ム %1B%24%42%25%60 -PASS U+30E1 メ %1B%24%42%25%61 -PASS U+30E2 モ %1B%24%42%25%62 -PASS U+30E3 ャ %1B%24%42%25%63 -PASS U+30E4 ヤ %1B%24%42%25%64 -PASS U+30E5 ュ %1B%24%42%25%65 -PASS U+30E6 ユ %1B%24%42%25%66 -PASS U+30E7 ョ %1B%24%42%25%67 -PASS U+30E8 ヨ %1B%24%42%25%68 -PASS U+30E9 ラ %1B%24%42%25%69 -PASS U+30EA リ %1B%24%42%25%6A -PASS U+30EB ル %1B%24%42%25%6B -PASS U+30EC レ %1B%24%42%25%6C -PASS U+30ED ロ %1B%24%42%25%6D -PASS U+30EE ヮ %1B%24%42%25%6E -PASS U+30EF ワ %1B%24%42%25%6F -PASS U+30F0 ヰ %1B%24%42%25%70 -PASS U+30F1 ヱ %1B%24%42%25%71 -PASS U+30F2 ヲ %1B%24%42%25%72 -PASS U+30F3 ン %1B%24%42%25%73 -PASS U+30F4 ヴ %1B%24%42%25%74 -PASS U+30F5 ヵ %1B%24%42%25%75 -PASS U+30F6 ヶ %1B%24%42%25%76 -PASS U+30FB ・ %1B%24%42%21%26 -PASS U+30FC ー %1B%24%42%21%3C -PASS U+30FD ヽ %1B%24%42%21%33 -PASS U+30FE ヾ %1B%24%42%21%34 -PASS U+3231 ㈱ %1B%24%42%2D%6A -PASS U+3232 ㈲ %1B%24%42%2D%6B -PASS U+3239 ㈹ %1B%24%42%2D%6C -PASS U+32A4 ㊤ %1B%24%42%2D%65 -PASS U+32A5 ㊥ %1B%24%42%2D%66 -PASS U+32A6 ㊦ %1B%24%42%2D%67 -PASS U+32A7 ㊧ %1B%24%42%2D%68 -PASS U+32A8 ㊨ %1B%24%42%2D%69 -PASS U+3303 ㌃ %1B%24%42%2D%46 -PASS U+330D ㌍ %1B%24%42%2D%4A -PASS U+3314 ㌔ %1B%24%42%2D%41 -PASS U+3318 ㌘ %1B%24%42%2D%44 -PASS U+3322 ㌢ %1B%24%42%2D%42 -PASS U+3323 ㌣ %1B%24%42%2D%4C -PASS U+3326 ㌦ %1B%24%42%2D%4B -PASS U+3327 ㌧ %1B%24%42%2D%45 -PASS U+332B ㌫ %1B%24%42%2D%4D -PASS U+3336 ㌶ %1B%24%42%2D%47 -PASS U+333B ㌻ %1B%24%42%2D%4F -PASS U+3349 ㍉ %1B%24%42%2D%40 -PASS U+334A ㍊ %1B%24%42%2D%4E -PASS U+334D ㍍ %1B%24%42%2D%43 -PASS U+3351 ㍑ %1B%24%42%2D%48 -PASS U+3357 ㍗ %1B%24%42%2D%49 -PASS U+337B ㍻ %1B%24%42%2D%5F -PASS U+337C ㍼ %1B%24%42%2D%6F -PASS U+337D ㍽ %1B%24%42%2D%6E -PASS U+337E ㍾ %1B%24%42%2D%6D -PASS U+338E ㎎ %1B%24%42%2D%53 -PASS U+338F ㎏ %1B%24%42%2D%54 -PASS U+339C ㎜ %1B%24%42%2D%50 -PASS U+339D ㎝ %1B%24%42%2D%51 -PASS U+339E ㎞ %1B%24%42%2D%52 -PASS U+33A1 ㎡ %1B%24%42%2D%56 -PASS U+33C4 ㏄ %1B%24%42%2D%55 -PASS U+33CD ㏍ %1B%24%42%2D%63 -PASS U+4E00 一 %1B%24%42%30%6C -PASS U+4E01 丁 %1B%24%42%43%7A -PASS U+4E03 七 %1B%24%42%3C%37 -PASS U+4E07 万 %1B%24%42%4B%7C -PASS U+4E08 丈 %1B%24%42%3E%66 -PASS U+4E09 三 %1B%24%42%3B%30 -PASS U+4E0A 上 %1B%24%42%3E%65 -PASS U+4E0B 下 %1B%24%42%32%3C -PASS U+4E0D 不 %1B%24%42%49%54 -PASS U+4E0E 与 %1B%24%42%4D%3F -PASS U+4E10 丐 %1B%24%42%50%22 -PASS U+4E11 丑 %1B%24%42%31%2F -PASS U+4E14 且 %1B%24%42%33%6E -PASS U+4E15 丕 %1B%24%42%50%23 -PASS U+4E16 世 %1B%24%42%40%24 -PASS U+4E17 丗 %1B%24%42%52%42 -PASS U+4E18 丘 %1B%24%42%35%56 -PASS U+4E19 丙 %1B%24%42%4A%3A -PASS U+4E1E 丞 %1B%24%42%3E%67 -PASS U+4E21 両 %1B%24%42%4E%3E -PASS U+4E26 並 %1B%24%42%4A%42 -FAIL U+4E28 丨 %1B%24%42%79%2D assert_equals: expected "%1B%24%42%79%2D" but got "%26%23%32%30%30%30%38%3B" -PASS U+4E2A 个 %1B%24%42%50%24 -PASS U+4E2D 中 %1B%24%42%43%66 -PASS U+4E31 丱 %1B%24%42%50%25 -PASS U+4E32 串 %1B%24%42%36%7A -PASS U+4E36 丶 %1B%24%42%50%26 -PASS U+4E38 丸 %1B%24%42%34%5D -PASS U+4E39 丹 %1B%24%42%43%30 -PASS U+4E3B 主 %1B%24%42%3C%67 -PASS U+4E3C 丼 %1B%24%42%50%27 -PASS U+4E3F 丿 %1B%24%42%50%28 -PASS U+4E42 乂 %1B%24%42%50%29 -PASS U+4E43 乃 %1B%24%42%47%35 -PASS U+4E45 久 %1B%24%42%35%57 -PASS U+4E4B 之 %1B%24%42%47%37 -PASS U+4E4D 乍 %1B%24%42%46%63 -PASS U+4E4E 乎 %1B%24%42%38%43 -PASS U+4E4F 乏 %1B%24%42%4B%33 -PASS U+4E55 乕 %1B%24%42%69%49 -PASS U+4E56 乖 %1B%24%42%50%2A -PASS U+4E57 乗 %1B%24%42%3E%68 -PASS U+4E58 乘 %1B%24%42%50%2B -PASS U+4E59 乙 %1B%24%42%32%35 -PASS U+4E5D 九 %1B%24%42%36%65 -PASS U+4E5E 乞 %1B%24%42%38%70 -PASS U+4E5F 也 %1B%24%42%4C%69 -PASS U+4E62 乢 %1B%24%42%56%26 -PASS U+4E71 乱 %1B%24%42%4D%70 -PASS U+4E73 乳 %1B%24%42%46%7D -PASS U+4E7E 乾 %1B%24%42%34%25 -PASS U+4E80 亀 %1B%24%42%35%35 -PASS U+4E82 亂 %1B%24%42%50%2C -PASS U+4E85 亅 %1B%24%42%50%2D -PASS U+4E86 了 %1B%24%42%4E%3B -PASS U+4E88 予 %1B%24%42%4D%3D -PASS U+4E89 争 %1B%24%42%41%68 -PASS U+4E8A 亊 %1B%24%42%50%2F -PASS U+4E8B 事 %1B%24%42%3B%76 -PASS U+4E8C 二 %1B%24%42%46%73 -PASS U+4E8E 于 %1B%24%42%50%32 -PASS U+4E91 云 %1B%24%42%31%3E -PASS U+4E92 互 %1B%24%42%38%5F -PASS U+4E94 五 %1B%24%42%38%5E -PASS U+4E95 井 %1B%24%42%30%66 -PASS U+4E98 亘 %1B%24%42%4F%4B -PASS U+4E99 亙 %1B%24%42%4F%4A -PASS U+4E9B 些 %1B%24%42%3A%33 -PASS U+4E9C 亜 %1B%24%42%30%21 -PASS U+4E9E 亞 %1B%24%42%50%33 -PASS U+4E9F 亟 %1B%24%42%50%34 -PASS U+4EA0 亠 %1B%24%42%50%35 -PASS U+4EA1 亡 %1B%24%42%4B%34 -PASS U+4EA2 亢 %1B%24%42%50%36 -PASS U+4EA4 交 %1B%24%42%38%72 -PASS U+4EA5 亥 %1B%24%42%30%67 -PASS U+4EA6 亦 %1B%24%42%4B%72 -PASS U+4EA8 亨 %1B%24%42%35%7C -PASS U+4EAB 享 %1B%24%42%35%7D -PASS U+4EAC 京 %1B%24%42%35%7E -PASS U+4EAD 亭 %1B%24%42%44%62 -PASS U+4EAE 亮 %1B%24%42%4E%3C -PASS U+4EB0 亰 %1B%24%42%50%37 -PASS U+4EB3 亳 %1B%24%42%50%38 -PASS U+4EB6 亶 %1B%24%42%50%39 -PASS U+4EBA 人 %1B%24%42%3F%4D -PASS U+4EC0 什 %1B%24%42%3D%3A -PASS U+4EC1 仁 %1B%24%42%3F%4E -PASS U+4EC2 仂 %1B%24%42%50%3E -PASS U+4EC4 仄 %1B%24%42%50%3C -PASS U+4EC6 仆 %1B%24%42%50%3D -PASS U+4EC7 仇 %1B%24%42%35%58 -PASS U+4ECA 今 %1B%24%42%3A%23 -PASS U+4ECB 介 %1B%24%42%32%70 -PASS U+4ECD 仍 %1B%24%42%50%3B -PASS U+4ECE 从 %1B%24%42%50%3A -PASS U+4ECF 仏 %1B%24%42%4A%29 -PASS U+4ED4 仔 %1B%24%42%3B%46 -PASS U+4ED5 仕 %1B%24%42%3B%45 -PASS U+4ED6 他 %1B%24%42%42%3E -PASS U+4ED7 仗 %1B%24%42%50%3F -PASS U+4ED8 付 %1B%24%42%49%55 -PASS U+4ED9 仙 %1B%24%42%40%67 -PASS U+4EDD 仝 %1B%24%42%21%38 -PASS U+4EDE 仞 %1B%24%42%50%40 -PASS U+4EDF 仟 %1B%24%42%50%42 -FAIL U+4EE1 仡 %1B%24%42%79%2E assert_equals: expected "%1B%24%42%79%2E" but got "%26%23%32%30%31%39%33%3B" -PASS U+4EE3 代 %1B%24%42%42%65 -PASS U+4EE4 令 %1B%24%42%4E%61 -PASS U+4EE5 以 %1B%24%42%30%4A -PASS U+4EED 仭 %1B%24%42%50%41 -PASS U+4EEE 仮 %1B%24%42%32%3E -PASS U+4EF0 仰 %1B%24%42%36%44 -PASS U+4EF2 仲 %1B%24%42%43%67 -PASS U+4EF6 件 %1B%24%42%37%6F -PASS U+4EF7 价 %1B%24%42%50%43 -PASS U+4EFB 任 %1B%24%42%47%24 -FAIL U+4EFC 仼 %1B%24%42%79%2F assert_equals: expected "%1B%24%42%79%2F" but got "%26%23%32%30%32%32%30%3B" -FAIL U+4F00 伀 %1B%24%42%79%30 assert_equals: expected "%1B%24%42%79%30" but got "%26%23%32%30%32%32%34%3B" -PASS U+4F01 企 %1B%24%42%34%6B -FAIL U+4F03 伃 %1B%24%42%79%31 assert_equals: expected "%1B%24%42%79%31" but got "%26%23%32%30%32%32%37%3B" -PASS U+4F09 伉 %1B%24%42%50%44 -PASS U+4F0A 伊 %1B%24%42%30%4B -PASS U+4F0D 伍 %1B%24%42%38%60 -PASS U+4F0E 伎 %1B%24%42%34%6C -PASS U+4F0F 伏 %1B%24%42%49%7A -PASS U+4F10 伐 %1B%24%42%48%32 -PASS U+4F11 休 %1B%24%42%35%59 -PASS U+4F1A 会 %1B%24%42%32%71 -PASS U+4F1C 伜 %1B%24%42%50%67 -PASS U+4F1D 伝 %1B%24%42%45%41 -PASS U+4F2F 伯 %1B%24%42%47%6C -PASS U+4F30 估 %1B%24%42%50%46 -PASS U+4F34 伴 %1B%24%42%48%3C -PASS U+4F36 伶 %1B%24%42%4E%62 -PASS U+4F38 伸 %1B%24%42%3F%2D -FAIL U+4F39 伹 %1B%24%42%79%32 assert_equals: expected "%1B%24%42%79%32" but got "%26%23%32%30%32%38%31%3B" -PASS U+4F3A 伺 %1B%24%42%3B%47 -PASS U+4F3C 似 %1B%24%42%3B%77 -PASS U+4F3D 伽 %1B%24%42%32%40 -PASS U+4F43 佃 %1B%24%42%44%51 -PASS U+4F46 但 %1B%24%42%43%22 -PASS U+4F47 佇 %1B%24%42%50%4A -PASS U+4F4D 位 %1B%24%42%30%4C -PASS U+4F4E 低 %1B%24%42%44%63 -PASS U+4F4F 住 %1B%24%42%3D%3B -PASS U+4F50 佐 %1B%24%42%3A%34 -PASS U+4F51 佑 %1B%24%42%4D%24 -PASS U+4F53 体 %1B%24%42%42%4E -PASS U+4F55 何 %1B%24%42%32%3F -FAIL U+4F56 佖 %1B%24%42%79%33 assert_equals: expected "%1B%24%42%79%33" but got "%26%23%32%30%33%31%30%3B" -PASS U+4F57 佗 %1B%24%42%50%49 -PASS U+4F59 余 %1B%24%42%4D%3E -PASS U+4F5A 佚 %1B%24%42%50%45 -PASS U+4F5B 佛 %1B%24%42%50%47 -PASS U+4F5C 作 %1B%24%42%3A%6E -PASS U+4F5D 佝 %1B%24%42%50%48 -PASS U+4F5E 佞 %1B%24%42%55%24 -PASS U+4F69 佩 %1B%24%42%50%50 -PASS U+4F6F 佯 %1B%24%42%50%53 -PASS U+4F70 佰 %1B%24%42%50%51 -PASS U+4F73 佳 %1B%24%42%32%42 -PASS U+4F75 併 %1B%24%42%4A%3B -PASS U+4F76 佶 %1B%24%42%50%4B -PASS U+4F7B 佻 %1B%24%42%50%4F -PASS U+4F7C 佼 %1B%24%42%38%73 -PASS U+4F7F 使 %1B%24%42%3B%48 -PASS U+4F83 侃 %1B%24%42%34%26 -PASS U+4F86 來 %1B%24%42%50%54 -PASS U+4F88 侈 %1B%24%42%50%4C -FAIL U+4F8A 侊 %1B%24%42%79%35 assert_equals: expected "%1B%24%42%79%35" but got "%26%23%32%30%33%36%32%3B" -PASS U+4F8B 例 %1B%24%42%4E%63 -PASS U+4F8D 侍 %1B%24%42%3B%78 -PASS U+4F8F 侏 %1B%24%42%50%4D -PASS U+4F91 侑 %1B%24%42%50%52 -FAIL U+4F92 侒 %1B%24%42%79%34 assert_equals: expected "%1B%24%42%79%34" but got "%26%23%32%30%33%37%30%3B" -FAIL U+4F94 侔 %1B%24%42%79%37 assert_equals: expected "%1B%24%42%79%37" but got "%26%23%32%30%33%37%32%3B" -PASS U+4F96 侖 %1B%24%42%50%55 -PASS U+4F98 侘 %1B%24%42%50%4E -FAIL U+4F9A 侚 %1B%24%42%79%36 assert_equals: expected "%1B%24%42%79%36" but got "%26%23%32%30%33%37%38%3B" -PASS U+4F9B 供 %1B%24%42%36%21 -PASS U+4F9D 依 %1B%24%42%30%4D -PASS U+4FA0 侠 %1B%24%42%36%22 -PASS U+4FA1 価 %1B%24%42%32%41 -PASS U+4FAB 侫 %1B%24%42%55%25 -PASS U+4FAD 侭 %1B%24%42%4B%79 -PASS U+4FAE 侮 %1B%24%42%49%6E -PASS U+4FAF 侯 %1B%24%42%38%74 -PASS U+4FB5 侵 %1B%24%42%3F%2F -PASS U+4FB6 侶 %1B%24%42%4E%37 -PASS U+4FBF 便 %1B%24%42%4A%58 -PASS U+4FC2 係 %1B%24%42%37%38 -PASS U+4FC3 促 %1B%24%42%42%25 -PASS U+4FC4 俄 %1B%24%42%32%64 -FAIL U+4FC9 俉 %1B%24%42%79%26 assert_equals: expected "%1B%24%42%79%26" but got "%26%23%32%30%34%32%35%3B" -PASS U+4FCA 俊 %1B%24%42%3D%53 -FAIL U+4FCD 俍 %1B%24%42%79%38 assert_equals: expected "%1B%24%42%79%38" but got "%26%23%32%30%34%32%39%3B" -PASS U+4FCE 俎 %1B%24%42%50%59 -PASS U+4FD0 俐 %1B%24%42%50%5E -PASS U+4FD1 俑 %1B%24%42%50%5C -PASS U+4FD4 俔 %1B%24%42%50%57 -PASS U+4FD7 俗 %1B%24%42%42%2F -PASS U+4FD8 俘 %1B%24%42%50%5A -PASS U+4FDA 俚 %1B%24%42%50%5D -PASS U+4FDB 俛 %1B%24%42%50%5B -PASS U+4FDD 保 %1B%24%42%4A%5D -PASS U+4FDF 俟 %1B%24%42%50%58 -PASS U+4FE1 信 %1B%24%42%3F%2E -PASS U+4FE3 俣 %1B%24%42%4B%73 -PASS U+4FE4 俤 %1B%24%42%50%5F -PASS U+4FE5 俥 %1B%24%42%50%60 -PASS U+4FEE 修 %1B%24%42%3D%24 -PASS U+4FEF 俯 %1B%24%42%50%6D -PASS U+4FF3 俳 %1B%24%42%47%50 -PASS U+4FF5 俵 %1B%24%42%49%36 -PASS U+4FF6 俶 %1B%24%42%50%68 -PASS U+4FF8 俸 %1B%24%42%4A%70 -PASS U+4FFA 俺 %1B%24%42%32%36 -PASS U+4FFE 俾 %1B%24%42%50%6C -FAIL U+4FFF 俿 %1B%24%42%79%3B assert_equals: expected "%1B%24%42%79%3B" but got "%26%23%32%30%34%37%39%3B" -PASS U+5005 倅 %1B%24%42%50%66 -PASS U+5006 倆 %1B%24%42%50%6F -PASS U+5009 倉 %1B%24%42%41%52 -PASS U+500B 個 %1B%24%42%38%44 -PASS U+500D 倍 %1B%24%42%47%5C -PASS U+500F 倏 %1B%24%42%60%47 -PASS U+5011 們 %1B%24%42%50%6E -PASS U+5012 倒 %1B%24%42%45%5D -PASS U+5014 倔 %1B%24%42%50%63 -PASS U+5016 倖 %1B%24%42%38%76 -PASS U+5019 候 %1B%24%42%38%75 -PASS U+501A 倚 %1B%24%42%50%61 -FAIL U+501E 倞 %1B%24%42%79%3C assert_equals: expected "%1B%24%42%79%3C" but got "%26%23%32%30%35%31%30%3B" -PASS U+501F 借 %1B%24%42%3C%5A -PASS U+5021 倡 %1B%24%42%50%69 -FAIL U+5022 倢 %1B%24%42%79%3A assert_equals: expected "%1B%24%42%79%3A" but got "%26%23%32%30%35%31%34%3B" -PASS U+5023 倣 %1B%24%42%4A%6F -PASS U+5024 値 %1B%24%42%43%4D -PASS U+5025 倥 %1B%24%42%50%65 -PASS U+5026 倦 %1B%24%42%37%71 -PASS U+5028 倨 %1B%24%42%50%62 -PASS U+5029 倩 %1B%24%42%50%6A -PASS U+502A 倪 %1B%24%42%50%64 -PASS U+502B 倫 %1B%24%42%4E%51 -PASS U+502C 倬 %1B%24%42%50%6B -PASS U+502D 倭 %1B%24%42%4F%41 -PASS U+5036 倶 %1B%24%42%36%66 -PASS U+5039 倹 %1B%24%42%37%70 -FAIL U+5040 偀 %1B%24%42%79%39 assert_equals: expected "%1B%24%42%79%39" but got "%26%23%32%30%35%34%34%3B" -FAIL U+5042 偂 %1B%24%42%79%3F assert_equals: expected "%1B%24%42%79%3F" but got "%26%23%32%30%35%34%36%3B" -PASS U+5043 偃 %1B%24%42%50%70 -FAIL U+5046 偆 %1B%24%42%79%3D assert_equals: expected "%1B%24%42%79%3D" but got "%26%23%32%30%35%35%30%3B" -PASS U+5047 假 %1B%24%42%50%71 -PASS U+5048 偈 %1B%24%42%50%75 -PASS U+5049 偉 %1B%24%42%30%4E -PASS U+504F 偏 %1B%24%42%4A%50 -PASS U+5050 偐 %1B%24%42%50%74 -PASS U+5055 偕 %1B%24%42%50%73 -PASS U+5056 偖 %1B%24%42%50%77 -PASS U+505A 做 %1B%24%42%50%76 -PASS U+505C 停 %1B%24%42%44%64 -PASS U+5065 健 %1B%24%42%37%72 -PASS U+506C 偬 %1B%24%42%50%78 -FAIL U+5070 偰 %1B%24%42%79%3E assert_equals: expected "%1B%24%42%79%3E" but got "%26%23%32%30%35%39%32%3B" -PASS U+5072 偲 %1B%24%42%3C%45 -PASS U+5074 側 %1B%24%42%42%26 -PASS U+5075 偵 %1B%24%42%44%65 -PASS U+5076 偶 %1B%24%42%36%76 -PASS U+5078 偸 %1B%24%42%50%79 -PASS U+507D 偽 %1B%24%42%35%36 -PASS U+5080 傀 %1B%24%42%50%7A -PASS U+5085 傅 %1B%24%42%50%7C -PASS U+508D 傍 %1B%24%42%4B%35 -PASS U+5091 傑 %1B%24%42%37%66 -FAIL U+5094 傔 %1B%24%42%79%40 assert_equals: expected "%1B%24%42%79%40" but got "%26%23%32%30%36%32%38%3B" -PASS U+5098 傘 %1B%24%42%3B%31 -PASS U+5099 備 %1B%24%42%48%77 -PASS U+509A 傚 %1B%24%42%50%7B -PASS U+50AC 催 %1B%24%42%3A%45 -PASS U+50AD 傭 %1B%24%42%4D%43 -PASS U+50B2 傲 %1B%24%42%50%7E -PASS U+50B3 傳 %1B%24%42%51%23 -PASS U+50B4 傴 %1B%24%42%50%7D -PASS U+50B5 債 %1B%24%42%3A%44 -PASS U+50B7 傷 %1B%24%42%3D%7D -PASS U+50BE 傾 %1B%24%42%37%39 -PASS U+50C2 僂 %1B%24%42%51%24 -PASS U+50C5 僅 %1B%24%42%36%4F -PASS U+50C9 僉 %1B%24%42%51%21 -PASS U+50CA 僊 %1B%24%42%51%22 -PASS U+50CD 働 %1B%24%42%46%2F -PASS U+50CF 像 %1B%24%42%41%7C -PASS U+50D1 僑 %1B%24%42%36%23 -PASS U+50D5 僕 %1B%24%42%4B%4D -PASS U+50D6 僖 %1B%24%42%51%25 -FAIL U+50D8 僘 %1B%24%42%79%42 assert_equals: expected "%1B%24%42%79%42" but got "%26%23%32%30%36%39%36%3B" -PASS U+50DA 僚 %1B%24%42%4E%3D -PASS U+50DE 僞 %1B%24%42%51%26 -PASS U+50E3 僣 %1B%24%42%51%29 -PASS U+50E5 僥 %1B%24%42%51%27 -PASS U+50E7 僧 %1B%24%42%41%4E -PASS U+50ED 僭 %1B%24%42%51%28 -PASS U+50EE 僮 %1B%24%42%51%2A -FAIL U+50F4 僴 %1B%24%42%79%41 assert_equals: expected "%1B%24%42%79%41" but got "%26%23%32%30%37%32%34%3B" -PASS U+50F5 僵 %1B%24%42%51%2C -PASS U+50F9 價 %1B%24%42%51%2B -PASS U+50FB 僻 %1B%24%42%4A%48 -PASS U+5100 儀 %1B%24%42%35%37 -PASS U+5101 儁 %1B%24%42%51%2E -PASS U+5102 儂 %1B%24%42%51%2F -PASS U+5104 億 %1B%24%42%32%2F -PASS U+5109 儉 %1B%24%42%51%2D -PASS U+5112 儒 %1B%24%42%3C%74 -PASS U+5114 儔 %1B%24%42%51%32 -PASS U+5115 儕 %1B%24%42%51%31 -PASS U+5116 儖 %1B%24%42%51%30 -PASS U+5118 儘 %1B%24%42%50%56 -PASS U+511A 儚 %1B%24%42%51%33 -PASS U+511F 償 %1B%24%42%3D%7E -PASS U+5121 儡 %1B%24%42%51%34 -PASS U+512A 優 %1B%24%42%4D%25 -PASS U+5132 儲 %1B%24%42%4C%59 -PASS U+5137 儷 %1B%24%42%51%36 -PASS U+513A 儺 %1B%24%42%51%35 -PASS U+513B 儻 %1B%24%42%51%38 -PASS U+513C 儼 %1B%24%42%51%37 -PASS U+513F 儿 %1B%24%42%51%39 -PASS U+5140 兀 %1B%24%42%51%3A -PASS U+5141 允 %1B%24%42%30%74 -PASS U+5143 元 %1B%24%42%38%35 -PASS U+5144 兄 %1B%24%42%37%3B -PASS U+5145 充 %1B%24%42%3D%3C -PASS U+5146 兆 %1B%24%42%43%7B -PASS U+5147 兇 %1B%24%42%36%24 -PASS U+5148 先 %1B%24%42%40%68 -PASS U+5149 光 %1B%24%42%38%77 -FAIL U+514A 兊 %1B%24%42%79%43 assert_equals: expected "%1B%24%42%79%43" but got "%26%23%32%30%38%31%30%3B" -PASS U+514B 克 %1B%24%42%39%6E -PASS U+514C 兌 %1B%24%42%51%3C -PASS U+514D 免 %1B%24%42%4C%48 -PASS U+514E 兎 %1B%24%42%45%46 -PASS U+5150 児 %1B%24%42%3B%79 -PASS U+5152 兒 %1B%24%42%51%3B -PASS U+5154 兔 %1B%24%42%51%3D -PASS U+515A 党 %1B%24%42%45%5E -PASS U+515C 兜 %1B%24%42%33%75 -PASS U+5162 兢 %1B%24%42%51%3E -FAIL U+5164 兤 %1B%24%42%79%44 assert_equals: expected "%1B%24%42%79%44" but got "%26%23%32%30%38%33%36%3B" -PASS U+5165 入 %1B%24%42%46%7E -PASS U+5168 全 %1B%24%42%41%34 -PASS U+5169 兩 %1B%24%42%51%40 -PASS U+516A 兪 %1B%24%42%51%41 -PASS U+516B 八 %1B%24%42%48%2C -PASS U+516C 公 %1B%24%42%38%78 -PASS U+516D 六 %1B%24%42%4F%3B -PASS U+516E 兮 %1B%24%42%51%42 -PASS U+5171 共 %1B%24%42%36%26 -PASS U+5175 兵 %1B%24%42%4A%3C -PASS U+5176 其 %1B%24%42%42%36 -PASS U+5177 具 %1B%24%42%36%71 -PASS U+5178 典 %1B%24%42%45%35 -PASS U+517C 兼 %1B%24%42%37%73 -PASS U+5180 冀 %1B%24%42%51%43 -PASS U+5182 冂 %1B%24%42%51%44 -PASS U+5185 内 %1B%24%42%46%62 -PASS U+5186 円 %1B%24%42%31%5F -PASS U+5189 冉 %1B%24%42%51%47 -PASS U+518A 冊 %1B%24%42%3A%7D -PASS U+518C 册 %1B%24%42%51%46 -PASS U+518D 再 %1B%24%42%3A%46 -PASS U+518F 冏 %1B%24%42%51%48 -PASS U+5190 冐 %1B%24%42%66%6E -PASS U+5191 冑 %1B%24%42%51%49 -PASS U+5192 冒 %1B%24%42%4B%41 -PASS U+5193 冓 %1B%24%42%51%4A -PASS U+5195 冕 %1B%24%42%51%4B -PASS U+5196 冖 %1B%24%42%51%4C -PASS U+5197 冗 %1B%24%42%3E%69 -PASS U+5199 写 %1B%24%42%3C%4C -FAIL U+519D 冝 %1B%24%42%79%45 assert_equals: expected "%1B%24%42%79%45" but got "%26%23%32%30%38%39%33%3B" -PASS U+51A0 冠 %1B%24%42%34%27 -PASS U+51A2 冢 %1B%24%42%51%4F -PASS U+51A4 冤 %1B%24%42%51%4D -PASS U+51A5 冥 %1B%24%42%4C%3D -PASS U+51A6 冦 %1B%24%42%51%4E -PASS U+51A8 冨 %1B%24%42%49%5A -PASS U+51A9 冩 %1B%24%42%51%50 -PASS U+51AA 冪 %1B%24%42%51%51 -PASS U+51AB 冫 %1B%24%42%51%52 -PASS U+51AC 冬 %1B%24%42%45%5F -PASS U+51B0 冰 %1B%24%42%51%56 -PASS U+51B1 冱 %1B%24%42%51%54 -PASS U+51B2 冲 %1B%24%42%51%55 -PASS U+51B3 决 %1B%24%42%51%53 -PASS U+51B4 冴 %1B%24%42%3A%63 -PASS U+51B5 况 %1B%24%42%51%57 -PASS U+51B6 冶 %1B%24%42%4C%6A -PASS U+51B7 冷 %1B%24%42%4E%64 -PASS U+51BD 冽 %1B%24%42%51%58 -FAIL U+51BE 冾 %1B%24%42%79%46 assert_equals: expected "%1B%24%42%79%46" but got "%26%23%32%30%39%32%36%3B" -PASS U+51C4 凄 %1B%24%42%40%28 -PASS U+51C5 凅 %1B%24%42%51%59 -PASS U+51C6 准 %1B%24%42%3D%5A -PASS U+51C9 凉 %1B%24%42%51%5A -PASS U+51CB 凋 %1B%24%42%43%7C -PASS U+51CC 凌 %1B%24%42%4E%3F -PASS U+51CD 凍 %1B%24%42%45%60 -PASS U+51D6 凖 %1B%24%42%52%45 -PASS U+51DB 凛 %1B%24%42%51%5B -PASS U+51DC 凜 %1B%24%42%74%25 -PASS U+51DD 凝 %1B%24%42%36%45 -PASS U+51E0 几 %1B%24%42%51%5C -PASS U+51E1 凡 %1B%24%42%4B%5E -PASS U+51E6 処 %1B%24%42%3D%68 -PASS U+51E7 凧 %1B%24%42%42%7C -PASS U+51E9 凩 %1B%24%42%51%5E -PASS U+51EA 凪 %1B%24%42%46%64 -FAIL U+51EC 凬 %1B%24%42%79%47 assert_equals: expected "%1B%24%42%79%47" but got "%26%23%32%30%39%37%32%3B" -PASS U+51ED 凭 %1B%24%42%51%5F -PASS U+51F0 凰 %1B%24%42%51%60 -PASS U+51F1 凱 %1B%24%42%33%2E -PASS U+51F5 凵 %1B%24%42%51%61 -PASS U+51F6 凶 %1B%24%42%36%27 -PASS U+51F8 凸 %1B%24%42%46%4C -PASS U+51F9 凹 %1B%24%42%31%7A -PASS U+51FA 出 %1B%24%42%3D%50 -PASS U+51FD 函 %1B%24%42%48%21 -PASS U+51FE 凾 %1B%24%42%51%62 -PASS U+5200 刀 %1B%24%42%45%61 -PASS U+5203 刃 %1B%24%42%3F%4F -PASS U+5204 刄 %1B%24%42%51%63 -PASS U+5206 分 %1B%24%42%4A%2C -PASS U+5207 切 %1B%24%42%40%5A -PASS U+5208 刈 %1B%24%42%34%22 -PASS U+520A 刊 %1B%24%42%34%29 -PASS U+520B 刋 %1B%24%42%51%64 -PASS U+520E 刎 %1B%24%42%51%66 -PASS U+5211 刑 %1B%24%42%37%3A -PASS U+5214 刔 %1B%24%42%51%65 -FAIL U+5215 刕 %1B%24%42%79%48 assert_equals: expected "%1B%24%42%79%48" but got "%26%23%32%31%30%31%33%3B" -PASS U+5217 列 %1B%24%42%4E%73 -PASS U+521D 初 %1B%24%42%3D%69 -PASS U+5224 判 %1B%24%42%48%3D -PASS U+5225 別 %1B%24%42%4A%4C -PASS U+5227 刧 %1B%24%42%51%67 -PASS U+5229 利 %1B%24%42%4D%78 -PASS U+522A 刪 %1B%24%42%51%68 -PASS U+522E 刮 %1B%24%42%51%69 -PASS U+5230 到 %1B%24%42%45%7E -PASS U+5233 刳 %1B%24%42%51%6A -PASS U+5236 制 %1B%24%42%40%29 -PASS U+5237 刷 %1B%24%42%3A%7E -PASS U+5238 券 %1B%24%42%37%74 -PASS U+5239 刹 %1B%24%42%51%6B -PASS U+523A 刺 %1B%24%42%3B%49 -PASS U+523B 刻 %1B%24%42%39%6F -PASS U+5243 剃 %1B%24%42%44%66 -PASS U+5244 剄 %1B%24%42%51%6D -PASS U+5247 則 %1B%24%42%42%27 -PASS U+524A 削 %1B%24%42%3A%6F -PASS U+524B 剋 %1B%24%42%51%6E -PASS U+524C 剌 %1B%24%42%51%6F -PASS U+524D 前 %1B%24%42%41%30 -PASS U+524F 剏 %1B%24%42%51%6C -PASS U+5254 剔 %1B%24%42%51%71 -PASS U+5256 剖 %1B%24%42%4B%36 -PASS U+525B 剛 %1B%24%42%39%64 -PASS U+525E 剞 %1B%24%42%51%70 -PASS U+5263 剣 %1B%24%42%37%75 -PASS U+5264 剤 %1B%24%42%3A%5E -PASS U+5265 剥 %1B%24%42%47%6D -PASS U+5269 剩 %1B%24%42%51%74 -PASS U+526A 剪 %1B%24%42%51%72 -PASS U+526F 副 %1B%24%42%49%7B -PASS U+5270 剰 %1B%24%42%3E%6A -PASS U+5271 剱 %1B%24%42%51%7B -PASS U+5272 割 %1B%24%42%33%64 -PASS U+5273 剳 %1B%24%42%51%75 -PASS U+5274 剴 %1B%24%42%51%73 -PASS U+5275 創 %1B%24%42%41%4F -PASS U+527D 剽 %1B%24%42%51%77 -PASS U+527F 剿 %1B%24%42%51%76 -PASS U+5283 劃 %1B%24%42%33%44 -PASS U+5287 劇 %1B%24%42%37%60 -PASS U+5288 劈 %1B%24%42%51%7C -PASS U+5289 劉 %1B%24%42%4E%2D -PASS U+528D 劍 %1B%24%42%51%78 -PASS U+5291 劑 %1B%24%42%51%7D -PASS U+5292 劒 %1B%24%42%51%7A -PASS U+5294 劔 %1B%24%42%51%79 -PASS U+529B 力 %1B%24%42%4E%4F -FAIL U+529C 劜 %1B%24%42%79%49 assert_equals: expected "%1B%24%42%79%49" but got "%26%23%32%31%31%34%38%3B" -PASS U+529F 功 %1B%24%42%38%79 -PASS U+52A0 加 %1B%24%42%32%43 -PASS U+52A3 劣 %1B%24%42%4E%74 -FAIL U+52A6 劦 %1B%24%42%79%4A assert_equals: expected "%1B%24%42%79%4A" but got "%26%23%32%31%31%35%38%3B" -PASS U+52A9 助 %1B%24%42%3D%75 -PASS U+52AA 努 %1B%24%42%45%58 -PASS U+52AB 劫 %1B%24%42%39%65 -PASS U+52AC 劬 %1B%24%42%52%22 -PASS U+52AD 劭 %1B%24%42%52%23 -FAIL U+52AF 劯 %1B%24%42%7B%3C assert_equals: expected "%1B%24%42%7B%3C" but got "%26%23%32%31%31%36%37%3B" -PASS U+52B1 励 %1B%24%42%4E%65 -PASS U+52B4 労 %1B%24%42%4F%2B -PASS U+52B5 劵 %1B%24%42%52%25 -PASS U+52B9 効 %1B%24%42%38%7A -PASS U+52BC 劼 %1B%24%42%52%24 -PASS U+52BE 劾 %1B%24%42%33%2F -FAIL U+52C0 勀 %1B%24%42%79%4B assert_equals: expected "%1B%24%42%79%4B" but got "%26%23%32%31%31%38%34%3B" -PASS U+52C1 勁 %1B%24%42%52%26 -PASS U+52C3 勃 %1B%24%42%4B%56 -PASS U+52C5 勅 %1B%24%42%44%3C -PASS U+52C7 勇 %1B%24%42%4D%26 -PASS U+52C9 勉 %1B%24%42%4A%59 -PASS U+52CD 勍 %1B%24%42%52%27 -PASS U+52D2 勒 %1B%24%42%70%55 -PASS U+52D5 動 %1B%24%42%46%30 -PASS U+52D7 勗 %1B%24%42%52%28 -PASS U+52D8 勘 %1B%24%42%34%2A -PASS U+52D9 務 %1B%24%42%4C%33 -FAIL U+52DB 勛 %1B%24%42%79%4C assert_equals: expected "%1B%24%42%79%4C" but got "%26%23%32%31%32%31%31%3B" -PASS U+52DD 勝 %1B%24%42%3E%21 -PASS U+52DE 勞 %1B%24%42%52%29 -PASS U+52DF 募 %1B%24%42%4A%67 -PASS U+52E0 勠 %1B%24%42%52%2D -PASS U+52E2 勢 %1B%24%42%40%2A -PASS U+52E3 勣 %1B%24%42%52%2A -PASS U+52E4 勤 %1B%24%42%36%50 -PASS U+52E6 勦 %1B%24%42%52%2B -PASS U+52E7 勧 %1B%24%42%34%2B -PASS U+52F2 勲 %1B%24%42%37%2E -PASS U+52F3 勳 %1B%24%42%52%2E -PASS U+52F5 勵 %1B%24%42%52%2F -PASS U+52F8 勸 %1B%24%42%52%30 -PASS U+52F9 勹 %1B%24%42%52%31 -PASS U+52FA 勺 %1B%24%42%3C%5B -PASS U+52FE 勾 %1B%24%42%38%7B -PASS U+52FF 勿 %1B%24%42%4C%5E -FAIL U+5300 匀 %1B%24%42%79%4D assert_equals: expected "%1B%24%42%79%4D" but got "%26%23%32%31%32%34%38%3B" -PASS U+5301 匁 %1B%24%42%4C%68 -PASS U+5302 匂 %1B%24%42%46%77 -PASS U+5305 包 %1B%24%42%4A%71 -PASS U+5306 匆 %1B%24%42%52%32 -FAIL U+5307 匇 %1B%24%42%79%4E assert_equals: expected "%1B%24%42%79%4E" but got "%26%23%32%31%32%35%35%3B" -PASS U+5308 匈 %1B%24%42%52%33 -PASS U+530D 匍 %1B%24%42%52%35 -PASS U+530F 匏 %1B%24%42%52%37 -PASS U+5310 匐 %1B%24%42%52%36 -PASS U+5315 匕 %1B%24%42%52%38 -PASS U+5316 化 %1B%24%42%32%3D -PASS U+5317 北 %1B%24%42%4B%4C -PASS U+5319 匙 %1B%24%42%3A%7C -PASS U+531A 匚 %1B%24%42%52%39 -PASS U+531D 匝 %1B%24%42%41%59 -PASS U+5320 匠 %1B%24%42%3E%22 -PASS U+5321 匡 %1B%24%42%36%29 -PASS U+5323 匣 %1B%24%42%52%3A -FAIL U+5324 匤 %1B%24%42%79%4F assert_equals: expected "%1B%24%42%79%4F" but got "%26%23%32%31%32%38%34%3B" -PASS U+532A 匪 %1B%24%42%48%5B -PASS U+532F 匯 %1B%24%42%52%3B -PASS U+5331 匱 %1B%24%42%52%3C -PASS U+5333 匳 %1B%24%42%52%3D -PASS U+5338 匸 %1B%24%42%52%3E -PASS U+5339 匹 %1B%24%42%49%24 -PASS U+533A 区 %1B%24%42%36%68 -PASS U+533B 医 %1B%24%42%30%65 -PASS U+533F 匿 %1B%24%42%46%3F -PASS U+5340 區 %1B%24%42%52%3F -PASS U+5341 十 %1B%24%42%3D%3D -PASS U+5343 千 %1B%24%42%40%69 -PASS U+5345 卅 %1B%24%42%52%41 -PASS U+5346 卆 %1B%24%42%52%40 -PASS U+5347 升 %1B%24%42%3E%23 -PASS U+5348 午 %1B%24%42%38%61 -PASS U+5349 卉 %1B%24%42%52%43 -PASS U+534A 半 %1B%24%42%48%3E -PASS U+534D 卍 %1B%24%42%52%44 -PASS U+5351 卑 %1B%24%42%48%5C -PASS U+5352 卒 %1B%24%42%42%34 -PASS U+5353 卓 %1B%24%42%42%6E -PASS U+5354 協 %1B%24%42%36%28 -PASS U+5357 南 %1B%24%42%46%6E -PASS U+5358 単 %1B%24%42%43%31 -PASS U+535A 博 %1B%24%42%47%6E -PASS U+535C 卜 %1B%24%42%4B%4E -PASS U+535E 卞 %1B%24%42%52%46 -PASS U+5360 占 %1B%24%42%40%6A -PASS U+5366 卦 %1B%24%42%37%35 -PASS U+5369 卩 %1B%24%42%52%47 -PASS U+536E 卮 %1B%24%42%52%48 -PASS U+536F 卯 %1B%24%42%31%2C -PASS U+5370 印 %1B%24%42%30%75 -PASS U+5371 危 %1B%24%42%34%6D -FAIL U+5372 卲 %1B%24%42%79%50 assert_equals: expected "%1B%24%42%79%50" but got "%26%23%32%31%33%36%32%3B" -PASS U+5373 即 %1B%24%42%42%28 -PASS U+5374 却 %1B%24%42%35%51 -PASS U+5375 卵 %1B%24%42%4D%71 -PASS U+5377 卷 %1B%24%42%52%4B -PASS U+5378 卸 %1B%24%42%32%37 -PASS U+537B 卻 %1B%24%42%52%4A -PASS U+537F 卿 %1B%24%42%36%2A -PASS U+5382 厂 %1B%24%42%52%4C -PASS U+5384 厄 %1B%24%42%4C%71 -FAIL U+5393 厓 %1B%24%42%79%51 assert_equals: expected "%1B%24%42%79%51" but got "%26%23%32%31%33%39%35%3B" -PASS U+5396 厖 %1B%24%42%52%4D -PASS U+5398 厘 %1B%24%42%4E%52 -PASS U+539A 厚 %1B%24%42%38%7C -PASS U+539F 原 %1B%24%42%38%36 -PASS U+53A0 厠 %1B%24%42%52%4E -PASS U+53A5 厥 %1B%24%42%52%50 -PASS U+53A6 厦 %1B%24%42%52%4F -PASS U+53A8 厨 %1B%24%42%3F%5F -PASS U+53A9 厩 %1B%24%42%31%39 -PASS U+53AD 厭 %1B%24%42%31%5E -PASS U+53AE 厮 %1B%24%42%52%51 -PASS U+53B0 厰 %1B%24%42%52%52 -FAIL U+53B2 厲 %1B%24%42%79%52 assert_equals: expected "%1B%24%42%79%52" but got "%26%23%32%31%34%32%36%3B" -PASS U+53B3 厳 %1B%24%42%38%37 -PASS U+53B6 厶 %1B%24%42%52%53 -PASS U+53BB 去 %1B%24%42%35%6E -PASS U+53C2 参 %1B%24%42%3B%32 -PASS U+53C3 參 %1B%24%42%52%54 -PASS U+53C8 又 %1B%24%42%4B%74 -PASS U+53C9 叉 %1B%24%42%3A%35 -PASS U+53CA 及 %1B%24%42%35%5A -PASS U+53CB 友 %1B%24%42%4D%27 -PASS U+53CC 双 %1B%24%42%41%50 -PASS U+53CD 反 %1B%24%42%48%3F -PASS U+53CE 収 %1B%24%42%3C%7D -PASS U+53D4 叔 %1B%24%42%3D%47 -PASS U+53D6 取 %1B%24%42%3C%68 -PASS U+53D7 受 %1B%24%42%3C%75 -PASS U+53D9 叙 %1B%24%42%3D%76 -PASS U+53DB 叛 %1B%24%42%48%40 -FAIL U+53DD 叝 %1B%24%42%79%53 assert_equals: expected "%1B%24%42%79%53" but got "%26%23%32%31%34%36%39%3B" -PASS U+53DF 叟 %1B%24%42%52%57 -PASS U+53E1 叡 %1B%24%42%31%43 -PASS U+53E2 叢 %1B%24%42%41%51 -PASS U+53E3 口 %1B%24%42%38%7D -PASS U+53E4 古 %1B%24%42%38%45 -PASS U+53E5 句 %1B%24%42%36%67 -PASS U+53E8 叨 %1B%24%42%52%5B -PASS U+53E9 叩 %1B%24%42%43%21 -PASS U+53EA 只 %1B%24%42%42%7E -PASS U+53EB 叫 %1B%24%42%36%2B -PASS U+53EC 召 %1B%24%42%3E%24 -PASS U+53ED 叭 %1B%24%42%52%5C -PASS U+53EE 叮 %1B%24%42%52%5A -PASS U+53EF 可 %1B%24%42%32%44 -PASS U+53F0 台 %1B%24%42%42%66 -PASS U+53F1 叱 %1B%24%42%3C%38 -PASS U+53F2 史 %1B%24%42%3B%4B -PASS U+53F3 右 %1B%24%42%31%26 -PASS U+53F6 叶 %1B%24%42%33%70 -PASS U+53F7 号 %1B%24%42%39%66 -PASS U+53F8 司 %1B%24%42%3B%4A -PASS U+53FA 叺 %1B%24%42%52%5D -PASS U+5401 吁 %1B%24%42%52%5E -PASS U+5403 吃 %1B%24%42%35%49 -PASS U+5404 各 %1B%24%42%33%46 -PASS U+5408 合 %1B%24%42%39%67 -PASS U+5409 吉 %1B%24%42%35%48 -PASS U+540A 吊 %1B%24%42%44%5F -PASS U+540B 吋 %1B%24%42%31%25 -PASS U+540C 同 %1B%24%42%46%31 -PASS U+540D 名 %1B%24%42%4C%3E -PASS U+540E 后 %1B%24%42%39%21 -PASS U+540F 吏 %1B%24%42%4D%79 -PASS U+5410 吐 %1B%24%42%45%47 -PASS U+5411 向 %1B%24%42%38%7E -PASS U+541B 君 %1B%24%42%37%2F -PASS U+541D 吝 %1B%24%42%52%67 -PASS U+541F 吟 %1B%24%42%36%63 -PASS U+5420 吠 %1B%24%42%4B%4A -PASS U+5426 否 %1B%24%42%48%5D -PASS U+5429 吩 %1B%24%42%52%66 -PASS U+542B 含 %1B%24%42%34%5E -PASS U+542C 听 %1B%24%42%52%61 -PASS U+542D 吭 %1B%24%42%52%62 -PASS U+542E 吮 %1B%24%42%52%64 -PASS U+5436 吶 %1B%24%42%52%65 -PASS U+5438 吸 %1B%24%42%35%5B -PASS U+5439 吹 %1B%24%42%3F%61 -PASS U+543B 吻 %1B%24%42%4A%2D -PASS U+543C 吼 %1B%24%42%52%63 -PASS U+543D 吽 %1B%24%42%52%5F -PASS U+543E 吾 %1B%24%42%38%63 -PASS U+5440 呀 %1B%24%42%52%60 -PASS U+5442 呂 %1B%24%42%4F%24 -PASS U+5446 呆 %1B%24%42%4A%72 -PASS U+5448 呈 %1B%24%42%44%68 -PASS U+5449 呉 %1B%24%42%38%62 -PASS U+544A 告 %1B%24%42%39%70 -PASS U+544E 呎 %1B%24%42%52%68 -PASS U+5451 呑 %1B%24%42%46%5D -PASS U+545F 呟 %1B%24%42%52%6C -PASS U+5468 周 %1B%24%42%3C%7E -PASS U+546A 呪 %1B%24%42%3C%76 -PASS U+5470 呰 %1B%24%42%52%6F -PASS U+5471 呱 %1B%24%42%52%6D -PASS U+5473 味 %1B%24%42%4C%23 -PASS U+5475 呵 %1B%24%42%52%6A -PASS U+5476 呶 %1B%24%42%52%73 -PASS U+5477 呷 %1B%24%42%52%6E -PASS U+547B 呻 %1B%24%42%52%71 -PASS U+547C 呼 %1B%24%42%38%46 -PASS U+547D 命 %1B%24%42%4C%3F -PASS U+5480 咀 %1B%24%42%52%72 -PASS U+5484 咄 %1B%24%42%52%74 -PASS U+5486 咆 %1B%24%42%52%76 -FAIL U+548A 咊 %1B%24%42%79%56 assert_equals: expected "%1B%24%42%79%56" but got "%26%23%32%31%36%34%32%3B" -PASS U+548B 咋 %1B%24%42%3A%70 -PASS U+548C 和 %1B%24%42%4F%42 -PASS U+548E 咎 %1B%24%42%52%6B -PASS U+548F 咏 %1B%24%42%52%69 -PASS U+5490 咐 %1B%24%42%52%75 -PASS U+5492 咒 %1B%24%42%52%70 -FAIL U+549C 咜 %1B%24%42%79%55 assert_equals: expected "%1B%24%42%79%55" but got "%26%23%32%31%36%36%30%3B" -PASS U+54A2 咢 %1B%24%42%52%78 -PASS U+54A4 咤 %1B%24%42%53%23 -PASS U+54A5 咥 %1B%24%42%52%7A -PASS U+54A8 咨 %1B%24%42%52%7E -FAIL U+54A9 咩 %1B%24%42%79%57 assert_equals: expected "%1B%24%42%79%57" but got "%26%23%32%31%36%37%33%3B" -PASS U+54AB 咫 %1B%24%42%53%21 -PASS U+54AC 咬 %1B%24%42%52%7B -PASS U+54AF 咯 %1B%24%42%53%3E -PASS U+54B2 咲 %1B%24%42%3A%69 -PASS U+54B3 咳 %1B%24%42%33%31 -PASS U+54B8 咸 %1B%24%42%52%79 -PASS U+54BC 咼 %1B%24%42%53%25 -PASS U+54BD 咽 %1B%24%42%30%76 -PASS U+54BE 咾 %1B%24%42%53%24 -PASS U+54C0 哀 %1B%24%42%30%25 -PASS U+54C1 品 %1B%24%42%49%4A -PASS U+54C2 哂 %1B%24%42%53%22 -PASS U+54C4 哄 %1B%24%42%52%7C -PASS U+54C7 哇 %1B%24%42%52%77 -PASS U+54C8 哈 %1B%24%42%52%7D -PASS U+54C9 哉 %1B%24%42%3A%48 -PASS U+54D8 哘 %1B%24%42%53%26 -PASS U+54E1 員 %1B%24%42%30%77 -PASS U+54E2 哢 %1B%24%42%53%2F -PASS U+54E5 哥 %1B%24%42%53%27 -PASS U+54E6 哦 %1B%24%42%53%28 -PASS U+54E8 哨 %1B%24%42%3E%25 -PASS U+54E9 哩 %1B%24%42%4B%69 -PASS U+54ED 哭 %1B%24%42%53%2D -PASS U+54EE 哮 %1B%24%42%53%2C -PASS U+54F2 哲 %1B%24%42%45%2F -PASS U+54FA 哺 %1B%24%42%53%2E -PASS U+54FD 哽 %1B%24%42%53%2B -FAIL U+54FF 哿 %1B%24%42%79%58 assert_equals: expected "%1B%24%42%79%58" but got "%26%23%32%31%37%35%39%3B" -PASS U+5504 唄 %1B%24%42%31%34 -PASS U+5506 唆 %1B%24%42%3A%36 -PASS U+5507 唇 %1B%24%42%3F%30 -PASS U+550F 唏 %1B%24%42%53%29 -PASS U+5510 唐 %1B%24%42%45%62 -PASS U+5514 唔 %1B%24%42%53%2A -PASS U+5516 唖 %1B%24%42%30%22 -PASS U+552E 售 %1B%24%42%53%34 -PASS U+552F 唯 %1B%24%42%4D%23 -PASS U+5531 唱 %1B%24%42%3E%27 -PASS U+5533 唳 %1B%24%42%53%3A -PASS U+5538 唸 %1B%24%42%53%39 -PASS U+5539 唹 %1B%24%42%53%30 -PASS U+553E 唾 %1B%24%42%42%43 -PASS U+5540 啀 %1B%24%42%53%31 -PASS U+5544 啄 %1B%24%42%42%6F -PASS U+5545 啅 %1B%24%42%53%36 -PASS U+5546 商 %1B%24%42%3E%26 -PASS U+554C 啌 %1B%24%42%53%33 -PASS U+554F 問 %1B%24%42%4C%64 -PASS U+5553 啓 %1B%24%42%37%3C -PASS U+5556 啖 %1B%24%42%53%37 -PASS U+5557 啗 %1B%24%42%53%38 -PASS U+555C 啜 %1B%24%42%53%35 -PASS U+555D 啝 %1B%24%42%53%3B -PASS U+5563 啣 %1B%24%42%53%32 -PASS U+557B 啻 %1B%24%42%53%41 -PASS U+557C 啼 %1B%24%42%53%46 -PASS U+557E 啾 %1B%24%42%53%42 -PASS U+5580 喀 %1B%24%42%53%3D -PASS U+5583 喃 %1B%24%42%53%47 -PASS U+5584 善 %1B%24%42%41%31 -FAIL U+5586 喆 %1B%24%42%79%59 assert_equals: expected "%1B%24%42%79%59" but got "%26%23%32%31%38%39%34%3B" -PASS U+5587 喇 %1B%24%42%53%49 -PASS U+5589 喉 %1B%24%42%39%22 -PASS U+558A 喊 %1B%24%42%53%3F -PASS U+558B 喋 %1B%24%42%43%7D -PASS U+5598 喘 %1B%24%42%53%43 -PASS U+5599 喙 %1B%24%42%53%3C -PASS U+559A 喚 %1B%24%42%34%2D -PASS U+559C 喜 %1B%24%42%34%6E -PASS U+559D 喝 %1B%24%42%33%65 -PASS U+559E 喞 %1B%24%42%53%44 -PASS U+559F 喟 %1B%24%42%53%40 -PASS U+55A7 喧 %1B%24%42%37%76 -PASS U+55A8 喨 %1B%24%42%53%4A -PASS U+55A9 喩 %1B%24%42%53%48 -PASS U+55AA 喪 %1B%24%42%41%53 -PASS U+55AB 喫 %1B%24%42%35%4A -PASS U+55AC 喬 %1B%24%42%36%2C -PASS U+55AE 單 %1B%24%42%53%45 -PASS U+55B0 喰 %1B%24%42%36%74 -PASS U+55B6 営 %1B%24%42%31%44 -PASS U+55C4 嗄 %1B%24%42%53%4E -PASS U+55C5 嗅 %1B%24%42%53%4C -PASS U+55C7 嗇 %1B%24%42%54%27 -PASS U+55D4 嗔 %1B%24%42%53%51 -PASS U+55DA 嗚 %1B%24%42%53%4B -PASS U+55DC 嗜 %1B%24%42%53%4F -PASS U+55DF 嗟 %1B%24%42%53%4D -PASS U+55E3 嗣 %1B%24%42%3B%4C -PASS U+55E4 嗤 %1B%24%42%53%50 -PASS U+55F7 嗷 %1B%24%42%53%53 -PASS U+55F9 嗹 %1B%24%42%53%58 -PASS U+55FD 嗽 %1B%24%42%53%56 -PASS U+55FE 嗾 %1B%24%42%53%55 -PASS U+5606 嘆 %1B%24%42%43%32 -PASS U+5609 嘉 %1B%24%42%32%45 -PASS U+5614 嘔 %1B%24%42%53%52 -PASS U+5616 嘖 %1B%24%42%53%54 -PASS U+5617 嘗 %1B%24%42%3E%28 -PASS U+5618 嘘 %1B%24%42%31%33 -PASS U+561B 嘛 %1B%24%42%53%57 -PASS U+5629 嘩 %1B%24%42%32%5E -PASS U+562F 嘯 %1B%24%42%53%62 -PASS U+5631 嘱 %1B%24%42%3E%7C -PASS U+5632 嘲 %1B%24%42%53%5E -PASS U+5634 嘴 %1B%24%42%53%5C -PASS U+5636 嘶 %1B%24%42%53%5D -PASS U+5638 嘸 %1B%24%42%53%5F -PASS U+5642 噂 %1B%24%42%31%3D -PASS U+564C 噌 %1B%24%42%41%39 -PASS U+564E 噎 %1B%24%42%53%59 -PASS U+5650 噐 %1B%24%42%53%5A -PASS U+565B 噛 %1B%24%42%33%7A -PASS U+5664 噤 %1B%24%42%53%61 -PASS U+5668 器 %1B%24%42%34%6F -PASS U+566A 噪 %1B%24%42%53%64 -PASS U+566B 噫 %1B%24%42%53%60 -PASS U+566C 噬 %1B%24%42%53%63 -PASS U+5674 噴 %1B%24%42%4A%2E -PASS U+5678 噸 %1B%24%42%46%55 -PASS U+567A 噺 %1B%24%42%48%38 -PASS U+5680 嚀 %1B%24%42%53%66 -PASS U+5686 嚆 %1B%24%42%53%65 -PASS U+5687 嚇 %1B%24%42%33%45 -PASS U+568A 嚊 %1B%24%42%53%67 -PASS U+568F 嚏 %1B%24%42%53%6A -PASS U+5694 嚔 %1B%24%42%53%69 -PASS U+56A0 嚠 %1B%24%42%53%68 -PASS U+56A2 嚢 %1B%24%42%47%39 -PASS U+56A5 嚥 %1B%24%42%53%6B -PASS U+56AE 嚮 %1B%24%42%53%6C -PASS U+56B4 嚴 %1B%24%42%53%6E -PASS U+56B6 嚶 %1B%24%42%53%6D -PASS U+56BC 嚼 %1B%24%42%53%70 -PASS U+56C0 囀 %1B%24%42%53%73 -PASS U+56C1 囁 %1B%24%42%53%71 -PASS U+56C2 囂 %1B%24%42%53%6F -PASS U+56C3 囃 %1B%24%42%53%72 -PASS U+56C8 囈 %1B%24%42%53%74 -PASS U+56CE 囎 %1B%24%42%53%75 -PASS U+56D1 囑 %1B%24%42%53%76 -PASS U+56D3 囓 %1B%24%42%53%77 -PASS U+56D7 囗 %1B%24%42%53%78 -PASS U+56D8 囘 %1B%24%42%51%45 -PASS U+56DA 囚 %1B%24%42%3C%7C -PASS U+56DB 四 %1B%24%42%3B%4D -PASS U+56DE 回 %1B%24%42%32%73 -PASS U+56E0 因 %1B%24%42%30%78 -PASS U+56E3 団 %1B%24%42%43%44 -PASS U+56EE 囮 %1B%24%42%53%79 -PASS U+56F0 困 %1B%24%42%3A%24 -PASS U+56F2 囲 %1B%24%42%30%4F -PASS U+56F3 図 %1B%24%42%3F%5E -PASS U+56F9 囹 %1B%24%42%53%7A -PASS U+56FA 固 %1B%24%42%38%47 -PASS U+56FD 国 %1B%24%42%39%71 -PASS U+56FF 囿 %1B%24%42%53%7C -PASS U+5700 圀 %1B%24%42%53%7B -PASS U+5703 圃 %1B%24%42%4A%60 -PASS U+5704 圄 %1B%24%42%53%7D -PASS U+5708 圈 %1B%24%42%54%21 -PASS U+5709 圉 %1B%24%42%53%7E -PASS U+570B 國 %1B%24%42%54%22 -PASS U+570D 圍 %1B%24%42%54%23 -PASS U+570F 圏 %1B%24%42%37%77 -PASS U+5712 園 %1B%24%42%31%60 -PASS U+5713 圓 %1B%24%42%54%24 -PASS U+5716 圖 %1B%24%42%54%26 -PASS U+5718 團 %1B%24%42%54%25 -PASS U+571C 圜 %1B%24%42%54%28 -PASS U+571F 土 %1B%24%42%45%5A -PASS U+5726 圦 %1B%24%42%54%29 -PASS U+5727 圧 %1B%24%42%30%35 -PASS U+5728 在 %1B%24%42%3A%5F -PASS U+572D 圭 %1B%24%42%37%3D -PASS U+5730 地 %1B%24%42%43%4F -PASS U+5737 圷 %1B%24%42%54%2A -PASS U+5738 圸 %1B%24%42%54%2B -PASS U+573B 圻 %1B%24%42%54%2D -PASS U+5740 址 %1B%24%42%54%2E -PASS U+5742 坂 %1B%24%42%3A%64 -PASS U+5747 均 %1B%24%42%36%51 -PASS U+574A 坊 %1B%24%42%4B%37 -PASS U+574E 坎 %1B%24%42%54%2C -PASS U+574F 坏 %1B%24%42%54%2F -PASS U+5750 坐 %1B%24%42%3A%41 -PASS U+5751 坑 %1B%24%42%39%23 -FAIL U+5759 坙 %1B%24%42%79%5A assert_equals: expected "%1B%24%42%79%5A" but got "%26%23%32%32%33%36%31%3B" -PASS U+5761 坡 %1B%24%42%54%33 -PASS U+5764 坤 %1B%24%42%3A%25 -FAIL U+5765 坥 %1B%24%42%79%5B assert_equals: expected "%1B%24%42%79%5B" but got "%26%23%32%32%33%37%33%3B" -PASS U+5766 坦 %1B%24%42%43%33 -PASS U+5769 坩 %1B%24%42%54%30 -PASS U+576A 坪 %1B%24%42%44%5A -PASS U+577F 坿 %1B%24%42%54%34 -PASS U+5782 垂 %1B%24%42%3F%62 -PASS U+5788 垈 %1B%24%42%54%32 -PASS U+5789 垉 %1B%24%42%54%35 -PASS U+578B 型 %1B%24%42%37%3F -PASS U+5793 垓 %1B%24%42%54%36 -PASS U+57A0 垠 %1B%24%42%54%37 -PASS U+57A2 垢 %1B%24%42%39%24 -PASS U+57A3 垣 %1B%24%42%33%40 -PASS U+57A4 垤 %1B%24%42%54%39 -PASS U+57AA 垪 %1B%24%42%54%3A -FAIL U+57AC 垬 %1B%24%42%79%5C assert_equals: expected "%1B%24%42%79%5C" but got "%26%23%32%32%34%34%34%3B" -PASS U+57B0 垰 %1B%24%42%54%3B -PASS U+57B3 垳 %1B%24%42%54%38 -PASS U+57C0 埀 %1B%24%42%54%31 -PASS U+57C3 埃 %1B%24%42%54%3C -PASS U+57C6 埆 %1B%24%42%54%3D -FAIL U+57C7 埇 %1B%24%42%79%5E assert_equals: expected "%1B%24%42%79%5E" but got "%26%23%32%32%34%37%31%3B" -FAIL U+57C8 埈 %1B%24%42%79%5D assert_equals: expected "%1B%24%42%79%5D" but got "%26%23%32%32%34%37%32%3B" -PASS U+57CB 埋 %1B%24%42%4B%64 -PASS U+57CE 城 %1B%24%42%3E%6B -PASS U+57D2 埒 %1B%24%42%54%3F -PASS U+57D3 埓 %1B%24%42%54%40 -PASS U+57D4 埔 %1B%24%42%54%3E -PASS U+57D6 埖 %1B%24%42%54%42 -PASS U+57DC 埜 %1B%24%42%47%38 -PASS U+57DF 域 %1B%24%42%30%68 -PASS U+57E0 埠 %1B%24%42%49%56 -PASS U+57E3 埣 %1B%24%42%54%43 -PASS U+57F4 埴 %1B%24%42%3E%7D -PASS U+57F7 執 %1B%24%42%3C%39 -PASS U+57F9 培 %1B%24%42%47%5D -PASS U+57FA 基 %1B%24%42%34%70 -PASS U+57FC 埼 %1B%24%42%3A%6B -PASS U+5800 堀 %1B%24%42%4B%59 -PASS U+5802 堂 %1B%24%42%46%32 -PASS U+5805 堅 %1B%24%42%37%78 -PASS U+5806 堆 %1B%24%42%42%4F -PASS U+580A 堊 %1B%24%42%54%41 -PASS U+580B 堋 %1B%24%42%54%44 -PASS U+5815 堕 %1B%24%42%42%44 -PASS U+5819 堙 %1B%24%42%54%45 -PASS U+581D 堝 %1B%24%42%54%46 -PASS U+5821 堡 %1B%24%42%54%48 -PASS U+5824 堤 %1B%24%42%44%69 -PASS U+582A 堪 %1B%24%42%34%2E -PASS U+582F 堯 %1B%24%42%74%21 -PASS U+5830 堰 %1B%24%42%31%61 -PASS U+5831 報 %1B%24%42%4A%73 -PASS U+5834 場 %1B%24%42%3E%6C -PASS U+5835 堵 %1B%24%42%45%48 -PASS U+583A 堺 %1B%24%42%3A%66 -PASS U+583D 堽 %1B%24%42%54%4E -PASS U+5840 塀 %1B%24%42%4A%3D -PASS U+5841 塁 %1B%24%42%4E%5D -PASS U+584A 塊 %1B%24%42%32%74 -PASS U+584B 塋 %1B%24%42%54%4A -PASS U+5851 塑 %1B%24%42%41%3A -PASS U+5852 塒 %1B%24%42%54%4D -PASS U+5854 塔 %1B%24%42%45%63 -PASS U+5857 塗 %1B%24%42%45%49 -PASS U+5858 塘 %1B%24%42%45%64 -PASS U+5859 塙 %1B%24%42%48%39 -PASS U+585A 塚 %1B%24%42%44%4D -PASS U+585E 塞 %1B%24%42%3A%49 -PASS U+5862 塢 %1B%24%42%54%49 -PASS U+5869 塩 %1B%24%42%31%76 -PASS U+586B 填 %1B%24%42%45%36 -PASS U+5870 塰 %1B%24%42%54%4B -PASS U+5872 塲 %1B%24%42%54%47 -PASS U+5875 塵 %1B%24%42%3F%50 -PASS U+5879 塹 %1B%24%42%54%4F -PASS U+587E 塾 %1B%24%42%3D%4E -PASS U+5883 境 %1B%24%42%36%2D -PASS U+5885 墅 %1B%24%42%54%50 -PASS U+5893 墓 %1B%24%42%4A%68 -PASS U+5897 増 %1B%24%42%41%7D -PASS U+589C 墜 %1B%24%42%44%46 -FAIL U+589E 增 %1B%24%42%79%61 assert_equals: expected "%1B%24%42%79%61" but got "%26%23%32%32%36%38%36%3B" -PASS U+589F 墟 %1B%24%42%54%52 -PASS U+58A8 墨 %1B%24%42%4B%4F -PASS U+58AB 墫 %1B%24%42%54%53 -PASS U+58AE 墮 %1B%24%42%54%58 -FAIL U+58B2 墲 %1B%24%42%79%62 assert_equals: expected "%1B%24%42%79%62" but got "%26%23%32%32%37%30%36%3B" -PASS U+58B3 墳 %1B%24%42%4A%2F -PASS U+58B8 墸 %1B%24%42%54%57 -PASS U+58B9 墹 %1B%24%42%54%51 -PASS U+58BA 墺 %1B%24%42%54%54 -PASS U+58BB 墻 %1B%24%42%54%56 -PASS U+58BE 墾 %1B%24%42%3A%26 -PASS U+58C1 壁 %1B%24%42%4A%49 -PASS U+58C5 壅 %1B%24%42%54%59 -PASS U+58C7 壇 %1B%24%42%43%45 -PASS U+58CA 壊 %1B%24%42%32%75 -PASS U+58CC 壌 %1B%24%42%3E%6D -PASS U+58D1 壑 %1B%24%42%54%5B -PASS U+58D3 壓 %1B%24%42%54%5A -PASS U+58D5 壕 %1B%24%42%39%68 -PASS U+58D7 壗 %1B%24%42%54%5C -PASS U+58D8 壘 %1B%24%42%54%5E -PASS U+58D9 壙 %1B%24%42%54%5D -PASS U+58DC 壜 %1B%24%42%54%60 -PASS U+58DE 壞 %1B%24%42%54%55 -PASS U+58DF 壟 %1B%24%42%54%62 -PASS U+58E4 壤 %1B%24%42%54%61 -PASS U+58E5 壥 %1B%24%42%54%5F -PASS U+58EB 士 %1B%24%42%3B%4E -PASS U+58EC 壬 %1B%24%42%3F%51 -PASS U+58EE 壮 %1B%24%42%41%54 -PASS U+58EF 壯 %1B%24%42%54%63 -PASS U+58F0 声 %1B%24%42%40%3C -PASS U+58F1 壱 %1B%24%42%30%6D -PASS U+58F2 売 %1B%24%42%47%64 -PASS U+58F7 壷 %1B%24%42%44%5B -PASS U+58F9 壹 %1B%24%42%54%65 -PASS U+58FA 壺 %1B%24%42%54%64 -PASS U+58FB 壻 %1B%24%42%54%66 -PASS U+58FC 壼 %1B%24%42%54%67 -PASS U+58FD 壽 %1B%24%42%54%68 -PASS U+5902 夂 %1B%24%42%54%69 -PASS U+5909 変 %1B%24%42%4A%51 -PASS U+590A 夊 %1B%24%42%54%6A -FAIL U+590B 夋 %1B%24%42%79%63 assert_equals: expected "%1B%24%42%79%63" but got "%26%23%32%32%37%39%35%3B" -PASS U+590F 夏 %1B%24%42%32%46 -PASS U+5910 夐 %1B%24%42%54%6B -PASS U+5915 夕 %1B%24%42%4D%3C -PASS U+5916 外 %1B%24%42%33%30 -PASS U+5918 夘 %1B%24%42%52%49 -PASS U+5919 夙 %1B%24%42%3D%48 -PASS U+591A 多 %1B%24%42%42%3F -PASS U+591B 夛 %1B%24%42%54%6C -PASS U+591C 夜 %1B%24%42%4C%6B -PASS U+5922 夢 %1B%24%42%4C%34 -PASS U+5925 夥 %1B%24%42%54%6E -PASS U+5927 大 %1B%24%42%42%67 -PASS U+5929 天 %1B%24%42%45%37 -PASS U+592A 太 %1B%24%42%42%40 -PASS U+592B 夫 %1B%24%42%49%57 -PASS U+592C 夬 %1B%24%42%54%6F -PASS U+592D 夭 %1B%24%42%54%70 -PASS U+592E 央 %1B%24%42%31%7B -PASS U+5931 失 %1B%24%42%3C%3A -PASS U+5932 夲 %1B%24%42%54%71 -PASS U+5937 夷 %1B%24%42%30%50 -PASS U+5938 夸 %1B%24%42%54%72 -PASS U+593E 夾 %1B%24%42%54%73 -PASS U+5944 奄 %1B%24%42%31%62 -PASS U+5947 奇 %1B%24%42%34%71 -PASS U+5948 奈 %1B%24%42%46%60 -PASS U+5949 奉 %1B%24%42%4A%74 -PASS U+594E 奎 %1B%24%42%54%77 -PASS U+594F 奏 %1B%24%42%41%55 -PASS U+5950 奐 %1B%24%42%54%76 -PASS U+5951 契 %1B%24%42%37%40 -FAIL U+5953 奓 %1B%24%42%79%64 assert_equals: expected "%1B%24%42%79%64" but got "%26%23%32%32%38%36%37%3B" -PASS U+5954 奔 %1B%24%42%4B%5B -PASS U+5955 奕 %1B%24%42%54%75 -PASS U+5957 套 %1B%24%42%45%65 -PASS U+5958 奘 %1B%24%42%54%79 -PASS U+595A 奚 %1B%24%42%54%78 -FAIL U+595B 奛 %1B%24%42%79%65 assert_equals: expected "%1B%24%42%79%65" but got "%26%23%32%32%38%37%35%3B" -FAIL U+595D 奝 %1B%24%42%79%66 assert_equals: expected "%1B%24%42%79%66" but got "%26%23%32%32%38%37%37%3B" -PASS U+5960 奠 %1B%24%42%54%7B -PASS U+5962 奢 %1B%24%42%54%7A -FAIL U+5963 奣 %1B%24%42%79%67 assert_equals: expected "%1B%24%42%79%67" but got "%26%23%32%32%38%38%33%3B" -PASS U+5965 奥 %1B%24%42%31%7C -PASS U+5967 奧 %1B%24%42%54%7C -PASS U+5968 奨 %1B%24%42%3E%29 -PASS U+5969 奩 %1B%24%42%54%7E -PASS U+596A 奪 %1B%24%42%43%25 -PASS U+596C 奬 %1B%24%42%54%7D -PASS U+596E 奮 %1B%24%42%4A%33 -PASS U+5973 女 %1B%24%42%3D%77 -PASS U+5974 奴 %1B%24%42%45%5B -PASS U+5978 奸 %1B%24%42%55%21 -PASS U+597D 好 %1B%24%42%39%25 -PASS U+5981 妁 %1B%24%42%55%22 -PASS U+5982 如 %1B%24%42%47%21 -PASS U+5983 妃 %1B%24%42%48%5E -PASS U+5984 妄 %1B%24%42%4C%51 -PASS U+598A 妊 %1B%24%42%47%25 -PASS U+598D 妍 %1B%24%42%55%2B -PASS U+5993 妓 %1B%24%42%35%38 -PASS U+5996 妖 %1B%24%42%4D%45 -PASS U+5999 妙 %1B%24%42%4C%2F -PASS U+599B 妛 %1B%24%42%56%2C -PASS U+599D 妝 %1B%24%42%55%23 -PASS U+59A3 妣 %1B%24%42%55%26 -FAIL U+59A4 妤 %1B%24%42%79%68 assert_equals: expected "%1B%24%42%79%68" but got "%26%23%32%32%39%34%38%3B" -PASS U+59A5 妥 %1B%24%42%42%45 -PASS U+59A8 妨 %1B%24%42%4B%38 -PASS U+59AC 妬 %1B%24%42%45%4A -PASS U+59B2 妲 %1B%24%42%55%27 -PASS U+59B9 妹 %1B%24%42%4B%65 -FAIL U+59BA 妺 %1B%24%42%79%69 assert_equals: expected "%1B%24%42%79%69" but got "%26%23%32%32%39%37%30%3B" -PASS U+59BB 妻 %1B%24%42%3A%4A -PASS U+59BE 妾 %1B%24%42%3E%2A -PASS U+59C6 姆 %1B%24%42%55%28 -PASS U+59C9 姉 %1B%24%42%3B%50 -PASS U+59CB 始 %1B%24%42%3B%4F -PASS U+59D0 姐 %1B%24%42%30%39 -PASS U+59D1 姑 %1B%24%42%38%48 -PASS U+59D3 姓 %1B%24%42%40%2B -PASS U+59D4 委 %1B%24%42%30%51 -PASS U+59D9 姙 %1B%24%42%55%2C -PASS U+59DA 姚 %1B%24%42%55%2D -PASS U+59DC 姜 %1B%24%42%55%2A -PASS U+59E5 姥 %1B%24%42%31%38 -PASS U+59E6 姦 %1B%24%42%34%2F -PASS U+59E8 姨 %1B%24%42%55%29 -PASS U+59EA 姪 %1B%24%42%4C%45 -PASS U+59EB 姫 %1B%24%42%49%31 -PASS U+59F6 姶 %1B%24%42%30%28 -PASS U+59FB 姻 %1B%24%42%30%79 -PASS U+59FF 姿 %1B%24%42%3B%51 -PASS U+5A01 威 %1B%24%42%30%52 -PASS U+5A03 娃 %1B%24%42%30%23 -PASS U+5A09 娉 %1B%24%42%55%32 -PASS U+5A11 娑 %1B%24%42%55%30 -PASS U+5A18 娘 %1B%24%42%4C%3C -PASS U+5A1A 娚 %1B%24%42%55%33 -PASS U+5A1C 娜 %1B%24%42%55%31 -PASS U+5A1F 娟 %1B%24%42%55%2F -PASS U+5A20 娠 %1B%24%42%3F%31 -PASS U+5A25 娥 %1B%24%42%55%2E -PASS U+5A29 娩 %1B%24%42%4A%5A -PASS U+5A2F 娯 %1B%24%42%38%64 -PASS U+5A35 娵 %1B%24%42%55%37 -PASS U+5A36 娶 %1B%24%42%55%38 -PASS U+5A3C 娼 %1B%24%42%3E%2B -PASS U+5A40 婀 %1B%24%42%55%34 -PASS U+5A41 婁 %1B%24%42%4F%2C -PASS U+5A46 婆 %1B%24%42%47%4C -PASS U+5A49 婉 %1B%24%42%55%36 -PASS U+5A5A 婚 %1B%24%42%3A%27 -PASS U+5A62 婢 %1B%24%42%55%39 -PASS U+5A66 婦 %1B%24%42%49%58 -PASS U+5A6A 婪 %1B%24%42%55%3A -PASS U+5A6C 婬 %1B%24%42%55%35 -PASS U+5A7F 婿 %1B%24%42%4C%3B -PASS U+5A92 媒 %1B%24%42%47%5E -PASS U+5A9A 媚 %1B%24%42%55%3B -PASS U+5A9B 媛 %1B%24%42%49%32 -PASS U+5ABC 媼 %1B%24%42%55%3C -PASS U+5ABD 媽 %1B%24%42%55%40 -PASS U+5ABE 媾 %1B%24%42%55%3D -PASS U+5AC1 嫁 %1B%24%42%32%47 -PASS U+5AC2 嫂 %1B%24%42%55%3F -PASS U+5AC9 嫉 %1B%24%42%3C%3B -PASS U+5ACB 嫋 %1B%24%42%55%3E -PASS U+5ACC 嫌 %1B%24%42%37%79 -PASS U+5AD0 嫐 %1B%24%42%55%4C -PASS U+5AD6 嫖 %1B%24%42%55%45 -PASS U+5AD7 嫗 %1B%24%42%55%42 -PASS U+5AE1 嫡 %1B%24%42%43%64 -PASS U+5AE3 嫣 %1B%24%42%55%41 -PASS U+5AE6 嫦 %1B%24%42%55%43 -PASS U+5AE9 嫩 %1B%24%42%55%44 -PASS U+5AFA 嫺 %1B%24%42%55%46 -PASS U+5AFB 嫻 %1B%24%42%55%47 -PASS U+5B09 嬉 %1B%24%42%34%72 -PASS U+5B0B 嬋 %1B%24%42%55%49 -PASS U+5B0C 嬌 %1B%24%42%55%48 -PASS U+5B16 嬖 %1B%24%42%55%4A -PASS U+5B22 嬢 %1B%24%42%3E%6E -PASS U+5B2A 嬪 %1B%24%42%55%4D -PASS U+5B2C 嬬 %1B%24%42%44%5C -PASS U+5B30 嬰 %1B%24%42%31%45 -PASS U+5B32 嬲 %1B%24%42%55%4B -PASS U+5B36 嬶 %1B%24%42%55%4E -PASS U+5B3E 嬾 %1B%24%42%55%4F -PASS U+5B40 孀 %1B%24%42%55%52 -PASS U+5B43 孃 %1B%24%42%55%50 -PASS U+5B45 孅 %1B%24%42%55%51 -PASS U+5B50 子 %1B%24%42%3B%52 -PASS U+5B51 孑 %1B%24%42%55%53 -PASS U+5B54 孔 %1B%24%42%39%26 -PASS U+5B55 孕 %1B%24%42%55%54 -FAIL U+5B56 孖 %1B%24%42%79%6A assert_equals: expected "%1B%24%42%79%6A" but got "%26%23%32%33%33%38%32%3B" -PASS U+5B57 字 %1B%24%42%3B%7A -PASS U+5B58 存 %1B%24%42%42%38 -PASS U+5B5A 孚 %1B%24%42%55%55 -PASS U+5B5B 孛 %1B%24%42%55%56 -PASS U+5B5C 孜 %1B%24%42%3B%5A -PASS U+5B5D 孝 %1B%24%42%39%27 -PASS U+5B5F 孟 %1B%24%42%4C%52 -PASS U+5B63 季 %1B%24%42%35%28 -PASS U+5B64 孤 %1B%24%42%38%49 -PASS U+5B65 孥 %1B%24%42%55%57 -PASS U+5B66 学 %1B%24%42%33%58 -PASS U+5B69 孩 %1B%24%42%55%58 -PASS U+5B6B 孫 %1B%24%42%42%39 -PASS U+5B70 孰 %1B%24%42%55%59 -PASS U+5B71 孱 %1B%24%42%56%23 -PASS U+5B73 孳 %1B%24%42%55%5A -PASS U+5B75 孵 %1B%24%42%55%5B -PASS U+5B78 學 %1B%24%42%55%5C -PASS U+5B7A 孺 %1B%24%42%55%5E -PASS U+5B80 宀 %1B%24%42%55%5F -PASS U+5B83 它 %1B%24%42%55%60 -PASS U+5B85 宅 %1B%24%42%42%70 -PASS U+5B87 宇 %1B%24%42%31%27 -PASS U+5B88 守 %1B%24%42%3C%69 -PASS U+5B89 安 %1B%24%42%30%42 -PASS U+5B8B 宋 %1B%24%42%41%57 -PASS U+5B8C 完 %1B%24%42%34%30 -PASS U+5B8D 宍 %1B%24%42%3C%35 -PASS U+5B8F 宏 %1B%24%42%39%28 -PASS U+5B95 宕 %1B%24%42%45%66 -PASS U+5B97 宗 %1B%24%42%3D%21 -PASS U+5B98 官 %1B%24%42%34%31 -PASS U+5B99 宙 %1B%24%42%43%68 -PASS U+5B9A 定 %1B%24%42%44%6A -PASS U+5B9B 宛 %1B%24%42%30%38 -PASS U+5B9C 宜 %1B%24%42%35%39 -PASS U+5B9D 宝 %1B%24%42%4A%75 -PASS U+5B9F 実 %1B%24%42%3C%42 -PASS U+5BA2 客 %1B%24%42%35%52 -PASS U+5BA3 宣 %1B%24%42%40%6B -PASS U+5BA4 室 %1B%24%42%3C%3C -PASS U+5BA5 宥 %1B%24%42%4D%28 -PASS U+5BA6 宦 %1B%24%42%55%61 -PASS U+5BAE 宮 %1B%24%42%35%5C -PASS U+5BB0 宰 %1B%24%42%3A%4B -PASS U+5BB3 害 %1B%24%42%33%32 -PASS U+5BB4 宴 %1B%24%42%31%63 -PASS U+5BB5 宵 %1B%24%42%3E%2C -PASS U+5BB6 家 %1B%24%42%32%48 -PASS U+5BB8 宸 %1B%24%42%55%62 -PASS U+5BB9 容 %1B%24%42%4D%46 -PASS U+5BBF 宿 %1B%24%42%3D%49 -FAIL U+5BC0 寀 %1B%24%42%79%6B assert_equals: expected "%1B%24%42%79%6B" but got "%26%23%32%33%34%38%38%3B" -PASS U+5BC2 寂 %1B%24%42%3C%64 -PASS U+5BC3 寃 %1B%24%42%55%63 -PASS U+5BC4 寄 %1B%24%42%34%73 -PASS U+5BC5 寅 %1B%24%42%46%52 -PASS U+5BC6 密 %1B%24%42%4C%29 -PASS U+5BC7 寇 %1B%24%42%55%64 -PASS U+5BC9 寉 %1B%24%42%55%65 -PASS U+5BCC 富 %1B%24%42%49%59 -PASS U+5BD0 寐 %1B%24%42%55%67 -PASS U+5BD2 寒 %1B%24%42%34%28 -PASS U+5BD3 寓 %1B%24%42%36%77 -PASS U+5BD4 寔 %1B%24%42%55%66 -FAIL U+5BD8 寘 %1B%24%42%79%6D assert_equals: expected "%1B%24%42%79%6D" but got "%26%23%32%33%35%31%32%3B" -PASS U+5BDB 寛 %1B%24%42%34%32 -PASS U+5BDD 寝 %1B%24%42%3F%32 -PASS U+5BDE 寞 %1B%24%42%55%6B -PASS U+5BDF 察 %1B%24%42%3B%21 -PASS U+5BE1 寡 %1B%24%42%32%49 -PASS U+5BE2 寢 %1B%24%42%55%6A -PASS U+5BE4 寤 %1B%24%42%55%68 -PASS U+5BE5 寥 %1B%24%42%55%6C -PASS U+5BE6 實 %1B%24%42%55%69 -PASS U+5BE7 寧 %1B%24%42%47%2B -PASS U+5BE8 寨 %1B%24%42%5C%4D -PASS U+5BE9 審 %1B%24%42%3F%33 -PASS U+5BEB 寫 %1B%24%42%55%6D -FAIL U+5BEC 寬 %1B%24%42%79%6E assert_equals: expected "%1B%24%42%79%6E" but got "%26%23%32%33%35%33%32%3B" -PASS U+5BEE 寮 %1B%24%42%4E%40 -PASS U+5BF0 寰 %1B%24%42%55%6E -PASS U+5BF3 寳 %1B%24%42%55%70 -PASS U+5BF5 寵 %1B%24%42%43%7E -PASS U+5BF6 寶 %1B%24%42%55%6F -PASS U+5BF8 寸 %1B%24%42%40%23 -PASS U+5BFA 寺 %1B%24%42%3B%7B -PASS U+5BFE 対 %1B%24%42%42%50 -PASS U+5BFF 寿 %1B%24%42%3C%77 -PASS U+5C01 封 %1B%24%42%49%75 -PASS U+5C02 専 %1B%24%42%40%6C -PASS U+5C04 射 %1B%24%42%3C%4D -PASS U+5C05 尅 %1B%24%42%55%71 -PASS U+5C06 将 %1B%24%42%3E%2D -PASS U+5C07 將 %1B%24%42%55%72 -PASS U+5C08 專 %1B%24%42%55%73 -PASS U+5C09 尉 %1B%24%42%30%53 -PASS U+5C0A 尊 %1B%24%42%42%3A -PASS U+5C0B 尋 %1B%24%42%3F%52 -PASS U+5C0D 對 %1B%24%42%55%74 -PASS U+5C0E 導 %1B%24%42%46%33 -PASS U+5C0F 小 %1B%24%42%3E%2E -PASS U+5C11 少 %1B%24%42%3E%2F -PASS U+5C13 尓 %1B%24%42%55%75 -PASS U+5C16 尖 %1B%24%42%40%6D -PASS U+5C1A 尚 %1B%24%42%3E%30 -FAIL U+5C1E 尞 %1B%24%42%79%6F assert_equals: expected "%1B%24%42%79%6F" but got "%26%23%32%33%35%38%32%3B" -PASS U+5C20 尠 %1B%24%42%55%76 -PASS U+5C22 尢 %1B%24%42%55%77 -PASS U+5C24 尤 %1B%24%42%4C%60 -PASS U+5C28 尨 %1B%24%42%55%78 -PASS U+5C2D 尭 %1B%24%42%36%46 -PASS U+5C31 就 %1B%24%42%3D%22 -PASS U+5C38 尸 %1B%24%42%55%79 -PASS U+5C39 尹 %1B%24%42%55%7A -PASS U+5C3A 尺 %1B%24%42%3C%5C -PASS U+5C3B 尻 %1B%24%42%3F%2C -PASS U+5C3C 尼 %1B%24%42%46%74 -PASS U+5C3D 尽 %1B%24%42%3F%54 -PASS U+5C3E 尾 %1B%24%42%48%78 -PASS U+5C3F 尿 %1B%24%42%47%22 -PASS U+5C40 局 %1B%24%42%36%49 -PASS U+5C41 屁 %1B%24%42%55%7B -PASS U+5C45 居 %1B%24%42%35%6F -PASS U+5C46 屆 %1B%24%42%55%7C -PASS U+5C48 屈 %1B%24%42%36%7E -PASS U+5C4A 届 %1B%24%42%46%4F -PASS U+5C4B 屋 %1B%24%42%32%30 -PASS U+5C4D 屍 %1B%24%42%3B%53 -PASS U+5C4E 屎 %1B%24%42%55%7D -PASS U+5C4F 屏 %1B%24%42%56%22 -PASS U+5C50 屐 %1B%24%42%56%21 -PASS U+5C51 屑 %1B%24%42%36%7D -PASS U+5C53 屓 %1B%24%42%55%7E -PASS U+5C55 展 %1B%24%42%45%38 -PASS U+5C5E 属 %1B%24%42%42%30 -PASS U+5C60 屠 %1B%24%42%45%4B -PASS U+5C61 屡 %1B%24%42%3C%48 -PASS U+5C64 層 %1B%24%42%41%58 -PASS U+5C65 履 %1B%24%42%4D%7A -PASS U+5C6C 屬 %1B%24%42%56%24 -PASS U+5C6E 屮 %1B%24%42%56%25 -PASS U+5C6F 屯 %1B%24%42%46%56 -PASS U+5C71 山 %1B%24%42%3B%33 -PASS U+5C76 屶 %1B%24%42%56%27 -PASS U+5C79 屹 %1B%24%42%56%28 -PASS U+5C8C 岌 %1B%24%42%56%29 -PASS U+5C90 岐 %1B%24%42%34%74 -PASS U+5C91 岑 %1B%24%42%56%2A -PASS U+5C94 岔 %1B%24%42%56%2B -PASS U+5CA1 岡 %1B%24%42%32%2C -FAIL U+5CA6 岦 %1B%24%42%79%70 assert_equals: expected "%1B%24%42%79%70" but got "%26%23%32%33%37%31%38%3B" -PASS U+5CA8 岨 %1B%24%42%41%3B -PASS U+5CA9 岩 %1B%24%42%34%64 -PASS U+5CAB 岫 %1B%24%42%56%2D -PASS U+5CAC 岬 %1B%24%42%4C%28 -PASS U+5CB1 岱 %1B%24%42%42%52 -PASS U+5CB3 岳 %1B%24%42%33%59 -PASS U+5CB6 岶 %1B%24%42%56%2F -PASS U+5CB7 岷 %1B%24%42%56%31 -PASS U+5CB8 岸 %1B%24%42%34%5F -FAIL U+5CBA 岺 %1B%24%42%79%71 assert_equals: expected "%1B%24%42%79%71" but got "%26%23%32%33%37%33%38%3B" -PASS U+5CBB 岻 %1B%24%42%56%2E -PASS U+5CBC 岼 %1B%24%42%56%30 -PASS U+5CBE 岾 %1B%24%42%56%33 -PASS U+5CC5 峅 %1B%24%42%56%32 -PASS U+5CC7 峇 %1B%24%42%56%34 -PASS U+5CD9 峙 %1B%24%42%56%35 -PASS U+5CE0 峠 %1B%24%42%46%3D -PASS U+5CE1 峡 %1B%24%42%36%2E -PASS U+5CE8 峨 %1B%24%42%32%65 -PASS U+5CE9 峩 %1B%24%42%56%36 -PASS U+5CEA 峪 %1B%24%42%56%3B -PASS U+5CED 峭 %1B%24%42%56%39 -PASS U+5CEF 峯 %1B%24%42%4A%77 -PASS U+5CF0 峰 %1B%24%42%4A%76 -FAIL U+5CF5 峵 %1B%24%42%79%72 assert_equals: expected "%1B%24%42%79%72" but got "%26%23%32%33%37%39%37%3B" -PASS U+5CF6 島 %1B%24%42%45%67 -PASS U+5CFA 峺 %1B%24%42%56%38 -PASS U+5CFB 峻 %1B%24%42%3D%54 -PASS U+5CFD 峽 %1B%24%42%56%37 -PASS U+5D07 崇 %1B%24%42%3F%72 -PASS U+5D0B 崋 %1B%24%42%56%3C -PASS U+5D0E 崎 %1B%24%42%3A%6A -PASS U+5D11 崑 %1B%24%42%56%42 -PASS U+5D14 崔 %1B%24%42%56%43 -PASS U+5D15 崕 %1B%24%42%56%3D -PASS U+5D16 崖 %1B%24%42%33%33 -PASS U+5D17 崗 %1B%24%42%56%3E -PASS U+5D18 崘 %1B%24%42%56%47 -PASS U+5D19 崙 %1B%24%42%56%46 -PASS U+5D1A 崚 %1B%24%42%56%45 -PASS U+5D1B 崛 %1B%24%42%56%41 -PASS U+5D1F 崟 %1B%24%42%56%40 -PASS U+5D22 崢 %1B%24%42%56%44 -FAIL U+5D27 崧 %1B%24%42%79%73 assert_equals: expected "%1B%24%42%79%73" but got "%26%23%32%33%38%34%37%3B" -PASS U+5D29 崩 %1B%24%42%4A%78 -FAIL U+5D42 嵂 %1B%24%42%79%76 assert_equals: expected "%1B%24%42%79%76" but got "%26%23%32%33%38%37%34%3B" -PASS U+5D4B 嵋 %1B%24%42%56%4B -PASS U+5D4C 嵌 %1B%24%42%56%48 -PASS U+5D4E 嵎 %1B%24%42%56%4A -PASS U+5D50 嵐 %1B%24%42%4D%72 -PASS U+5D52 嵒 %1B%24%42%56%49 -FAIL U+5D53 嵓 %1B%24%42%79%74 assert_equals: expected "%1B%24%42%79%74" but got "%26%23%32%33%38%39%31%3B" -PASS U+5D5C 嵜 %1B%24%42%56%3F -PASS U+5D69 嵩 %1B%24%42%3F%73 -PASS U+5D6C 嵬 %1B%24%42%56%4C -FAIL U+5D6D 嵭 %1B%24%42%79%77 assert_equals: expected "%1B%24%42%79%77" but got "%26%23%32%33%39%31%37%3B" -PASS U+5D6F 嵯 %1B%24%42%3A%37 -PASS U+5D73 嵳 %1B%24%42%56%4D -PASS U+5D76 嵶 %1B%24%42%56%4E -PASS U+5D82 嶂 %1B%24%42%56%51 -PASS U+5D84 嶄 %1B%24%42%56%50 -PASS U+5D87 嶇 %1B%24%42%56%4F -PASS U+5D8B 嶋 %1B%24%42%45%68 -PASS U+5D8C 嶌 %1B%24%42%56%3A -PASS U+5D90 嶐 %1B%24%42%56%57 -PASS U+5D9D 嶝 %1B%24%42%56%53 -PASS U+5DA2 嶢 %1B%24%42%56%52 -PASS U+5DAC 嶬 %1B%24%42%56%54 -PASS U+5DAE 嶮 %1B%24%42%56%55 -PASS U+5DB7 嶷 %1B%24%42%56%58 -FAIL U+5DB8 嶸 %1B%24%42%79%78 assert_equals: expected "%1B%24%42%79%78" but got "%26%23%32%33%39%39%32%3B" -FAIL U+5DB9 嶹 %1B%24%42%79%79 assert_equals: expected "%1B%24%42%79%79" but got "%26%23%32%33%39%39%33%3B" -PASS U+5DBA 嶺 %1B%24%42%4E%66 -PASS U+5DBC 嶼 %1B%24%42%56%59 -PASS U+5DBD 嶽 %1B%24%42%56%56 -PASS U+5DC9 巉 %1B%24%42%56%5A -PASS U+5DCC 巌 %1B%24%42%34%60 -PASS U+5DCD 巍 %1B%24%42%56%5B -FAIL U+5DD0 巐 %1B%24%42%79%7A assert_equals: expected "%1B%24%42%79%7A" but got "%26%23%32%34%30%31%36%3B" -PASS U+5DD2 巒 %1B%24%42%56%5D -PASS U+5DD3 巓 %1B%24%42%56%5C -PASS U+5DD6 巖 %1B%24%42%56%5E -PASS U+5DDB 巛 %1B%24%42%56%5F -PASS U+5DDD 川 %1B%24%42%40%6E -PASS U+5DDE 州 %1B%24%42%3D%23 -PASS U+5DE1 巡 %1B%24%42%3D%64 -PASS U+5DE3 巣 %1B%24%42%41%63 -PASS U+5DE5 工 %1B%24%42%39%29 -PASS U+5DE6 左 %1B%24%42%3A%38 -PASS U+5DE7 巧 %1B%24%42%39%2A -PASS U+5DE8 巨 %1B%24%42%35%70 -PASS U+5DEB 巫 %1B%24%42%56%60 -PASS U+5DEE 差 %1B%24%42%3A%39 -PASS U+5DF1 己 %1B%24%42%38%4A -PASS U+5DF2 已 %1B%24%42%56%61 -PASS U+5DF3 巳 %1B%24%42%4C%26 -PASS U+5DF4 巴 %1B%24%42%47%43 -PASS U+5DF5 巵 %1B%24%42%56%62 -PASS U+5DF7 巷 %1B%24%42%39%2B -PASS U+5DFB 巻 %1B%24%42%34%2C -PASS U+5DFD 巽 %1B%24%42%43%27 -PASS U+5DFE 巾 %1B%24%42%36%52 -PASS U+5E02 市 %1B%24%42%3B%54 -PASS U+5E03 布 %1B%24%42%49%5B -PASS U+5E06 帆 %1B%24%42%48%41 -PASS U+5E0B 帋 %1B%24%42%56%63 -PASS U+5E0C 希 %1B%24%42%34%75 -PASS U+5E11 帑 %1B%24%42%56%66 -PASS U+5E16 帖 %1B%24%42%44%21 -PASS U+5E19 帙 %1B%24%42%56%65 -PASS U+5E1A 帚 %1B%24%42%56%64 -PASS U+5E1B 帛 %1B%24%42%56%67 -PASS U+5E1D 帝 %1B%24%42%44%6B -PASS U+5E25 帥 %1B%24%42%3F%63 -PASS U+5E2B 師 %1B%24%42%3B%55 -PASS U+5E2D 席 %1B%24%42%40%4A -PASS U+5E2F 帯 %1B%24%42%42%53 -PASS U+5E30 帰 %1B%24%42%35%22 -PASS U+5E33 帳 %1B%24%42%44%22 -PASS U+5E36 帶 %1B%24%42%56%68 -PASS U+5E37 帷 %1B%24%42%56%69 -PASS U+5E38 常 %1B%24%42%3E%6F -PASS U+5E3D 帽 %1B%24%42%4B%39 -PASS U+5E40 幀 %1B%24%42%56%6C -PASS U+5E43 幃 %1B%24%42%56%6B -PASS U+5E44 幄 %1B%24%42%56%6A -PASS U+5E45 幅 %1B%24%42%49%7D -PASS U+5E47 幇 %1B%24%42%56%73 -PASS U+5E4C 幌 %1B%24%42%4B%5A -PASS U+5E4E 幎 %1B%24%42%56%6D -PASS U+5E54 幔 %1B%24%42%56%6F -PASS U+5E55 幕 %1B%24%42%4B%6B -PASS U+5E57 幗 %1B%24%42%56%6E -PASS U+5E5F 幟 %1B%24%42%56%70 -PASS U+5E61 幡 %1B%24%42%48%28 -PASS U+5E62 幢 %1B%24%42%56%71 -PASS U+5E63 幣 %1B%24%42%4A%3E -PASS U+5E64 幤 %1B%24%42%56%72 -PASS U+5E72 干 %1B%24%42%34%33 -PASS U+5E73 平 %1B%24%42%4A%3F -PASS U+5E74 年 %1B%24%42%47%2F -PASS U+5E75 幵 %1B%24%42%56%74 -PASS U+5E76 并 %1B%24%42%56%75 -PASS U+5E78 幸 %1B%24%42%39%2C -PASS U+5E79 幹 %1B%24%42%34%34 -PASS U+5E7A 幺 %1B%24%42%56%76 -PASS U+5E7B 幻 %1B%24%42%38%38 -PASS U+5E7C 幼 %1B%24%42%4D%44 -PASS U+5E7D 幽 %1B%24%42%4D%29 -PASS U+5E7E 幾 %1B%24%42%34%76 -PASS U+5E7F 广 %1B%24%42%56%78 -PASS U+5E81 庁 %1B%24%42%44%23 -PASS U+5E83 広 %1B%24%42%39%2D -PASS U+5E84 庄 %1B%24%42%3E%31 -PASS U+5E87 庇 %1B%24%42%48%5F -PASS U+5E8A 床 %1B%24%42%3E%32 -PASS U+5E8F 序 %1B%24%42%3D%78 -PASS U+5E95 底 %1B%24%42%44%6C -PASS U+5E96 庖 %1B%24%42%4A%79 -PASS U+5E97 店 %1B%24%42%45%39 -PASS U+5E9A 庚 %1B%24%42%39%2E -PASS U+5E9C 府 %1B%24%42%49%5C -PASS U+5EA0 庠 %1B%24%42%56%79 -PASS U+5EA6 度 %1B%24%42%45%59 -PASS U+5EA7 座 %1B%24%42%3A%42 -PASS U+5EAB 庫 %1B%24%42%38%4B -PASS U+5EAD 庭 %1B%24%42%44%6D -PASS U+5EB5 庵 %1B%24%42%30%43 -PASS U+5EB6 庶 %1B%24%42%3D%6E -PASS U+5EB7 康 %1B%24%42%39%2F -PASS U+5EB8 庸 %1B%24%42%4D%47 -PASS U+5EC1 廁 %1B%24%42%56%7A -PASS U+5EC2 廂 %1B%24%42%56%7B -PASS U+5EC3 廃 %1B%24%42%47%51 -PASS U+5EC8 廈 %1B%24%42%56%7C -PASS U+5EC9 廉 %1B%24%42%4E%77 -PASS U+5ECA 廊 %1B%24%42%4F%2D -PASS U+5ECF 廏 %1B%24%42%56%7E -PASS U+5ED0 廐 %1B%24%42%56%7D -PASS U+5ED3 廓 %1B%24%42%33%47 -PASS U+5ED6 廖 %1B%24%42%57%21 -PASS U+5EDA 廚 %1B%24%42%57%24 -PASS U+5EDB 廛 %1B%24%42%57%25 -PASS U+5EDD 廝 %1B%24%42%57%23 -PASS U+5EDF 廟 %1B%24%42%49%40 -PASS U+5EE0 廠 %1B%24%42%3E%33 -PASS U+5EE1 廡 %1B%24%42%57%27 -PASS U+5EE2 廢 %1B%24%42%57%26 -PASS U+5EE3 廣 %1B%24%42%57%22 -PASS U+5EE8 廨 %1B%24%42%57%28 -PASS U+5EE9 廩 %1B%24%42%57%29 -PASS U+5EEC 廬 %1B%24%42%57%2A -PASS U+5EF0 廰 %1B%24%42%57%2D -PASS U+5EF1 廱 %1B%24%42%57%2B -PASS U+5EF3 廳 %1B%24%42%57%2C -PASS U+5EF4 廴 %1B%24%42%57%2E -PASS U+5EF6 延 %1B%24%42%31%64 -PASS U+5EF7 廷 %1B%24%42%44%6E -PASS U+5EF8 廸 %1B%24%42%57%2F -PASS U+5EFA 建 %1B%24%42%37%7A -PASS U+5EFB 廻 %1B%24%42%32%76 -PASS U+5EFC 廼 %1B%24%42%47%36 -PASS U+5EFE 廾 %1B%24%42%57%30 -PASS U+5EFF 廿 %1B%24%42%46%7B -PASS U+5F01 弁 %1B%24%42%4A%5B -PASS U+5F03 弃 %1B%24%42%57%31 -PASS U+5F04 弄 %1B%24%42%4F%2E -PASS U+5F09 弉 %1B%24%42%57%32 -PASS U+5F0A 弊 %1B%24%42%4A%40 -PASS U+5F0B 弋 %1B%24%42%57%35 -PASS U+5F0C 弌 %1B%24%42%50%21 -PASS U+5F0D 弍 %1B%24%42%50%31 -PASS U+5F0F 式 %1B%24%42%3C%30 -PASS U+5F10 弐 %1B%24%42%46%75 -PASS U+5F11 弑 %1B%24%42%57%36 -PASS U+5F13 弓 %1B%24%42%35%5D -PASS U+5F14 弔 %1B%24%42%44%24 -PASS U+5F15 引 %1B%24%42%30%7A -PASS U+5F16 弖 %1B%24%42%57%37 -PASS U+5F17 弗 %1B%24%42%4A%26 -PASS U+5F18 弘 %1B%24%42%39%30 -PASS U+5F1B 弛 %1B%24%42%43%50 -PASS U+5F1F 弟 %1B%24%42%44%6F -FAIL U+5F21 弡 %1B%24%42%79%7B assert_equals: expected "%1B%24%42%79%7B" but got "%26%23%32%34%33%35%33%3B" -PASS U+5F25 弥 %1B%24%42%4C%6F -PASS U+5F26 弦 %1B%24%42%38%39 -PASS U+5F27 弧 %1B%24%42%38%4C -PASS U+5F29 弩 %1B%24%42%57%38 -PASS U+5F2D 弭 %1B%24%42%57%39 -PASS U+5F2F 弯 %1B%24%42%57%3F -PASS U+5F31 弱 %1B%24%42%3C%65 -FAIL U+5F34 弴 %1B%24%42%79%7C assert_equals: expected "%1B%24%42%79%7C" but got "%26%23%32%34%33%37%32%3B" -PASS U+5F35 張 %1B%24%42%44%25 -PASS U+5F37 強 %1B%24%42%36%2F -PASS U+5F38 弸 %1B%24%42%57%3A -PASS U+5F3C 弼 %1B%24%42%49%2B -PASS U+5F3E 弾 %1B%24%42%43%46 -PASS U+5F41 彁 %1B%24%42%57%3B -FAIL U+5F45 彅 %1B%24%42%79%2C assert_equals: expected "%1B%24%42%79%2C" but got "%26%23%32%34%33%38%39%3B" -PASS U+5F48 彈 %1B%24%42%57%3C -PASS U+5F4A 彊 %1B%24%42%36%30 -PASS U+5F4C 彌 %1B%24%42%57%3D -PASS U+5F4E 彎 %1B%24%42%57%3E -PASS U+5F51 彑 %1B%24%42%57%40 -PASS U+5F53 当 %1B%24%42%45%76 -PASS U+5F56 彖 %1B%24%42%57%41 -PASS U+5F57 彗 %1B%24%42%57%42 -PASS U+5F59 彙 %1B%24%42%57%43 -PASS U+5F5C 彜 %1B%24%42%57%34 -PASS U+5F5D 彝 %1B%24%42%57%33 -PASS U+5F61 彡 %1B%24%42%57%44 -PASS U+5F62 形 %1B%24%42%37%41 -PASS U+5F66 彦 %1B%24%42%49%27 -FAIL U+5F67 彧 %1B%24%42%79%7D assert_equals: expected "%1B%24%42%79%7D" but got "%26%23%32%34%34%32%33%3B" -PASS U+5F69 彩 %1B%24%42%3A%4C -PASS U+5F6A 彪 %1B%24%42%49%37 -PASS U+5F6B 彫 %1B%24%42%44%26 -PASS U+5F6C 彬 %1B%24%42%49%4B -PASS U+5F6D 彭 %1B%24%42%57%45 -PASS U+5F70 彰 %1B%24%42%3E%34 -PASS U+5F71 影 %1B%24%42%31%46 -PASS U+5F73 彳 %1B%24%42%57%46 -PASS U+5F77 彷 %1B%24%42%57%47 -PASS U+5F79 役 %1B%24%42%4C%72 -PASS U+5F7C 彼 %1B%24%42%48%60 -PASS U+5F7F 彿 %1B%24%42%57%4A -PASS U+5F80 往 %1B%24%42%31%7D -PASS U+5F81 征 %1B%24%42%40%2C -PASS U+5F82 徂 %1B%24%42%57%49 -PASS U+5F83 徃 %1B%24%42%57%48 -PASS U+5F84 径 %1B%24%42%37%42 -PASS U+5F85 待 %1B%24%42%42%54 -PASS U+5F87 徇 %1B%24%42%57%4E -PASS U+5F88 很 %1B%24%42%57%4C -PASS U+5F8A 徊 %1B%24%42%57%4B -PASS U+5F8B 律 %1B%24%42%4E%27 -PASS U+5F8C 後 %1B%24%42%38%65 -PASS U+5F90 徐 %1B%24%42%3D%79 -PASS U+5F91 徑 %1B%24%42%57%4D -PASS U+5F92 徒 %1B%24%42%45%4C -PASS U+5F93 従 %1B%24%42%3D%3E -PASS U+5F97 得 %1B%24%42%46%40 -PASS U+5F98 徘 %1B%24%42%57%51 -PASS U+5F99 徙 %1B%24%42%57%50 -PASS U+5F9E 從 %1B%24%42%57%4F -PASS U+5FA0 徠 %1B%24%42%57%52 -PASS U+5FA1 御 %1B%24%42%38%66 -PASS U+5FA8 徨 %1B%24%42%57%53 -PASS U+5FA9 復 %1B%24%42%49%7C -PASS U+5FAA 循 %1B%24%42%3D%5B -PASS U+5FAD 徭 %1B%24%42%57%54 -PASS U+5FAE 微 %1B%24%42%48%79 -PASS U+5FB3 徳 %1B%24%42%46%41 -PASS U+5FB4 徴 %1B%24%42%44%27 -FAIL U+5FB7 德 %1B%24%42%79%7E assert_equals: expected "%1B%24%42%79%7E" but got "%26%23%32%34%35%30%33%3B" -PASS U+5FB9 徹 %1B%24%42%45%30 -PASS U+5FBC 徼 %1B%24%42%57%55 -PASS U+5FBD 徽 %1B%24%42%35%2B -PASS U+5FC3 心 %1B%24%42%3F%34 -PASS U+5FC5 必 %1B%24%42%49%2C -PASS U+5FCC 忌 %1B%24%42%34%77 -PASS U+5FCD 忍 %1B%24%42%47%26 -PASS U+5FD6 忖 %1B%24%42%57%56 -PASS U+5FD7 志 %1B%24%42%3B%56 -PASS U+5FD8 忘 %1B%24%42%4B%3A -PASS U+5FD9 忙 %1B%24%42%4B%3B -PASS U+5FDC 応 %1B%24%42%31%7E -PASS U+5FDD 忝 %1B%24%42%57%5B -FAIL U+5FDE 忞 %1B%24%42%7A%21 assert_equals: expected "%1B%24%42%7A%21" but got "%26%23%32%34%35%34%32%3B" -PASS U+5FE0 忠 %1B%24%42%43%69 -PASS U+5FE4 忤 %1B%24%42%57%58 -PASS U+5FEB 快 %1B%24%42%32%77 -PASS U+5FF0 忰 %1B%24%42%58%2D -PASS U+5FF1 忱 %1B%24%42%57%5A -PASS U+5FF5 念 %1B%24%42%47%30 -PASS U+5FF8 忸 %1B%24%42%57%59 -PASS U+5FFB 忻 %1B%24%42%57%57 -PASS U+5FFD 忽 %1B%24%42%39%7A -PASS U+5FFF 忿 %1B%24%42%57%5D -PASS U+600E 怎 %1B%24%42%57%63 -PASS U+600F 怏 %1B%24%42%57%69 -PASS U+6010 怐 %1B%24%42%57%61 -PASS U+6012 怒 %1B%24%42%45%5C -PASS U+6015 怕 %1B%24%42%57%66 -PASS U+6016 怖 %1B%24%42%49%5D -PASS U+6019 怙 %1B%24%42%57%60 -PASS U+601B 怛 %1B%24%42%57%65 -PASS U+601C 怜 %1B%24%42%4E%67 -PASS U+601D 思 %1B%24%42%3B%57 -PASS U+6020 怠 %1B%24%42%42%55 -PASS U+6021 怡 %1B%24%42%57%5E -PASS U+6025 急 %1B%24%42%35%5E -PASS U+6026 怦 %1B%24%42%57%68 -PASS U+6027 性 %1B%24%42%40%2D -PASS U+6028 怨 %1B%24%42%31%65 -PASS U+6029 怩 %1B%24%42%57%62 -PASS U+602A 怪 %1B%24%42%32%78 -PASS U+602B 怫 %1B%24%42%57%67 -PASS U+602F 怯 %1B%24%42%36%31 -PASS U+6031 怱 %1B%24%42%57%64 -PASS U+603A 怺 %1B%24%42%57%6A -PASS U+6041 恁 %1B%24%42%57%6C -PASS U+6042 恂 %1B%24%42%57%76 -PASS U+6043 恃 %1B%24%42%57%74 -PASS U+6046 恆 %1B%24%42%57%71 -PASS U+604A 恊 %1B%24%42%57%70 -PASS U+604B 恋 %1B%24%42%4E%78 -PASS U+604D 恍 %1B%24%42%57%72 -PASS U+6050 恐 %1B%24%42%36%32 -PASS U+6052 恒 %1B%24%42%39%31 -PASS U+6055 恕 %1B%24%42%3D%7A -PASS U+6059 恙 %1B%24%42%57%79 -PASS U+605A 恚 %1B%24%42%57%6B -FAIL U+605D 恝 %1B%24%42%7A%22 assert_equals: expected "%1B%24%42%7A%22" but got "%26%23%32%34%36%36%39%3B" -PASS U+605F 恟 %1B%24%42%57%6F -PASS U+6060 恠 %1B%24%42%57%5F -PASS U+6062 恢 %1B%24%42%32%7A -PASS U+6063 恣 %1B%24%42%57%73 -PASS U+6064 恤 %1B%24%42%57%75 -PASS U+6065 恥 %1B%24%42%43%51 -PASS U+6068 恨 %1B%24%42%3A%28 -PASS U+6069 恩 %1B%24%42%32%38 -PASS U+606A 恪 %1B%24%42%57%6D -PASS U+606B 恫 %1B%24%42%57%78 -PASS U+606C 恬 %1B%24%42%57%77 -PASS U+606D 恭 %1B%24%42%36%33 -PASS U+606F 息 %1B%24%42%42%29 -PASS U+6070 恰 %1B%24%42%33%66 -PASS U+6075 恵 %1B%24%42%37%43 -PASS U+6077 恷 %1B%24%42%57%6E -PASS U+6081 悁 %1B%24%42%57%7A -PASS U+6083 悃 %1B%24%42%57%7D -PASS U+6084 悄 %1B%24%42%58%21 -FAIL U+6085 悅 %1B%24%42%7A%23 assert_equals: expected "%1B%24%42%7A%23" but got "%26%23%32%34%37%30%39%3B" -PASS U+6089 悉 %1B%24%42%3C%3D -FAIL U+608A 悊 %1B%24%42%7A%24 assert_equals: expected "%1B%24%42%7A%24" but got "%26%23%32%34%37%31%34%3B" -PASS U+608B 悋 %1B%24%42%58%27 -PASS U+608C 悌 %1B%24%42%44%70 -PASS U+608D 悍 %1B%24%42%57%7B -PASS U+6092 悒 %1B%24%42%58%25 -PASS U+6094 悔 %1B%24%42%32%79 -PASS U+6096 悖 %1B%24%42%58%23 -PASS U+6097 悗 %1B%24%42%58%24 -PASS U+609A 悚 %1B%24%42%57%7E -PASS U+609B 悛 %1B%24%42%58%22 -PASS U+609F 悟 %1B%24%42%38%67 -PASS U+60A0 悠 %1B%24%42%4D%2A -PASS U+60A3 患 %1B%24%42%34%35 -PASS U+60A6 悦 %1B%24%42%31%59 -PASS U+60A7 悧 %1B%24%42%58%26 -PASS U+60A9 悩 %1B%24%42%47%3A -PASS U+60AA 悪 %1B%24%42%30%2D -PASS U+60B2 悲 %1B%24%42%48%61 -PASS U+60B3 悳 %1B%24%42%57%5C -PASS U+60B4 悴 %1B%24%42%58%2C -PASS U+60B5 悵 %1B%24%42%58%30 -PASS U+60B6 悶 %1B%24%42%4C%65 -PASS U+60B8 悸 %1B%24%42%58%29 -PASS U+60BC 悼 %1B%24%42%45%69 -PASS U+60BD 悽 %1B%24%42%58%2E -PASS U+60C5 情 %1B%24%42%3E%70 -PASS U+60C6 惆 %1B%24%42%58%2F -PASS U+60C7 惇 %1B%24%42%46%57 -PASS U+60D1 惑 %1B%24%42%4F%47 -PASS U+60D3 惓 %1B%24%42%58%2B -FAIL U+60D5 惕 %1B%24%42%7A%26 assert_equals: expected "%1B%24%42%7A%26" but got "%26%23%32%34%37%38%39%3B" -PASS U+60D8 惘 %1B%24%42%58%31 -PASS U+60DA 惚 %1B%24%42%39%7B -PASS U+60DC 惜 %1B%24%42%40%4B -FAIL U+60DE 惞 %1B%24%42%7A%25 assert_equals: expected "%1B%24%42%7A%25" but got "%26%23%32%34%37%39%38%3B" -PASS U+60DF 惟 %1B%24%42%30%54 -PASS U+60E0 惠 %1B%24%42%58%2A -PASS U+60E1 惡 %1B%24%42%58%28 -PASS U+60E3 惣 %1B%24%42%41%5A -PASS U+60E7 惧 %1B%24%42%57%7C -PASS U+60E8 惨 %1B%24%42%3B%34 -PASS U+60F0 惰 %1B%24%42%42%46 -PASS U+60F1 惱 %1B%24%42%58%3D -FAIL U+60F2 惲 %1B%24%42%7A%28 assert_equals: expected "%1B%24%42%7A%28" but got "%26%23%32%34%38%31%38%3B" -PASS U+60F3 想 %1B%24%42%41%5B -PASS U+60F4 惴 %1B%24%42%58%38 -PASS U+60F6 惶 %1B%24%42%58%35 -PASS U+60F7 惷 %1B%24%42%58%36 -PASS U+60F9 惹 %1B%24%42%3C%66 -PASS U+60FA 惺 %1B%24%42%58%39 -PASS U+60FB 惻 %1B%24%42%58%3C -PASS U+6100 愀 %1B%24%42%58%37 -PASS U+6101 愁 %1B%24%42%3D%25 -PASS U+6103 愃 %1B%24%42%58%3A -PASS U+6106 愆 %1B%24%42%58%34 -PASS U+6108 愈 %1B%24%42%4C%7C -PASS U+6109 愉 %1B%24%42%4C%7B -PASS U+610D 愍 %1B%24%42%58%3E -PASS U+610E 愎 %1B%24%42%58%3F -PASS U+610F 意 %1B%24%42%30%55 -FAIL U+6111 愑 %1B%24%42%7A%29 assert_equals: expected "%1B%24%42%7A%29" but got "%26%23%32%34%38%34%39%3B" -PASS U+6115 愕 %1B%24%42%58%33 -PASS U+611A 愚 %1B%24%42%36%72 -PASS U+611B 愛 %1B%24%42%30%26 -PASS U+611F 感 %1B%24%42%34%36 -FAIL U+6120 愠 %1B%24%42%7A%27 assert_equals: expected "%1B%24%42%7A%27" but got "%26%23%32%34%38%36%34%3B" -PASS U+6121 愡 %1B%24%42%58%3B -PASS U+6127 愧 %1B%24%42%58%43 -PASS U+6128 愨 %1B%24%42%58%42 -PASS U+612C 愬 %1B%24%42%58%47 -FAIL U+6130 愰 %1B%24%42%7A%2B assert_equals: expected "%1B%24%42%7A%2B" but got "%26%23%32%34%38%38%30%3B" -PASS U+6134 愴 %1B%24%42%58%48 -FAIL U+6137 愷 %1B%24%42%7A%2A assert_equals: expected "%1B%24%42%7A%2A" but got "%26%23%32%34%38%38%37%3B" -PASS U+613C 愼 %1B%24%42%58%46 -PASS U+613D 愽 %1B%24%42%58%49 -PASS U+613E 愾 %1B%24%42%58%41 -PASS U+613F 愿 %1B%24%42%58%45 -PASS U+6142 慂 %1B%24%42%58%4A -PASS U+6144 慄 %1B%24%42%58%4B -PASS U+6147 慇 %1B%24%42%58%40 -PASS U+6148 慈 %1B%24%42%3B%7C -PASS U+614A 慊 %1B%24%42%58%44 -PASS U+614B 態 %1B%24%42%42%56 -PASS U+614C 慌 %1B%24%42%39%32 -PASS U+614D 慍 %1B%24%42%58%32 -PASS U+614E 慎 %1B%24%42%3F%35 -PASS U+6153 慓 %1B%24%42%58%58 -PASS U+6155 慕 %1B%24%42%4A%69 -PASS U+6158 慘 %1B%24%42%58%4E -PASS U+6159 慙 %1B%24%42%58%4F -PASS U+615A 慚 %1B%24%42%58%50 -PASS U+615D 慝 %1B%24%42%58%57 -PASS U+615F 慟 %1B%24%42%58%56 -PASS U+6162 慢 %1B%24%42%4B%7D -PASS U+6163 慣 %1B%24%42%34%37 -PASS U+6165 慥 %1B%24%42%58%54 -PASS U+6167 慧 %1B%24%42%37%45 -PASS U+6168 慨 %1B%24%42%33%34 -PASS U+616B 慫 %1B%24%42%58%51 -PASS U+616E 慮 %1B%24%42%4E%38 -PASS U+616F 慯 %1B%24%42%58%53 -PASS U+6170 慰 %1B%24%42%30%56 -PASS U+6171 慱 %1B%24%42%58%55 -PASS U+6173 慳 %1B%24%42%58%4C -PASS U+6174 慴 %1B%24%42%58%52 -PASS U+6175 慵 %1B%24%42%58%59 -PASS U+6176 慶 %1B%24%42%37%44 -PASS U+6177 慷 %1B%24%42%58%4D -PASS U+617E 慾 %1B%24%42%4D%5D -PASS U+6182 憂 %1B%24%42%4D%2B -PASS U+6187 憇 %1B%24%42%58%5C -PASS U+618A 憊 %1B%24%42%58%60 -PASS U+618E 憎 %1B%24%42%41%7E -PASS U+6190 憐 %1B%24%42%4E%79 -PASS U+6191 憑 %1B%24%42%58%61 -PASS U+6194 憔 %1B%24%42%58%5E -PASS U+6196 憖 %1B%24%42%58%5B -FAIL U+6198 憘 %1B%24%42%7A%2C assert_equals: expected "%1B%24%42%7A%2C" but got "%26%23%32%34%39%38%34%3B" -PASS U+6199 憙 %1B%24%42%58%5A -PASS U+619A 憚 %1B%24%42%58%5F -PASS U+61A4 憤 %1B%24%42%4A%30 -PASS U+61A7 憧 %1B%24%42%46%34 -PASS U+61A9 憩 %1B%24%42%37%46 -PASS U+61AB 憫 %1B%24%42%58%62 -PASS U+61AC 憬 %1B%24%42%58%5D -PASS U+61AE 憮 %1B%24%42%58%63 -PASS U+61B2 憲 %1B%24%42%37%7B -PASS U+61B6 憶 %1B%24%42%32%31 -PASS U+61BA 憺 %1B%24%42%58%6B -PASS U+61BE 憾 %1B%24%42%34%38 -PASS U+61C3 懃 %1B%24%42%58%69 -PASS U+61C6 懆 %1B%24%42%58%6A -PASS U+61C7 懇 %1B%24%42%3A%29 -PASS U+61C8 懈 %1B%24%42%58%68 -PASS U+61C9 應 %1B%24%42%58%66 -PASS U+61CA 懊 %1B%24%42%58%65 -PASS U+61CB 懋 %1B%24%42%58%6C -PASS U+61CC 懌 %1B%24%42%58%64 -PASS U+61CD 懍 %1B%24%42%58%6E -PASS U+61D0 懐 %1B%24%42%32%7B -PASS U+61E3 懣 %1B%24%42%58%70 -PASS U+61E6 懦 %1B%24%42%58%6F -PASS U+61F2 懲 %1B%24%42%44%28 -PASS U+61F4 懴 %1B%24%42%58%73 -PASS U+61F6 懶 %1B%24%42%58%71 -PASS U+61F7 懷 %1B%24%42%58%67 -PASS U+61F8 懸 %1B%24%42%37%7C -PASS U+61FA 懺 %1B%24%42%58%72 -PASS U+61FC 懼 %1B%24%42%58%76 -PASS U+61FD 懽 %1B%24%42%58%75 -PASS U+61FE 懾 %1B%24%42%58%77 -PASS U+61FF 懿 %1B%24%42%58%74 -PASS U+6200 戀 %1B%24%42%58%78 -PASS U+6208 戈 %1B%24%42%58%79 -PASS U+6209 戉 %1B%24%42%58%7A -PASS U+620A 戊 %1B%24%42%4A%6A -PASS U+620C 戌 %1B%24%42%58%7C -PASS U+620D 戍 %1B%24%42%58%7B -PASS U+620E 戎 %1B%24%42%3D%3F -PASS U+6210 成 %1B%24%42%40%2E -PASS U+6211 我 %1B%24%42%32%66 -PASS U+6212 戒 %1B%24%42%32%7C -FAIL U+6213 戓 %1B%24%42%7A%2D assert_equals: expected "%1B%24%42%7A%2D" but got "%26%23%32%35%31%30%37%3B" -PASS U+6214 戔 %1B%24%42%58%7D -PASS U+6216 或 %1B%24%42%30%3F -PASS U+621A 戚 %1B%24%42%40%4C -PASS U+621B 戛 %1B%24%42%58%7E -PASS U+621D 戝 %1B%24%42%6C%43 -PASS U+621E 戞 %1B%24%42%59%21 -PASS U+621F 戟 %1B%24%42%37%61 -PASS U+6221 戡 %1B%24%42%59%22 -PASS U+6226 戦 %1B%24%42%40%6F -PASS U+622A 截 %1B%24%42%59%23 -PASS U+622E 戮 %1B%24%42%59%24 -PASS U+622F 戯 %1B%24%42%35%3A -PASS U+6230 戰 %1B%24%42%59%25 -PASS U+6232 戲 %1B%24%42%59%26 -PASS U+6233 戳 %1B%24%42%59%27 -PASS U+6234 戴 %1B%24%42%42%57 -PASS U+6238 戸 %1B%24%42%38%4D -PASS U+623B 戻 %1B%24%42%4C%61 -PASS U+623F 房 %1B%24%42%4B%3C -PASS U+6240 所 %1B%24%42%3D%6A -PASS U+6241 扁 %1B%24%42%59%28 -PASS U+6247 扇 %1B%24%42%40%70 -PASS U+6248 扈 %1B%24%42%6E%3D -PASS U+6249 扉 %1B%24%42%48%62 -PASS U+624B 手 %1B%24%42%3C%6A -PASS U+624D 才 %1B%24%42%3A%4D -PASS U+624E 扎 %1B%24%42%59%29 -PASS U+6253 打 %1B%24%42%42%47 -PASS U+6255 払 %1B%24%42%4A%27 -PASS U+6258 托 %1B%24%42%42%71 -PASS U+625B 扛 %1B%24%42%59%2C -PASS U+625E 扞 %1B%24%42%59%2A -PASS U+6260 扠 %1B%24%42%59%2D -PASS U+6263 扣 %1B%24%42%59%2B -PASS U+6268 扨 %1B%24%42%59%2E -PASS U+626E 扮 %1B%24%42%4A%31 -PASS U+6271 扱 %1B%24%42%30%37 -PASS U+6276 扶 %1B%24%42%49%5E -PASS U+6279 批 %1B%24%42%48%63 -PASS U+627C 扼 %1B%24%42%59%2F -PASS U+627E 找 %1B%24%42%59%32 -PASS U+627F 承 %1B%24%42%3E%35 -PASS U+6280 技 %1B%24%42%35%3B -PASS U+6282 抂 %1B%24%42%59%30 -PASS U+6283 抃 %1B%24%42%59%37 -PASS U+6284 抄 %1B%24%42%3E%36 -PASS U+6289 抉 %1B%24%42%59%31 -PASS U+628A 把 %1B%24%42%47%44 -PASS U+6291 抑 %1B%24%42%4D%5E -PASS U+6292 抒 %1B%24%42%59%33 -PASS U+6293 抓 %1B%24%42%59%34 -PASS U+6294 抔 %1B%24%42%59%38 -PASS U+6295 投 %1B%24%42%45%6A -PASS U+6296 抖 %1B%24%42%59%35 -PASS U+6297 抗 %1B%24%42%39%33 -PASS U+6298 折 %1B%24%42%40%5E -PASS U+629B 抛 %1B%24%42%59%46 -PASS U+629C 抜 %1B%24%42%48%34 -PASS U+629E 択 %1B%24%42%42%72 -FAIL U+62A6 抦 %1B%24%42%7A%2E assert_equals: expected "%1B%24%42%7A%2E" but got "%26%23%32%35%32%35%34%3B" -PASS U+62AB 披 %1B%24%42%48%64 -PASS U+62AC 抬 %1B%24%42%5A%2D -PASS U+62B1 抱 %1B%24%42%4A%7A -PASS U+62B5 抵 %1B%24%42%44%71 -PASS U+62B9 抹 %1B%24%42%4B%75 -PASS U+62BB 抻 %1B%24%42%59%3B -PASS U+62BC 押 %1B%24%42%32%21 -PASS U+62BD 抽 %1B%24%42%43%6A -PASS U+62C2 拂 %1B%24%42%59%44 -PASS U+62C5 担 %1B%24%42%43%34 -PASS U+62C6 拆 %1B%24%42%59%3E -PASS U+62C7 拇 %1B%24%42%59%45 -PASS U+62C8 拈 %1B%24%42%59%40 -PASS U+62C9 拉 %1B%24%42%59%47 -PASS U+62CA 拊 %1B%24%42%59%43 -PASS U+62CC 拌 %1B%24%42%59%42 -PASS U+62CD 拍 %1B%24%42%47%6F -PASS U+62CF 拏 %1B%24%42%59%3C -PASS U+62D0 拐 %1B%24%42%32%7D -PASS U+62D1 拑 %1B%24%42%59%3A -PASS U+62D2 拒 %1B%24%42%35%71 -PASS U+62D3 拓 %1B%24%42%42%73 -PASS U+62D4 拔 %1B%24%42%59%36 -PASS U+62D7 拗 %1B%24%42%59%39 -PASS U+62D8 拘 %1B%24%42%39%34 -PASS U+62D9 拙 %1B%24%42%40%5B -PASS U+62DB 招 %1B%24%42%3E%37 -PASS U+62DC 拜 %1B%24%42%59%41 -PASS U+62DD 拝 %1B%24%42%47%52 -PASS U+62E0 拠 %1B%24%42%35%72 -PASS U+62E1 拡 %1B%24%42%33%48 -PASS U+62EC 括 %1B%24%42%33%67 -PASS U+62ED 拭 %1B%24%42%3F%21 -PASS U+62EE 拮 %1B%24%42%59%49 -PASS U+62EF 拯 %1B%24%42%59%4E -PASS U+62F1 拱 %1B%24%42%59%4A -PASS U+62F3 拳 %1B%24%42%37%7D -PASS U+62F5 拵 %1B%24%42%59%4F -PASS U+62F6 拶 %1B%24%42%3B%22 -PASS U+62F7 拷 %1B%24%42%39%69 -PASS U+62FE 拾 %1B%24%42%3D%26 -PASS U+62FF 拿 %1B%24%42%59%3D -PASS U+6301 持 %1B%24%42%3B%7D -PASS U+6302 挂 %1B%24%42%59%4C -PASS U+6307 指 %1B%24%42%3B%58 -PASS U+6308 挈 %1B%24%42%59%4D -PASS U+6309 按 %1B%24%42%30%44 -PASS U+630C 挌 %1B%24%42%59%48 -PASS U+6311 挑 %1B%24%42%44%29 -PASS U+6319 挙 %1B%24%42%35%73 -PASS U+631F 挟 %1B%24%42%36%34 -PASS U+6327 挧 %1B%24%42%59%4B -PASS U+6328 挨 %1B%24%42%30%27 -PASS U+632B 挫 %1B%24%42%3A%43 -PASS U+632F 振 %1B%24%42%3F%36 -PASS U+633A 挺 %1B%24%42%44%72 -PASS U+633D 挽 %1B%24%42%48%54 -PASS U+633E 挾 %1B%24%42%59%51 -PASS U+633F 挿 %1B%24%42%41%5E -PASS U+6349 捉 %1B%24%42%42%2A -PASS U+634C 捌 %1B%24%42%3B%2B -PASS U+634D 捍 %1B%24%42%59%52 -PASS U+634F 捏 %1B%24%42%59%54 -PASS U+6350 捐 %1B%24%42%59%50 -PASS U+6355 捕 %1B%24%42%4A%61 -PASS U+6357 捗 %1B%24%42%44%3D -PASS U+635C 捜 %1B%24%42%41%5C -PASS U+6367 捧 %1B%24%42%4A%7B -PASS U+6368 捨 %1B%24%42%3C%4E -PASS U+6369 捩 %1B%24%42%59%60 -PASS U+636B 捫 %1B%24%42%59%5F -PASS U+636E 据 %1B%24%42%3F%78 -PASS U+6372 捲 %1B%24%42%37%7E -PASS U+6376 捶 %1B%24%42%59%59 -PASS U+6377 捷 %1B%24%42%3E%39 -PASS U+637A 捺 %1B%24%42%46%68 -PASS U+637B 捻 %1B%24%42%47%31 -PASS U+6380 掀 %1B%24%42%59%57 -PASS U+6383 掃 %1B%24%42%41%5D -PASS U+6388 授 %1B%24%42%3C%78 -PASS U+6389 掉 %1B%24%42%59%5C -PASS U+638C 掌 %1B%24%42%3E%38 -PASS U+638E 掎 %1B%24%42%59%56 -PASS U+638F 掏 %1B%24%42%59%5B -PASS U+6392 排 %1B%24%42%47%53 -PASS U+6396 掖 %1B%24%42%59%55 -PASS U+6398 掘 %1B%24%42%37%21 -PASS U+639B 掛 %1B%24%42%33%5D -PASS U+639F 掟 %1B%24%42%59%5D -PASS U+63A0 掠 %1B%24%42%4E%2B -PASS U+63A1 採 %1B%24%42%3A%4E -PASS U+63A2 探 %1B%24%42%43%35 -PASS U+63A3 掣 %1B%24%42%59%5A -PASS U+63A5 接 %1B%24%42%40%5C -PASS U+63A7 控 %1B%24%42%39%35 -PASS U+63A8 推 %1B%24%42%3F%64 -PASS U+63A9 掩 %1B%24%42%31%66 -PASS U+63AA 措 %1B%24%42%41%3C -PASS U+63AB 掫 %1B%24%42%59%58 -PASS U+63AC 掬 %1B%24%42%35%45 -PASS U+63B2 掲 %1B%24%42%37%47 -PASS U+63B4 掴 %1B%24%42%44%4F -PASS U+63B5 掵 %1B%24%42%59%5E -PASS U+63BB 掻 %1B%24%42%41%5F -PASS U+63BE 掾 %1B%24%42%59%61 -PASS U+63C0 揀 %1B%24%42%59%63 -PASS U+63C3 揃 %1B%24%42%42%37 -PASS U+63C4 揄 %1B%24%42%59%69 -PASS U+63C6 揆 %1B%24%42%59%64 -PASS U+63C9 揉 %1B%24%42%59%66 -PASS U+63CF 描 %1B%24%42%49%41 -PASS U+63D0 提 %1B%24%42%44%73 -PASS U+63D2 插 %1B%24%42%59%67 -PASS U+63D6 揖 %1B%24%42%4D%2C -PASS U+63DA 揚 %1B%24%42%4D%48 -PASS U+63DB 換 %1B%24%42%34%39 -PASS U+63E1 握 %1B%24%42%30%2E -PASS U+63E3 揣 %1B%24%42%59%65 -PASS U+63E9 揩 %1B%24%42%59%62 -PASS U+63EE 揮 %1B%24%42%34%78 -PASS U+63F4 援 %1B%24%42%31%67 -FAIL U+63F5 揵 %1B%24%42%7A%2F assert_equals: expected "%1B%24%42%7A%2F" but got "%26%23%32%35%35%38%39%3B" -PASS U+63F6 揶 %1B%24%42%59%68 -PASS U+63FA 揺 %1B%24%42%4D%49 -PASS U+6406 搆 %1B%24%42%59%6C -PASS U+640D 損 %1B%24%42%42%3B -PASS U+640F 搏 %1B%24%42%59%73 -PASS U+6413 搓 %1B%24%42%59%6D -PASS U+6416 搖 %1B%24%42%59%6A -PASS U+6417 搗 %1B%24%42%59%71 -PASS U+641C 搜 %1B%24%42%59%53 -PASS U+6426 搦 %1B%24%42%59%6E -PASS U+6428 搨 %1B%24%42%59%72 -PASS U+642C 搬 %1B%24%42%48%42 -PASS U+642D 搭 %1B%24%42%45%6B -PASS U+6434 搴 %1B%24%42%59%6B -PASS U+6436 搶 %1B%24%42%59%6F -PASS U+643A 携 %1B%24%42%37%48 -PASS U+643E 搾 %1B%24%42%3A%71 -PASS U+6442 摂 %1B%24%42%40%5D -PASS U+644E 摎 %1B%24%42%59%77 -PASS U+6458 摘 %1B%24%42%45%26 -FAIL U+6460 摠 %1B%24%42%7A%30 assert_equals: expected "%1B%24%42%7A%30" but got "%26%23%32%35%36%39%36%3B" -PASS U+6467 摧 %1B%24%42%59%74 -PASS U+6469 摩 %1B%24%42%4B%60 -PASS U+646F 摯 %1B%24%42%59%75 -PASS U+6476 摶 %1B%24%42%59%76 -PASS U+6478 摸 %1B%24%42%4C%4E -PASS U+647A 摺 %1B%24%42%40%22 -PASS U+6483 撃 %1B%24%42%37%62 -PASS U+6488 撈 %1B%24%42%59%7D -PASS U+6492 撒 %1B%24%42%3B%35 -PASS U+6493 撓 %1B%24%42%59%7A -PASS U+6495 撕 %1B%24%42%59%79 -PASS U+649A 撚 %1B%24%42%47%32 -FAIL U+649D 撝 %1B%24%42%7A%31 assert_equals: expected "%1B%24%42%7A%31" but got "%26%23%32%35%37%35%37%3B" -PASS U+649E 撞 %1B%24%42%46%35 -PASS U+64A4 撤 %1B%24%42%45%31 -PASS U+64A5 撥 %1B%24%42%59%7B -PASS U+64A9 撩 %1B%24%42%59%7C -PASS U+64AB 撫 %1B%24%42%49%6F -PASS U+64AD 播 %1B%24%42%47%45 -PASS U+64AE 撮 %1B%24%42%3B%23 -PASS U+64B0 撰 %1B%24%42%40%71 -PASS U+64B2 撲 %1B%24%42%4B%50 -PASS U+64B9 撹 %1B%24%42%33%49 -PASS U+64BB 撻 %1B%24%42%5A%25 -PASS U+64BC 撼 %1B%24%42%59%7E -PASS U+64C1 擁 %1B%24%42%4D%4A -PASS U+64C2 擂 %1B%24%42%5A%27 -PASS U+64C5 擅 %1B%24%42%5A%23 -PASS U+64C7 擇 %1B%24%42%5A%24 -PASS U+64CD 操 %1B%24%42%41%60 -FAIL U+64CE 擎 %1B%24%42%7A%32 assert_equals: expected "%1B%24%42%7A%32" but got "%26%23%32%35%38%30%36%3B" -PASS U+64D2 擒 %1B%24%42%5A%22 -PASS U+64D4 擔 %1B%24%42%59%3F -PASS U+64D8 擘 %1B%24%42%5A%26 -PASS U+64DA 據 %1B%24%42%5A%21 -PASS U+64E0 擠 %1B%24%42%5A%2B -PASS U+64E1 擡 %1B%24%42%5A%2C -PASS U+64E2 擢 %1B%24%42%45%27 -PASS U+64E3 擣 %1B%24%42%5A%2E -PASS U+64E6 擦 %1B%24%42%3B%24 -PASS U+64E7 擧 %1B%24%42%5A%29 -PASS U+64EC 擬 %1B%24%42%35%3C -PASS U+64EF 擯 %1B%24%42%5A%2F -PASS U+64F1 擱 %1B%24%42%5A%28 -PASS U+64F2 擲 %1B%24%42%5A%33 -PASS U+64F4 擴 %1B%24%42%5A%32 -PASS U+64F6 擶 %1B%24%42%5A%31 -PASS U+64FA 擺 %1B%24%42%5A%34 -PASS U+64FD 擽 %1B%24%42%5A%36 -PASS U+64FE 擾 %1B%24%42%3E%71 -PASS U+6500 攀 %1B%24%42%5A%35 -PASS U+6505 攅 %1B%24%42%5A%39 -PASS U+6518 攘 %1B%24%42%5A%37 -PASS U+651C 攜 %1B%24%42%5A%38 -PASS U+651D 攝 %1B%24%42%59%70 -PASS U+6523 攣 %1B%24%42%5A%3B -PASS U+6524 攤 %1B%24%42%5A%3A -PASS U+652A 攪 %1B%24%42%59%78 -PASS U+652B 攫 %1B%24%42%5A%3C -PASS U+652C 攬 %1B%24%42%5A%30 -PASS U+652F 支 %1B%24%42%3B%59 -PASS U+6534 攴 %1B%24%42%5A%3D -PASS U+6535 攵 %1B%24%42%5A%3E -PASS U+6536 收 %1B%24%42%5A%40 -PASS U+6537 攷 %1B%24%42%5A%3F -PASS U+6538 攸 %1B%24%42%5A%41 -PASS U+6539 改 %1B%24%42%32%7E -PASS U+653B 攻 %1B%24%42%39%36 -PASS U+653E 放 %1B%24%42%4A%7C -PASS U+653F 政 %1B%24%42%40%2F -PASS U+6545 故 %1B%24%42%38%4E -PASS U+6548 效 %1B%24%42%5A%43 -PASS U+654D 敍 %1B%24%42%5A%46 -FAIL U+654E 敎 %1B%24%42%7A%33 assert_equals: expected "%1B%24%42%7A%33" but got "%26%23%32%35%39%33%34%3B" -PASS U+654F 敏 %1B%24%42%49%52 -PASS U+6551 救 %1B%24%42%35%5F -PASS U+6555 敕 %1B%24%42%5A%45 -PASS U+6556 敖 %1B%24%42%5A%44 -PASS U+6557 敗 %1B%24%42%47%54 -PASS U+6558 敘 %1B%24%42%5A%47 -PASS U+6559 教 %1B%24%42%36%35 -PASS U+655D 敝 %1B%24%42%5A%49 -PASS U+655E 敞 %1B%24%42%5A%48 -PASS U+6562 敢 %1B%24%42%34%3A -PASS U+6563 散 %1B%24%42%3B%36 -PASS U+6566 敦 %1B%24%42%46%58 -PASS U+656C 敬 %1B%24%42%37%49 -PASS U+6570 数 %1B%24%42%3F%74 -PASS U+6572 敲 %1B%24%42%5A%4A -PASS U+6574 整 %1B%24%42%40%30 -PASS U+6575 敵 %1B%24%42%45%28 -PASS U+6577 敷 %1B%24%42%49%5F -PASS U+6578 數 %1B%24%42%5A%4B -PASS U+6582 斂 %1B%24%42%5A%4C -PASS U+6583 斃 %1B%24%42%5A%4D -PASS U+6587 文 %1B%24%42%4A%38 -PASS U+6588 斈 %1B%24%42%55%5D -PASS U+6589 斉 %1B%24%42%40%46 -PASS U+658C 斌 %1B%24%42%49%4C -PASS U+658E 斎 %1B%24%42%3A%58 -PASS U+6590 斐 %1B%24%42%48%65 -PASS U+6591 斑 %1B%24%42%48%43 -PASS U+6597 斗 %1B%24%42%45%4D -PASS U+6599 料 %1B%24%42%4E%41 -PASS U+659B 斛 %1B%24%42%5A%4F -PASS U+659C 斜 %1B%24%42%3C%50 -PASS U+659F 斟 %1B%24%42%5A%50 -PASS U+65A1 斡 %1B%24%42%30%36 -PASS U+65A4 斤 %1B%24%42%36%54 -PASS U+65A5 斥 %1B%24%42%40%4D -PASS U+65A7 斧 %1B%24%42%49%60 -PASS U+65AB 斫 %1B%24%42%5A%51 -PASS U+65AC 斬 %1B%24%42%3B%42 -PASS U+65AD 断 %1B%24%42%43%47 -PASS U+65AF 斯 %1B%24%42%3B%5B -PASS U+65B0 新 %1B%24%42%3F%37 -PASS U+65B7 斷 %1B%24%42%5A%52 -PASS U+65B9 方 %1B%24%42%4A%7D -PASS U+65BC 於 %1B%24%42%31%77 -PASS U+65BD 施 %1B%24%42%3B%5C -PASS U+65C1 旁 %1B%24%42%5A%55 -PASS U+65C3 旃 %1B%24%42%5A%53 -PASS U+65C4 旄 %1B%24%42%5A%56 -PASS U+65C5 旅 %1B%24%42%4E%39 -PASS U+65C6 旆 %1B%24%42%5A%54 -PASS U+65CB 旋 %1B%24%42%40%7B -PASS U+65CC 旌 %1B%24%42%5A%57 -PASS U+65CF 族 %1B%24%42%42%32 -PASS U+65D2 旒 %1B%24%42%5A%58 -PASS U+65D7 旗 %1B%24%42%34%7A -PASS U+65D9 旙 %1B%24%42%5A%5A -PASS U+65DB 旛 %1B%24%42%5A%59 -PASS U+65E0 无 %1B%24%42%5A%5B -PASS U+65E1 旡 %1B%24%42%5A%5C -PASS U+65E2 既 %1B%24%42%34%7B -PASS U+65E5 日 %1B%24%42%46%7C -PASS U+65E6 旦 %1B%24%42%43%36 -PASS U+65E7 旧 %1B%24%42%35%6C -PASS U+65E8 旨 %1B%24%42%3B%5D -PASS U+65E9 早 %1B%24%42%41%61 -PASS U+65EC 旬 %1B%24%42%3D%5C -PASS U+65ED 旭 %1B%24%42%30%30 -PASS U+65F1 旱 %1B%24%42%5A%5D -PASS U+65FA 旺 %1B%24%42%32%22 -PASS U+65FB 旻 %1B%24%42%5A%61 -FAIL U+6600 昀 %1B%24%42%7A%34 assert_equals: expected "%1B%24%42%7A%34" but got "%26%23%32%36%31%31%32%3B" -PASS U+6602 昂 %1B%24%42%39%37 -PASS U+6603 昃 %1B%24%42%5A%60 -PASS U+6606 昆 %1B%24%42%3A%2B -PASS U+6607 昇 %1B%24%42%3E%3A -FAIL U+6609 昉 %1B%24%42%7A%37 assert_equals: expected "%1B%24%42%7A%37" but got "%26%23%32%36%31%32%31%3B" -PASS U+660A 昊 %1B%24%42%5A%5F -PASS U+660C 昌 %1B%24%42%3E%3B -PASS U+660E 明 %1B%24%42%4C%40 -PASS U+660F 昏 %1B%24%42%3A%2A -PASS U+6613 易 %1B%24%42%30%57 -PASS U+6614 昔 %1B%24%42%40%4E -FAIL U+6615 昕 %1B%24%42%7A%35 assert_equals: expected "%1B%24%42%7A%35" but got "%26%23%32%36%31%33%33%3B" -PASS U+661C 昜 %1B%24%42%5A%66 -FAIL U+661E 昞 %1B%24%42%7A%39 assert_equals: expected "%1B%24%42%7A%39" but got "%26%23%32%36%31%34%32%3B" -PASS U+661F 星 %1B%24%42%40%31 -PASS U+6620 映 %1B%24%42%31%47 -FAIL U+6624 昤 %1B%24%42%7A%3A assert_equals: expected "%1B%24%42%7A%3A" but got "%26%23%32%36%31%34%38%3B" -PASS U+6625 春 %1B%24%42%3D%55 -PASS U+6627 昧 %1B%24%42%4B%66 -PASS U+6628 昨 %1B%24%42%3A%72 -PASS U+662D 昭 %1B%24%42%3E%3C -FAIL U+662E 昮 %1B%24%42%7A%38 assert_equals: expected "%1B%24%42%7A%38" but got "%26%23%32%36%31%35%38%3B" -PASS U+662F 是 %1B%24%42%40%27 -FAIL U+6631 昱 %1B%24%42%79%28 assert_equals: expected "%1B%24%42%79%28" but got "%26%23%32%36%31%36%31%3B" -PASS U+6634 昴 %1B%24%42%5A%65 -PASS U+6635 昵 %1B%24%42%5A%63 -PASS U+6636 昶 %1B%24%42%5A%64 -FAIL U+663B 昻 %1B%24%42%7A%36 assert_equals: expected "%1B%24%42%7A%36" but got "%26%23%32%36%31%37%31%3B" -PASS U+663C 昼 %1B%24%42%43%6B -PASS U+663F 昿 %1B%24%42%5B%26 -PASS U+6641 晁 %1B%24%42%5A%6A -PASS U+6642 時 %1B%24%42%3B%7E -PASS U+6643 晃 %1B%24%42%39%38 -PASS U+6644 晄 %1B%24%42%5A%68 -PASS U+6649 晉 %1B%24%42%5A%69 -PASS U+664B 晋 %1B%24%42%3F%38 -PASS U+664F 晏 %1B%24%42%5A%67 -PASS U+6652 晒 %1B%24%42%3B%2F -FAIL U+6657 晗 %1B%24%42%7A%3C assert_equals: expected "%1B%24%42%7A%3C" but got "%26%23%32%36%31%39%39%3B" -FAIL U+6659 晙 %1B%24%42%7A%3D assert_equals: expected "%1B%24%42%7A%3D" but got "%26%23%32%36%32%30%31%3B" -PASS U+665D 晝 %1B%24%42%5A%6C -PASS U+665E 晞 %1B%24%42%5A%6B -PASS U+665F 晟 %1B%24%42%5A%70 -PASS U+6662 晢 %1B%24%42%5A%71 -PASS U+6664 晤 %1B%24%42%5A%6D -FAIL U+6665 晥 %1B%24%42%7A%3B assert_equals: expected "%1B%24%42%7A%3B" but got "%26%23%32%36%32%31%33%3B" -PASS U+6666 晦 %1B%24%42%33%22 -PASS U+6667 晧 %1B%24%42%5A%6E -PASS U+6668 晨 %1B%24%42%5A%6F -PASS U+6669 晩 %1B%24%42%48%55 -PASS U+666E 普 %1B%24%42%49%61 -PASS U+666F 景 %1B%24%42%37%4A -PASS U+6670 晰 %1B%24%42%5A%72 -FAIL U+6673 晳 %1B%24%42%7A%3F assert_equals: expected "%1B%24%42%7A%3F" but got "%26%23%32%36%32%32%37%3B" -PASS U+6674 晴 %1B%24%42%40%32 -PASS U+6676 晶 %1B%24%42%3E%3D -PASS U+667A 智 %1B%24%42%43%52 -PASS U+6681 暁 %1B%24%42%36%47 -PASS U+6683 暃 %1B%24%42%5A%73 -PASS U+6684 暄 %1B%24%42%5A%77 -PASS U+6687 暇 %1B%24%42%32%4B -PASS U+6688 暈 %1B%24%42%5A%74 -PASS U+6689 暉 %1B%24%42%5A%76 -PASS U+668E 暎 %1B%24%42%5A%75 -PASS U+6691 暑 %1B%24%42%3D%6B -PASS U+6696 暖 %1B%24%42%43%48 -PASS U+6697 暗 %1B%24%42%30%45 -PASS U+6698 暘 %1B%24%42%5A%78 -FAIL U+6699 暙 %1B%24%42%7A%40 assert_equals: expected "%1B%24%42%7A%40" but got "%26%23%32%36%32%36%35%3B" -PASS U+669D 暝 %1B%24%42%5A%79 -FAIL U+66A0 暠 %1B%24%42%7A%41 assert_equals: expected "%1B%24%42%7A%41" but got "%26%23%32%36%32%37%32%3B" -PASS U+66A2 暢 %1B%24%42%44%2A -PASS U+66A6 暦 %1B%24%42%4E%71 -PASS U+66AB 暫 %1B%24%42%3B%43 -PASS U+66AE 暮 %1B%24%42%4A%6B -FAIL U+66B2 暲 %1B%24%42%7A%42 assert_equals: expected "%1B%24%42%7A%42" but got "%26%23%32%36%32%39%30%3B" -PASS U+66B4 暴 %1B%24%42%4B%3D -PASS U+66B8 暸 %1B%24%42%5B%22 -PASS U+66B9 暹 %1B%24%42%5A%7B -PASS U+66BC 暼 %1B%24%42%5A%7E -PASS U+66BE 暾 %1B%24%42%5A%7D -FAIL U+66BF 暿 %1B%24%42%7A%43 assert_equals: expected "%1B%24%42%7A%43" but got "%26%23%32%36%33%30%33%3B" -PASS U+66C1 曁 %1B%24%42%5A%7A -PASS U+66C4 曄 %1B%24%42%5B%21 -PASS U+66C7 曇 %1B%24%42%46%5E -PASS U+66C9 曉 %1B%24%42%5A%7C -PASS U+66D6 曖 %1B%24%42%5B%23 -PASS U+66D9 曙 %1B%24%42%3D%6C -PASS U+66DA 曚 %1B%24%42%5B%24 -PASS U+66DC 曜 %1B%24%42%4D%4B -PASS U+66DD 曝 %1B%24%42%47%78 -PASS U+66E0 曠 %1B%24%42%5B%25 -PASS U+66E6 曦 %1B%24%42%5B%27 -PASS U+66E9 曩 %1B%24%42%5B%28 -PASS U+66F0 曰 %1B%24%42%5B%29 -PASS U+66F2 曲 %1B%24%42%36%4A -PASS U+66F3 曳 %1B%24%42%31%48 -PASS U+66F4 更 %1B%24%42%39%39 -PASS U+66F5 曵 %1B%24%42%5B%2A -PASS U+66F7 曷 %1B%24%42%5B%2B -PASS U+66F8 書 %1B%24%42%3D%71 -PASS U+66F9 曹 %1B%24%42%41%62 -FAIL U+66FA 曺 %1B%24%42%7A%44 assert_equals: expected "%1B%24%42%7A%44" but got "%26%23%32%36%33%36%32%3B" -FAIL U+66FB 曻 %1B%24%42%79%2B assert_equals: expected "%1B%24%42%79%2B" but got "%26%23%32%36%33%36%33%3B" -PASS U+66FC 曼 %1B%24%42%52%58 -PASS U+66FD 曽 %1B%24%42%41%3E -PASS U+66FE 曾 %1B%24%42%41%3D -PASS U+66FF 替 %1B%24%42%42%58 -PASS U+6700 最 %1B%24%42%3A%47 -PASS U+6703 會 %1B%24%42%50%72 -PASS U+6708 月 %1B%24%42%37%6E -PASS U+6709 有 %1B%24%42%4D%2D -PASS U+670B 朋 %1B%24%42%4A%7E -PASS U+670D 服 %1B%24%42%49%7E -FAIL U+670E 朎 %1B%24%42%7A%45 assert_equals: expected "%1B%24%42%7A%45" but got "%26%23%32%36%33%38%32%3B" -PASS U+670F 朏 %1B%24%42%5B%2C -PASS U+6714 朔 %1B%24%42%3A%73 -PASS U+6715 朕 %1B%24%42%44%3F -PASS U+6716 朖 %1B%24%42%5B%2D -PASS U+6717 朗 %1B%24%42%4F%2F -PASS U+671B 望 %1B%24%42%4B%3E -PASS U+671D 朝 %1B%24%42%44%2B -PASS U+671E 朞 %1B%24%42%5B%2E -PASS U+671F 期 %1B%24%42%34%7C -PASS U+6726 朦 %1B%24%42%5B%2F -PASS U+6727 朧 %1B%24%42%5B%30 -PASS U+6728 木 %1B%24%42%4C%5A -PASS U+672A 未 %1B%24%42%4C%24 -PASS U+672B 末 %1B%24%42%4B%76 -PASS U+672C 本 %1B%24%42%4B%5C -PASS U+672D 札 %1B%24%42%3B%25 -PASS U+672E 朮 %1B%24%42%5B%32 -PASS U+6731 朱 %1B%24%42%3C%6B -PASS U+6734 朴 %1B%24%42%4B%51 -PASS U+6736 朶 %1B%24%42%5B%34 -PASS U+6737 朷 %1B%24%42%5B%37 -PASS U+6738 朸 %1B%24%42%5B%36 -PASS U+673A 机 %1B%24%42%34%79 -PASS U+673D 朽 %1B%24%42%35%60 -PASS U+673F 朿 %1B%24%42%5B%33 -PASS U+6741 杁 %1B%24%42%5B%35 -PASS U+6746 杆 %1B%24%42%5B%38 -PASS U+6749 杉 %1B%24%42%3F%79 -PASS U+674E 李 %1B%24%42%4D%7B -PASS U+674F 杏 %1B%24%42%30%49 -PASS U+6750 材 %1B%24%42%3A%60 -PASS U+6751 村 %1B%24%42%42%3C -PASS U+6753 杓 %1B%24%42%3C%5D -PASS U+6756 杖 %1B%24%42%3E%73 -PASS U+6759 杙 %1B%24%42%5B%3B -PASS U+675C 杜 %1B%24%42%45%4E -PASS U+675E 杞 %1B%24%42%5B%39 -PASS U+675F 束 %1B%24%42%42%2B -PASS U+6760 杠 %1B%24%42%5B%3A -PASS U+6761 条 %1B%24%42%3E%72 -PASS U+6762 杢 %1B%24%42%4C%5D -PASS U+6763 杣 %1B%24%42%5B%3C -PASS U+6764 杤 %1B%24%42%5B%3D -PASS U+6765 来 %1B%24%42%4D%68 -FAIL U+6766 杦 %1B%24%42%7A%47 assert_equals: expected "%1B%24%42%7A%47" but got "%26%23%32%36%34%37%30%3B" -PASS U+676A 杪 %1B%24%42%5B%42 -PASS U+676D 杭 %1B%24%42%39%3A -PASS U+676F 杯 %1B%24%42%47%55 -PASS U+6770 杰 %1B%24%42%5B%3F -PASS U+6771 東 %1B%24%42%45%6C -PASS U+6772 杲 %1B%24%42%5A%5E -PASS U+6773 杳 %1B%24%42%5A%62 -PASS U+6775 杵 %1B%24%42%35%4F -PASS U+6777 杷 %1B%24%42%47%47 -PASS U+677C 杼 %1B%24%42%5B%41 -PASS U+677E 松 %1B%24%42%3E%3E -PASS U+677F 板 %1B%24%42%48%44 -PASS U+6785 枅 %1B%24%42%5B%47 -PASS U+6787 枇 %1B%24%42%48%7A -PASS U+6789 枉 %1B%24%42%5B%3E -PASS U+678B 枋 %1B%24%42%5B%44 -PASS U+678C 枌 %1B%24%42%5B%43 -PASS U+6790 析 %1B%24%42%40%4F -PASS U+6795 枕 %1B%24%42%4B%6D -PASS U+6797 林 %1B%24%42%4E%53 -PASS U+679A 枚 %1B%24%42%4B%67 -PASS U+679C 果 %1B%24%42%32%4C -PASS U+679D 枝 %1B%24%42%3B%5E -PASS U+67A0 枠 %1B%24%42%4F%48 -PASS U+67A1 枡 %1B%24%42%5B%46 -PASS U+67A2 枢 %1B%24%42%3F%75 -PASS U+67A6 枦 %1B%24%42%5B%45 -PASS U+67A9 枩 %1B%24%42%5B%40 -PASS U+67AF 枯 %1B%24%42%38%4F -PASS U+67B3 枳 %1B%24%42%5B%4C -PASS U+67B4 枴 %1B%24%42%5B%4A -PASS U+67B6 架 %1B%24%42%32%4D -PASS U+67B7 枷 %1B%24%42%5B%48 -PASS U+67B8 枸 %1B%24%42%5B%4E -PASS U+67B9 枹 %1B%24%42%5B%54 -FAIL U+67BB 枻 %1B%24%42%7A%48 assert_equals: expected "%1B%24%42%7A%48" but got "%26%23%32%36%35%35%35%3B" -FAIL U+67C0 柀 %1B%24%42%7A%4A assert_equals: expected "%1B%24%42%7A%4A" but got "%26%23%32%36%35%36%30%3B" -PASS U+67C1 柁 %1B%24%42%42%48 -PASS U+67C4 柄 %1B%24%42%4A%41 -PASS U+67C6 柆 %1B%24%42%5B%56 -PASS U+67CA 柊 %1B%24%42%49%22 -PASS U+67CE 柎 %1B%24%42%5B%55 -PASS U+67CF 柏 %1B%24%42%47%70 -PASS U+67D0 某 %1B%24%42%4B%3F -PASS U+67D1 柑 %1B%24%42%34%3B -PASS U+67D3 染 %1B%24%42%40%77 -PASS U+67D4 柔 %1B%24%42%3D%40 -PASS U+67D8 柘 %1B%24%42%44%53 -PASS U+67DA 柚 %1B%24%42%4D%2E -PASS U+67DD 柝 %1B%24%42%5B%51 -PASS U+67DE 柞 %1B%24%42%5B%50 -PASS U+67E2 柢 %1B%24%42%5B%52 -PASS U+67E4 柤 %1B%24%42%5B%4F -PASS U+67E7 柧 %1B%24%42%5B%57 -PASS U+67E9 柩 %1B%24%42%5B%4D -PASS U+67EC 柬 %1B%24%42%5B%4B -PASS U+67EE 柮 %1B%24%42%5B%53 -PASS U+67EF 柯 %1B%24%42%5B%49 -PASS U+67F1 柱 %1B%24%42%43%6C -PASS U+67F3 柳 %1B%24%42%4C%78 -PASS U+67F4 柴 %1B%24%42%3C%46 -PASS U+67F5 柵 %1B%24%42%3A%74 -PASS U+67FB 査 %1B%24%42%3A%3A -PASS U+67FE 柾 %1B%24%42%4B%6F -PASS U+67FF 柿 %1B%24%42%33%41 -FAIL U+6801 栁 %1B%24%42%7A%4B assert_equals: expected "%1B%24%42%7A%4B" but got "%26%23%32%36%36%32%35%3B" -PASS U+6802 栂 %1B%24%42%44%4E -PASS U+6803 栃 %1B%24%42%46%4A -PASS U+6804 栄 %1B%24%42%31%49 -PASS U+6813 栓 %1B%24%42%40%72 -PASS U+6816 栖 %1B%24%42%40%34 -PASS U+6817 栗 %1B%24%42%37%2A -PASS U+681E 栞 %1B%24%42%5B%59 -PASS U+6821 校 %1B%24%42%39%3B -PASS U+6822 栢 %1B%24%42%33%7C -PASS U+6829 栩 %1B%24%42%5B%5B -PASS U+682A 株 %1B%24%42%33%74 -PASS U+682B 栫 %1B%24%42%5B%61 -PASS U+6832 栲 %1B%24%42%5B%5E -PASS U+6834 栴 %1B%24%42%40%73 -PASS U+6838 核 %1B%24%42%33%4B -PASS U+6839 根 %1B%24%42%3A%2C -PASS U+683C 格 %1B%24%42%33%4A -PASS U+683D 栽 %1B%24%42%3A%4F -PASS U+6840 桀 %1B%24%42%5B%5C -PASS U+6841 桁 %1B%24%42%37%65 -PASS U+6842 桂 %1B%24%42%37%4B -PASS U+6843 桃 %1B%24%42%45%6D -FAIL U+6844 桄 %1B%24%42%7A%4C assert_equals: expected "%1B%24%42%7A%4C" but got "%26%23%32%36%36%39%32%3B" -PASS U+6846 框 %1B%24%42%5B%5A -PASS U+6848 案 %1B%24%42%30%46 -PASS U+684D 桍 %1B%24%42%5B%5D -PASS U+684E 桎 %1B%24%42%5B%5F -PASS U+6850 桐 %1B%24%42%36%4D -PASS U+6851 桑 %1B%24%42%37%2C -FAIL U+6852 桒 %1B%24%42%7A%49 assert_equals: expected "%1B%24%42%7A%49" but got "%26%23%32%36%37%30%36%3B" -PASS U+6853 桓 %1B%24%42%34%3C -PASS U+6854 桔 %1B%24%42%35%4B -PASS U+6859 桙 %1B%24%42%5B%62 -PASS U+685C 桜 %1B%24%42%3A%79 -PASS U+685D 桝 %1B%24%42%4B%71 -PASS U+685F 桟 %1B%24%42%3B%37 -PASS U+6863 档 %1B%24%42%5B%63 -PASS U+6867 桧 %1B%24%42%49%30 -PASS U+6874 桴 %1B%24%42%5B%6F -PASS U+6876 桶 %1B%24%42%32%33 -PASS U+6877 桷 %1B%24%42%5B%64 -PASS U+687E 桾 %1B%24%42%5B%75 -PASS U+687F 桿 %1B%24%42%5B%65 -PASS U+6881 梁 %1B%24%42%4E%42 -PASS U+6883 梃 %1B%24%42%5B%6C -PASS U+6885 梅 %1B%24%42%47%5F -PASS U+688D 梍 %1B%24%42%5B%74 -PASS U+688F 梏 %1B%24%42%5B%67 -PASS U+6893 梓 %1B%24%42%30%34 -PASS U+6894 梔 %1B%24%42%5B%69 -PASS U+6897 梗 %1B%24%42%39%3C -PASS U+689B 梛 %1B%24%42%5B%6B -PASS U+689D 條 %1B%24%42%5B%6A -PASS U+689F 梟 %1B%24%42%5B%66 -PASS U+68A0 梠 %1B%24%42%5B%71 -PASS U+68A2 梢 %1B%24%42%3E%3F -PASS U+68A6 梦 %1B%24%42%54%6D -PASS U+68A7 梧 %1B%24%42%38%68 -PASS U+68A8 梨 %1B%24%42%4D%7C -PASS U+68AD 梭 %1B%24%42%5B%68 -PASS U+68AF 梯 %1B%24%42%44%74 -PASS U+68B0 械 %1B%24%42%33%23 -PASS U+68B1 梱 %1B%24%42%3A%2D -PASS U+68B3 梳 %1B%24%42%5B%60 -PASS U+68B5 梵 %1B%24%42%5B%70 -PASS U+68B6 梶 %1B%24%42%33%61 -PASS U+68B9 梹 %1B%24%42%5B%6E -PASS U+68BA 梺 %1B%24%42%5B%72 -PASS U+68BC 梼 %1B%24%42%45%6E -PASS U+68C4 棄 %1B%24%42%34%7E -PASS U+68C6 棆 %1B%24%42%5C%32 -FAIL U+68C8 棈 %1B%24%42%79%29 assert_equals: expected "%1B%24%42%79%29" but got "%26%23%32%36%38%32%34%3B" -PASS U+68C9 棉 %1B%24%42%4C%49 -PASS U+68CA 棊 %1B%24%42%5B%77 -PASS U+68CB 棋 %1B%24%42%34%7D -PASS U+68CD 棍 %1B%24%42%5B%7E -FAIL U+68CF 棏 %1B%24%42%7A%4D assert_equals: expected "%1B%24%42%7A%4D" but got "%26%23%32%36%38%33%31%3B" -PASS U+68D2 棒 %1B%24%42%4B%40 -PASS U+68D4 棔 %1B%24%42%5C%21 -PASS U+68D5 棕 %1B%24%42%5C%23 -PASS U+68D7 棗 %1B%24%42%5C%27 -PASS U+68D8 棘 %1B%24%42%5B%79 -PASS U+68DA 棚 %1B%24%42%43%2A -PASS U+68DF 棟 %1B%24%42%45%6F -PASS U+68E0 棠 %1B%24%42%5C%2B -PASS U+68E1 棡 %1B%24%42%5B%7C -PASS U+68E3 棣 %1B%24%42%5C%28 -PASS U+68E7 棧 %1B%24%42%5C%22 -PASS U+68EE 森 %1B%24%42%3F%39 -PASS U+68EF 棯 %1B%24%42%5C%2C -PASS U+68F2 棲 %1B%24%42%40%33 -PASS U+68F9 棹 %1B%24%42%5C%2A -PASS U+68FA 棺 %1B%24%42%34%3D -PASS U+6900 椀 %1B%24%42%4F%50 -PASS U+6901 椁 %1B%24%42%5B%76 -PASS U+6904 椄 %1B%24%42%5C%26 -PASS U+6905 椅 %1B%24%42%30%58 -PASS U+6908 椈 %1B%24%42%5B%78 -PASS U+690B 椋 %1B%24%42%4C%3A -PASS U+690C 椌 %1B%24%42%5B%7D -PASS U+690D 植 %1B%24%42%3F%22 -PASS U+690E 椎 %1B%24%42%44%47 -PASS U+690F 椏 %1B%24%42%5B%73 -PASS U+6912 椒 %1B%24%42%5C%25 -PASS U+6919 椙 %1B%24%42%3F%7A -PASS U+691A 椚 %1B%24%42%5C%2F -PASS U+691B 椛 %1B%24%42%33%71 -PASS U+691C 検 %1B%24%42%38%21 -PASS U+6921 椡 %1B%24%42%5C%31 -PASS U+6922 椢 %1B%24%42%5B%7A -PASS U+6923 椣 %1B%24%42%5C%30 -PASS U+6925 椥 %1B%24%42%5C%29 -PASS U+6926 椦 %1B%24%42%5B%7B -PASS U+6928 椨 %1B%24%42%5C%2D -PASS U+692A 椪 %1B%24%42%5C%2E -PASS U+6930 椰 %1B%24%42%5C%3F -PASS U+6934 椴 %1B%24%42%46%4E -PASS U+6936 椶 %1B%24%42%5C%24 -PASS U+6939 椹 %1B%24%42%5C%3B -PASS U+693D 椽 %1B%24%42%5C%3D -PASS U+693F 椿 %1B%24%42%44%58 -PASS U+694A 楊 %1B%24%42%4D%4C -PASS U+6953 楓 %1B%24%42%49%76 -PASS U+6954 楔 %1B%24%42%5C%38 -PASS U+6955 楕 %1B%24%42%42%4A -PASS U+6959 楙 %1B%24%42%5C%3E -PASS U+695A 楚 %1B%24%42%41%3F -PASS U+695C 楜 %1B%24%42%5C%35 -PASS U+695D 楝 %1B%24%42%5C%42 -PASS U+695E 楞 %1B%24%42%5C%41 -PASS U+6960 楠 %1B%24%42%46%6F -PASS U+6961 楡 %1B%24%42%5C%40 -PASS U+6962 楢 %1B%24%42%46%6A -FAIL U+6968 楨 %1B%24%42%7A%4F assert_equals: expected "%1B%24%42%7A%4F" but got "%26%23%32%36%39%38%34%3B" -PASS U+696A 楪 %1B%24%42%5C%44 -PASS U+696B 楫 %1B%24%42%5C%37 -PASS U+696D 業 %1B%24%42%36%48 -PASS U+696E 楮 %1B%24%42%5C%3A -PASS U+696F 楯 %1B%24%42%3D%5D -PASS U+6973 楳 %1B%24%42%47%60 -PASS U+6974 楴 %1B%24%42%5C%3C -PASS U+6975 極 %1B%24%42%36%4B -PASS U+6977 楷 %1B%24%42%5C%34 -PASS U+6978 楸 %1B%24%42%5C%36 -PASS U+6979 楹 %1B%24%42%5C%33 -PASS U+697C 楼 %1B%24%42%4F%30 -PASS U+697D 楽 %1B%24%42%33%5A -PASS U+697E 楾 %1B%24%42%5C%39 -PASS U+6981 榁 %1B%24%42%5C%43 -PASS U+6982 概 %1B%24%42%33%35 -PASS U+698A 榊 %1B%24%42%3A%67 -PASS U+698E 榎 %1B%24%42%31%5D -PASS U+6991 榑 %1B%24%42%5C%54 -PASS U+6994 榔 %1B%24%42%4F%31 -PASS U+6995 榕 %1B%24%42%5C%57 -FAIL U+6998 榘 %1B%24%42%7A%51 assert_equals: expected "%1B%24%42%7A%51" but got "%26%23%32%37%30%33%32%3B" -PASS U+699B 榛 %1B%24%42%3F%3A -PASS U+699C 榜 %1B%24%42%5C%56 -PASS U+69A0 榠 %1B%24%42%5C%55 -PASS U+69A7 榧 %1B%24%42%5C%52 -PASS U+69AE 榮 %1B%24%42%5C%46 -PASS U+69B1 榱 %1B%24%42%5C%63 -PASS U+69B2 榲 %1B%24%42%5C%45 -PASS U+69B4 榴 %1B%24%42%5C%58 -PASS U+69BB 榻 %1B%24%42%5C%50 -PASS U+69BE 榾 %1B%24%42%5C%4B -PASS U+69BF 榿 %1B%24%42%5C%48 -PASS U+69C1 槁 %1B%24%42%5C%49 -PASS U+69C3 槃 %1B%24%42%5C%51 -PASS U+69C7 槇 %1B%24%42%74%22 -PASS U+69CA 槊 %1B%24%42%5C%4E -PASS U+69CB 構 %1B%24%42%39%3D -PASS U+69CC 槌 %1B%24%42%44%48 -PASS U+69CD 槍 %1B%24%42%41%64 -PASS U+69CE 槎 %1B%24%42%5C%4C -PASS U+69D0 槐 %1B%24%42%5C%47 -PASS U+69D3 槓 %1B%24%42%5C%4A -PASS U+69D8 様 %1B%24%42%4D%4D -PASS U+69D9 槙 %1B%24%42%4B%6A -PASS U+69DD 槝 %1B%24%42%5C%4F -PASS U+69DE 槞 %1B%24%42%5C%59 -FAIL U+69E2 槢 %1B%24%42%7A%52 assert_equals: expected "%1B%24%42%7A%52" but got "%26%23%32%37%31%30%36%3B" -PASS U+69E7 槧 %1B%24%42%5C%61 -PASS U+69E8 槨 %1B%24%42%5C%5A -PASS U+69EB 槫 %1B%24%42%5C%67 -PASS U+69ED 槭 %1B%24%42%5C%65 -PASS U+69F2 槲 %1B%24%42%5C%60 -PASS U+69F9 槹 %1B%24%42%5C%5F -PASS U+69FB 槻 %1B%24%42%44%50 -PASS U+69FD 槽 %1B%24%42%41%65 -PASS U+69FF 槿 %1B%24%42%5C%5D -PASS U+6A02 樂 %1B%24%42%5C%5B -PASS U+6A05 樅 %1B%24%42%5C%62 -PASS U+6A0A 樊 %1B%24%42%5C%68 -PASS U+6A0B 樋 %1B%24%42%48%75 -PASS U+6A0C 樌 %1B%24%42%5C%6E -PASS U+6A12 樒 %1B%24%42%5C%69 -PASS U+6A13 樓 %1B%24%42%5C%6C -PASS U+6A14 樔 %1B%24%42%5C%66 -PASS U+6A17 樗 %1B%24%42%43%74 -PASS U+6A19 標 %1B%24%42%49%38 -PASS U+6A1B 樛 %1B%24%42%5C%5C -PASS U+6A1E 樞 %1B%24%42%5C%64 -PASS U+6A1F 樟 %1B%24%42%3E%40 -PASS U+6A21 模 %1B%24%42%4C%4F -PASS U+6A22 樢 %1B%24%42%5C%78 -PASS U+6A23 樣 %1B%24%42%5C%6B -PASS U+6A29 権 %1B%24%42%38%22 -PASS U+6A2A 横 %1B%24%42%32%23 -PASS U+6A2B 樫 %1B%24%42%33%5F -PASS U+6A2E 樮 %1B%24%42%5C%53 -FAIL U+6A30 樰 %1B%24%42%7A%53 assert_equals: expected "%1B%24%42%7A%53" but got "%26%23%32%37%31%38%34%3B" -PASS U+6A35 樵 %1B%24%42%3E%41 -PASS U+6A36 樶 %1B%24%42%5C%70 -PASS U+6A38 樸 %1B%24%42%5C%77 -PASS U+6A39 樹 %1B%24%42%3C%79 -PASS U+6A3A 樺 %1B%24%42%33%72 -PASS U+6A3D 樽 %1B%24%42%43%2E -PASS U+6A44 橄 %1B%24%42%5C%6D -FAIL U+6A46 橆 %1B%24%42%7A%55 assert_equals: expected "%1B%24%42%7A%55" but got "%26%23%32%37%32%30%36%3B" -PASS U+6A47 橇 %1B%24%42%5C%72 -PASS U+6A48 橈 %1B%24%42%5C%76 -PASS U+6A4B 橋 %1B%24%42%36%36 -PASS U+6A58 橘 %1B%24%42%35%4C -PASS U+6A59 橙 %1B%24%42%5C%74 -PASS U+6A5F 機 %1B%24%42%35%21 -PASS U+6A61 橡 %1B%24%42%46%4B -PASS U+6A62 橢 %1B%24%42%5C%73 -PASS U+6A66 橦 %1B%24%42%5C%75 -FAIL U+6A6B 橫 %1B%24%42%7A%54 assert_equals: expected "%1B%24%42%7A%54" but got "%26%23%32%37%32%34%33%3B" -PASS U+6A72 橲 %1B%24%42%5C%6F -FAIL U+6A73 橳 %1B%24%42%7A%56 assert_equals: expected "%1B%24%42%7A%56" but got "%26%23%32%37%32%35%31%3B" -PASS U+6A78 橸 %1B%24%42%5C%71 -FAIL U+6A7E 橾 %1B%24%42%7A%57 assert_equals: expected "%1B%24%42%7A%57" but got "%26%23%32%37%32%36%32%3B" -PASS U+6A7F 橿 %1B%24%42%33%60 -PASS U+6A80 檀 %1B%24%42%43%49 -PASS U+6A84 檄 %1B%24%42%5C%7C -PASS U+6A8D 檍 %1B%24%42%5C%7A -PASS U+6A8E 檎 %1B%24%42%38%69 -PASS U+6A90 檐 %1B%24%42%5C%79 -PASS U+6A97 檗 %1B%24%42%5D%21 -PASS U+6A9C 檜 %1B%24%42%5B%58 -PASS U+6AA0 檠 %1B%24%42%5C%7B -PASS U+6AA2 檢 %1B%24%42%5C%7D -PASS U+6AA3 檣 %1B%24%42%5C%7E -PASS U+6AAA 檪 %1B%24%42%5D%2C -PASS U+6AAC 檬 %1B%24%42%5D%28 -PASS U+6AAE 檮 %1B%24%42%5B%6D -PASS U+6AB3 檳 %1B%24%42%5D%27 -PASS U+6AB8 檸 %1B%24%42%5D%26 -PASS U+6ABB 檻 %1B%24%42%5D%23 -PASS U+6AC1 櫁 %1B%24%42%5C%6A -PASS U+6AC2 櫂 %1B%24%42%5D%25 -PASS U+6AC3 櫃 %1B%24%42%5D%24 -PASS U+6AD1 櫑 %1B%24%42%5D%2A -PASS U+6AD3 櫓 %1B%24%42%4F%26 -PASS U+6ADA 櫚 %1B%24%42%5D%2D -PASS U+6ADB 櫛 %1B%24%42%36%7B -PASS U+6ADE 櫞 %1B%24%42%5D%29 -PASS U+6ADF 櫟 %1B%24%42%5D%2B -FAIL U+6AE2 櫢 %1B%24%42%7A%58 assert_equals: expected "%1B%24%42%7A%58" but got "%26%23%32%37%33%36%32%3B" -FAIL U+6AE4 櫤 %1B%24%42%7A%59 assert_equals: expected "%1B%24%42%7A%59" but got "%26%23%32%37%33%36%34%3B" -PASS U+6AE8 櫨 %1B%24%42%48%27 -PASS U+6AEA 櫪 %1B%24%42%5D%2E -PASS U+6AFA 櫺 %1B%24%42%5D%32 -PASS U+6AFB 櫻 %1B%24%42%5D%2F -PASS U+6B04 欄 %1B%24%42%4D%73 -PASS U+6B05 欅 %1B%24%42%5D%30 -PASS U+6B0A 權 %1B%24%42%5C%5E -PASS U+6B12 欒 %1B%24%42%5D%33 -PASS U+6B16 欖 %1B%24%42%5D%34 -PASS U+6B1D 欝 %1B%24%42%31%35 -PASS U+6B1F 欟 %1B%24%42%5D%36 -PASS U+6B20 欠 %1B%24%42%37%67 -PASS U+6B21 次 %1B%24%42%3C%21 -PASS U+6B23 欣 %1B%24%42%36%55 -PASS U+6B27 欧 %1B%24%42%32%24 -PASS U+6B32 欲 %1B%24%42%4D%5F -PASS U+6B37 欷 %1B%24%42%5D%38 -PASS U+6B38 欸 %1B%24%42%5D%37 -PASS U+6B39 欹 %1B%24%42%5D%3A -PASS U+6B3A 欺 %1B%24%42%35%3D -PASS U+6B3D 欽 %1B%24%42%36%56 -PASS U+6B3E 款 %1B%24%42%34%3E -PASS U+6B43 歃 %1B%24%42%5D%3D -PASS U+6B47 歇 %1B%24%42%5D%3C -PASS U+6B49 歉 %1B%24%42%5D%3E -PASS U+6B4C 歌 %1B%24%42%32%4E -PASS U+6B4E 歎 %1B%24%42%43%37 -PASS U+6B50 歐 %1B%24%42%5D%3F -PASS U+6B53 歓 %1B%24%42%34%3F -PASS U+6B54 歔 %1B%24%42%5D%41 -PASS U+6B59 歙 %1B%24%42%5D%40 -PASS U+6B5B 歛 %1B%24%42%5D%42 -PASS U+6B5F 歟 %1B%24%42%5D%43 -PASS U+6B61 歡 %1B%24%42%5D%44 -PASS U+6B62 止 %1B%24%42%3B%5F -PASS U+6B63 正 %1B%24%42%40%35 -PASS U+6B64 此 %1B%24%42%3A%21 -PASS U+6B66 武 %1B%24%42%49%70 -PASS U+6B69 歩 %1B%24%42%4A%62 -PASS U+6B6A 歪 %1B%24%42%4F%44 -PASS U+6B6F 歯 %1B%24%42%3B%75 -PASS U+6B73 歳 %1B%24%42%3A%50 -PASS U+6B74 歴 %1B%24%42%4E%72 -PASS U+6B78 歸 %1B%24%42%5D%45 -PASS U+6B79 歹 %1B%24%42%5D%46 -PASS U+6B7B 死 %1B%24%42%3B%60 -PASS U+6B7F 歿 %1B%24%42%5D%47 -PASS U+6B80 殀 %1B%24%42%5D%48 -PASS U+6B83 殃 %1B%24%42%5D%4A -PASS U+6B84 殄 %1B%24%42%5D%49 -PASS U+6B86 殆 %1B%24%42%4B%58 -PASS U+6B89 殉 %1B%24%42%3D%5E -PASS U+6B8A 殊 %1B%24%42%3C%6C -PASS U+6B8B 残 %1B%24%42%3B%44 -PASS U+6B8D 殍 %1B%24%42%5D%4B -PASS U+6B95 殕 %1B%24%42%5D%4D -PASS U+6B96 殖 %1B%24%42%3F%23 -PASS U+6B98 殘 %1B%24%42%5D%4C -PASS U+6B9E 殞 %1B%24%42%5D%4E -PASS U+6BA4 殤 %1B%24%42%5D%4F -PASS U+6BAA 殪 %1B%24%42%5D%50 -PASS U+6BAB 殫 %1B%24%42%5D%51 -PASS U+6BAF 殯 %1B%24%42%5D%52 -PASS U+6BB1 殱 %1B%24%42%5D%54 -PASS U+6BB2 殲 %1B%24%42%5D%53 -PASS U+6BB3 殳 %1B%24%42%5D%55 -PASS U+6BB4 殴 %1B%24%42%32%25 -PASS U+6BB5 段 %1B%24%42%43%4A -PASS U+6BB7 殷 %1B%24%42%5D%56 -PASS U+6BBA 殺 %1B%24%42%3B%26 -PASS U+6BBB 殻 %1B%24%42%33%4C -PASS U+6BBC 殼 %1B%24%42%5D%57 -PASS U+6BBF 殿 %1B%24%42%45%42 -PASS U+6BC0 毀 %1B%24%42%54%4C -PASS U+6BC5 毅 %1B%24%42%35%23 -PASS U+6BC6 毆 %1B%24%42%5D%58 -PASS U+6BCB 毋 %1B%24%42%5D%59 -PASS U+6BCD 母 %1B%24%42%4A%6C -PASS U+6BCE 毎 %1B%24%42%4B%68 -PASS U+6BD2 毒 %1B%24%42%46%47 -PASS U+6BD3 毓 %1B%24%42%5D%5A -PASS U+6BD4 比 %1B%24%42%48%66 -FAIL U+6BD6 毖 %1B%24%42%7A%5A assert_equals: expected "%1B%24%42%7A%5A" but got "%26%23%32%37%36%30%36%3B" -PASS U+6BD8 毘 %1B%24%42%48%7B -PASS U+6BDB 毛 %1B%24%42%4C%53 -PASS U+6BDF 毟 %1B%24%42%5D%5B -PASS U+6BEB 毫 %1B%24%42%5D%5D -PASS U+6BEC 毬 %1B%24%42%5D%5C -PASS U+6BEF 毯 %1B%24%42%5D%5F -PASS U+6BF3 毳 %1B%24%42%5D%5E -PASS U+6C08 氈 %1B%24%42%5D%61 -PASS U+6C0F 氏 %1B%24%42%3B%61 -PASS U+6C11 民 %1B%24%42%4C%31 -PASS U+6C13 氓 %1B%24%42%5D%62 -PASS U+6C14 气 %1B%24%42%5D%63 -PASS U+6C17 気 %1B%24%42%35%24 -PASS U+6C1B 氛 %1B%24%42%5D%64 -PASS U+6C23 氣 %1B%24%42%5D%66 -PASS U+6C24 氤 %1B%24%42%5D%65 -PASS U+6C34 水 %1B%24%42%3F%65 -PASS U+6C37 氷 %1B%24%42%49%39 -PASS U+6C38 永 %1B%24%42%31%4A -PASS U+6C3E 氾 %1B%24%42%48%45 -FAIL U+6C3F 氿 %1B%24%42%7A%5B assert_equals: expected "%1B%24%42%7A%5B" but got "%26%23%32%37%37%31%31%3B" -PASS U+6C40 汀 %1B%24%42%44%75 -PASS U+6C41 汁 %1B%24%42%3D%41 -PASS U+6C42 求 %1B%24%42%35%61 -PASS U+6C4E 汎 %1B%24%42%48%46 -PASS U+6C50 汐 %1B%24%42%3C%2E -PASS U+6C55 汕 %1B%24%42%5D%68 -PASS U+6C57 汗 %1B%24%42%34%40 -PASS U+6C5A 汚 %1B%24%42%31%78 -FAIL U+6C5C 汜 %1B%24%42%7A%5C assert_equals: expected "%1B%24%42%7A%5C" but got "%26%23%32%37%37%34%30%3B" -PASS U+6C5D 汝 %1B%24%42%46%72 -PASS U+6C5E 汞 %1B%24%42%5D%67 -PASS U+6C5F 江 %1B%24%42%39%3E -PASS U+6C60 池 %1B%24%42%43%53 -PASS U+6C62 汢 %1B%24%42%5D%69 -PASS U+6C68 汨 %1B%24%42%5D%71 -PASS U+6C6A 汪 %1B%24%42%5D%6A -FAIL U+6C6F 汯 %1B%24%42%7A%5E assert_equals: expected "%1B%24%42%7A%5E" but got "%26%23%32%37%37%35%39%3B" -PASS U+6C70 汰 %1B%24%42%42%41 -PASS U+6C72 汲 %1B%24%42%35%62 -PASS U+6C73 汳 %1B%24%42%5D%72 -PASS U+6C7A 決 %1B%24%42%37%68 -PASS U+6C7D 汽 %1B%24%42%35%25 -PASS U+6C7E 汾 %1B%24%42%5D%70 -PASS U+6C81 沁 %1B%24%42%5D%6E -PASS U+6C82 沂 %1B%24%42%5D%6B -PASS U+6C83 沃 %1B%24%42%4D%60 -FAIL U+6C86 沆 %1B%24%42%7A%5D assert_equals: expected "%1B%24%42%7A%5D" but got "%26%23%32%37%37%38%32%3B" -PASS U+6C88 沈 %1B%24%42%44%40 -PASS U+6C8C 沌 %1B%24%42%46%59 -PASS U+6C8D 沍 %1B%24%42%5D%6C -PASS U+6C90 沐 %1B%24%42%5D%74 -PASS U+6C92 沒 %1B%24%42%5D%73 -PASS U+6C93 沓 %1B%24%42%37%23 -PASS U+6C96 沖 %1B%24%42%32%2D -PASS U+6C99 沙 %1B%24%42%3A%3B -PASS U+6C9A 沚 %1B%24%42%5D%6D -PASS U+6C9B 沛 %1B%24%42%5D%6F -PASS U+6CA1 没 %1B%24%42%4B%57 -PASS U+6CA2 沢 %1B%24%42%42%74 -PASS U+6CAB 沫 %1B%24%42%4B%77 -PASS U+6CAE 沮 %1B%24%42%5D%7C -PASS U+6CB1 沱 %1B%24%42%5D%7D -PASS U+6CB3 河 %1B%24%42%32%4F -PASS U+6CB8 沸 %1B%24%42%4A%28 -PASS U+6CB9 油 %1B%24%42%4C%7D -PASS U+6CBA 沺 %1B%24%42%5E%21 -PASS U+6CBB 治 %1B%24%42%3C%23 -PASS U+6CBC 沼 %1B%24%42%3E%42 -PASS U+6CBD 沽 %1B%24%42%5D%78 -PASS U+6CBE 沾 %1B%24%42%5D%7E -PASS U+6CBF 沿 %1B%24%42%31%68 -PASS U+6CC1 況 %1B%24%42%36%37 -PASS U+6CC4 泄 %1B%24%42%5D%75 -PASS U+6CC5 泅 %1B%24%42%5D%7A -PASS U+6CC9 泉 %1B%24%42%40%74 -PASS U+6CCA 泊 %1B%24%42%47%71 -PASS U+6CCC 泌 %1B%24%42%48%67 -PASS U+6CD3 泓 %1B%24%42%5D%77 -PASS U+6CD5 法 %1B%24%42%4B%21 -PASS U+6CD7 泗 %1B%24%42%5D%79 -PASS U+6CD9 泙 %1B%24%42%5E%24 -FAIL U+6CDA 泚 %1B%24%42%7A%5F assert_equals: expected "%1B%24%42%7A%5F" but got "%26%23%32%37%38%36%36%3B" -PASS U+6CDB 泛 %1B%24%42%5E%22 -PASS U+6CDD 泝 %1B%24%42%5D%7B -PASS U+6CE1 泡 %1B%24%42%4B%22 -PASS U+6CE2 波 %1B%24%42%47%48 -PASS U+6CE3 泣 %1B%24%42%35%63 -PASS U+6CE5 泥 %1B%24%42%45%25 -PASS U+6CE8 注 %1B%24%42%43%6D -PASS U+6CEA 泪 %1B%24%42%5E%25 -PASS U+6CEF 泯 %1B%24%42%5E%23 -PASS U+6CF0 泰 %1B%24%42%42%59 -PASS U+6CF1 泱 %1B%24%42%5D%76 -PASS U+6CF3 泳 %1B%24%42%31%4B -FAIL U+6D04 洄 %1B%24%42%7A%60 assert_equals: expected "%1B%24%42%7A%60" but got "%26%23%32%37%39%30%38%3B" -PASS U+6D0B 洋 %1B%24%42%4D%4E -PASS U+6D0C 洌 %1B%24%42%5E%30 -PASS U+6D12 洒 %1B%24%42%5E%2F -PASS U+6D17 洗 %1B%24%42%40%76 -PASS U+6D19 洙 %1B%24%42%5E%2C -PASS U+6D1B 洛 %1B%24%42%4D%6C -PASS U+6D1E 洞 %1B%24%42%46%36 -PASS U+6D1F 洟 %1B%24%42%5E%26 -PASS U+6D25 津 %1B%24%42%44%45 -PASS U+6D29 洩 %1B%24%42%31%4C -PASS U+6D2A 洪 %1B%24%42%39%3F -PASS U+6D2B 洫 %1B%24%42%5E%29 -PASS U+6D32 洲 %1B%24%42%3D%27 -PASS U+6D33 洳 %1B%24%42%5E%2E -PASS U+6D35 洵 %1B%24%42%5E%2D -PASS U+6D36 洶 %1B%24%42%5E%28 -PASS U+6D38 洸 %1B%24%42%5E%2B -PASS U+6D3B 活 %1B%24%42%33%68 -PASS U+6D3D 洽 %1B%24%42%5E%2A -PASS U+6D3E 派 %1B%24%42%47%49 -PASS U+6D41 流 %1B%24%42%4E%2E -PASS U+6D44 浄 %1B%24%42%3E%74 -PASS U+6D45 浅 %1B%24%42%40%75 -PASS U+6D59 浙 %1B%24%42%5E%36 -PASS U+6D5A 浚 %1B%24%42%5E%34 -PASS U+6D5C 浜 %1B%24%42%49%4D -PASS U+6D63 浣 %1B%24%42%5E%31 -PASS U+6D64 浤 %1B%24%42%5E%33 -PASS U+6D66 浦 %1B%24%42%31%3A -PASS U+6D69 浩 %1B%24%42%39%40 -PASS U+6D6A 浪 %1B%24%42%4F%32 -PASS U+6D6C 浬 %1B%24%42%33%3D -PASS U+6D6E 浮 %1B%24%42%49%62 -FAIL U+6D6F 浯 %1B%24%42%7A%62 assert_equals: expected "%1B%24%42%7A%62" but got "%26%23%32%38%30%31%35%3B" -PASS U+6D74 浴 %1B%24%42%4D%61 -PASS U+6D77 海 %1B%24%42%33%24 -PASS U+6D78 浸 %1B%24%42%3F%3B -PASS U+6D79 浹 %1B%24%42%5E%35 -PASS U+6D85 涅 %1B%24%42%5E%3A -FAIL U+6D87 涇 %1B%24%42%7A%61 assert_equals: expected "%1B%24%42%7A%61" but got "%26%23%32%38%30%33%39%3B" -PASS U+6D88 消 %1B%24%42%3E%43 -PASS U+6D8C 涌 %1B%24%42%4D%30 -PASS U+6D8E 涎 %1B%24%42%5E%37 -PASS U+6D93 涓 %1B%24%42%5E%32 -PASS U+6D95 涕 %1B%24%42%5E%38 -FAIL U+6D96 涖 %1B%24%42%7A%63 assert_equals: expected "%1B%24%42%7A%63" but got "%26%23%32%38%30%35%34%3B" -PASS U+6D99 涙 %1B%24%42%4E%5E -PASS U+6D9B 涛 %1B%24%42%45%73 -PASS U+6D9C 涜 %1B%24%42%46%42 -FAIL U+6DAC 涬 %1B%24%42%7A%64 assert_equals: expected "%1B%24%42%7A%64" but got "%26%23%32%38%30%37%36%3B" -PASS U+6DAF 涯 %1B%24%42%33%36 -PASS U+6DB2 液 %1B%24%42%31%55 -PASS U+6DB5 涵 %1B%24%42%5E%3E -PASS U+6DB8 涸 %1B%24%42%5E%41 -PASS U+6DBC 涼 %1B%24%42%4E%43 -PASS U+6DC0 淀 %1B%24%42%4D%64 -PASS U+6DC5 淅 %1B%24%42%5E%48 -PASS U+6DC6 淆 %1B%24%42%5E%42 -PASS U+6DC7 淇 %1B%24%42%5E%3F -PASS U+6DCB 淋 %1B%24%42%4E%54 -PASS U+6DCC 淌 %1B%24%42%5E%45 -FAIL U+6DCF 淏 %1B%24%42%7A%65 assert_equals: expected "%1B%24%42%7A%65" but got "%26%23%32%38%31%31%31%3B" -PASS U+6DD1 淑 %1B%24%42%3D%4A -PASS U+6DD2 淒 %1B%24%42%5E%47 -PASS U+6DD5 淕 %1B%24%42%5E%4C -PASS U+6DD8 淘 %1B%24%42%45%71 -PASS U+6DD9 淙 %1B%24%42%5E%4A -PASS U+6DDE 淞 %1B%24%42%5E%44 -PASS U+6DE1 淡 %1B%24%42%43%38 -PASS U+6DE4 淤 %1B%24%42%5E%4B -PASS U+6DE6 淦 %1B%24%42%5E%40 -PASS U+6DE8 淨 %1B%24%42%5E%46 -PASS U+6DEA 淪 %1B%24%42%5E%4D -PASS U+6DEB 淫 %1B%24%42%30%7C -PASS U+6DEC 淬 %1B%24%42%5E%43 -PASS U+6DEE 淮 %1B%24%42%5E%4E -PASS U+6DF1 深 %1B%24%42%3F%3C -FAIL U+6DF2 淲 %1B%24%42%7A%67 assert_equals: expected "%1B%24%42%7A%67" but got "%26%23%32%38%31%34%36%3B" -PASS U+6DF3 淳 %1B%24%42%3D%5F -PASS U+6DF5 淵 %1B%24%42%4A%25 -PASS U+6DF7 混 %1B%24%42%3A%2E -FAIL U+6DF8 淸 %1B%24%42%7A%66 assert_equals: expected "%1B%24%42%7A%66" but got "%26%23%32%38%31%35%32%3B" -PASS U+6DF9 淹 %1B%24%42%5E%3B -PASS U+6DFA 淺 %1B%24%42%5E%49 -PASS U+6DFB 添 %1B%24%42%45%3A -FAIL U+6DFC 淼 %1B%24%42%7A%68 assert_equals: expected "%1B%24%42%7A%68" but got "%26%23%32%38%31%35%36%3B" -PASS U+6E05 清 %1B%24%42%40%36 -PASS U+6E07 渇 %1B%24%42%33%69 -PASS U+6E08 済 %1B%24%42%3A%51 -PASS U+6E09 渉 %1B%24%42%3E%44 -PASS U+6E0A 渊 %1B%24%42%5E%3D -PASS U+6E0B 渋 %1B%24%42%3D%42 -PASS U+6E13 渓 %1B%24%42%37%4C -PASS U+6E15 渕 %1B%24%42%5E%3C -PASS U+6E19 渙 %1B%24%42%5E%52 -PASS U+6E1A 渚 %1B%24%42%3D%6D -PASS U+6E1B 減 %1B%24%42%38%3A -PASS U+6E1D 渝 %1B%24%42%5E%61 -PASS U+6E1F 渟 %1B%24%42%5E%5B -PASS U+6E20 渠 %1B%24%42%35%74 -PASS U+6E21 渡 %1B%24%42%45%4F -PASS U+6E23 渣 %1B%24%42%5E%56 -PASS U+6E24 渤 %1B%24%42%5E%5F -PASS U+6E25 渥 %1B%24%42%30%2F -PASS U+6E26 渦 %1B%24%42%31%32 -FAIL U+6E27 渧 %1B%24%42%7A%6B assert_equals: expected "%1B%24%42%7A%6B" but got "%26%23%32%38%31%39%39%3B" -PASS U+6E29 温 %1B%24%42%32%39 -PASS U+6E2B 渫 %1B%24%42%5E%58 -PASS U+6E2C 測 %1B%24%42%42%2C -PASS U+6E2D 渭 %1B%24%42%5E%4F -PASS U+6E2E 渮 %1B%24%42%5E%51 -PASS U+6E2F 港 %1B%24%42%39%41 -PASS U+6E38 游 %1B%24%42%5E%62 -FAIL U+6E39 渹 %1B%24%42%7A%69 assert_equals: expected "%1B%24%42%7A%69" but got "%26%23%32%38%32%31%37%3B" -PASS U+6E3A 渺 %1B%24%42%5E%5D -FAIL U+6E3C 渼 %1B%24%42%7A%6C assert_equals: expected "%1B%24%42%7A%6C" but got "%26%23%32%38%32%32%30%3B" -PASS U+6E3E 渾 %1B%24%42%5E%55 -PASS U+6E43 湃 %1B%24%42%5E%5C -PASS U+6E4A 湊 %1B%24%42%4C%2B -PASS U+6E4D 湍 %1B%24%42%5E%5A -PASS U+6E4E 湎 %1B%24%42%5E%5E -PASS U+6E56 湖 %1B%24%42%38%50 -PASS U+6E58 湘 %1B%24%42%3E%45 -PASS U+6E5B 湛 %1B%24%42%43%39 -FAIL U+6E5C 湜 %1B%24%42%7A%6A assert_equals: expected "%1B%24%42%7A%6A" but got "%26%23%32%38%32%35%32%3B" -PASS U+6E5F 湟 %1B%24%42%5E%54 -PASS U+6E67 湧 %1B%24%42%4D%2F -PASS U+6E6B 湫 %1B%24%42%5E%57 -PASS U+6E6E 湮 %1B%24%42%5E%50 -PASS U+6E6F 湯 %1B%24%42%45%72 -PASS U+6E72 湲 %1B%24%42%5E%53 -PASS U+6E76 湶 %1B%24%42%5E%59 -PASS U+6E7E 湾 %1B%24%42%4F%51 -PASS U+6E7F 湿 %1B%24%42%3C%3E -PASS U+6E80 満 %1B%24%42%4B%7E -PASS U+6E82 溂 %1B%24%42%5E%63 -PASS U+6E8C 溌 %1B%24%42%48%2E -PASS U+6E8F 溏 %1B%24%42%5E%6F -PASS U+6E90 源 %1B%24%42%38%3B -PASS U+6E96 準 %1B%24%42%3D%60 -PASS U+6E98 溘 %1B%24%42%5E%65 -PASS U+6E9C 溜 %1B%24%42%4E%2F -PASS U+6E9D 溝 %1B%24%42%39%42 -PASS U+6E9F 溟 %1B%24%42%5E%72 -PASS U+6EA2 溢 %1B%24%42%30%6E -PASS U+6EA5 溥 %1B%24%42%5E%70 -PASS U+6EAA 溪 %1B%24%42%5E%64 -PASS U+6EAF 溯 %1B%24%42%5E%6A -PASS U+6EB2 溲 %1B%24%42%5E%6C -PASS U+6EB6 溶 %1B%24%42%4D%4F -PASS U+6EB7 溷 %1B%24%42%5E%67 -PASS U+6EBA 溺 %1B%24%42%45%2E -PASS U+6EBD 溽 %1B%24%42%5E%69 -FAIL U+6EBF 溿 %1B%24%42%7A%6D assert_equals: expected "%1B%24%42%7A%6D" but got "%26%23%32%38%33%35%31%3B" -PASS U+6EC2 滂 %1B%24%42%5E%71 -PASS U+6EC4 滄 %1B%24%42%5E%6B -PASS U+6EC5 滅 %1B%24%42%4C%47 -PASS U+6EC9 滉 %1B%24%42%5E%66 -PASS U+6ECB 滋 %1B%24%42%3C%22 -PASS U+6ECC 滌 %1B%24%42%5E%7E -PASS U+6ED1 滑 %1B%24%42%33%6A -PASS U+6ED3 滓 %1B%24%42%5E%68 -PASS U+6ED4 滔 %1B%24%42%5E%6D -PASS U+6ED5 滕 %1B%24%42%5E%6E -PASS U+6EDD 滝 %1B%24%42%42%6C -PASS U+6EDE 滞 %1B%24%42%42%5A -PASS U+6EEC 滬 %1B%24%42%5E%76 -PASS U+6EEF 滯 %1B%24%42%5E%7C -PASS U+6EF2 滲 %1B%24%42%5E%7A -PASS U+6EF4 滴 %1B%24%42%45%29 -PASS U+6EF7 滷 %1B%24%42%5F%23 -PASS U+6EF8 滸 %1B%24%42%5E%77 -PASS U+6EFE 滾 %1B%24%42%5E%78 -PASS U+6EFF 滿 %1B%24%42%5E%60 -PASS U+6F01 漁 %1B%24%42%35%79 -PASS U+6F02 漂 %1B%24%42%49%3A -PASS U+6F06 漆 %1B%24%42%3C%3F -PASS U+6F09 漉 %1B%24%42%39%77 -PASS U+6F0F 漏 %1B%24%42%4F%33 -PASS U+6F11 漑 %1B%24%42%5E%74 -PASS U+6F13 漓 %1B%24%42%5F%22 -PASS U+6F14 演 %1B%24%42%31%69 -PASS U+6F15 漕 %1B%24%42%41%66 -PASS U+6F20 漠 %1B%24%42%47%79 -PASS U+6F22 漢 %1B%24%42%34%41 -PASS U+6F23 漣 %1B%24%42%4E%7A -PASS U+6F2B 漫 %1B%24%42%4C%21 -PASS U+6F2C 漬 %1B%24%42%44%52 -PASS U+6F31 漱 %1B%24%42%5E%7B -PASS U+6F32 漲 %1B%24%42%5E%7D -PASS U+6F38 漸 %1B%24%42%41%32 -PASS U+6F3E 漾 %1B%24%42%5F%21 -PASS U+6F3F 漿 %1B%24%42%5E%79 -PASS U+6F41 潁 %1B%24%42%5E%73 -PASS U+6F45 潅 %1B%24%42%34%43 -PASS U+6F54 潔 %1B%24%42%37%69 -PASS U+6F58 潘 %1B%24%42%5F%2F -PASS U+6F5B 潛 %1B%24%42%5F%2A -PASS U+6F5C 潜 %1B%24%42%40%78 -PASS U+6F5F 潟 %1B%24%42%33%63 -PASS U+6F64 潤 %1B%24%42%3D%61 -PASS U+6F66 潦 %1B%24%42%5F%33 -PASS U+6F6D 潭 %1B%24%42%5F%2C -PASS U+6F6E 潮 %1B%24%42%44%2C -PASS U+6F6F 潯 %1B%24%42%5F%29 -PASS U+6F70 潰 %1B%24%42%44%59 -PASS U+6F74 潴 %1B%24%42%5F%4C -PASS U+6F78 潸 %1B%24%42%5F%26 -PASS U+6F7A 潺 %1B%24%42%5F%25 -PASS U+6F7C 潼 %1B%24%42%5F%2E -PASS U+6F80 澀 %1B%24%42%5F%28 -PASS U+6F81 澁 %1B%24%42%5F%27 -PASS U+6F82 澂 %1B%24%42%5F%2D -PASS U+6F84 澄 %1B%24%42%40%21 -PASS U+6F86 澆 %1B%24%42%5F%24 -FAIL U+6F88 澈 %1B%24%42%7A%6E assert_equals: expected "%1B%24%42%7A%6E" but got "%26%23%32%38%35%35%32%3B" -PASS U+6F8E 澎 %1B%24%42%5F%30 -PASS U+6F91 澑 %1B%24%42%5F%31 -PASS U+6F97 澗 %1B%24%42%34%42 -PASS U+6FA1 澡 %1B%24%42%5F%36 -PASS U+6FA3 澣 %1B%24%42%5F%35 -PASS U+6FA4 澤 %1B%24%42%5F%37 -PASS U+6FAA 澪 %1B%24%42%5F%3A -PASS U+6FB1 澱 %1B%24%42%45%43 -PASS U+6FB3 澳 %1B%24%42%5F%34 -FAIL U+6FB5 澵 %1B%24%42%7A%6F assert_equals: expected "%1B%24%42%7A%6F" but got "%26%23%32%38%35%39%37%3B" -PASS U+6FB9 澹 %1B%24%42%5F%38 -PASS U+6FC0 激 %1B%24%42%37%63 -PASS U+6FC1 濁 %1B%24%42%42%79 -PASS U+6FC2 濂 %1B%24%42%5F%32 -PASS U+6FC3 濃 %1B%24%42%47%3B -PASS U+6FC6 濆 %1B%24%42%5F%39 -PASS U+6FD4 濔 %1B%24%42%5F%3E -PASS U+6FD5 濕 %1B%24%42%5F%3C -PASS U+6FD8 濘 %1B%24%42%5F%3F -PASS U+6FDB 濛 %1B%24%42%5F%42 -PASS U+6FDF 濟 %1B%24%42%5F%3B -PASS U+6FE0 濠 %1B%24%42%39%6A -PASS U+6FE1 濡 %1B%24%42%47%28 -PASS U+6FE4 濤 %1B%24%42%5E%39 -PASS U+6FEB 濫 %1B%24%42%4D%74 -PASS U+6FEC 濬 %1B%24%42%5F%3D -PASS U+6FEE 濮 %1B%24%42%5F%41 -PASS U+6FEF 濯 %1B%24%42%42%75 -PASS U+6FF1 濱 %1B%24%42%5F%40 -PASS U+6FF3 濳 %1B%24%42%5F%2B -FAIL U+6FF5 濵 %1B%24%42%7A%70 assert_equals: expected "%1B%24%42%7A%70" but got "%26%23%32%38%36%36%31%3B" -PASS U+6FF6 濶 %1B%24%42%6F%69 -PASS U+6FFA 濺 %1B%24%42%5F%45 -PASS U+6FFE 濾 %1B%24%42%5F%49 -PASS U+7001 瀁 %1B%24%42%5F%47 -FAIL U+7005 瀅 %1B%24%42%7A%71 assert_equals: expected "%1B%24%42%7A%71" but got "%26%23%32%38%36%37%37%3B" -FAIL U+7007 瀇 %1B%24%42%7A%72 assert_equals: expected "%1B%24%42%7A%72" but got "%26%23%32%38%36%37%39%3B" -PASS U+7009 瀉 %1B%24%42%5F%43 -PASS U+700B 瀋 %1B%24%42%5F%44 -PASS U+700F 瀏 %1B%24%42%5F%48 -PASS U+7011 瀑 %1B%24%42%5F%46 -PASS U+7015 瀕 %1B%24%42%49%4E -PASS U+7018 瀘 %1B%24%42%5F%4E -PASS U+701A 瀚 %1B%24%42%5F%4B -PASS U+701B 瀛 %1B%24%42%5F%4A -PASS U+701D 瀝 %1B%24%42%5F%4D -PASS U+701E 瀞 %1B%24%42%46%54 -PASS U+701F 瀟 %1B%24%42%5F%4F -PASS U+7026 瀦 %1B%24%42%43%75 -PASS U+7027 瀧 %1B%24%42%42%6D -FAIL U+7028 瀨 %1B%24%42%7A%73 assert_equals: expected "%1B%24%42%7A%73" but got "%26%23%32%38%37%31%32%3B" -PASS U+702C 瀬 %1B%24%42%40%25 -PASS U+7030 瀰 %1B%24%42%5F%50 -PASS U+7032 瀲 %1B%24%42%5F%52 -PASS U+703E 瀾 %1B%24%42%5F%51 -PASS U+704C 灌 %1B%24%42%5E%75 -PASS U+7051 灑 %1B%24%42%5F%53 -PASS U+7058 灘 %1B%24%42%46%67 -PASS U+7063 灣 %1B%24%42%5F%54 -PASS U+706B 火 %1B%24%42%32%50 -PASS U+706F 灯 %1B%24%42%45%74 -PASS U+7070 灰 %1B%24%42%33%25 -PASS U+7078 灸 %1B%24%42%35%64 -PASS U+707C 灼 %1B%24%42%3C%5E -PASS U+707D 災 %1B%24%42%3A%52 -FAIL U+7085 炅 %1B%24%42%7A%74 assert_equals: expected "%1B%24%42%7A%74" but got "%26%23%32%38%38%30%35%3B" -PASS U+7089 炉 %1B%24%42%4F%27 -PASS U+708A 炊 %1B%24%42%3F%66 -PASS U+708E 炎 %1B%24%42%31%6A -PASS U+7092 炒 %1B%24%42%5F%56 -PASS U+7099 炙 %1B%24%42%5F%55 -FAIL U+70AB 炫 %1B%24%42%7A%75 assert_equals: expected "%1B%24%42%7A%75" but got "%26%23%32%38%38%34%33%3B" -PASS U+70AC 炬 %1B%24%42%5F%59 -PASS U+70AD 炭 %1B%24%42%43%3A -PASS U+70AE 炮 %1B%24%42%5F%5C -PASS U+70AF 炯 %1B%24%42%5F%57 -PASS U+70B3 炳 %1B%24%42%5F%5B -PASS U+70B8 炸 %1B%24%42%5F%5A -PASS U+70B9 点 %1B%24%42%45%40 -PASS U+70BA 為 %1B%24%42%30%59 -FAIL U+70BB 炻 %1B%24%42%79%27 assert_equals: expected "%1B%24%42%79%27" but got "%26%23%32%38%38%35%39%3B" -PASS U+70C8 烈 %1B%24%42%4E%75 -PASS U+70CB 烋 %1B%24%42%5F%5E -PASS U+70CF 烏 %1B%24%42%31%28 -PASS U+70D9 烙 %1B%24%42%5F%60 -PASS U+70DD 烝 %1B%24%42%5F%5F -PASS U+70DF 烟 %1B%24%42%5F%5D -PASS U+70F1 烱 %1B%24%42%5F%58 -PASS U+70F9 烹 %1B%24%42%4B%23 -PASS U+70FD 烽 %1B%24%42%5F%62 -FAIL U+7104 焄 %1B%24%42%7A%77 assert_equals: expected "%1B%24%42%7A%77" but got "%26%23%32%38%39%33%32%3B" -PASS U+7109 焉 %1B%24%42%5F%61 -FAIL U+710F 焏 %1B%24%42%7A%76 assert_equals: expected "%1B%24%42%7A%76" but got "%26%23%32%38%39%34%33%3B" -PASS U+7114 焔 %1B%24%42%31%6B -PASS U+7119 焙 %1B%24%42%5F%64 -PASS U+711A 焚 %1B%24%42%4A%32 -PASS U+711C 焜 %1B%24%42%5F%63 -PASS U+7121 無 %1B%24%42%4C%35 -PASS U+7126 焦 %1B%24%42%3E%47 -PASS U+7136 然 %1B%24%42%41%33 -PASS U+713C 焼 %1B%24%42%3E%46 -FAIL U+7146 煆 %1B%24%42%7A%79 assert_equals: expected "%1B%24%42%7A%79" but got "%26%23%32%38%39%39%38%3B" -FAIL U+7147 煇 %1B%24%42%7A%7A assert_equals: expected "%1B%24%42%7A%7A" but got "%26%23%32%38%39%39%39%3B" -PASS U+7149 煉 %1B%24%42%4E%7B -PASS U+714C 煌 %1B%24%42%5F%6A -PASS U+714E 煎 %1B%24%42%40%79 -PASS U+7155 煕 %1B%24%42%5F%66 -PASS U+7156 煖 %1B%24%42%5F%6B -PASS U+7159 煙 %1B%24%42%31%6C -FAIL U+715C 煜 %1B%24%42%7A%78 assert_equals: expected "%1B%24%42%7A%78" but got "%26%23%32%39%30%32%30%3B" -PASS U+7162 煢 %1B%24%42%5F%69 -PASS U+7164 煤 %1B%24%42%47%61 -PASS U+7165 煥 %1B%24%42%5F%65 -PASS U+7166 煦 %1B%24%42%5F%68 -PASS U+7167 照 %1B%24%42%3E%48 -PASS U+7169 煩 %1B%24%42%48%51 -PASS U+716C 煬 %1B%24%42%5F%6C -PASS U+716E 煮 %1B%24%42%3C%51 -PASS U+717D 煽 %1B%24%42%40%7A -PASS U+7184 熄 %1B%24%42%5F%6F -PASS U+7188 熈 %1B%24%42%5F%67 -PASS U+718A 熊 %1B%24%42%37%27 -PASS U+718F 熏 %1B%24%42%5F%6D -PASS U+7194 熔 %1B%24%42%4D%50 -PASS U+7195 熕 %1B%24%42%5F%70 -PASS U+7199 熙 %1B%24%42%74%26 -PASS U+719F 熟 %1B%24%42%3D%4F -PASS U+71A8 熨 %1B%24%42%5F%71 -PASS U+71AC 熬 %1B%24%42%5F%72 -PASS U+71B1 熱 %1B%24%42%47%2E -PASS U+71B9 熹 %1B%24%42%5F%74 -PASS U+71BE 熾 %1B%24%42%5F%75 -FAIL U+71C1 燁 %1B%24%42%7A%7C assert_equals: expected "%1B%24%42%7A%7C" but got "%26%23%32%39%31%32%31%3B" -PASS U+71C3 燃 %1B%24%42%47%33 -PASS U+71C8 燈 %1B%24%42%45%75 -PASS U+71C9 燉 %1B%24%42%5F%77 -PASS U+71CE 燎 %1B%24%42%5F%79 -PASS U+71D0 燐 %1B%24%42%4E%55 -PASS U+71D2 燒 %1B%24%42%5F%76 -PASS U+71D4 燔 %1B%24%42%5F%78 -PASS U+71D5 燕 %1B%24%42%31%6D -PASS U+71D7 燗 %1B%24%42%5F%73 -PASS U+71DF 營 %1B%24%42%53%5B -PASS U+71E0 燠 %1B%24%42%5F%7A -PASS U+71E5 燥 %1B%24%42%41%67 -PASS U+71E6 燦 %1B%24%42%3B%38 -PASS U+71E7 燧 %1B%24%42%5F%7C -PASS U+71EC 燬 %1B%24%42%5F%7B -PASS U+71ED 燭 %1B%24%42%3F%24 -PASS U+71EE 燮 %1B%24%42%52%59 -PASS U+71F5 燵 %1B%24%42%5F%7D -PASS U+71F9 燹 %1B%24%42%60%21 -PASS U+71FB 燻 %1B%24%42%5F%6E -PASS U+71FC 燼 %1B%24%42%5F%7E -FAIL U+71FE 燾 %1B%24%42%7A%7D assert_equals: expected "%1B%24%42%7A%7D" but got "%26%23%32%39%31%38%32%3B" -PASS U+71FF 燿 %1B%24%42%60%22 -PASS U+7206 爆 %1B%24%42%47%7A -PASS U+720D 爍 %1B%24%42%60%23 -PASS U+7210 爐 %1B%24%42%60%24 -PASS U+721B 爛 %1B%24%42%60%25 -PASS U+7228 爨 %1B%24%42%60%26 -PASS U+722A 爪 %1B%24%42%44%5E -PASS U+722C 爬 %1B%24%42%60%28 -PASS U+722D 爭 %1B%24%42%60%27 -PASS U+7230 爰 %1B%24%42%60%29 -PASS U+7232 爲 %1B%24%42%60%2A -PASS U+7235 爵 %1B%24%42%3C%5F -PASS U+7236 父 %1B%24%42%49%63 -PASS U+723A 爺 %1B%24%42%4C%6C -PASS U+723B 爻 %1B%24%42%60%2B -PASS U+723C 爼 %1B%24%42%60%2C -PASS U+723D 爽 %1B%24%42%41%56 -PASS U+723E 爾 %1B%24%42%3C%24 -PASS U+723F 爿 %1B%24%42%60%2D -PASS U+7240 牀 %1B%24%42%60%2E -PASS U+7246 牆 %1B%24%42%60%2F -PASS U+7247 片 %1B%24%42%4A%52 -PASS U+7248 版 %1B%24%42%48%47 -PASS U+724B 牋 %1B%24%42%60%30 -PASS U+724C 牌 %1B%24%42%47%57 -PASS U+7252 牒 %1B%24%42%44%2D -PASS U+7258 牘 %1B%24%42%60%31 -PASS U+7259 牙 %1B%24%42%32%67 -PASS U+725B 牛 %1B%24%42%35%6D -PASS U+725D 牝 %1B%24%42%4C%46 -PASS U+725F 牟 %1B%24%42%4C%36 -PASS U+7261 牡 %1B%24%42%32%34 -PASS U+7262 牢 %1B%24%42%4F%34 -PASS U+7267 牧 %1B%24%42%4B%52 -PASS U+7269 物 %1B%24%42%4A%2A -PASS U+7272 牲 %1B%24%42%40%37 -PASS U+7274 牴 %1B%24%42%60%32 -PASS U+7279 特 %1B%24%42%46%43 -PASS U+727D 牽 %1B%24%42%38%23 -PASS U+727E 牾 %1B%24%42%60%33 -PASS U+7280 犀 %1B%24%42%3A%54 -PASS U+7281 犁 %1B%24%42%60%35 -PASS U+7282 犂 %1B%24%42%60%34 -PASS U+7287 犇 %1B%24%42%60%36 -PASS U+7292 犒 %1B%24%42%60%37 -PASS U+7296 犖 %1B%24%42%60%38 -PASS U+72A0 犠 %1B%24%42%35%3E -PASS U+72A2 犢 %1B%24%42%60%39 -PASS U+72A7 犧 %1B%24%42%60%3A -PASS U+72AC 犬 %1B%24%42%38%24 -PASS U+72AF 犯 %1B%24%42%48%48 -FAIL U+72B1 犱 %1B%24%42%7A%7E assert_equals: expected "%1B%24%42%7A%7E" but got "%26%23%32%39%33%36%31%3B" -PASS U+72B2 犲 %1B%24%42%60%3C -PASS U+72B6 状 %1B%24%42%3E%75 -PASS U+72B9 犹 %1B%24%42%60%3B -FAIL U+72BE 犾 %1B%24%42%7B%21 assert_equals: expected "%1B%24%42%7B%21" but got "%26%23%32%39%33%37%34%3B" -PASS U+72C2 狂 %1B%24%42%36%38 -PASS U+72C3 狃 %1B%24%42%60%3D -PASS U+72C4 狄 %1B%24%42%60%3F -PASS U+72C6 狆 %1B%24%42%60%3E -PASS U+72CE 狎 %1B%24%42%60%40 -PASS U+72D0 狐 %1B%24%42%38%51 -PASS U+72D2 狒 %1B%24%42%60%41 -PASS U+72D7 狗 %1B%24%42%36%69 -PASS U+72D9 狙 %1B%24%42%41%40 -PASS U+72DB 狛 %1B%24%42%39%7D -PASS U+72E0 狠 %1B%24%42%60%43 -PASS U+72E1 狡 %1B%24%42%60%44 -PASS U+72E2 狢 %1B%24%42%60%42 -PASS U+72E9 狩 %1B%24%42%3C%6D -PASS U+72EC 独 %1B%24%42%46%48 -PASS U+72ED 狭 %1B%24%42%36%39 -PASS U+72F7 狷 %1B%24%42%60%46 -PASS U+72F8 狸 %1B%24%42%43%2C -PASS U+72F9 狹 %1B%24%42%60%45 -PASS U+72FC 狼 %1B%24%42%4F%35 -PASS U+72FD 狽 %1B%24%42%47%62 -PASS U+730A 猊 %1B%24%42%60%49 -PASS U+7316 猖 %1B%24%42%60%4B -PASS U+7317 猗 %1B%24%42%60%48 -PASS U+731B 猛 %1B%24%42%4C%54 -PASS U+731C 猜 %1B%24%42%60%4A -PASS U+731D 猝 %1B%24%42%60%4C -PASS U+731F 猟 %1B%24%42%4E%44 -FAIL U+7324 猤 %1B%24%42%7B%22 assert_equals: expected "%1B%24%42%7B%22" but got "%26%23%32%39%34%37%36%3B" -PASS U+7325 猥 %1B%24%42%60%50 -PASS U+7329 猩 %1B%24%42%60%4F -PASS U+732A 猪 %1B%24%42%43%76 -PASS U+732B 猫 %1B%24%42%47%2D -PASS U+732E 献 %1B%24%42%38%25 -PASS U+732F 猯 %1B%24%42%60%4E -PASS U+7334 猴 %1B%24%42%60%4D -PASS U+7336 猶 %1B%24%42%4D%31 -PASS U+7337 猷 %1B%24%42%4D%32 -PASS U+733E 猾 %1B%24%42%60%51 -PASS U+733F 猿 %1B%24%42%31%6E -PASS U+7344 獄 %1B%24%42%39%76 -PASS U+7345 獅 %1B%24%42%3B%62 -PASS U+734E 獎 %1B%24%42%60%52 -PASS U+734F 獏 %1B%24%42%60%53 -PASS U+7357 獗 %1B%24%42%60%55 -PASS U+7363 獣 %1B%24%42%3D%43 -PASS U+7368 獨 %1B%24%42%60%57 -PASS U+736A 獪 %1B%24%42%60%56 -PASS U+7370 獰 %1B%24%42%60%58 -PASS U+7372 獲 %1B%24%42%33%4D -PASS U+7375 獵 %1B%24%42%60%5A -FAIL U+7377 獷 %1B%24%42%7B%24 assert_equals: expected "%1B%24%42%7B%24" but got "%26%23%32%39%35%35%39%3B" -PASS U+7378 獸 %1B%24%42%60%59 -PASS U+737A 獺 %1B%24%42%60%5C -PASS U+737B 獻 %1B%24%42%60%5B -PASS U+7384 玄 %1B%24%42%38%3C -PASS U+7387 率 %1B%24%42%4E%28 -PASS U+7389 玉 %1B%24%42%36%4C -PASS U+738B 王 %1B%24%42%32%26 -PASS U+7396 玖 %1B%24%42%36%6A -PASS U+73A9 玩 %1B%24%42%34%61 -PASS U+73B2 玲 %1B%24%42%4E%68 -PASS U+73B3 玳 %1B%24%42%60%5E -PASS U+73BB 玻 %1B%24%42%60%60 -FAIL U+73BD 玽 %1B%24%42%7B%25 assert_equals: expected "%1B%24%42%7B%25" but got "%26%23%32%39%36%32%39%3B" -PASS U+73C0 珀 %1B%24%42%60%61 -PASS U+73C2 珂 %1B%24%42%32%51 -PASS U+73C8 珈 %1B%24%42%60%5D -FAIL U+73C9 珉 %1B%24%42%7B%26 assert_equals: expected "%1B%24%42%7B%26" but got "%26%23%32%39%36%34%31%3B" -PASS U+73CA 珊 %1B%24%42%3B%39 -PASS U+73CD 珍 %1B%24%42%44%41 -PASS U+73CE 珎 %1B%24%42%60%5F -FAIL U+73D2 珒 %1B%24%42%7B%29 assert_equals: expected "%1B%24%42%7B%29" but got "%26%23%32%39%36%35%30%3B" -FAIL U+73D6 珖 %1B%24%42%7B%27 assert_equals: expected "%1B%24%42%7B%27" but got "%26%23%32%39%36%35%34%3B" -PASS U+73DE 珞 %1B%24%42%60%64 -PASS U+73E0 珠 %1B%24%42%3C%6E -FAIL U+73E3 珣 %1B%24%42%7B%28 assert_equals: expected "%1B%24%42%7B%28" but got "%26%23%32%39%36%36%37%3B" -PASS U+73E5 珥 %1B%24%42%60%62 -PASS U+73EA 珪 %1B%24%42%37%3E -PASS U+73ED 班 %1B%24%42%48%49 -PASS U+73EE 珮 %1B%24%42%60%63 -PASS U+73F1 珱 %1B%24%42%60%7E -FAIL U+73F5 珵 %1B%24%42%7B%2B assert_equals: expected "%1B%24%42%7B%2B" but got "%26%23%32%39%36%38%35%3B" -PASS U+73F8 珸 %1B%24%42%60%69 -PASS U+73FE 現 %1B%24%42%38%3D -PASS U+7403 球 %1B%24%42%35%65 -PASS U+7405 琅 %1B%24%42%60%66 -PASS U+7406 理 %1B%24%42%4D%7D -FAIL U+7407 琇 %1B%24%42%7B%2A assert_equals: expected "%1B%24%42%7B%2A" but got "%26%23%32%39%37%30%33%3B" -PASS U+7409 琉 %1B%24%42%4E%30 -PASS U+7422 琢 %1B%24%42%42%76 -PASS U+7425 琥 %1B%24%42%60%68 -FAIL U+7426 琦 %1B%24%42%7B%2C assert_equals: expected "%1B%24%42%7B%2C" but got "%26%23%32%39%37%33%34%3B" -FAIL U+7429 琩 %1B%24%42%7B%2E assert_equals: expected "%1B%24%42%7B%2E" but got "%26%23%32%39%37%33%37%3B" -FAIL U+742A 琪 %1B%24%42%7B%2D assert_equals: expected "%1B%24%42%7B%2D" but got "%26%23%32%39%37%33%38%3B" -FAIL U+742E 琮 %1B%24%42%7B%2F assert_equals: expected "%1B%24%42%7B%2F" but got "%26%23%32%39%37%34%32%3B" -PASS U+7432 琲 %1B%24%42%60%6A -PASS U+7433 琳 %1B%24%42%4E%56 -PASS U+7434 琴 %1B%24%42%36%57 -PASS U+7435 琵 %1B%24%42%48%7C -PASS U+7436 琶 %1B%24%42%47%4A -PASS U+743A 琺 %1B%24%42%60%6B -PASS U+743F 琿 %1B%24%42%60%6D -PASS U+7441 瑁 %1B%24%42%60%70 -PASS U+7455 瑕 %1B%24%42%60%6C -PASS U+7459 瑙 %1B%24%42%60%6F -PASS U+745A 瑚 %1B%24%42%38%6A -PASS U+745B 瑛 %1B%24%42%31%4D -PASS U+745C 瑜 %1B%24%42%60%71 -PASS U+745E 瑞 %1B%24%42%3F%70 -PASS U+745F 瑟 %1B%24%42%60%6E -PASS U+7460 瑠 %1B%24%42%4E%5C -FAIL U+7462 瑢 %1B%24%42%7B%30 assert_equals: expected "%1B%24%42%7B%30" but got "%26%23%32%39%37%39%34%3B" -PASS U+7463 瑣 %1B%24%42%60%74 -PASS U+7464 瑤 %1B%24%42%74%24 -PASS U+7469 瑩 %1B%24%42%60%72 -PASS U+746A 瑪 %1B%24%42%60%75 -PASS U+746F 瑯 %1B%24%42%60%67 -PASS U+7470 瑰 %1B%24%42%60%73 -PASS U+7473 瑳 %1B%24%42%3A%3C -PASS U+7476 瑶 %1B%24%42%60%76 -PASS U+747E 瑾 %1B%24%42%60%77 -PASS U+7483 璃 %1B%24%42%4D%7E -FAIL U+7489 璉 %1B%24%42%7B%31 assert_equals: expected "%1B%24%42%7B%31" but got "%26%23%32%39%38%33%33%3B" -PASS U+748B 璋 %1B%24%42%60%78 -PASS U+749E 璞 %1B%24%42%60%79 -FAIL U+749F 璟 %1B%24%42%7B%32 assert_equals: expected "%1B%24%42%7B%32" but got "%26%23%32%39%38%35%35%3B" -PASS U+74A2 璢 %1B%24%42%60%65 -PASS U+74A7 璧 %1B%24%42%60%7A -PASS U+74B0 環 %1B%24%42%34%44 -PASS U+74BD 璽 %1B%24%42%3C%25 -PASS U+74CA 瓊 %1B%24%42%60%7B -PASS U+74CF 瓏 %1B%24%42%60%7C -PASS U+74D4 瓔 %1B%24%42%60%7D -PASS U+74DC 瓜 %1B%24%42%31%3B -PASS U+74E0 瓠 %1B%24%42%61%21 -PASS U+74E2 瓢 %1B%24%42%49%3B -PASS U+74E3 瓣 %1B%24%42%61%22 -PASS U+74E6 瓦 %1B%24%42%34%24 -PASS U+74E7 瓧 %1B%24%42%61%23 -PASS U+74E9 瓩 %1B%24%42%61%24 -PASS U+74EE 瓮 %1B%24%42%61%25 -PASS U+74F0 瓰 %1B%24%42%61%27 -PASS U+74F1 瓱 %1B%24%42%61%28 -PASS U+74F2 瓲 %1B%24%42%61%26 -PASS U+74F6 瓶 %1B%24%42%49%53 -PASS U+74F7 瓷 %1B%24%42%61%2A -PASS U+74F8 瓸 %1B%24%42%61%29 -FAIL U+7501 甁 %1B%24%42%7B%33 assert_equals: expected "%1B%24%42%7B%33" but got "%26%23%32%39%39%35%33%3B" -PASS U+7503 甃 %1B%24%42%61%2C -PASS U+7504 甄 %1B%24%42%61%2B -PASS U+7505 甅 %1B%24%42%61%2D -PASS U+750C 甌 %1B%24%42%61%2E -PASS U+750D 甍 %1B%24%42%61%30 -PASS U+750E 甎 %1B%24%42%61%2F -PASS U+7511 甑 %1B%24%42%39%79 -PASS U+7513 甓 %1B%24%42%61%32 -PASS U+7515 甕 %1B%24%42%61%31 -PASS U+7518 甘 %1B%24%42%34%45 -PASS U+751A 甚 %1B%24%42%3F%53 -PASS U+751C 甜 %1B%24%42%45%3C -PASS U+751E 甞 %1B%24%42%61%33 -PASS U+751F 生 %1B%24%42%40%38 -PASS U+7523 産 %1B%24%42%3B%3A -PASS U+7525 甥 %1B%24%42%31%79 -PASS U+7526 甦 %1B%24%42%61%34 -PASS U+7528 用 %1B%24%42%4D%51 -PASS U+752B 甫 %1B%24%42%4A%63 -PASS U+752C 甬 %1B%24%42%61%35 -FAIL U+752F 甯 %1B%24%42%79%6C assert_equals: expected "%1B%24%42%79%6C" but got "%26%23%32%39%39%39%39%3B" -PASS U+7530 田 %1B%24%42%45%44 -PASS U+7531 由 %1B%24%42%4D%33 -PASS U+7532 甲 %1B%24%42%39%43 -PASS U+7533 申 %1B%24%42%3F%3D -PASS U+7537 男 %1B%24%42%43%4B -PASS U+7538 甸 %1B%24%42%52%34 -PASS U+753A 町 %1B%24%42%44%2E -PASS U+753B 画 %1B%24%42%32%68 -PASS U+753C 甼 %1B%24%42%61%36 -PASS U+7544 畄 %1B%24%42%61%37 -PASS U+7546 畆 %1B%24%42%61%3C -PASS U+7549 畉 %1B%24%42%61%3A -PASS U+754A 畊 %1B%24%42%61%39 -PASS U+754B 畋 %1B%24%42%5A%42 -PASS U+754C 界 %1B%24%42%33%26 -PASS U+754D 畍 %1B%24%42%61%38 -PASS U+754F 畏 %1B%24%42%30%5A -PASS U+7551 畑 %1B%24%42%48%2A -PASS U+7554 畔 %1B%24%42%48%4A -PASS U+7559 留 %1B%24%42%4E%31 -PASS U+755A 畚 %1B%24%42%61%3D -PASS U+755B 畛 %1B%24%42%61%3B -PASS U+755C 畜 %1B%24%42%43%5C -PASS U+755D 畝 %1B%24%42%40%26 -PASS U+7560 畠 %1B%24%42%48%2B -PASS U+7562 畢 %1B%24%42%49%2D -PASS U+7564 畤 %1B%24%42%61%3F -PASS U+7565 略 %1B%24%42%4E%2C -PASS U+7566 畦 %1B%24%42%37%4D -PASS U+7567 畧 %1B%24%42%61%40 -PASS U+7569 畩 %1B%24%42%61%3E -PASS U+756A 番 %1B%24%42%48%56 -PASS U+756B 畫 %1B%24%42%61%41 -PASS U+756D 畭 %1B%24%42%61%42 -FAIL U+756F 畯 %1B%24%42%7B%34 assert_equals: expected "%1B%24%42%7B%34" but got "%26%23%33%30%30%36%33%3B" -PASS U+7570 異 %1B%24%42%30%5B -PASS U+7573 畳 %1B%24%42%3E%76 -PASS U+7574 畴 %1B%24%42%61%47 -PASS U+7576 當 %1B%24%42%61%44 -PASS U+7577 畷 %1B%24%42%46%6D -PASS U+7578 畸 %1B%24%42%61%43 -PASS U+757F 畿 %1B%24%42%35%26 -PASS U+7582 疂 %1B%24%42%61%4A -PASS U+7586 疆 %1B%24%42%61%45 -PASS U+7587 疇 %1B%24%42%61%46 -PASS U+7589 疉 %1B%24%42%61%49 -PASS U+758A 疊 %1B%24%42%61%48 -PASS U+758B 疋 %1B%24%42%49%25 -PASS U+758E 疎 %1B%24%42%41%42 -PASS U+758F 疏 %1B%24%42%41%41 -PASS U+7591 疑 %1B%24%42%35%3F -PASS U+7594 疔 %1B%24%42%61%4B -PASS U+759A 疚 %1B%24%42%61%4C -PASS U+759D 疝 %1B%24%42%61%4D -PASS U+75A3 疣 %1B%24%42%61%4F -PASS U+75A5 疥 %1B%24%42%61%4E -PASS U+75AB 疫 %1B%24%42%31%56 -PASS U+75B1 疱 %1B%24%42%61%57 -PASS U+75B2 疲 %1B%24%42%48%68 -PASS U+75B3 疳 %1B%24%42%61%51 -PASS U+75B5 疵 %1B%24%42%61%53 -PASS U+75B8 疸 %1B%24%42%61%55 -PASS U+75B9 疹 %1B%24%42%3F%3E -PASS U+75BC 疼 %1B%24%42%61%56 -PASS U+75BD 疽 %1B%24%42%61%54 -PASS U+75BE 疾 %1B%24%42%3C%40 -PASS U+75C2 痂 %1B%24%42%61%50 -PASS U+75C3 痃 %1B%24%42%61%52 -PASS U+75C5 病 %1B%24%42%49%42 -PASS U+75C7 症 %1B%24%42%3E%49 -PASS U+75CA 痊 %1B%24%42%61%59 -PASS U+75CD 痍 %1B%24%42%61%58 -PASS U+75D2 痒 %1B%24%42%61%5A -PASS U+75D4 痔 %1B%24%42%3C%26 -PASS U+75D5 痕 %1B%24%42%3A%2F -PASS U+75D8 痘 %1B%24%42%45%77 -PASS U+75D9 痙 %1B%24%42%61%5B -PASS U+75DB 痛 %1B%24%42%44%4B -PASS U+75DE 痞 %1B%24%42%61%5D -PASS U+75E2 痢 %1B%24%42%4E%21 -PASS U+75E3 痣 %1B%24%42%61%5C -PASS U+75E9 痩 %1B%24%42%41%69 -PASS U+75F0 痰 %1B%24%42%61%62 -PASS U+75F2 痲 %1B%24%42%61%64 -PASS U+75F3 痳 %1B%24%42%61%65 -PASS U+75F4 痴 %1B%24%42%43%54 -PASS U+75FA 痺 %1B%24%42%61%63 -PASS U+75FC 痼 %1B%24%42%61%60 -PASS U+75FE 痾 %1B%24%42%61%5E -PASS U+75FF 痿 %1B%24%42%61%5F -PASS U+7601 瘁 %1B%24%42%61%61 -PASS U+7609 瘉 %1B%24%42%61%68 -PASS U+760B 瘋 %1B%24%42%61%66 -PASS U+760D 瘍 %1B%24%42%61%67 -PASS U+761F 瘟 %1B%24%42%61%69 -PASS U+7620 瘠 %1B%24%42%61%6B -PASS U+7621 瘡 %1B%24%42%61%6C -PASS U+7622 瘢 %1B%24%42%61%6D -PASS U+7624 瘤 %1B%24%42%61%6E -PASS U+7627 瘧 %1B%24%42%61%6A -PASS U+7630 瘰 %1B%24%42%61%70 -PASS U+7634 瘴 %1B%24%42%61%6F -PASS U+763B 瘻 %1B%24%42%61%71 -PASS U+7642 療 %1B%24%42%4E%45 -PASS U+7646 癆 %1B%24%42%61%74 -PASS U+7647 癇 %1B%24%42%61%72 -PASS U+7648 癈 %1B%24%42%61%73 -PASS U+764C 癌 %1B%24%42%34%62 -PASS U+7652 癒 %1B%24%42%4C%7E -PASS U+7656 癖 %1B%24%42%4A%4A -PASS U+7658 癘 %1B%24%42%61%76 -PASS U+765C 癜 %1B%24%42%61%75 -PASS U+7661 癡 %1B%24%42%61%77 -PASS U+7662 癢 %1B%24%42%61%78 -PASS U+7667 癧 %1B%24%42%61%7C -PASS U+7668 癨 %1B%24%42%61%79 -PASS U+7669 癩 %1B%24%42%61%7A -PASS U+766A 癪 %1B%24%42%61%7B -PASS U+766C 癬 %1B%24%42%61%7D -PASS U+7670 癰 %1B%24%42%61%7E -PASS U+7672 癲 %1B%24%42%62%21 -PASS U+7676 癶 %1B%24%42%62%22 -PASS U+7678 癸 %1B%24%42%62%23 -PASS U+767A 発 %1B%24%42%48%2F -PASS U+767B 登 %1B%24%42%45%50 -PASS U+767C 發 %1B%24%42%62%24 -PASS U+767D 白 %1B%24%42%47%72 -PASS U+767E 百 %1B%24%42%49%34 -PASS U+7680 皀 %1B%24%42%62%25 -FAIL U+7682 皂 %1B%24%42%7B%35 assert_equals: expected "%1B%24%42%7B%35" but got "%26%23%33%30%33%33%38%3B" -PASS U+7683 皃 %1B%24%42%62%26 -PASS U+7684 的 %1B%24%42%45%2A -PASS U+7686 皆 %1B%24%42%33%27 -PASS U+7687 皇 %1B%24%42%39%44 -PASS U+7688 皈 %1B%24%42%62%27 -PASS U+768B 皋 %1B%24%42%62%28 -PASS U+768E 皎 %1B%24%42%62%29 -PASS U+7690 皐 %1B%24%42%3B%29 -PASS U+7693 皓 %1B%24%42%62%2B -PASS U+7696 皖 %1B%24%42%62%2A -PASS U+7699 皙 %1B%24%42%62%2C -PASS U+769A 皚 %1B%24%42%62%2D -FAIL U+769B 皛 %1B%24%42%7B%38 assert_equals: expected "%1B%24%42%7B%38" but got "%26%23%33%30%33%36%33%3B" -FAIL U+769C 皜 %1B%24%42%7B%36 assert_equals: expected "%1B%24%42%7B%36" but got "%26%23%33%30%33%36%34%3B" -FAIL U+769E 皞 %1B%24%42%7B%37 assert_equals: expected "%1B%24%42%7B%37" but got "%26%23%33%30%33%36%36%3B" -FAIL U+76A6 皦 %1B%24%42%7B%39 assert_equals: expected "%1B%24%42%7B%39" but got "%26%23%33%30%33%37%34%3B" -PASS U+76AE 皮 %1B%24%42%48%69 -PASS U+76B0 皰 %1B%24%42%62%2E -PASS U+76B4 皴 %1B%24%42%62%2F -PASS U+76B7 皷 %1B%24%42%73%69 -PASS U+76B8 皸 %1B%24%42%62%30 -PASS U+76B9 皹 %1B%24%42%62%31 -PASS U+76BA 皺 %1B%24%42%62%32 -PASS U+76BF 皿 %1B%24%42%3B%2E -PASS U+76C2 盂 %1B%24%42%62%33 -PASS U+76C3 盃 %1B%24%42%47%56 -PASS U+76C6 盆 %1B%24%42%4B%5F -PASS U+76C8 盈 %1B%24%42%31%4E -PASS U+76CA 益 %1B%24%42%31%57 -PASS U+76CD 盍 %1B%24%42%62%34 -PASS U+76D2 盒 %1B%24%42%62%36 -PASS U+76D6 盖 %1B%24%42%62%35 -PASS U+76D7 盗 %1B%24%42%45%70 -PASS U+76DB 盛 %1B%24%42%40%39 -PASS U+76DC 盜 %1B%24%42%5D%39 -PASS U+76DE 盞 %1B%24%42%62%37 -PASS U+76DF 盟 %1B%24%42%4C%41 -PASS U+76E1 盡 %1B%24%42%62%38 -PASS U+76E3 監 %1B%24%42%34%46 -PASS U+76E4 盤 %1B%24%42%48%57 -PASS U+76E5 盥 %1B%24%42%62%39 -PASS U+76E7 盧 %1B%24%42%62%3A -PASS U+76EA 盪 %1B%24%42%62%3B -PASS U+76EE 目 %1B%24%42%4C%5C -PASS U+76F2 盲 %1B%24%42%4C%55 -PASS U+76F4 直 %1B%24%42%44%3E -PASS U+76F8 相 %1B%24%42%41%6A -PASS U+76FB 盻 %1B%24%42%62%3D -PASS U+76FE 盾 %1B%24%42%3D%62 -PASS U+7701 省 %1B%24%42%3E%4A -PASS U+7704 眄 %1B%24%42%62%40 -PASS U+7707 眇 %1B%24%42%62%3F -PASS U+7708 眈 %1B%24%42%62%3E -PASS U+7709 眉 %1B%24%42%48%7D -PASS U+770B 看 %1B%24%42%34%47 -PASS U+770C 県 %1B%24%42%38%29 -PASS U+771B 眛 %1B%24%42%62%46 -PASS U+771E 眞 %1B%24%42%62%43 -PASS U+771F 真 %1B%24%42%3F%3F -PASS U+7720 眠 %1B%24%42%4C%32 -PASS U+7724 眤 %1B%24%42%62%42 -PASS U+7725 眥 %1B%24%42%62%44 -PASS U+7726 眦 %1B%24%42%62%45 -PASS U+7729 眩 %1B%24%42%62%41 -PASS U+7737 眷 %1B%24%42%62%47 -PASS U+7738 眸 %1B%24%42%62%48 -PASS U+773A 眺 %1B%24%42%44%2F -PASS U+773C 眼 %1B%24%42%34%63 -PASS U+7740 着 %1B%24%42%43%65 -FAIL U+7746 睆 %1B%24%42%7B%3B assert_equals: expected "%1B%24%42%7B%3B" but got "%26%23%33%30%35%33%34%3B" -PASS U+7747 睇 %1B%24%42%62%49 -PASS U+775A 睚 %1B%24%42%62%4A -PASS U+775B 睛 %1B%24%42%62%4D -PASS U+7761 睡 %1B%24%42%3F%67 -PASS U+7763 督 %1B%24%42%46%44 -PASS U+7765 睥 %1B%24%42%62%4E -PASS U+7766 睦 %1B%24%42%4B%53 -PASS U+7768 睨 %1B%24%42%62%4B -PASS U+776B 睫 %1B%24%42%62%4C -PASS U+7779 睹 %1B%24%42%62%51 -PASS U+777E 睾 %1B%24%42%62%50 -PASS U+777F 睿 %1B%24%42%62%4F -PASS U+778B 瞋 %1B%24%42%62%53 -PASS U+778E 瞎 %1B%24%42%62%52 -PASS U+7791 瞑 %1B%24%42%62%54 -PASS U+779E 瞞 %1B%24%42%62%56 -PASS U+77A0 瞠 %1B%24%42%62%55 -PASS U+77A5 瞥 %1B%24%42%4A%4D -PASS U+77AC 瞬 %1B%24%42%3D%56 -PASS U+77AD 瞭 %1B%24%42%4E%46 -PASS U+77B0 瞰 %1B%24%42%62%57 -PASS U+77B3 瞳 %1B%24%42%46%37 -PASS U+77B6 瞶 %1B%24%42%62%58 -PASS U+77B9 瞹 %1B%24%42%62%59 -PASS U+77BB 瞻 %1B%24%42%62%5D -PASS U+77BC 瞼 %1B%24%42%62%5B -PASS U+77BD 瞽 %1B%24%42%62%5C -PASS U+77BF 瞿 %1B%24%42%62%5A -PASS U+77C7 矇 %1B%24%42%62%5E -PASS U+77CD 矍 %1B%24%42%62%5F -PASS U+77D7 矗 %1B%24%42%62%60 -PASS U+77DA 矚 %1B%24%42%62%61 -PASS U+77DB 矛 %1B%24%42%4C%37 -PASS U+77DC 矜 %1B%24%42%62%62 -PASS U+77E2 矢 %1B%24%42%4C%70 -PASS U+77E3 矣 %1B%24%42%62%63 -PASS U+77E5 知 %1B%24%42%43%4E -PASS U+77E7 矧 %1B%24%42%47%6A -PASS U+77E9 矩 %1B%24%42%36%6B -PASS U+77ED 短 %1B%24%42%43%3B -PASS U+77EE 矮 %1B%24%42%62%64 -PASS U+77EF 矯 %1B%24%42%36%3A -PASS U+77F3 石 %1B%24%42%40%50 -PASS U+77FC 矼 %1B%24%42%62%65 -PASS U+7802 砂 %1B%24%42%3A%3D -PASS U+780C 砌 %1B%24%42%62%66 -PASS U+7812 砒 %1B%24%42%62%67 -PASS U+7814 研 %1B%24%42%38%26 -PASS U+7815 砕 %1B%24%42%3A%55 -PASS U+7820 砠 %1B%24%42%62%69 -FAIL U+7821 砡 %1B%24%42%7B%3D assert_equals: expected "%1B%24%42%7B%3D" but got "%26%23%33%30%37%35%33%3B" -PASS U+7825 砥 %1B%24%42%45%56 -PASS U+7826 砦 %1B%24%42%3A%56 -PASS U+7827 砧 %1B%24%42%35%4E -PASS U+7832 砲 %1B%24%42%4B%24 -PASS U+7834 破 %1B%24%42%47%4B -PASS U+783A 砺 %1B%24%42%45%57 -PASS U+783F 砿 %1B%24%42%39%5C -PASS U+7845 硅 %1B%24%42%62%6B -FAIL U+784E 硎 %1B%24%42%7B%3E assert_equals: expected "%1B%24%42%7B%3E" but got "%26%23%33%30%37%39%38%3B" -PASS U+785D 硝 %1B%24%42%3E%4B -FAIL U+7864 硤 %1B%24%42%7B%3F assert_equals: expected "%1B%24%42%7B%3F" but got "%26%23%33%30%38%32%30%3B" -PASS U+786B 硫 %1B%24%42%4E%32 -PASS U+786C 硬 %1B%24%42%39%45 -PASS U+786F 硯 %1B%24%42%38%27 -PASS U+7872 硲 %1B%24%42%48%23 -PASS U+7874 硴 %1B%24%42%62%6D -FAIL U+787A 硺 %1B%24%42%7B%40 assert_equals: expected "%1B%24%42%7B%40" but got "%26%23%33%30%38%34%32%3B" -PASS U+787C 硼 %1B%24%42%62%6F -PASS U+7881 碁 %1B%24%42%38%6B -PASS U+7886 碆 %1B%24%42%62%6E -PASS U+7887 碇 %1B%24%42%44%76 -PASS U+788C 碌 %1B%24%42%62%71 -PASS U+788D 碍 %1B%24%42%33%37 -PASS U+788E 碎 %1B%24%42%62%6C -PASS U+7891 碑 %1B%24%42%48%6A -PASS U+7893 碓 %1B%24%42%31%30 -PASS U+7895 碕 %1B%24%42%3A%6C -PASS U+7897 碗 %1B%24%42%4F%52 -PASS U+789A 碚 %1B%24%42%62%70 -PASS U+78A3 碣 %1B%24%42%62%72 -PASS U+78A7 碧 %1B%24%42%4A%4B -PASS U+78A9 碩 %1B%24%42%40%59 -PASS U+78AA 碪 %1B%24%42%62%74 -PASS U+78AF 碯 %1B%24%42%62%75 -PASS U+78B5 碵 %1B%24%42%62%73 -PASS U+78BA 確 %1B%24%42%33%4E -PASS U+78BC 碼 %1B%24%42%62%7B -PASS U+78BE 碾 %1B%24%42%62%7A -PASS U+78C1 磁 %1B%24%42%3C%27 -PASS U+78C5 磅 %1B%24%42%62%7C -PASS U+78C6 磆 %1B%24%42%62%77 -PASS U+78CA 磊 %1B%24%42%62%7D -PASS U+78CB 磋 %1B%24%42%62%78 -PASS U+78D0 磐 %1B%24%42%48%58 -PASS U+78D1 磑 %1B%24%42%62%76 -PASS U+78D4 磔 %1B%24%42%62%79 -PASS U+78DA 磚 %1B%24%42%63%22 -PASS U+78E7 磧 %1B%24%42%63%21 -PASS U+78E8 磨 %1B%24%42%4B%61 -PASS U+78EC 磬 %1B%24%42%62%7E -PASS U+78EF 磯 %1B%24%42%30%6B -PASS U+78F4 磴 %1B%24%42%63%24 -PASS U+78FD 磽 %1B%24%42%63%23 -PASS U+7901 礁 %1B%24%42%3E%4C -PASS U+7907 礇 %1B%24%42%63%25 -PASS U+790E 礎 %1B%24%42%41%43 -PASS U+7911 礑 %1B%24%42%63%27 -PASS U+7912 礒 %1B%24%42%63%26 -PASS U+7919 礙 %1B%24%42%63%28 -PASS U+7926 礦 %1B%24%42%62%68 -PASS U+792A 礪 %1B%24%42%62%6A -PASS U+792B 礫 %1B%24%42%63%2A -PASS U+792C 礬 %1B%24%42%63%29 -FAIL U+7930 礰 %1B%24%42%7B%41 assert_equals: expected "%1B%24%42%7B%41" but got "%26%23%33%31%30%32%34%3B" -PASS U+793A 示 %1B%24%42%3C%28 -PASS U+793C 礼 %1B%24%42%4E%69 -PASS U+793E 社 %1B%24%42%3C%52 -PASS U+7940 祀 %1B%24%42%63%2B -PASS U+7941 祁 %1B%24%42%37%37 -PASS U+7947 祇 %1B%24%42%35%40 -PASS U+7948 祈 %1B%24%42%35%27 -PASS U+7949 祉 %1B%24%42%3B%63 -PASS U+7950 祐 %1B%24%42%4D%34 -PASS U+7953 祓 %1B%24%42%63%31 -PASS U+7955 祕 %1B%24%42%63%30 -PASS U+7956 祖 %1B%24%42%41%44 -PASS U+7957 祗 %1B%24%42%63%2D -PASS U+795A 祚 %1B%24%42%63%2F -PASS U+795D 祝 %1B%24%42%3D%4B -PASS U+795E 神 %1B%24%42%3F%40 -PASS U+795F 祟 %1B%24%42%63%2E -PASS U+7960 祠 %1B%24%42%63%2C -PASS U+7962 祢 %1B%24%42%47%2A -PASS U+7965 祥 %1B%24%42%3E%4D -PASS U+7968 票 %1B%24%42%49%3C -PASS U+796D 祭 %1B%24%42%3A%57 -PASS U+7977 祷 %1B%24%42%45%78 -PASS U+797A 祺 %1B%24%42%63%32 -PASS U+797F 祿 %1B%24%42%63%33 -PASS U+7980 禀 %1B%24%42%63%49 -PASS U+7981 禁 %1B%24%42%36%58 -PASS U+7984 禄 %1B%24%42%4F%3D -PASS U+7985 禅 %1B%24%42%41%35 -PASS U+798A 禊 %1B%24%42%63%34 -PASS U+798D 禍 %1B%24%42%32%52 -PASS U+798E 禎 %1B%24%42%44%77 -PASS U+798F 福 %1B%24%42%4A%21 -FAIL U+7994 禔 %1B%24%42%7B%45 assert_equals: expected "%1B%24%42%7B%45" but got "%26%23%33%31%31%32%34%3B" -FAIL U+799B 禛 %1B%24%42%7B%47 assert_equals: expected "%1B%24%42%7B%47" but got "%26%23%33%31%31%33%31%3B" -PASS U+799D 禝 %1B%24%42%63%35 -PASS U+79A6 禦 %1B%24%42%35%7A -PASS U+79A7 禧 %1B%24%42%63%36 -PASS U+79AA 禪 %1B%24%42%63%38 -PASS U+79AE 禮 %1B%24%42%63%39 -PASS U+79B0 禰 %1B%24%42%47%29 -PASS U+79B3 禳 %1B%24%42%63%3A -PASS U+79B9 禹 %1B%24%42%63%3B -PASS U+79BA 禺 %1B%24%42%63%3C -PASS U+79BD 禽 %1B%24%42%36%59 -PASS U+79BE 禾 %1B%24%42%32%53 -PASS U+79BF 禿 %1B%24%42%46%45 -PASS U+79C0 秀 %1B%24%42%3D%28 -PASS U+79C1 私 %1B%24%42%3B%64 -PASS U+79C9 秉 %1B%24%42%63%3D -PASS U+79CB 秋 %1B%24%42%3D%29 -PASS U+79D1 科 %1B%24%42%32%4A -PASS U+79D2 秒 %1B%24%42%49%43 -PASS U+79D5 秕 %1B%24%42%63%3E -PASS U+79D8 秘 %1B%24%42%48%6B -PASS U+79DF 租 %1B%24%42%41%45 -PASS U+79E1 秡 %1B%24%42%63%41 -PASS U+79E3 秣 %1B%24%42%63%42 -PASS U+79E4 秤 %1B%24%42%47%69 -PASS U+79E6 秦 %1B%24%42%3F%41 -PASS U+79E7 秧 %1B%24%42%63%3F -PASS U+79E9 秩 %1B%24%42%43%61 -PASS U+79EC 秬 %1B%24%42%63%40 -PASS U+79F0 称 %1B%24%42%3E%4E -PASS U+79FB 移 %1B%24%42%30%5C -PASS U+7A00 稀 %1B%24%42%35%29 -PASS U+7A08 稈 %1B%24%42%63%43 -PASS U+7A0B 程 %1B%24%42%44%78 -PASS U+7A0D 稍 %1B%24%42%63%44 -PASS U+7A0E 税 %1B%24%42%40%47 -PASS U+7A14 稔 %1B%24%42%4C%2D -PASS U+7A17 稗 %1B%24%42%49%23 -PASS U+7A18 稘 %1B%24%42%63%45 -PASS U+7A19 稙 %1B%24%42%63%46 -PASS U+7A1A 稚 %1B%24%42%43%55 -PASS U+7A1C 稜 %1B%24%42%4E%47 -PASS U+7A1F 稟 %1B%24%42%63%48 -PASS U+7A20 稠 %1B%24%42%63%47 -PASS U+7A2E 種 %1B%24%42%3C%6F -PASS U+7A31 稱 %1B%24%42%63%4A -PASS U+7A32 稲 %1B%24%42%30%70 -PASS U+7A37 稷 %1B%24%42%63%4D -PASS U+7A3B 稻 %1B%24%42%63%4B -PASS U+7A3C 稼 %1B%24%42%32%54 -PASS U+7A3D 稽 %1B%24%42%37%4E -PASS U+7A3E 稾 %1B%24%42%63%4C -PASS U+7A3F 稿 %1B%24%42%39%46 -PASS U+7A40 穀 %1B%24%42%39%72 -PASS U+7A42 穂 %1B%24%42%4A%66 -PASS U+7A43 穃 %1B%24%42%63%4E -PASS U+7A46 穆 %1B%24%42%4B%54 -PASS U+7A49 穉 %1B%24%42%63%50 -PASS U+7A4D 積 %1B%24%42%40%51 -PASS U+7A4E 穎 %1B%24%42%31%4F -PASS U+7A4F 穏 %1B%24%42%32%3A -PASS U+7A50 穐 %1B%24%42%30%2C -PASS U+7A57 穗 %1B%24%42%63%4F -PASS U+7A61 穡 %1B%24%42%63%51 -PASS U+7A62 穢 %1B%24%42%63%52 -PASS U+7A63 穣 %1B%24%42%3E%77 -PASS U+7A69 穩 %1B%24%42%63%53 -PASS U+7A6B 穫 %1B%24%42%33%4F -PASS U+7A70 穰 %1B%24%42%63%55 -PASS U+7A74 穴 %1B%24%42%37%6A -PASS U+7A76 究 %1B%24%42%35%66 -PASS U+7A79 穹 %1B%24%42%63%56 -PASS U+7A7A 空 %1B%24%42%36%75 -PASS U+7A7D 穽 %1B%24%42%63%57 -PASS U+7A7F 穿 %1B%24%42%40%7C -PASS U+7A81 突 %1B%24%42%46%4D -PASS U+7A83 窃 %1B%24%42%40%60 -PASS U+7A84 窄 %1B%24%42%3A%75 -PASS U+7A88 窈 %1B%24%42%63%58 -PASS U+7A92 窒 %1B%24%42%43%62 -PASS U+7A93 窓 %1B%24%42%41%6B -PASS U+7A95 窕 %1B%24%42%63%5A -PASS U+7A96 窖 %1B%24%42%63%5C -PASS U+7A97 窗 %1B%24%42%63%59 -PASS U+7A98 窘 %1B%24%42%63%5B -PASS U+7A9F 窟 %1B%24%42%37%22 -PASS U+7AA9 窩 %1B%24%42%63%5D -PASS U+7AAA 窪 %1B%24%42%37%26 -PASS U+7AAE 窮 %1B%24%42%35%67 -PASS U+7AAF 窯 %1B%24%42%4D%52 -PASS U+7AB0 窰 %1B%24%42%63%5F -PASS U+7AB6 窶 %1B%24%42%63%60 -PASS U+7ABA 窺 %1B%24%42%31%2E -PASS U+7ABF 窿 %1B%24%42%63%63 -PASS U+7AC3 竃 %1B%24%42%33%76 -PASS U+7AC4 竄 %1B%24%42%63%62 -PASS U+7AC5 竅 %1B%24%42%63%61 -PASS U+7AC7 竇 %1B%24%42%63%65 -PASS U+7AC8 竈 %1B%24%42%63%5E -PASS U+7ACA 竊 %1B%24%42%63%66 -PASS U+7ACB 立 %1B%24%42%4E%29 -PASS U+7ACD 竍 %1B%24%42%63%67 -PASS U+7ACF 竏 %1B%24%42%63%68 -FAIL U+7AD1 竑 %1B%24%42%7B%48 assert_equals: expected "%1B%24%42%7B%48" but got "%26%23%33%31%34%34%31%3B" -PASS U+7AD2 竒 %1B%24%42%54%74 -PASS U+7AD3 竓 %1B%24%42%63%6A -PASS U+7AD5 竕 %1B%24%42%63%69 -PASS U+7AD9 站 %1B%24%42%63%6B -PASS U+7ADA 竚 %1B%24%42%63%6C -PASS U+7ADC 竜 %1B%24%42%4E%35 -PASS U+7ADD 竝 %1B%24%42%63%6D -PASS U+7ADF 竟 %1B%24%42%70%6F -PASS U+7AE0 章 %1B%24%42%3E%4F -PASS U+7AE1 竡 %1B%24%42%63%6E -PASS U+7AE2 竢 %1B%24%42%63%6F -PASS U+7AE3 竣 %1B%24%42%3D%57 -PASS U+7AE5 童 %1B%24%42%46%38 -PASS U+7AE6 竦 %1B%24%42%63%70 -FAIL U+7AE7 竧 %1B%24%42%7B%49 assert_equals: expected "%1B%24%42%7B%49" but got "%26%23%33%31%34%36%33%3B" -PASS U+7AEA 竪 %1B%24%42%43%28 -FAIL U+7AEB 竫 %1B%24%42%7B%4B assert_equals: expected "%1B%24%42%7B%4B" but got "%26%23%33%31%34%36%37%3B" -PASS U+7AED 竭 %1B%24%42%63%71 -PASS U+7AEF 端 %1B%24%42%43%3C -PASS U+7AF0 竰 %1B%24%42%63%72 -PASS U+7AF6 競 %1B%24%42%36%25 -PASS U+7AF8 竸 %1B%24%42%51%3F -PASS U+7AF9 竹 %1B%24%42%43%5D -PASS U+7AFA 竺 %1B%24%42%3C%33 -PASS U+7AFF 竿 %1B%24%42%34%48 -PASS U+7B02 笂 %1B%24%42%63%73 -PASS U+7B04 笄 %1B%24%42%64%22 -PASS U+7B06 笆 %1B%24%42%63%76 -PASS U+7B08 笈 %1B%24%42%35%68 -PASS U+7B0A 笊 %1B%24%42%63%75 -PASS U+7B0B 笋 %1B%24%42%64%24 -PASS U+7B0F 笏 %1B%24%42%63%74 -PASS U+7B11 笑 %1B%24%42%3E%50 -PASS U+7B18 笘 %1B%24%42%63%78 -PASS U+7B19 笙 %1B%24%42%63%79 -PASS U+7B1B 笛 %1B%24%42%45%2B -PASS U+7B1E 笞 %1B%24%42%63%7A -PASS U+7B20 笠 %1B%24%42%33%5E -PASS U+7B25 笥 %1B%24%42%3F%5A -PASS U+7B26 符 %1B%24%42%49%64 -PASS U+7B28 笨 %1B%24%42%63%7C -PASS U+7B2C 第 %1B%24%42%42%68 -PASS U+7B33 笳 %1B%24%42%63%77 -PASS U+7B35 笵 %1B%24%42%63%7B -PASS U+7B36 笶 %1B%24%42%63%7D -PASS U+7B39 笹 %1B%24%42%3A%7B -PASS U+7B45 筅 %1B%24%42%64%26 -PASS U+7B46 筆 %1B%24%42%49%2E -PASS U+7B48 筈 %1B%24%42%48%26 -PASS U+7B49 等 %1B%24%42%45%79 -PASS U+7B4B 筋 %1B%24%42%36%5A -PASS U+7B4C 筌 %1B%24%42%64%25 -PASS U+7B4D 筍 %1B%24%42%64%23 -PASS U+7B4F 筏 %1B%24%42%48%35 -PASS U+7B50 筐 %1B%24%42%63%7E -PASS U+7B51 筑 %1B%24%42%43%5E -PASS U+7B52 筒 %1B%24%42%45%7B -PASS U+7B54 答 %1B%24%42%45%7A -PASS U+7B56 策 %1B%24%42%3A%76 -PASS U+7B5D 筝 %1B%24%42%64%38 -PASS U+7B65 筥 %1B%24%42%64%28 -PASS U+7B67 筧 %1B%24%42%64%2A -PASS U+7B6C 筬 %1B%24%42%64%2D -PASS U+7B6E 筮 %1B%24%42%64%2E -PASS U+7B70 筰 %1B%24%42%64%2B -PASS U+7B71 筱 %1B%24%42%64%2C -PASS U+7B74 筴 %1B%24%42%64%29 -PASS U+7B75 筵 %1B%24%42%64%27 -PASS U+7B7A 筺 %1B%24%42%64%21 -PASS U+7B86 箆 %1B%24%42%4A%4F -PASS U+7B87 箇 %1B%24%42%32%55 -PASS U+7B8B 箋 %1B%24%42%64%35 -PASS U+7B8D 箍 %1B%24%42%64%32 -PASS U+7B8F 箏 %1B%24%42%64%37 -PASS U+7B92 箒 %1B%24%42%64%36 -PASS U+7B94 箔 %1B%24%42%47%73 -PASS U+7B95 箕 %1B%24%42%4C%27 -PASS U+7B97 算 %1B%24%42%3B%3B -PASS U+7B98 箘 %1B%24%42%64%30 -PASS U+7B99 箙 %1B%24%42%64%39 -PASS U+7B9A 箚 %1B%24%42%64%34 -PASS U+7B9C 箜 %1B%24%42%64%33 -PASS U+7B9D 箝 %1B%24%42%64%2F -FAIL U+7B9E 箞 %1B%24%42%7B%4C assert_equals: expected "%1B%24%42%7B%4C" but got "%26%23%33%31%36%34%36%3B" -PASS U+7B9F 箟 %1B%24%42%64%31 -PASS U+7BA1 管 %1B%24%42%34%49 -PASS U+7BAA 箪 %1B%24%42%43%3D -PASS U+7BAD 箭 %1B%24%42%40%7D -PASS U+7BB1 箱 %1B%24%42%48%22 -PASS U+7BB4 箴 %1B%24%42%64%3E -PASS U+7BB8 箸 %1B%24%42%48%24 -PASS U+7BC0 節 %1B%24%42%40%61 -PASS U+7BC1 篁 %1B%24%42%64%3B -PASS U+7BC4 範 %1B%24%42%48%4F -PASS U+7BC6 篆 %1B%24%42%64%3F -PASS U+7BC7 篇 %1B%24%42%4A%53 -PASS U+7BC9 築 %1B%24%42%43%5B -PASS U+7BCB 篋 %1B%24%42%64%3A -PASS U+7BCC 篌 %1B%24%42%64%3C -PASS U+7BCF 篏 %1B%24%42%64%3D -PASS U+7BDD 篝 %1B%24%42%64%40 -PASS U+7BE0 篠 %1B%24%42%3C%44 -PASS U+7BE4 篤 %1B%24%42%46%46 -PASS U+7BE5 篥 %1B%24%42%64%45 -PASS U+7BE6 篦 %1B%24%42%64%44 -PASS U+7BE9 篩 %1B%24%42%64%41 -PASS U+7BED 篭 %1B%24%42%4F%36 -PASS U+7BF3 篳 %1B%24%42%64%4A -PASS U+7BF6 篶 %1B%24%42%64%4E -PASS U+7BF7 篷 %1B%24%42%64%4B -PASS U+7C00 簀 %1B%24%42%64%47 -PASS U+7C07 簇 %1B%24%42%64%48 -PASS U+7C0D 簍 %1B%24%42%64%4D -PASS U+7C11 簑 %1B%24%42%64%42 -PASS U+7C12 簒 %1B%24%42%52%55 -PASS U+7C13 簓 %1B%24%42%64%49 -PASS U+7C14 簔 %1B%24%42%64%43 -PASS U+7C17 簗 %1B%24%42%64%4C -PASS U+7C1F 簟 %1B%24%42%64%52 -PASS U+7C21 簡 %1B%24%42%34%4A -PASS U+7C23 簣 %1B%24%42%64%4F -PASS U+7C27 簧 %1B%24%42%64%50 -PASS U+7C2A 簪 %1B%24%42%64%51 -PASS U+7C2B 簫 %1B%24%42%64%54 -PASS U+7C37 簷 %1B%24%42%64%53 -PASS U+7C38 簸 %1B%24%42%48%76 -PASS U+7C3D 簽 %1B%24%42%64%55 -PASS U+7C3E 簾 %1B%24%42%4E%7C -PASS U+7C3F 簿 %1B%24%42%4A%6D -PASS U+7C40 籀 %1B%24%42%64%5A -PASS U+7C43 籃 %1B%24%42%64%57 -PASS U+7C4C 籌 %1B%24%42%64%56 -PASS U+7C4D 籍 %1B%24%42%40%52 -PASS U+7C4F 籏 %1B%24%42%64%59 -PASS U+7C50 籐 %1B%24%42%64%5B -PASS U+7C54 籔 %1B%24%42%64%58 -PASS U+7C56 籖 %1B%24%42%64%5F -PASS U+7C58 籘 %1B%24%42%64%5C -PASS U+7C5F 籟 %1B%24%42%64%5D -PASS U+7C60 籠 %1B%24%42%64%46 -PASS U+7C64 籤 %1B%24%42%64%5E -PASS U+7C65 籥 %1B%24%42%64%60 -PASS U+7C6C 籬 %1B%24%42%64%61 -PASS U+7C73 米 %1B%24%42%4A%46 -PASS U+7C75 籵 %1B%24%42%64%62 -PASS U+7C7E 籾 %1B%24%42%4C%62 -PASS U+7C81 粁 %1B%24%42%36%4E -PASS U+7C82 粂 %1B%24%42%37%29 -PASS U+7C83 粃 %1B%24%42%64%63 -PASS U+7C89 粉 %1B%24%42%4A%34 -PASS U+7C8B 粋 %1B%24%42%3F%68 -PASS U+7C8D 粍 %1B%24%42%4C%30 -PASS U+7C90 粐 %1B%24%42%64%64 -PASS U+7C92 粒 %1B%24%42%4E%33 -PASS U+7C95 粕 %1B%24%42%47%74 -PASS U+7C97 粗 %1B%24%42%41%46 -PASS U+7C98 粘 %1B%24%42%47%34 -PASS U+7C9B 粛 %1B%24%42%3D%4D -PASS U+7C9F 粟 %1B%24%42%30%40 -PASS U+7CA1 粡 %1B%24%42%64%69 -PASS U+7CA2 粢 %1B%24%42%64%67 -PASS U+7CA4 粤 %1B%24%42%64%65 -PASS U+7CA5 粥 %1B%24%42%34%21 -PASS U+7CA7 粧 %1B%24%42%3E%51 -PASS U+7CA8 粨 %1B%24%42%64%6A -PASS U+7CAB 粫 %1B%24%42%64%68 -PASS U+7CAD 粭 %1B%24%42%64%66 -PASS U+7CAE 粮 %1B%24%42%64%6E -PASS U+7CB1 粱 %1B%24%42%64%6D -PASS U+7CB2 粲 %1B%24%42%64%6C -PASS U+7CB3 粳 %1B%24%42%64%6B -PASS U+7CB9 粹 %1B%24%42%64%6F -PASS U+7CBD 粽 %1B%24%42%64%70 -PASS U+7CBE 精 %1B%24%42%40%3A -PASS U+7CC0 糀 %1B%24%42%64%71 -PASS U+7CC2 糂 %1B%24%42%64%73 -PASS U+7CC5 糅 %1B%24%42%64%72 -PASS U+7CCA 糊 %1B%24%42%38%52 -PASS U+7CCE 糎 %1B%24%42%41%38 -PASS U+7CD2 糒 %1B%24%42%64%75 -PASS U+7CD6 糖 %1B%24%42%45%7C -PASS U+7CD8 糘 %1B%24%42%64%74 -PASS U+7CDC 糜 %1B%24%42%64%76 -PASS U+7CDE 糞 %1B%24%42%4A%35 -PASS U+7CDF 糟 %1B%24%42%41%6C -PASS U+7CE0 糠 %1B%24%42%39%47 -PASS U+7CE2 糢 %1B%24%42%64%77 -PASS U+7CE7 糧 %1B%24%42%4E%48 -PASS U+7CEF 糯 %1B%24%42%64%79 -PASS U+7CF2 糲 %1B%24%42%64%7A -PASS U+7CF4 糴 %1B%24%42%64%7B -PASS U+7CF6 糶 %1B%24%42%64%7C -PASS U+7CF8 糸 %1B%24%42%3B%65 -PASS U+7CFA 糺 %1B%24%42%64%7D -PASS U+7CFB 系 %1B%24%42%37%4F -PASS U+7CFE 糾 %1B%24%42%35%6A -PASS U+7D00 紀 %1B%24%42%35%2A -PASS U+7D02 紂 %1B%24%42%65%21 -PASS U+7D04 約 %1B%24%42%4C%73 -PASS U+7D05 紅 %1B%24%42%39%48 -PASS U+7D06 紆 %1B%24%42%64%7E -PASS U+7D0A 紊 %1B%24%42%65%24 -PASS U+7D0B 紋 %1B%24%42%4C%66 -PASS U+7D0D 納 %1B%24%42%47%3C -PASS U+7D10 紐 %1B%24%42%49%33 -PASS U+7D14 純 %1B%24%42%3D%63 -PASS U+7D15 紕 %1B%24%42%65%23 -PASS U+7D17 紗 %1B%24%42%3C%53 -PASS U+7D18 紘 %1B%24%42%39%49 -PASS U+7D19 紙 %1B%24%42%3B%66 -PASS U+7D1A 級 %1B%24%42%35%69 -PASS U+7D1B 紛 %1B%24%42%4A%36 -PASS U+7D1C 紜 %1B%24%42%65%22 -PASS U+7D20 素 %1B%24%42%41%47 -PASS U+7D21 紡 %1B%24%42%4B%42 -PASS U+7D22 索 %1B%24%42%3A%77 -PASS U+7D2B 紫 %1B%24%42%3B%67 -PASS U+7D2C 紬 %1B%24%42%44%5D -PASS U+7D2E 紮 %1B%24%42%65%27 -PASS U+7D2F 累 %1B%24%42%4E%5F -PASS U+7D30 細 %1B%24%42%3A%59 -PASS U+7D32 紲 %1B%24%42%65%28 -PASS U+7D33 紳 %1B%24%42%3F%42 -PASS U+7D35 紵 %1B%24%42%65%2A -PASS U+7D39 紹 %1B%24%42%3E%52 -PASS U+7D3A 紺 %1B%24%42%3A%30 -PASS U+7D3F 紿 %1B%24%42%65%29 -PASS U+7D42 終 %1B%24%42%3D%2A -PASS U+7D43 絃 %1B%24%42%38%3E -PASS U+7D44 組 %1B%24%42%41%48 -PASS U+7D45 絅 %1B%24%42%65%25 -PASS U+7D46 絆 %1B%24%42%65%2B -FAIL U+7D48 絈 %1B%24%42%7B%4E assert_equals: expected "%1B%24%42%7B%4E" but got "%26%23%33%32%30%37%32%3B" -PASS U+7D4B 絋 %1B%24%42%65%26 -PASS U+7D4C 経 %1B%24%42%37%50 -PASS U+7D4E 絎 %1B%24%42%65%2E -PASS U+7D4F 絏 %1B%24%42%65%32 -PASS U+7D50 結 %1B%24%42%37%6B -PASS U+7D56 絖 %1B%24%42%65%2D -PASS U+7D5B 絛 %1B%24%42%65%36 -FAIL U+7D5C 絜 %1B%24%42%7B%4F assert_equals: expected "%1B%24%42%7B%4F" but got "%26%23%33%32%30%39%32%3B" -PASS U+7D5E 絞 %1B%24%42%39%4A -PASS U+7D61 絡 %1B%24%42%4D%6D -PASS U+7D62 絢 %1B%24%42%30%3C -PASS U+7D63 絣 %1B%24%42%65%33 -PASS U+7D66 給 %1B%24%42%35%6B -PASS U+7D68 絨 %1B%24%42%65%30 -PASS U+7D6E 絮 %1B%24%42%65%31 -PASS U+7D71 統 %1B%24%42%45%7D -PASS U+7D72 絲 %1B%24%42%65%2F -PASS U+7D73 絳 %1B%24%42%65%2C -PASS U+7D75 絵 %1B%24%42%33%28 -PASS U+7D76 絶 %1B%24%42%40%64 -PASS U+7D79 絹 %1B%24%42%38%28 -PASS U+7D7D 絽 %1B%24%42%65%38 -PASS U+7D89 綉 %1B%24%42%65%35 -PASS U+7D8F 綏 %1B%24%42%65%37 -PASS U+7D93 經 %1B%24%42%65%34 -PASS U+7D99 継 %1B%24%42%37%51 -PASS U+7D9A 続 %1B%24%42%42%33 -PASS U+7D9B 綛 %1B%24%42%65%39 -PASS U+7D9C 綜 %1B%24%42%41%6E -PASS U+7D9F 綟 %1B%24%42%65%46 -FAIL U+7DA0 綠 %1B%24%42%7B%51 assert_equals: expected "%1B%24%42%7B%51" but got "%26%23%33%32%31%36%30%3B" -PASS U+7DA2 綢 %1B%24%42%65%42 -PASS U+7DA3 綣 %1B%24%42%65%3C -PASS U+7DAB 綫 %1B%24%42%65%40 -PASS U+7DAC 綬 %1B%24%42%3C%7A -PASS U+7DAD 維 %1B%24%42%30%5D -PASS U+7DAE 綮 %1B%24%42%65%3B -PASS U+7DAF 綯 %1B%24%42%65%43 -PASS U+7DB0 綰 %1B%24%42%65%47 -PASS U+7DB1 綱 %1B%24%42%39%4B -PASS U+7DB2 網 %1B%24%42%4C%56 -PASS U+7DB4 綴 %1B%24%42%44%56 -PASS U+7DB5 綵 %1B%24%42%65%3D -FAIL U+7DB7 綷 %1B%24%42%7B%50 assert_equals: expected "%1B%24%42%7B%50" but got "%26%23%33%32%31%38%33%3B" -PASS U+7DB8 綸 %1B%24%42%65%45 -PASS U+7DBA 綺 %1B%24%42%65%3A -PASS U+7DBB 綻 %1B%24%42%43%3E -PASS U+7DBD 綽 %1B%24%42%65%3F -PASS U+7DBE 綾 %1B%24%42%30%3D -PASS U+7DBF 綿 %1B%24%42%4C%4A -PASS U+7DC7 緇 %1B%24%42%65%3E -PASS U+7DCA 緊 %1B%24%42%36%5B -PASS U+7DCB 緋 %1B%24%42%48%6C -PASS U+7DCF 総 %1B%24%42%41%6D -PASS U+7DD1 緑 %1B%24%42%4E%50 -PASS U+7DD2 緒 %1B%24%42%3D%6F -PASS U+7DD5 緕 %1B%24%42%65%6E -FAIL U+7DD6 緖 %1B%24%42%7B%52 assert_equals: expected "%1B%24%42%7B%52" but got "%26%23%33%32%32%31%34%3B" -PASS U+7DD8 緘 %1B%24%42%65%48 -PASS U+7DDA 線 %1B%24%42%40%7E -PASS U+7DDC 緜 %1B%24%42%65%44 -PASS U+7DDD 緝 %1B%24%42%65%49 -PASS U+7DDE 緞 %1B%24%42%65%4B -PASS U+7DE0 締 %1B%24%42%44%79 -PASS U+7DE1 緡 %1B%24%42%65%4E -PASS U+7DE4 緤 %1B%24%42%65%4A -PASS U+7DE8 編 %1B%24%42%4A%54 -PASS U+7DE9 緩 %1B%24%42%34%4B -PASS U+7DEC 緬 %1B%24%42%4C%4B -PASS U+7DEF 緯 %1B%24%42%30%5E -PASS U+7DF2 緲 %1B%24%42%65%4D -PASS U+7DF4 練 %1B%24%42%4E%7D -PASS U+7DFB 緻 %1B%24%42%65%4C -PASS U+7E01 縁 %1B%24%42%31%6F -PASS U+7E04 縄 %1B%24%42%46%6C -PASS U+7E05 縅 %1B%24%42%65%4F -PASS U+7E09 縉 %1B%24%42%65%56 -PASS U+7E0A 縊 %1B%24%42%65%50 -PASS U+7E0B 縋 %1B%24%42%65%57 -PASS U+7E12 縒 %1B%24%42%65%53 -PASS U+7E1B 縛 %1B%24%42%47%7B -PASS U+7E1E 縞 %1B%24%42%3C%4A -PASS U+7E1F 縟 %1B%24%42%65%55 -PASS U+7E21 縡 %1B%24%42%65%52 -PASS U+7E22 縢 %1B%24%42%65%58 -PASS U+7E23 縣 %1B%24%42%65%51 -PASS U+7E26 縦 %1B%24%42%3D%44 -PASS U+7E2B 縫 %1B%24%42%4B%25 -PASS U+7E2E 縮 %1B%24%42%3D%4C -PASS U+7E31 縱 %1B%24%42%65%54 -PASS U+7E32 縲 %1B%24%42%65%60 -PASS U+7E35 縵 %1B%24%42%65%5C -PASS U+7E37 縷 %1B%24%42%65%5F -PASS U+7E39 縹 %1B%24%42%65%5D -PASS U+7E3A 縺 %1B%24%42%65%61 -PASS U+7E3B 縻 %1B%24%42%65%5B -PASS U+7E3D 總 %1B%24%42%65%41 -PASS U+7E3E 績 %1B%24%42%40%53 -PASS U+7E41 繁 %1B%24%42%48%4B -PASS U+7E43 繃 %1B%24%42%65%5E -PASS U+7E46 繆 %1B%24%42%65%59 -PASS U+7E4A 繊 %1B%24%42%41%21 -PASS U+7E4B 繋 %1B%24%42%37%52 -PASS U+7E4D 繍 %1B%24%42%3D%2B -FAIL U+7E52 繒 %1B%24%42%7B%53 assert_equals: expected "%1B%24%42%7B%53" but got "%26%23%33%32%33%33%38%3B" -PASS U+7E54 織 %1B%24%42%3F%25 -PASS U+7E55 繕 %1B%24%42%41%36 -PASS U+7E56 繖 %1B%24%42%65%64 -PASS U+7E59 繙 %1B%24%42%65%66 -PASS U+7E5A 繚 %1B%24%42%65%67 -PASS U+7E5D 繝 %1B%24%42%65%63 -PASS U+7E5E 繞 %1B%24%42%65%65 -PASS U+7E66 繦 %1B%24%42%65%5A -PASS U+7E67 繧 %1B%24%42%65%62 -PASS U+7E69 繩 %1B%24%42%65%6A -PASS U+7E6A 繪 %1B%24%42%65%69 -PASS U+7E6D 繭 %1B%24%42%4B%7A -PASS U+7E70 繰 %1B%24%42%37%2B -PASS U+7E79 繹 %1B%24%42%65%68 -PASS U+7E7B 繻 %1B%24%42%65%6C -PASS U+7E7C 繼 %1B%24%42%65%6B -PASS U+7E7D 繽 %1B%24%42%65%6F -PASS U+7E7F 繿 %1B%24%42%65%71 -PASS U+7E82 纂 %1B%24%42%3B%3C -PASS U+7E83 纃 %1B%24%42%65%6D -PASS U+7E88 纈 %1B%24%42%65%72 -PASS U+7E89 纉 %1B%24%42%65%73 -FAIL U+7E8A 纊 %1B%24%42%79%21 assert_equals: expected "%1B%24%42%79%21" but got "%26%23%33%32%33%39%34%3B" -PASS U+7E8C 續 %1B%24%42%65%74 -PASS U+7E8E 纎 %1B%24%42%65%7A -PASS U+7E8F 纏 %1B%24%42%45%3B -PASS U+7E90 纐 %1B%24%42%65%76 -PASS U+7E92 纒 %1B%24%42%65%75 -PASS U+7E93 纓 %1B%24%42%65%77 -PASS U+7E94 纔 %1B%24%42%65%78 -PASS U+7E96 纖 %1B%24%42%65%79 -PASS U+7E9B 纛 %1B%24%42%65%7B -PASS U+7E9C 纜 %1B%24%42%65%7C -PASS U+7F36 缶 %1B%24%42%34%4C -PASS U+7F38 缸 %1B%24%42%65%7D -PASS U+7F3A 缺 %1B%24%42%65%7E -PASS U+7F45 罅 %1B%24%42%66%21 -FAIL U+7F47 罇 %1B%24%42%7B%54 assert_equals: expected "%1B%24%42%7B%54" but got "%26%23%33%32%35%38%33%3B" -PASS U+7F4C 罌 %1B%24%42%66%22 -PASS U+7F4D 罍 %1B%24%42%66%23 -PASS U+7F4E 罎 %1B%24%42%66%24 -PASS U+7F50 罐 %1B%24%42%66%25 -PASS U+7F51 网 %1B%24%42%66%26 -PASS U+7F54 罔 %1B%24%42%66%28 -PASS U+7F55 罕 %1B%24%42%66%27 -PASS U+7F58 罘 %1B%24%42%66%29 -PASS U+7F5F 罟 %1B%24%42%66%2A -PASS U+7F60 罠 %1B%24%42%66%2B -PASS U+7F67 罧 %1B%24%42%66%2E -PASS U+7F68 罨 %1B%24%42%66%2C -PASS U+7F69 罩 %1B%24%42%66%2D -PASS U+7F6A 罪 %1B%24%42%3A%61 -PASS U+7F6B 罫 %1B%24%42%37%53 -PASS U+7F6E 置 %1B%24%42%43%56 -PASS U+7F70 罰 %1B%24%42%48%33 -PASS U+7F72 署 %1B%24%42%3D%70 -PASS U+7F75 罵 %1B%24%42%47%4D -PASS U+7F77 罷 %1B%24%42%48%6D -PASS U+7F78 罸 %1B%24%42%66%2F -PASS U+7F79 罹 %1B%24%42%58%6D -PASS U+7F82 羂 %1B%24%42%66%30 -PASS U+7F83 羃 %1B%24%42%66%32 -PASS U+7F85 羅 %1B%24%42%4D%65 -PASS U+7F86 羆 %1B%24%42%66%31 -PASS U+7F87 羇 %1B%24%42%66%34 -PASS U+7F88 羈 %1B%24%42%66%33 -PASS U+7F8A 羊 %1B%24%42%4D%53 -PASS U+7F8C 羌 %1B%24%42%66%35 -PASS U+7F8E 美 %1B%24%42%48%7E -PASS U+7F94 羔 %1B%24%42%66%36 -PASS U+7F9A 羚 %1B%24%42%66%39 -PASS U+7F9D 羝 %1B%24%42%66%38 -PASS U+7F9E 羞 %1B%24%42%66%37 -FAIL U+7FA1 羡 %1B%24%42%7B%55 assert_equals: expected "%1B%24%42%7B%55" but got "%26%23%33%32%36%37%33%3B" -PASS U+7FA3 羣 %1B%24%42%66%3A -PASS U+7FA4 群 %1B%24%42%37%32 -PASS U+7FA8 羨 %1B%24%42%41%22 -PASS U+7FA9 義 %1B%24%42%35%41 -PASS U+7FAE 羮 %1B%24%42%66%3E -PASS U+7FAF 羯 %1B%24%42%66%3B -PASS U+7FB2 羲 %1B%24%42%66%3C -PASS U+7FB6 羶 %1B%24%42%66%3F -PASS U+7FB8 羸 %1B%24%42%66%40 -PASS U+7FB9 羹 %1B%24%42%66%3D -PASS U+7FBD 羽 %1B%24%42%31%29 -PASS U+7FC1 翁 %1B%24%42%32%27 -PASS U+7FC5 翅 %1B%24%42%66%42 -PASS U+7FC6 翆 %1B%24%42%66%43 -PASS U+7FCA 翊 %1B%24%42%66%44 -PASS U+7FCC 翌 %1B%24%42%4D%62 -PASS U+7FD2 習 %1B%24%42%3D%2C -PASS U+7FD4 翔 %1B%24%42%66%46 -PASS U+7FD5 翕 %1B%24%42%66%45 -PASS U+7FE0 翠 %1B%24%42%3F%69 -PASS U+7FE1 翡 %1B%24%42%66%47 -PASS U+7FE6 翦 %1B%24%42%66%48 -PASS U+7FE9 翩 %1B%24%42%66%49 -PASS U+7FEB 翫 %1B%24%42%34%65 -PASS U+7FF0 翰 %1B%24%42%34%4D -PASS U+7FF3 翳 %1B%24%42%66%4A -PASS U+7FF9 翹 %1B%24%42%66%4B -PASS U+7FFB 翻 %1B%24%42%4B%5D -PASS U+7FFC 翼 %1B%24%42%4D%63 -PASS U+8000 耀 %1B%24%42%4D%54 -PASS U+8001 老 %1B%24%42%4F%37 -PASS U+8003 考 %1B%24%42%39%4D -PASS U+8004 耄 %1B%24%42%66%4E -PASS U+8005 者 %1B%24%42%3C%54 -PASS U+8006 耆 %1B%24%42%66%4D -PASS U+800B 耋 %1B%24%42%66%4F -PASS U+800C 而 %1B%24%42%3C%29 -PASS U+8010 耐 %1B%24%42%42%51 -PASS U+8012 耒 %1B%24%42%66%50 -PASS U+8015 耕 %1B%24%42%39%4C -PASS U+8017 耗 %1B%24%42%4C%57 -PASS U+8018 耘 %1B%24%42%66%51 -PASS U+8019 耙 %1B%24%42%66%52 -PASS U+801C 耜 %1B%24%42%66%53 -PASS U+8021 耡 %1B%24%42%66%54 -PASS U+8028 耨 %1B%24%42%66%55 -PASS U+8033 耳 %1B%24%42%3C%2A -PASS U+8036 耶 %1B%24%42%4C%6D -PASS U+803B 耻 %1B%24%42%66%57 -PASS U+803D 耽 %1B%24%42%43%3F -PASS U+803F 耿 %1B%24%42%66%56 -PASS U+8046 聆 %1B%24%42%66%59 -PASS U+804A 聊 %1B%24%42%66%58 -PASS U+8052 聒 %1B%24%42%66%5A -PASS U+8056 聖 %1B%24%42%40%3B -PASS U+8058 聘 %1B%24%42%66%5B -PASS U+805A 聚 %1B%24%42%66%5C -PASS U+805E 聞 %1B%24%42%4A%39 -PASS U+805F 聟 %1B%24%42%66%5D -PASS U+8061 聡 %1B%24%42%41%6F -PASS U+8062 聢 %1B%24%42%66%5E -PASS U+8068 聨 %1B%24%42%66%5F -PASS U+806F 聯 %1B%24%42%4E%7E -PASS U+8070 聰 %1B%24%42%66%62 -PASS U+8072 聲 %1B%24%42%66%61 -PASS U+8073 聳 %1B%24%42%66%60 -PASS U+8074 聴 %1B%24%42%44%30 -PASS U+8076 聶 %1B%24%42%66%63 -PASS U+8077 職 %1B%24%42%3F%26 -PASS U+8079 聹 %1B%24%42%66%64 -PASS U+807D 聽 %1B%24%42%66%65 -PASS U+807E 聾 %1B%24%42%4F%38 -PASS U+807F 聿 %1B%24%42%66%66 -PASS U+8084 肄 %1B%24%42%66%67 -PASS U+8085 肅 %1B%24%42%66%69 -PASS U+8086 肆 %1B%24%42%66%68 -PASS U+8087 肇 %1B%24%42%48%25 -PASS U+8089 肉 %1B%24%42%46%79 -PASS U+808B 肋 %1B%24%42%4F%3E -PASS U+808C 肌 %1B%24%42%48%29 -PASS U+8093 肓 %1B%24%42%66%6B -PASS U+8096 肖 %1B%24%42%3E%53 -PASS U+8098 肘 %1B%24%42%49%2A -PASS U+809A 肚 %1B%24%42%66%6C -PASS U+809B 肛 %1B%24%42%66%6A -PASS U+809D 肝 %1B%24%42%34%4E -PASS U+80A1 股 %1B%24%42%38%54 -PASS U+80A2 肢 %1B%24%42%3B%68 -PASS U+80A5 肥 %1B%24%42%48%6E -PASS U+80A9 肩 %1B%24%42%38%2A -PASS U+80AA 肪 %1B%24%42%4B%43 -PASS U+80AC 肬 %1B%24%42%66%6F -PASS U+80AD 肭 %1B%24%42%66%6D -PASS U+80AF 肯 %1B%24%42%39%4E -PASS U+80B1 肱 %1B%24%42%39%4F -PASS U+80B2 育 %1B%24%42%30%69 -PASS U+80B4 肴 %1B%24%42%3A%68 -PASS U+80BA 肺 %1B%24%42%47%59 -PASS U+80C3 胃 %1B%24%42%30%5F -PASS U+80C4 胄 %1B%24%42%66%74 -PASS U+80C6 胆 %1B%24%42%43%40 -PASS U+80CC 背 %1B%24%42%47%58 -PASS U+80CE 胎 %1B%24%42%42%5B -PASS U+80D6 胖 %1B%24%42%66%76 -PASS U+80D9 胙 %1B%24%42%66%72 -PASS U+80DA 胚 %1B%24%42%66%75 -PASS U+80DB 胛 %1B%24%42%66%70 -PASS U+80DD 胝 %1B%24%42%66%73 -PASS U+80DE 胞 %1B%24%42%4B%26 -PASS U+80E1 胡 %1B%24%42%38%55 -PASS U+80E4 胤 %1B%24%42%30%7D -PASS U+80E5 胥 %1B%24%42%66%71 -PASS U+80EF 胯 %1B%24%42%66%78 -PASS U+80F1 胱 %1B%24%42%66%79 -PASS U+80F4 胴 %1B%24%42%46%39 -PASS U+80F8 胸 %1B%24%42%36%3B -PASS U+80FC 胼 %1B%24%42%67%26 -PASS U+80FD 能 %1B%24%42%47%3D -PASS U+8102 脂 %1B%24%42%3B%69 -PASS U+8105 脅 %1B%24%42%36%3C -PASS U+8106 脆 %1B%24%42%40%48 -PASS U+8107 脇 %1B%24%42%4F%46 -PASS U+8108 脈 %1B%24%42%4C%2E -PASS U+8109 脉 %1B%24%42%66%77 -PASS U+810A 脊 %1B%24%42%40%54 -PASS U+811A 脚 %1B%24%42%35%53 -PASS U+811B 脛 %1B%24%42%66%7A -PASS U+8123 脣 %1B%24%42%66%7C -PASS U+8129 脩 %1B%24%42%66%7B -PASS U+812F 脯 %1B%24%42%66%7D -PASS U+8131 脱 %1B%24%42%43%26 -PASS U+8133 脳 %1B%24%42%47%3E -PASS U+8139 脹 %1B%24%42%44%31 -PASS U+813E 脾 %1B%24%42%67%23 -PASS U+8146 腆 %1B%24%42%67%22 -PASS U+814B 腋 %1B%24%42%66%7E -PASS U+814E 腎 %1B%24%42%3F%55 -PASS U+8150 腐 %1B%24%42%49%65 -PASS U+8151 腑 %1B%24%42%67%25 -PASS U+8153 腓 %1B%24%42%67%24 -PASS U+8154 腔 %1B%24%42%39%50 -PASS U+8155 腕 %1B%24%42%4F%53 -PASS U+815F 腟 %1B%24%42%67%35 -PASS U+8165 腥 %1B%24%42%67%29 -PASS U+8166 腦 %1B%24%42%67%2A -PASS U+816B 腫 %1B%24%42%3C%70 -PASS U+816E 腮 %1B%24%42%67%28 -PASS U+8170 腰 %1B%24%42%39%78 -PASS U+8171 腱 %1B%24%42%67%27 -PASS U+8174 腴 %1B%24%42%67%2B -PASS U+8178 腸 %1B%24%42%44%32 -PASS U+8179 腹 %1B%24%42%4A%22 -PASS U+817A 腺 %1B%24%42%41%23 -PASS U+817F 腿 %1B%24%42%42%5C -PASS U+8180 膀 %1B%24%42%67%2F -PASS U+8182 膂 %1B%24%42%67%30 -PASS U+8183 膃 %1B%24%42%67%2C -PASS U+8188 膈 %1B%24%42%67%2D -PASS U+818A 膊 %1B%24%42%67%2E -PASS U+818F 膏 %1B%24%42%39%51 -PASS U+8193 膓 %1B%24%42%67%36 -PASS U+8195 膕 %1B%24%42%67%32 -PASS U+819A 膚 %1B%24%42%49%66 -PASS U+819C 膜 %1B%24%42%4B%6C -PASS U+819D 膝 %1B%24%42%49%28 -PASS U+81A0 膠 %1B%24%42%67%31 -PASS U+81A3 膣 %1B%24%42%67%34 -PASS U+81A4 膤 %1B%24%42%67%33 -PASS U+81A8 膨 %1B%24%42%4B%44 -PASS U+81A9 膩 %1B%24%42%67%37 -PASS U+81B0 膰 %1B%24%42%67%38 -PASS U+81B3 膳 %1B%24%42%41%37 -PASS U+81B5 膵 %1B%24%42%67%39 -PASS U+81B8 膸 %1B%24%42%67%3B -PASS U+81BA 膺 %1B%24%42%67%3F -PASS U+81BD 膽 %1B%24%42%67%3C -PASS U+81BE 膾 %1B%24%42%67%3A -PASS U+81BF 膿 %1B%24%42%47%3F -PASS U+81C0 臀 %1B%24%42%67%3D -PASS U+81C2 臂 %1B%24%42%67%3E -PASS U+81C6 臆 %1B%24%42%32%32 -PASS U+81C8 臈 %1B%24%42%67%45 -PASS U+81C9 臉 %1B%24%42%67%40 -PASS U+81CD 臍 %1B%24%42%67%41 -PASS U+81D1 臑 %1B%24%42%67%42 -PASS U+81D3 臓 %1B%24%42%42%21 -PASS U+81D8 臘 %1B%24%42%67%44 -PASS U+81D9 臙 %1B%24%42%67%43 -PASS U+81DA 臚 %1B%24%42%67%46 -PASS U+81DF 臟 %1B%24%42%67%47 -PASS U+81E0 臠 %1B%24%42%67%48 -PASS U+81E3 臣 %1B%24%42%3F%43 -PASS U+81E5 臥 %1B%24%42%32%69 -PASS U+81E7 臧 %1B%24%42%67%49 -PASS U+81E8 臨 %1B%24%42%4E%57 -PASS U+81EA 自 %1B%24%42%3C%2B -PASS U+81ED 臭 %1B%24%42%3D%2D -PASS U+81F3 至 %1B%24%42%3B%6A -PASS U+81F4 致 %1B%24%42%43%57 -PASS U+81FA 臺 %1B%24%42%67%4A -PASS U+81FB 臻 %1B%24%42%67%4B -PASS U+81FC 臼 %1B%24%42%31%31 -PASS U+81FE 臾 %1B%24%42%67%4C -PASS U+8201 舁 %1B%24%42%67%4D -PASS U+8202 舂 %1B%24%42%67%4E -PASS U+8205 舅 %1B%24%42%67%4F -PASS U+8207 與 %1B%24%42%67%50 -PASS U+8208 興 %1B%24%42%36%3D -PASS U+8209 舉 %1B%24%42%5A%2A -PASS U+820A 舊 %1B%24%42%67%51 -PASS U+820C 舌 %1B%24%42%40%65 -PASS U+820D 舍 %1B%24%42%67%52 -PASS U+820E 舎 %1B%24%42%3C%4B -PASS U+8210 舐 %1B%24%42%67%53 -PASS U+8212 舒 %1B%24%42%50%30 -PASS U+8216 舖 %1B%24%42%67%54 -PASS U+8217 舗 %1B%24%42%4A%5E -PASS U+8218 舘 %1B%24%42%34%5C -PASS U+821B 舛 %1B%24%42%41%24 -PASS U+821C 舜 %1B%24%42%3D%58 -PASS U+821E 舞 %1B%24%42%49%71 -PASS U+821F 舟 %1B%24%42%3D%2E -PASS U+8229 舩 %1B%24%42%67%55 -PASS U+822A 航 %1B%24%42%39%52 -PASS U+822B 舫 %1B%24%42%67%56 -PASS U+822C 般 %1B%24%42%48%4C -PASS U+822E 舮 %1B%24%42%67%64 -PASS U+8233 舳 %1B%24%42%67%58 -PASS U+8235 舵 %1B%24%42%42%49 -PASS U+8236 舶 %1B%24%42%47%75 -PASS U+8237 舷 %1B%24%42%38%3F -PASS U+8238 舸 %1B%24%42%67%57 -PASS U+8239 船 %1B%24%42%41%25 -PASS U+8240 艀 %1B%24%42%67%59 -PASS U+8247 艇 %1B%24%42%44%7A -PASS U+8258 艘 %1B%24%42%67%5B -PASS U+8259 艙 %1B%24%42%67%5A -PASS U+825A 艚 %1B%24%42%67%5D -PASS U+825D 艝 %1B%24%42%67%5C -PASS U+825F 艟 %1B%24%42%67%5E -PASS U+8262 艢 %1B%24%42%67%60 -PASS U+8264 艤 %1B%24%42%67%5F -PASS U+8266 艦 %1B%24%42%34%4F -PASS U+8268 艨 %1B%24%42%67%61 -PASS U+826A 艪 %1B%24%42%67%62 -PASS U+826B 艫 %1B%24%42%67%63 -PASS U+826E 艮 %1B%24%42%3A%31 -PASS U+826F 良 %1B%24%42%4E%49 -PASS U+8271 艱 %1B%24%42%67%65 -PASS U+8272 色 %1B%24%42%3F%27 -PASS U+8276 艶 %1B%24%42%31%70 -PASS U+8277 艷 %1B%24%42%67%66 -PASS U+8278 艸 %1B%24%42%67%67 -PASS U+827E 艾 %1B%24%42%67%68 -PASS U+828B 芋 %1B%24%42%30%72 -PASS U+828D 芍 %1B%24%42%67%69 -PASS U+8292 芒 %1B%24%42%67%6A -PASS U+8299 芙 %1B%24%42%49%67 -PASS U+829D 芝 %1B%24%42%3C%47 -PASS U+829F 芟 %1B%24%42%67%6C -PASS U+82A5 芥 %1B%24%42%33%29 -PASS U+82A6 芦 %1B%24%42%30%32 -PASS U+82AB 芫 %1B%24%42%67%6B -PASS U+82AC 芬 %1B%24%42%67%6E -PASS U+82AD 芭 %1B%24%42%47%4E -PASS U+82AF 芯 %1B%24%42%3F%44 -PASS U+82B1 花 %1B%24%42%32%56 -PASS U+82B3 芳 %1B%24%42%4B%27 -PASS U+82B8 芸 %1B%24%42%37%5D -PASS U+82B9 芹 %1B%24%42%36%5C -PASS U+82BB 芻 %1B%24%42%67%6D -PASS U+82BD 芽 %1B%24%42%32%6A -PASS U+82C5 苅 %1B%24%42%34%23 -PASS U+82D1 苑 %1B%24%42%31%71 -PASS U+82D2 苒 %1B%24%42%67%72 -PASS U+82D3 苓 %1B%24%42%4E%6A -PASS U+82D4 苔 %1B%24%42%42%5D -PASS U+82D7 苗 %1B%24%42%49%44 -PASS U+82D9 苙 %1B%24%42%67%7E -PASS U+82DB 苛 %1B%24%42%32%57 -PASS U+82DC 苜 %1B%24%42%67%7C -PASS U+82DE 苞 %1B%24%42%67%7A -PASS U+82DF 苟 %1B%24%42%67%71 -PASS U+82E1 苡 %1B%24%42%67%6F -PASS U+82E3 苣 %1B%24%42%67%70 -PASS U+82E5 若 %1B%24%42%3C%63 -PASS U+82E6 苦 %1B%24%42%36%6C -PASS U+82E7 苧 %1B%24%42%43%77 -PASS U+82EB 苫 %1B%24%42%46%51 -PASS U+82F1 英 %1B%24%42%31%51 -PASS U+82F3 苳 %1B%24%42%67%74 -PASS U+82F4 苴 %1B%24%42%67%73 -PASS U+82F9 苹 %1B%24%42%67%79 -PASS U+82FA 苺 %1B%24%42%67%75 -PASS U+82FB 苻 %1B%24%42%67%78 -FAIL U+8301 茁 %1B%24%42%7B%57 assert_equals: expected "%1B%24%42%7B%57" but got "%26%23%33%33%35%33%37%3B" -PASS U+8302 茂 %1B%24%42%4C%50 -PASS U+8303 范 %1B%24%42%67%77 -PASS U+8304 茄 %1B%24%42%32%58 -PASS U+8305 茅 %1B%24%42%33%7D -PASS U+8306 茆 %1B%24%42%67%7B -PASS U+8309 茉 %1B%24%42%67%7D -PASS U+830E 茎 %1B%24%42%37%54 -PASS U+8316 茖 %1B%24%42%68%23 -PASS U+8317 茗 %1B%24%42%68%2C -PASS U+8318 茘 %1B%24%42%68%2D -PASS U+831C 茜 %1B%24%42%30%2B -PASS U+8323 茣 %1B%24%42%68%34 -PASS U+8328 茨 %1B%24%42%30%71 -PASS U+832B 茫 %1B%24%42%68%2B -PASS U+832F 茯 %1B%24%42%68%2A -PASS U+8331 茱 %1B%24%42%68%25 -PASS U+8332 茲 %1B%24%42%68%24 -PASS U+8334 茴 %1B%24%42%68%22 -PASS U+8335 茵 %1B%24%42%68%21 -PASS U+8336 茶 %1B%24%42%43%63 -PASS U+8338 茸 %1B%24%42%42%7B -PASS U+8339 茹 %1B%24%42%68%27 -PASS U+8340 荀 %1B%24%42%68%26 -PASS U+8345 荅 %1B%24%42%68%29 -PASS U+8349 草 %1B%24%42%41%70 -PASS U+834A 荊 %1B%24%42%37%55 -PASS U+834F 荏 %1B%24%42%31%41 -PASS U+8350 荐 %1B%24%42%68%28 -PASS U+8352 荒 %1B%24%42%39%53 -PASS U+8358 荘 %1B%24%42%41%71 -FAIL U+8362 荢 %1B%24%42%7B%58 assert_equals: expected "%1B%24%42%7B%58" but got "%26%23%33%33%36%33%34%3B" -PASS U+8373 荳 %1B%24%42%68%3A -PASS U+8375 荵 %1B%24%42%68%3B -PASS U+8377 荷 %1B%24%42%32%59 -PASS U+837B 荻 %1B%24%42%32%2E -PASS U+837C 荼 %1B%24%42%68%38 -FAIL U+837F 荿 %1B%24%42%7B%59 assert_equals: expected "%1B%24%42%7B%59" but got "%26%23%33%33%36%36%33%3B" -PASS U+8385 莅 %1B%24%42%68%2E -PASS U+8387 莇 %1B%24%42%68%36 -PASS U+8389 莉 %1B%24%42%68%3D -PASS U+838A 莊 %1B%24%42%68%37 -PASS U+838E 莎 %1B%24%42%68%35 -PASS U+8393 莓 %1B%24%42%67%76 -PASS U+8396 莖 %1B%24%42%68%33 -PASS U+839A 莚 %1B%24%42%68%2F -PASS U+839E 莞 %1B%24%42%34%50 -PASS U+839F 莟 %1B%24%42%68%31 -PASS U+83A0 莠 %1B%24%42%68%3C -PASS U+83A2 莢 %1B%24%42%68%32 -PASS U+83A8 莨 %1B%24%42%68%3E -PASS U+83AA 莪 %1B%24%42%68%30 -PASS U+83AB 莫 %1B%24%42%47%7C -PASS U+83B1 莱 %1B%24%42%4D%69 -PASS U+83B5 莵 %1B%24%42%68%39 -PASS U+83BD 莽 %1B%24%42%68%4F -PASS U+83C1 菁 %1B%24%42%68%47 -PASS U+83C5 菅 %1B%24%42%3F%7B -FAIL U+83C7 菇 %1B%24%42%7B%5A assert_equals: expected "%1B%24%42%7B%5A" but got "%26%23%33%33%37%33%35%3B" -PASS U+83CA 菊 %1B%24%42%35%46 -PASS U+83CC 菌 %1B%24%42%36%5D -PASS U+83CE 菎 %1B%24%42%68%42 -PASS U+83D3 菓 %1B%24%42%32%5B -PASS U+83D6 菖 %1B%24%42%3E%54 -PASS U+83D8 菘 %1B%24%42%68%45 -PASS U+83DC 菜 %1B%24%42%3A%5A -PASS U+83DF 菟 %1B%24%42%45%51 -PASS U+83E0 菠 %1B%24%42%68%4A -PASS U+83E9 菩 %1B%24%42%4A%6E -PASS U+83EB 菫 %1B%24%42%68%41 -PASS U+83EF 華 %1B%24%42%32%5A -PASS U+83F0 菰 %1B%24%42%38%56 -PASS U+83F1 菱 %1B%24%42%49%29 -PASS U+83F2 菲 %1B%24%42%68%4B -PASS U+83F4 菴 %1B%24%42%68%3F -FAIL U+83F6 菶 %1B%24%42%7B%5B assert_equals: expected "%1B%24%42%7B%5B" but got "%26%23%33%33%37%38%32%3B" -PASS U+83F7 菷 %1B%24%42%68%48 -PASS U+83FB 菻 %1B%24%42%68%52 -PASS U+83FD 菽 %1B%24%42%68%43 -PASS U+8403 萃 %1B%24%42%68%44 -PASS U+8404 萄 %1B%24%42%46%3A -PASS U+8407 萇 %1B%24%42%68%49 -PASS U+840B 萋 %1B%24%42%68%46 -PASS U+840C 萌 %1B%24%42%4B%28 -PASS U+840D 萍 %1B%24%42%68%4C -PASS U+840E 萎 %1B%24%42%30%60 -PASS U+8413 萓 %1B%24%42%68%40 -PASS U+8420 萠 %1B%24%42%68%4E -PASS U+8422 萢 %1B%24%42%68%4D -PASS U+8429 萩 %1B%24%42%47%6B -PASS U+842A 萪 %1B%24%42%68%54 -PASS U+842C 萬 %1B%24%42%68%5F -PASS U+8431 萱 %1B%24%42%33%7E -PASS U+8435 萵 %1B%24%42%68%62 -PASS U+8438 萸 %1B%24%42%68%50 -PASS U+843C 萼 %1B%24%42%68%55 -PASS U+843D 落 %1B%24%42%4D%6E -PASS U+8446 葆 %1B%24%42%68%5E -FAIL U+8448 葈 %1B%24%42%7B%5C assert_equals: expected "%1B%24%42%7B%5C" but got "%26%23%33%33%38%36%34%3B" -PASS U+8449 葉 %1B%24%42%4D%55 -PASS U+844E 葎 %1B%24%42%4E%2A -PASS U+8457 著 %1B%24%42%43%78 -PASS U+845B 葛 %1B%24%42%33%6B -PASS U+8461 葡 %1B%24%42%49%72 -PASS U+8462 葢 %1B%24%42%68%64 -PASS U+8463 董 %1B%24%42%46%21 -PASS U+8466 葦 %1B%24%42%30%31 -PASS U+8469 葩 %1B%24%42%68%5D -PASS U+846B 葫 %1B%24%42%68%59 -PASS U+846C 葬 %1B%24%42%41%72 -PASS U+846D 葭 %1B%24%42%68%53 -PASS U+846E 葮 %1B%24%42%68%5B -PASS U+846F 葯 %1B%24%42%68%60 -PASS U+8471 葱 %1B%24%42%47%2C -PASS U+8475 葵 %1B%24%42%30%2A -PASS U+8477 葷 %1B%24%42%68%58 -PASS U+8479 葹 %1B%24%42%68%61 -PASS U+847A 葺 %1B%24%42%49%78 -PASS U+8482 蒂 %1B%24%42%68%5C -PASS U+8484 蒄 %1B%24%42%68%57 -PASS U+848B 蒋 %1B%24%42%3E%55 -PASS U+8490 蒐 %1B%24%42%3D%2F -PASS U+8494 蒔 %1B%24%42%3C%2C -PASS U+8499 蒙 %1B%24%42%4C%58 -PASS U+849C 蒜 %1B%24%42%49%47 -PASS U+849F 蒟 %1B%24%42%68%67 -PASS U+84A1 蒡 %1B%24%42%68%70 -PASS U+84AD 蒭 %1B%24%42%68%5A -PASS U+84B2 蒲 %1B%24%42%33%77 -FAIL U+84B4 蒴 %1B%24%42%7B%5D assert_equals: expected "%1B%24%42%7B%5D" but got "%26%23%33%33%39%37%32%3B" -PASS U+84B8 蒸 %1B%24%42%3E%78 -PASS U+84B9 蒹 %1B%24%42%68%65 -PASS U+84BB 蒻 %1B%24%42%68%6A -PASS U+84BC 蒼 %1B%24%42%41%73 -PASS U+84BF 蒿 %1B%24%42%68%66 -PASS U+84C1 蓁 %1B%24%42%68%6D -PASS U+84C4 蓄 %1B%24%42%43%5F -PASS U+84C6 蓆 %1B%24%42%68%6E -PASS U+84C9 蓉 %1B%24%42%4D%56 -PASS U+84CA 蓊 %1B%24%42%68%63 -PASS U+84CB 蓋 %1B%24%42%33%38 -PASS U+84CD 蓍 %1B%24%42%68%69 -PASS U+84D0 蓐 %1B%24%42%68%6C -PASS U+84D1 蓑 %1B%24%42%4C%2C -PASS U+84D6 蓖 %1B%24%42%68%6F -PASS U+84D9 蓙 %1B%24%42%68%68 -PASS U+84DA 蓚 %1B%24%42%68%6B -FAIL U+84DC 蓜 %1B%24%42%79%25 assert_equals: expected "%1B%24%42%79%25" but got "%26%23%33%34%30%31%32%3B" -PASS U+84EC 蓬 %1B%24%42%4B%29 -PASS U+84EE 蓮 %1B%24%42%4F%21 -PASS U+84F4 蓴 %1B%24%42%68%73 -PASS U+84FC 蓼 %1B%24%42%68%7A -PASS U+84FF 蓿 %1B%24%42%68%72 -PASS U+8500 蔀 %1B%24%42%3C%43 -PASS U+8506 蔆 %1B%24%42%68%51 -PASS U+8511 蔑 %1B%24%42%4A%4E -PASS U+8513 蔓 %1B%24%42%4C%22 -PASS U+8514 蔔 %1B%24%42%68%79 -PASS U+8515 蔕 %1B%24%42%68%78 -PASS U+8517 蔗 %1B%24%42%68%74 -PASS U+8518 蔘 %1B%24%42%68%75 -PASS U+851A 蔚 %1B%24%42%31%36 -PASS U+851F 蔟 %1B%24%42%68%77 -PASS U+8521 蔡 %1B%24%42%68%71 -PASS U+8526 蔦 %1B%24%42%44%55 -PASS U+852C 蔬 %1B%24%42%68%76 -PASS U+852D 蔭 %1B%24%42%30%7E -PASS U+8535 蔵 %1B%24%42%42%22 -PASS U+853D 蔽 %1B%24%42%4A%43 -PASS U+8540 蕀 %1B%24%42%68%7B -PASS U+8541 蕁 %1B%24%42%69%21 -PASS U+8543 蕃 %1B%24%42%48%59 -PASS U+8548 蕈 %1B%24%42%68%7E -PASS U+8549 蕉 %1B%24%42%3E%56 -PASS U+854A 蕊 %1B%24%42%3C%49 -PASS U+854B 蕋 %1B%24%42%69%23 -PASS U+854E 蕎 %1B%24%42%36%3E -FAIL U+8553 蕓 %1B%24%42%7B%5E assert_equals: expected "%1B%24%42%7B%5E" but got "%26%23%33%34%31%33%31%3B" -PASS U+8555 蕕 %1B%24%42%69%24 -PASS U+8557 蕗 %1B%24%42%49%79 -PASS U+8558 蕘 %1B%24%42%68%7D -FAIL U+8559 蕙 %1B%24%42%7B%5F assert_equals: expected "%1B%24%42%7B%5F" but got "%26%23%33%34%31%33%37%3B" -PASS U+855A 蕚 %1B%24%42%68%56 -PASS U+8563 蕣 %1B%24%42%68%7C -PASS U+8568 蕨 %1B%24%42%4F%4F -PASS U+8569 蕩 %1B%24%42%46%22 -PASS U+856A 蕪 %1B%24%42%49%73 -FAIL U+856B 蕫 %1B%24%42%7B%60 assert_equals: expected "%1B%24%42%7B%60" but got "%26%23%33%34%31%35%35%3B" -PASS U+856D 蕭 %1B%24%42%69%2B -PASS U+8577 蕷 %1B%24%42%69%31 -PASS U+857E 蕾 %1B%24%42%69%32 -PASS U+8580 薀 %1B%24%42%69%25 -PASS U+8584 薄 %1B%24%42%47%76 -PASS U+8587 薇 %1B%24%42%69%2F -PASS U+8588 薈 %1B%24%42%69%27 -PASS U+858A 薊 %1B%24%42%69%29 -PASS U+8590 薐 %1B%24%42%69%33 -PASS U+8591 薑 %1B%24%42%69%28 -PASS U+8594 薔 %1B%24%42%69%2C -PASS U+8597 薗 %1B%24%42%31%72 -PASS U+8599 薙 %1B%24%42%46%65 -PASS U+859B 薛 %1B%24%42%69%2D -PASS U+859C 薜 %1B%24%42%69%30 -PASS U+85A4 薤 %1B%24%42%69%26 -PASS U+85A6 薦 %1B%24%42%41%26 -PASS U+85A8 薨 %1B%24%42%69%2A -PASS U+85A9 薩 %1B%24%42%3B%27 -PASS U+85AA 薪 %1B%24%42%3F%45 -PASS U+85AB 薫 %1B%24%42%37%30 -PASS U+85AC 薬 %1B%24%42%4C%74 -PASS U+85AE 薮 %1B%24%42%4C%79 -PASS U+85AF 薯 %1B%24%42%3D%72 -FAIL U+85B0 薰 %1B%24%42%7B%62 assert_equals: expected "%1B%24%42%7B%62" but got "%26%23%33%34%32%32%34%3B" -PASS U+85B9 薹 %1B%24%42%69%37 -PASS U+85BA 薺 %1B%24%42%69%35 -PASS U+85C1 藁 %1B%24%42%4F%4E -PASS U+85C9 藉 %1B%24%42%69%34 -PASS U+85CD 藍 %1B%24%42%4D%75 -PASS U+85CF 藏 %1B%24%42%69%36 -PASS U+85D0 藐 %1B%24%42%69%38 -PASS U+85D5 藕 %1B%24%42%69%39 -PASS U+85DC 藜 %1B%24%42%69%3C -PASS U+85DD 藝 %1B%24%42%69%3A -PASS U+85E4 藤 %1B%24%42%46%23 -PASS U+85E5 藥 %1B%24%42%69%3B -PASS U+85E9 藩 %1B%24%42%48%4D -PASS U+85EA 藪 %1B%24%42%69%2E -PASS U+85F7 藷 %1B%24%42%3D%73 -PASS U+85F9 藹 %1B%24%42%69%3D -PASS U+85FA 藺 %1B%24%42%69%42 -PASS U+85FB 藻 %1B%24%42%41%74 -PASS U+85FE 藾 %1B%24%42%69%41 -PASS U+8602 蘂 %1B%24%42%69%22 -PASS U+8606 蘆 %1B%24%42%69%43 -PASS U+8607 蘇 %1B%24%42%41%49 -PASS U+860A 蘊 %1B%24%42%69%3E -PASS U+860B 蘋 %1B%24%42%69%40 -PASS U+8613 蘓 %1B%24%42%69%3F -PASS U+8616 蘖 %1B%24%42%5D%31 -PASS U+8617 蘗 %1B%24%42%5D%22 -PASS U+861A 蘚 %1B%24%42%69%45 -PASS U+8622 蘢 %1B%24%42%69%44 -PASS U+862D 蘭 %1B%24%42%4D%76 -PASS U+862F 蘯 %1B%24%42%62%3C -PASS U+8630 蘰 %1B%24%42%69%46 -PASS U+863F 蘿 %1B%24%42%69%47 -PASS U+864D 虍 %1B%24%42%69%48 -PASS U+864E 虎 %1B%24%42%38%57 -PASS U+8650 虐 %1B%24%42%35%54 -PASS U+8654 虔 %1B%24%42%69%4A -PASS U+8655 處 %1B%24%42%51%5D -PASS U+865A 虚 %1B%24%42%35%75 -PASS U+865C 虜 %1B%24%42%4E%3A -PASS U+865E 虞 %1B%24%42%36%73 -PASS U+865F 號 %1B%24%42%69%4B -PASS U+8667 虧 %1B%24%42%69%4C -PASS U+866B 虫 %1B%24%42%43%6E -PASS U+8671 虱 %1B%24%42%69%4D -PASS U+8679 虹 %1B%24%42%46%7A -PASS U+867B 虻 %1B%24%42%30%3A -PASS U+868A 蚊 %1B%24%42%32%63 -PASS U+868B 蚋 %1B%24%42%69%52 -PASS U+868C 蚌 %1B%24%42%69%53 -PASS U+8693 蚓 %1B%24%42%69%4E -PASS U+8695 蚕 %1B%24%42%3B%3D -PASS U+86A3 蚣 %1B%24%42%69%4F -PASS U+86A4 蚤 %1B%24%42%47%42 -PASS U+86A9 蚩 %1B%24%42%69%50 -PASS U+86AA 蚪 %1B%24%42%69%51 -PASS U+86AB 蚫 %1B%24%42%69%5B -PASS U+86AF 蚯 %1B%24%42%69%55 -PASS U+86B0 蚰 %1B%24%42%69%58 -PASS U+86B6 蚶 %1B%24%42%69%54 -PASS U+86C4 蛄 %1B%24%42%69%56 -PASS U+86C6 蛆 %1B%24%42%69%57 -PASS U+86C7 蛇 %1B%24%42%3C%58 -PASS U+86C9 蛉 %1B%24%42%69%59 -PASS U+86CB 蛋 %1B%24%42%43%41 -PASS U+86CD 蛍 %1B%24%42%37%56 -PASS U+86CE 蛎 %1B%24%42%33%42 -PASS U+86D4 蛔 %1B%24%42%69%5C -PASS U+86D9 蛙 %1B%24%42%33%3F -PASS U+86DB 蛛 %1B%24%42%69%61 -PASS U+86DE 蛞 %1B%24%42%69%5D -PASS U+86DF 蛟 %1B%24%42%69%60 -PASS U+86E4 蛤 %1B%24%42%48%3A -PASS U+86E9 蛩 %1B%24%42%69%5E -PASS U+86EC 蛬 %1B%24%42%69%5F -PASS U+86ED 蛭 %1B%24%42%49%48 -PASS U+86EE 蛮 %1B%24%42%48%5A -PASS U+86EF 蛯 %1B%24%42%69%62 -PASS U+86F8 蛸 %1B%24%42%42%7D -PASS U+86F9 蛹 %1B%24%42%69%6C -PASS U+86FB 蛻 %1B%24%42%69%68 -PASS U+86FE 蛾 %1B%24%42%32%6B -PASS U+8700 蜀 %1B%24%42%69%66 -PASS U+8702 蜂 %1B%24%42%4B%2A -PASS U+8703 蜃 %1B%24%42%69%67 -PASS U+8706 蜆 %1B%24%42%69%64 -PASS U+8708 蜈 %1B%24%42%69%65 -PASS U+8709 蜉 %1B%24%42%69%6A -PASS U+870A 蜊 %1B%24%42%69%6D -PASS U+870D 蜍 %1B%24%42%69%6B -PASS U+8711 蜑 %1B%24%42%69%69 -PASS U+8712 蜒 %1B%24%42%69%63 -PASS U+8718 蜘 %1B%24%42%43%58 -PASS U+871A 蜚 %1B%24%42%69%74 -PASS U+871C 蜜 %1B%24%42%4C%2A -PASS U+8725 蜥 %1B%24%42%69%72 -PASS U+8729 蜩 %1B%24%42%69%73 -PASS U+8734 蜴 %1B%24%42%69%6E -PASS U+8737 蜷 %1B%24%42%69%70 -PASS U+873B 蜻 %1B%24%42%69%71 -PASS U+873F 蜿 %1B%24%42%69%6F -PASS U+8749 蝉 %1B%24%42%40%66 -PASS U+874B 蝋 %1B%24%42%4F%39 -PASS U+874C 蝌 %1B%24%42%69%78 -PASS U+874E 蝎 %1B%24%42%69%79 -PASS U+8753 蝓 %1B%24%42%6A%21 -PASS U+8755 蝕 %1B%24%42%3F%2A -PASS U+8757 蝗 %1B%24%42%69%7B -PASS U+8759 蝙 %1B%24%42%69%7E -PASS U+875F 蝟 %1B%24%42%69%76 -PASS U+8760 蝠 %1B%24%42%69%75 -PASS U+8763 蝣 %1B%24%42%6A%22 -PASS U+8766 蝦 %1B%24%42%32%5C -PASS U+8768 蝨 %1B%24%42%69%7C -PASS U+876A 蝪 %1B%24%42%6A%23 -PASS U+876E 蝮 %1B%24%42%69%7D -PASS U+8774 蝴 %1B%24%42%69%7A -PASS U+8776 蝶 %1B%24%42%44%33 -PASS U+8778 蝸 %1B%24%42%69%77 -PASS U+877F 蝿 %1B%24%42%47%68 -PASS U+8782 螂 %1B%24%42%6A%27 -PASS U+878D 融 %1B%24%42%4D%3B -PASS U+879F 螟 %1B%24%42%6A%26 -PASS U+87A2 螢 %1B%24%42%6A%25 -PASS U+87AB 螫 %1B%24%42%6A%2E -PASS U+87AF 螯 %1B%24%42%6A%28 -PASS U+87B3 螳 %1B%24%42%6A%30 -PASS U+87BA 螺 %1B%24%42%4D%66 -PASS U+87BB 螻 %1B%24%42%6A%33 -PASS U+87BD 螽 %1B%24%42%6A%2A -PASS U+87C0 蟀 %1B%24%42%6A%2B -PASS U+87C4 蟄 %1B%24%42%6A%2F -PASS U+87C6 蟆 %1B%24%42%6A%32 -PASS U+87C7 蟇 %1B%24%42%6A%31 -PASS U+87CB 蟋 %1B%24%42%6A%29 -PASS U+87D0 蟐 %1B%24%42%6A%2C -PASS U+87D2 蟒 %1B%24%42%6A%3D -PASS U+87E0 蟠 %1B%24%42%6A%36 -PASS U+87EF 蟯 %1B%24%42%6A%34 -PASS U+87F2 蟲 %1B%24%42%6A%35 -PASS U+87F6 蟶 %1B%24%42%6A%3A -PASS U+87F7 蟷 %1B%24%42%6A%3B -PASS U+87F9 蟹 %1B%24%42%33%2A -PASS U+87FB 蟻 %1B%24%42%35%42 -PASS U+87FE 蟾 %1B%24%42%6A%39 -PASS U+8805 蠅 %1B%24%42%6A%24 -FAIL U+8807 蠇 %1B%24%42%7B%65 assert_equals: expected "%1B%24%42%7B%65" but got "%26%23%33%34%38%32%33%3B" -PASS U+880D 蠍 %1B%24%42%6A%38 -PASS U+880E 蠎 %1B%24%42%6A%3C -PASS U+880F 蠏 %1B%24%42%6A%37 -PASS U+8811 蠑 %1B%24%42%6A%3E -PASS U+8815 蠕 %1B%24%42%6A%40 -PASS U+8816 蠖 %1B%24%42%6A%3F -PASS U+8821 蠡 %1B%24%42%6A%42 -PASS U+8822 蠢 %1B%24%42%6A%41 -PASS U+8823 蠣 %1B%24%42%69%5A -PASS U+8827 蠧 %1B%24%42%6A%46 -PASS U+8831 蠱 %1B%24%42%6A%43 -PASS U+8836 蠶 %1B%24%42%6A%44 -PASS U+8839 蠹 %1B%24%42%6A%45 -PASS U+883B 蠻 %1B%24%42%6A%47 -PASS U+8840 血 %1B%24%42%37%6C -PASS U+8842 衂 %1B%24%42%6A%49 -PASS U+8844 衄 %1B%24%42%6A%48 -PASS U+8846 衆 %1B%24%42%3D%30 -PASS U+884C 行 %1B%24%42%39%54 -PASS U+884D 衍 %1B%24%42%5E%27 -PASS U+8852 衒 %1B%24%42%6A%4A -PASS U+8853 術 %1B%24%42%3D%51 -PASS U+8857 街 %1B%24%42%33%39 -PASS U+8859 衙 %1B%24%42%6A%4B -PASS U+885B 衛 %1B%24%42%31%52 -PASS U+885D 衝 %1B%24%42%3E%57 -PASS U+885E 衞 %1B%24%42%6A%4C -PASS U+8861 衡 %1B%24%42%39%55 -PASS U+8862 衢 %1B%24%42%6A%4D -PASS U+8863 衣 %1B%24%42%30%61 -PASS U+8868 表 %1B%24%42%49%3D -PASS U+886B 衫 %1B%24%42%6A%4E -PASS U+8870 衰 %1B%24%42%3F%6A -PASS U+8872 衲 %1B%24%42%6A%55 -PASS U+8875 衵 %1B%24%42%6A%52 -PASS U+8877 衷 %1B%24%42%43%6F -PASS U+887D 衽 %1B%24%42%6A%53 -PASS U+887E 衾 %1B%24%42%6A%50 -PASS U+887F 衿 %1B%24%42%36%5E -PASS U+8881 袁 %1B%24%42%6A%4F -PASS U+8882 袂 %1B%24%42%6A%56 -PASS U+8888 袈 %1B%24%42%37%36 -PASS U+888B 袋 %1B%24%42%42%5E -PASS U+888D 袍 %1B%24%42%6A%5C -PASS U+8892 袒 %1B%24%42%6A%58 -PASS U+8896 袖 %1B%24%42%42%35 -PASS U+8897 袗 %1B%24%42%6A%57 -PASS U+8899 袙 %1B%24%42%6A%5A -PASS U+889E 袞 %1B%24%42%6A%51 -PASS U+88A2 袢 %1B%24%42%6A%5B -PASS U+88A4 袤 %1B%24%42%6A%5D -PASS U+88AB 被 %1B%24%42%48%6F -PASS U+88AE 袮 %1B%24%42%6A%59 -PASS U+88B0 袰 %1B%24%42%6A%5E -PASS U+88B1 袱 %1B%24%42%6A%60 -PASS U+88B4 袴 %1B%24%42%38%53 -PASS U+88B5 袵 %1B%24%42%6A%54 -PASS U+88B7 袷 %1B%24%42%30%41 -PASS U+88BF 袿 %1B%24%42%6A%5F -PASS U+88C1 裁 %1B%24%42%3A%5B -PASS U+88C2 裂 %1B%24%42%4E%76 -PASS U+88C3 裃 %1B%24%42%6A%61 -PASS U+88C4 裄 %1B%24%42%6A%62 -PASS U+88C5 装 %1B%24%42%41%75 -PASS U+88CF 裏 %1B%24%42%4E%22 -PASS U+88D4 裔 %1B%24%42%6A%63 -PASS U+88D5 裕 %1B%24%42%4D%35 -PASS U+88D8 裘 %1B%24%42%6A%64 -PASS U+88D9 裙 %1B%24%42%6A%65 -PASS U+88DC 補 %1B%24%42%4A%64 -PASS U+88DD 裝 %1B%24%42%6A%66 -PASS U+88DF 裟 %1B%24%42%3A%40 -PASS U+88E1 裡 %1B%24%42%4E%23 -PASS U+88E8 裨 %1B%24%42%6A%6B -PASS U+88F2 裲 %1B%24%42%6A%6C -PASS U+88F3 裳 %1B%24%42%3E%58 -PASS U+88F4 裴 %1B%24%42%6A%6A -FAIL U+88F5 裵 %1B%24%42%7B%66 assert_equals: expected "%1B%24%42%7B%66" but got "%26%23%33%35%30%36%31%3B" -PASS U+88F8 裸 %1B%24%42%4D%67 -PASS U+88F9 裹 %1B%24%42%6A%67 -PASS U+88FC 裼 %1B%24%42%6A%69 -PASS U+88FD 製 %1B%24%42%40%3D -PASS U+88FE 裾 %1B%24%42%3F%7E -PASS U+8902 褂 %1B%24%42%6A%68 -PASS U+8904 褄 %1B%24%42%6A%6D -PASS U+8907 複 %1B%24%42%4A%23 -PASS U+890A 褊 %1B%24%42%6A%6F -PASS U+890C 褌 %1B%24%42%6A%6E -PASS U+8910 褐 %1B%24%42%33%6C -PASS U+8912 褒 %1B%24%42%4B%2B -PASS U+8913 褓 %1B%24%42%6A%70 -FAIL U+891C 褜 %1B%24%42%79%22 assert_equals: expected "%1B%24%42%79%22" but got "%26%23%33%35%31%30%30%3B" -PASS U+891D 褝 %1B%24%42%6A%7C -PASS U+891E 褞 %1B%24%42%6A%72 -PASS U+8925 褥 %1B%24%42%6A%73 -PASS U+892A 褪 %1B%24%42%6A%74 -PASS U+892B 褫 %1B%24%42%6A%75 -PASS U+8936 褶 %1B%24%42%6A%79 -PASS U+8938 褸 %1B%24%42%6A%7A -PASS U+893B 褻 %1B%24%42%6A%78 -PASS U+8941 襁 %1B%24%42%6A%76 -PASS U+8943 襃 %1B%24%42%6A%71 -PASS U+8944 襄 %1B%24%42%6A%77 -PASS U+894C 襌 %1B%24%42%6A%7B -PASS U+894D 襍 %1B%24%42%70%37 -PASS U+8956 襖 %1B%24%42%32%28 -PASS U+895E 襞 %1B%24%42%6A%7E -PASS U+895F 襟 %1B%24%42%36%5F -PASS U+8960 襠 %1B%24%42%6A%7D -PASS U+8964 襤 %1B%24%42%6B%22 -PASS U+8966 襦 %1B%24%42%6B%21 -PASS U+896A 襪 %1B%24%42%6B%24 -PASS U+896D 襭 %1B%24%42%6B%23 -PASS U+896F 襯 %1B%24%42%6B%25 -PASS U+8972 襲 %1B%24%42%3D%31 -PASS U+8974 襴 %1B%24%42%6B%26 -PASS U+8977 襷 %1B%24%42%6B%27 -PASS U+897E 襾 %1B%24%42%6B%28 -PASS U+897F 西 %1B%24%42%40%3E -PASS U+8981 要 %1B%24%42%4D%57 -PASS U+8983 覃 %1B%24%42%6B%29 -PASS U+8986 覆 %1B%24%42%4A%24 -PASS U+8987 覇 %1B%24%42%47%46 -PASS U+8988 覈 %1B%24%42%6B%2A -PASS U+898A 覊 %1B%24%42%6B%2B -PASS U+898B 見 %1B%24%42%38%2B -PASS U+898F 規 %1B%24%42%35%2C -PASS U+8993 覓 %1B%24%42%6B%2C -PASS U+8996 視 %1B%24%42%3B%6B -PASS U+8997 覗 %1B%24%42%47%41 -PASS U+8998 覘 %1B%24%42%6B%2D -PASS U+899A 覚 %1B%24%42%33%50 -PASS U+89A1 覡 %1B%24%42%6B%2E -PASS U+89A6 覦 %1B%24%42%6B%30 -PASS U+89A7 覧 %1B%24%42%4D%77 -PASS U+89A9 覩 %1B%24%42%6B%2F -PASS U+89AA 親 %1B%24%42%3F%46 -PASS U+89AC 覬 %1B%24%42%6B%31 -PASS U+89AF 覯 %1B%24%42%6B%32 -PASS U+89B2 覲 %1B%24%42%6B%33 -PASS U+89B3 観 %1B%24%42%34%51 -PASS U+89BA 覺 %1B%24%42%6B%34 -PASS U+89BD 覽 %1B%24%42%6B%35 -PASS U+89BF 覿 %1B%24%42%6B%36 -PASS U+89C0 觀 %1B%24%42%6B%37 -PASS U+89D2 角 %1B%24%42%33%51 -PASS U+89DA 觚 %1B%24%42%6B%38 -PASS U+89DC 觜 %1B%24%42%6B%39 -PASS U+89DD 觝 %1B%24%42%6B%3A -PASS U+89E3 解 %1B%24%42%32%72 -PASS U+89E6 触 %1B%24%42%3F%28 -PASS U+89E7 觧 %1B%24%42%6B%3B -PASS U+89F4 觴 %1B%24%42%6B%3C -PASS U+89F8 觸 %1B%24%42%6B%3D -PASS U+8A00 言 %1B%24%42%38%40 -PASS U+8A02 訂 %1B%24%42%44%7B -PASS U+8A03 訃 %1B%24%42%6B%3E -PASS U+8A08 計 %1B%24%42%37%57 -PASS U+8A0A 訊 %1B%24%42%3F%56 -PASS U+8A0C 訌 %1B%24%42%6B%41 -PASS U+8A0E 討 %1B%24%42%46%24 -PASS U+8A10 訐 %1B%24%42%6B%40 -FAIL U+8A12 訒 %1B%24%42%7B%67 assert_equals: expected "%1B%24%42%7B%67" but got "%26%23%33%35%33%34%36%3B" -PASS U+8A13 訓 %1B%24%42%37%31 -PASS U+8A16 訖 %1B%24%42%6B%3F -PASS U+8A17 託 %1B%24%42%42%77 -PASS U+8A18 記 %1B%24%42%35%2D -PASS U+8A1B 訛 %1B%24%42%6B%42 -PASS U+8A1D 訝 %1B%24%42%6B%43 -PASS U+8A1F 訟 %1B%24%42%3E%59 -PASS U+8A23 訣 %1B%24%42%37%6D -PASS U+8A25 訥 %1B%24%42%6B%44 -PASS U+8A2A 訪 %1B%24%42%4B%2C -PASS U+8A2D 設 %1B%24%42%40%5F -PASS U+8A31 許 %1B%24%42%35%76 -PASS U+8A33 訳 %1B%24%42%4C%75 -PASS U+8A34 訴 %1B%24%42%41%4A -PASS U+8A36 訶 %1B%24%42%6B%45 -FAIL U+8A37 訷 %1B%24%42%7B%68 assert_equals: expected "%1B%24%42%7B%68" but got "%26%23%33%35%33%38%33%3B" -PASS U+8A3A 診 %1B%24%42%3F%47 -PASS U+8A3B 註 %1B%24%42%43%70 -PASS U+8A3C 証 %1B%24%42%3E%5A -PASS U+8A41 詁 %1B%24%42%6B%46 -PASS U+8A46 詆 %1B%24%42%6B%49 -PASS U+8A48 詈 %1B%24%42%6B%4A -PASS U+8A50 詐 %1B%24%42%3A%3E -PASS U+8A51 詑 %1B%24%42%42%42 -PASS U+8A52 詒 %1B%24%42%6B%48 -PASS U+8A54 詔 %1B%24%42%3E%5B -PASS U+8A55 評 %1B%24%42%49%3E -PASS U+8A5B 詛 %1B%24%42%6B%47 -PASS U+8A5E 詞 %1B%24%42%3B%6C -PASS U+8A60 詠 %1B%24%42%31%53 -PASS U+8A62 詢 %1B%24%42%6B%4E -PASS U+8A63 詣 %1B%24%42%37%58 -PASS U+8A66 試 %1B%24%42%3B%6E -PASS U+8A69 詩 %1B%24%42%3B%6D -PASS U+8A6B 詫 %1B%24%42%4F%4D -PASS U+8A6C 詬 %1B%24%42%6B%4D -PASS U+8A6D 詭 %1B%24%42%6B%4C -PASS U+8A6E 詮 %1B%24%42%41%27 -PASS U+8A70 詰 %1B%24%42%35%4D -PASS U+8A71 話 %1B%24%42%4F%43 -PASS U+8A72 該 %1B%24%42%33%3A -PASS U+8A73 詳 %1B%24%42%3E%5C -FAIL U+8A79 詹 %1B%24%42%7B%69 assert_equals: expected "%1B%24%42%7B%69" but got "%26%23%33%35%34%34%39%3B" -PASS U+8A7C 詼 %1B%24%42%6B%4B -PASS U+8A82 誂 %1B%24%42%6B%50 -PASS U+8A84 誄 %1B%24%42%6B%51 -PASS U+8A85 誅 %1B%24%42%6B%4F -PASS U+8A87 誇 %1B%24%42%38%58 -PASS U+8A89 誉 %1B%24%42%4D%40 -PASS U+8A8C 誌 %1B%24%42%3B%6F -PASS U+8A8D 認 %1B%24%42%47%27 -PASS U+8A91 誑 %1B%24%42%6B%54 -PASS U+8A93 誓 %1B%24%42%40%40 -PASS U+8A95 誕 %1B%24%42%43%42 -PASS U+8A98 誘 %1B%24%42%4D%36 -PASS U+8A9A 誚 %1B%24%42%6B%57 -PASS U+8A9E 語 %1B%24%42%38%6C -PASS U+8AA0 誠 %1B%24%42%40%3F -PASS U+8AA1 誡 %1B%24%42%6B%53 -PASS U+8AA3 誣 %1B%24%42%6B%58 -PASS U+8AA4 誤 %1B%24%42%38%6D -PASS U+8AA5 誥 %1B%24%42%6B%55 -PASS U+8AA6 誦 %1B%24%42%6B%56 -FAIL U+8AA7 誧 %1B%24%42%7B%6A assert_equals: expected "%1B%24%42%7B%6A" but got "%26%23%33%35%34%39%35%3B" -PASS U+8AA8 誨 %1B%24%42%6B%52 -PASS U+8AAC 説 %1B%24%42%40%62 -PASS U+8AAD 読 %1B%24%42%46%49 -PASS U+8AB0 誰 %1B%24%42%43%2F -PASS U+8AB2 課 %1B%24%42%32%5D -PASS U+8AB9 誹 %1B%24%42%48%70 -PASS U+8ABC 誼 %1B%24%42%35%43 -FAIL U+8ABE 誾 %1B%24%42%7B%6B assert_equals: expected "%1B%24%42%7B%6B" but got "%26%23%33%35%35%31%38%3B" -PASS U+8ABF 調 %1B%24%42%44%34 -PASS U+8AC2 諂 %1B%24%42%6B%5B -PASS U+8AC4 諄 %1B%24%42%6B%59 -PASS U+8AC7 談 %1B%24%42%43%4C -PASS U+8ACB 請 %1B%24%42%40%41 -PASS U+8ACC 諌 %1B%24%42%34%52 -PASS U+8ACD 諍 %1B%24%42%6B%5A -PASS U+8ACF 諏 %1B%24%42%3F%5B -PASS U+8AD2 諒 %1B%24%42%4E%4A -PASS U+8AD6 論 %1B%24%42%4F%40 -PASS U+8ADA 諚 %1B%24%42%6B%5C -PASS U+8ADB 諛 %1B%24%42%6B%67 -PASS U+8ADC 諜 %1B%24%42%44%35 -PASS U+8ADE 諞 %1B%24%42%6B%66 -FAIL U+8ADF 諟 %1B%24%42%7B%6C assert_equals: expected "%1B%24%42%7B%6C" but got "%26%23%33%35%35%35%31%3B" -PASS U+8AE0 諠 %1B%24%42%6B%63 -PASS U+8AE1 諡 %1B%24%42%6B%6B -PASS U+8AE2 諢 %1B%24%42%6B%64 -PASS U+8AE4 諤 %1B%24%42%6B%60 -PASS U+8AE6 諦 %1B%24%42%44%7C -PASS U+8AE7 諧 %1B%24%42%6B%5F -PASS U+8AEB 諫 %1B%24%42%6B%5D -PASS U+8AED 諭 %1B%24%42%4D%21 -PASS U+8AEE 諮 %1B%24%42%3B%70 -PASS U+8AF1 諱 %1B%24%42%6B%61 -PASS U+8AF3 諳 %1B%24%42%6B%5E -FAIL U+8AF6 諶 %1B%24%42%7B%6E assert_equals: expected "%1B%24%42%7B%6E" but got "%26%23%33%35%35%37%34%3B" -PASS U+8AF7 諷 %1B%24%42%6B%65 -PASS U+8AF8 諸 %1B%24%42%3D%74 -PASS U+8AFA 諺 %1B%24%42%38%41 -PASS U+8AFE 諾 %1B%24%42%42%7A -PASS U+8B00 謀 %1B%24%42%4B%45 -PASS U+8B01 謁 %1B%24%42%31%5A -PASS U+8B02 謂 %1B%24%42%30%62 -PASS U+8B04 謄 %1B%24%42%46%25 -PASS U+8B07 謇 %1B%24%42%6B%69 -PASS U+8B0C 謌 %1B%24%42%6B%68 -PASS U+8B0E 謎 %1B%24%42%46%66 -PASS U+8B10 謐 %1B%24%42%6B%6D -PASS U+8B14 謔 %1B%24%42%6B%62 -PASS U+8B16 謖 %1B%24%42%6B%6C -PASS U+8B17 謗 %1B%24%42%6B%6E -PASS U+8B19 謙 %1B%24%42%38%2C -PASS U+8B1A 謚 %1B%24%42%6B%6A -PASS U+8B1B 講 %1B%24%42%39%56 -PASS U+8B1D 謝 %1B%24%42%3C%55 -PASS U+8B20 謠 %1B%24%42%6B%6F -PASS U+8B21 謡 %1B%24%42%4D%58 -PASS U+8B26 謦 %1B%24%42%6B%72 -PASS U+8B28 謨 %1B%24%42%6B%75 -PASS U+8B2B 謫 %1B%24%42%6B%73 -PASS U+8B2C 謬 %1B%24%42%49%35 -PASS U+8B33 謳 %1B%24%42%6B%70 -PASS U+8B39 謹 %1B%24%42%36%60 -PASS U+8B3E 謾 %1B%24%42%6B%74 -PASS U+8B41 譁 %1B%24%42%6B%76 -PASS U+8B49 證 %1B%24%42%6B%7A -PASS U+8B4C 譌 %1B%24%42%6B%77 -PASS U+8B4E 譎 %1B%24%42%6B%79 -PASS U+8B4F 譏 %1B%24%42%6B%78 -FAIL U+8B53 譓 %1B%24%42%7B%6F assert_equals: expected "%1B%24%42%7B%6F" but got "%26%23%33%35%36%36%37%3B" -PASS U+8B56 譖 %1B%24%42%6B%7B -PASS U+8B58 識 %1B%24%42%3C%31 -PASS U+8B5A 譚 %1B%24%42%6B%7D -PASS U+8B5B 譛 %1B%24%42%6B%7C -PASS U+8B5C 譜 %1B%24%42%49%68 -PASS U+8B5F 譟 %1B%24%42%6C%21 -PASS U+8B66 警 %1B%24%42%37%59 -PASS U+8B6B 譫 %1B%24%42%6B%7E -PASS U+8B6C 譬 %1B%24%42%6C%22 -PASS U+8B6F 譯 %1B%24%42%6C%23 -PASS U+8B70 議 %1B%24%42%35%44 -PASS U+8B71 譱 %1B%24%42%66%41 -PASS U+8B72 譲 %1B%24%42%3E%79 -PASS U+8B74 譴 %1B%24%42%6C%24 -PASS U+8B77 護 %1B%24%42%38%6E -PASS U+8B7D 譽 %1B%24%42%6C%25 -FAIL U+8B7F 譿 %1B%24%42%7B%70 assert_equals: expected "%1B%24%42%7B%70" but got "%26%23%33%35%37%31%31%3B" -PASS U+8B80 讀 %1B%24%42%6C%26 -PASS U+8B83 讃 %1B%24%42%3B%3E -PASS U+8B8A 變 %1B%24%42%5A%4E -PASS U+8B8C 讌 %1B%24%42%6C%27 -PASS U+8B8E 讎 %1B%24%42%6C%28 -PASS U+8B90 讐 %1B%24%42%3D%32 -PASS U+8B92 讒 %1B%24%42%6C%29 -PASS U+8B93 讓 %1B%24%42%6C%2A -PASS U+8B96 讖 %1B%24%42%6C%2B -PASS U+8B99 讙 %1B%24%42%6C%2C -PASS U+8B9A 讚 %1B%24%42%6C%2D -PASS U+8C37 谷 %1B%24%42%43%2B -PASS U+8C3A 谺 %1B%24%42%6C%2E -PASS U+8C3F 谿 %1B%24%42%6C%30 -PASS U+8C41 豁 %1B%24%42%6C%2F -PASS U+8C46 豆 %1B%24%42%46%26 -PASS U+8C48 豈 %1B%24%42%6C%31 -PASS U+8C4A 豊 %1B%24%42%4B%2D -PASS U+8C4C 豌 %1B%24%42%6C%32 -PASS U+8C4E 豎 %1B%24%42%6C%33 -PASS U+8C50 豐 %1B%24%42%6C%34 -PASS U+8C55 豕 %1B%24%42%6C%35 -PASS U+8C5A 豚 %1B%24%42%46%5A -PASS U+8C61 象 %1B%24%42%3E%5D -PASS U+8C62 豢 %1B%24%42%6C%36 -PASS U+8C6A 豪 %1B%24%42%39%6B -PASS U+8C6B 豫 %1B%24%42%50%2E -PASS U+8C6C 豬 %1B%24%42%6C%37 -PASS U+8C78 豸 %1B%24%42%6C%38 -PASS U+8C79 豹 %1B%24%42%49%3F -PASS U+8C7A 豺 %1B%24%42%6C%39 -PASS U+8C7C 豼 %1B%24%42%6C%41 -PASS U+8C82 貂 %1B%24%42%6C%3A -PASS U+8C85 貅 %1B%24%42%6C%3C -PASS U+8C89 貉 %1B%24%42%6C%3B -PASS U+8C8A 貊 %1B%24%42%6C%3D -PASS U+8C8C 貌 %1B%24%42%4B%46 -PASS U+8C8D 貍 %1B%24%42%6C%3E -PASS U+8C8E 貎 %1B%24%42%6C%3F -PASS U+8C94 貔 %1B%24%42%6C%40 -PASS U+8C98 貘 %1B%24%42%6C%42 -PASS U+8C9D 貝 %1B%24%42%33%2D -PASS U+8C9E 貞 %1B%24%42%44%67 -PASS U+8CA0 負 %1B%24%42%49%69 -PASS U+8CA1 財 %1B%24%42%3A%62 -PASS U+8CA2 貢 %1B%24%42%39%57 -PASS U+8CA7 貧 %1B%24%42%49%4F -PASS U+8CA8 貨 %1B%24%42%32%5F -PASS U+8CA9 販 %1B%24%42%48%4E -PASS U+8CAA 貪 %1B%24%42%6C%45 -PASS U+8CAB 貫 %1B%24%42%34%53 -PASS U+8CAC 責 %1B%24%42%40%55 -PASS U+8CAD 貭 %1B%24%42%6C%44 -PASS U+8CAE 貮 %1B%24%42%6C%49 -PASS U+8CAF 貯 %1B%24%42%43%79 -PASS U+8CB0 貰 %1B%24%42%4C%63 -PASS U+8CB2 貲 %1B%24%42%6C%47 -PASS U+8CB3 貳 %1B%24%42%6C%48 -PASS U+8CB4 貴 %1B%24%42%35%2E -PASS U+8CB6 貶 %1B%24%42%6C%4A -PASS U+8CB7 買 %1B%24%42%47%63 -PASS U+8CB8 貸 %1B%24%42%42%5F -PASS U+8CBB 費 %1B%24%42%48%71 -PASS U+8CBC 貼 %1B%24%42%45%3D -PASS U+8CBD 貽 %1B%24%42%6C%46 -PASS U+8CBF 貿 %1B%24%42%4B%47 -PASS U+8CC0 賀 %1B%24%42%32%6C -PASS U+8CC1 賁 %1B%24%42%6C%4C -PASS U+8CC2 賂 %1B%24%42%4F%28 -PASS U+8CC3 賃 %1B%24%42%44%42 -PASS U+8CC4 賄 %1B%24%42%4F%45 -PASS U+8CC7 資 %1B%24%42%3B%71 -PASS U+8CC8 賈 %1B%24%42%6C%4B -PASS U+8CCA 賊 %1B%24%42%42%31 -PASS U+8CCD 賍 %1B%24%42%6C%5C -PASS U+8CCE 賎 %1B%24%42%41%28 -PASS U+8CD1 賑 %1B%24%42%46%78 -PASS U+8CD3 賓 %1B%24%42%49%50 -PASS U+8CDA 賚 %1B%24%42%6C%4F -PASS U+8CDB 賛 %1B%24%42%3B%3F -PASS U+8CDC 賜 %1B%24%42%3B%72 -PASS U+8CDE 賞 %1B%24%42%3E%5E -PASS U+8CE0 賠 %1B%24%42%47%65 -PASS U+8CE2 賢 %1B%24%42%38%2D -PASS U+8CE3 賣 %1B%24%42%6C%4E -PASS U+8CE4 賤 %1B%24%42%6C%4D -PASS U+8CE6 賦 %1B%24%42%49%6A -PASS U+8CEA 質 %1B%24%42%3C%41 -PASS U+8CED 賭 %1B%24%42%45%52 -FAIL U+8CF0 賰 %1B%24%42%7B%71 assert_equals: expected "%1B%24%42%7B%71" but got "%26%23%33%36%30%38%30%3B" -FAIL U+8CF4 賴 %1B%24%42%7B%72 assert_equals: expected "%1B%24%42%7B%72" but got "%26%23%33%36%30%38%34%3B" -PASS U+8CFA 賺 %1B%24%42%6C%51 -PASS U+8CFB 賻 %1B%24%42%6C%52 -PASS U+8CFC 購 %1B%24%42%39%58 -PASS U+8CFD 賽 %1B%24%42%6C%50 -PASS U+8D04 贄 %1B%24%42%6C%53 -PASS U+8D05 贅 %1B%24%42%6C%54 -PASS U+8D07 贇 %1B%24%42%6C%56 -PASS U+8D08 贈 %1B%24%42%42%23 -PASS U+8D0A 贊 %1B%24%42%6C%55 -PASS U+8D0B 贋 %1B%24%42%34%66 -PASS U+8D0D 贍 %1B%24%42%6C%58 -PASS U+8D0F 贏 %1B%24%42%6C%57 -PASS U+8D10 贐 %1B%24%42%6C%59 -FAIL U+8D12 贒 %1B%24%42%7B%73 assert_equals: expected "%1B%24%42%7B%73" but got "%26%23%33%36%31%31%34%3B" -PASS U+8D13 贓 %1B%24%42%6C%5B -PASS U+8D14 贔 %1B%24%42%6C%5D -PASS U+8D16 贖 %1B%24%42%6C%5E -PASS U+8D64 赤 %1B%24%42%40%56 -PASS U+8D66 赦 %1B%24%42%3C%4F -PASS U+8D67 赧 %1B%24%42%6C%5F -PASS U+8D6B 赫 %1B%24%42%33%52 -PASS U+8D6D 赭 %1B%24%42%6C%60 -PASS U+8D70 走 %1B%24%42%41%76 -PASS U+8D71 赱 %1B%24%42%6C%61 -PASS U+8D73 赳 %1B%24%42%6C%62 -PASS U+8D74 赴 %1B%24%42%49%6B -FAIL U+8D76 赶 %1B%24%42%7B%74 assert_equals: expected "%1B%24%42%7B%74" but got "%26%23%33%36%32%31%34%3B" -PASS U+8D77 起 %1B%24%42%35%2F -PASS U+8D81 趁 %1B%24%42%6C%63 -PASS U+8D85 超 %1B%24%42%44%36 -PASS U+8D8A 越 %1B%24%42%31%5B -PASS U+8D99 趙 %1B%24%42%6C%64 -PASS U+8DA3 趣 %1B%24%42%3C%71 -PASS U+8DA8 趨 %1B%24%42%3F%76 -PASS U+8DB3 足 %1B%24%42%42%2D -PASS U+8DBA 趺 %1B%24%42%6C%67 -PASS U+8DBE 趾 %1B%24%42%6C%66 -PASS U+8DC2 跂 %1B%24%42%6C%65 -PASS U+8DCB 跋 %1B%24%42%6C%6D -PASS U+8DCC 跌 %1B%24%42%6C%6B -PASS U+8DCF 跏 %1B%24%42%6C%68 -PASS U+8DD6 跖 %1B%24%42%6C%6A -PASS U+8DDA 跚 %1B%24%42%6C%69 -PASS U+8DDB 跛 %1B%24%42%6C%6C -PASS U+8DDD 距 %1B%24%42%35%77 -PASS U+8DDF 跟 %1B%24%42%6C%70 -PASS U+8DE1 跡 %1B%24%42%40%57 -PASS U+8DE3 跣 %1B%24%42%6C%71 -PASS U+8DE8 跨 %1B%24%42%38%59 -PASS U+8DEA 跪 %1B%24%42%6C%6E -PASS U+8DEB 跫 %1B%24%42%6C%6F -PASS U+8DEF 路 %1B%24%42%4F%29 -PASS U+8DF3 跳 %1B%24%42%44%37 -PASS U+8DF5 践 %1B%24%42%41%29 -PASS U+8DFC 跼 %1B%24%42%6C%72 -PASS U+8DFF 跿 %1B%24%42%6C%75 -PASS U+8E08 踈 %1B%24%42%6C%73 -PASS U+8E09 踉 %1B%24%42%6C%74 -PASS U+8E0A 踊 %1B%24%42%4D%59 -PASS U+8E0F 踏 %1B%24%42%46%27 -PASS U+8E10 踐 %1B%24%42%6C%78 -PASS U+8E1D 踝 %1B%24%42%6C%76 -PASS U+8E1E 踞 %1B%24%42%6C%77 -PASS U+8E1F 踟 %1B%24%42%6C%79 -PASS U+8E2A 踪 %1B%24%42%6D%29 -PASS U+8E30 踰 %1B%24%42%6C%7C -PASS U+8E34 踴 %1B%24%42%6C%7D -PASS U+8E35 踵 %1B%24%42%6C%7B -PASS U+8E42 蹂 %1B%24%42%6C%7A -PASS U+8E44 蹄 %1B%24%42%44%7D -PASS U+8E47 蹇 %1B%24%42%6D%21 -PASS U+8E48 蹈 %1B%24%42%6D%25 -PASS U+8E49 蹉 %1B%24%42%6D%22 -PASS U+8E4A 蹊 %1B%24%42%6C%7E -PASS U+8E4C 蹌 %1B%24%42%6D%23 -PASS U+8E50 蹐 %1B%24%42%6D%24 -PASS U+8E55 蹕 %1B%24%42%6D%2B -PASS U+8E59 蹙 %1B%24%42%6D%26 -PASS U+8E5F 蹟 %1B%24%42%40%58 -PASS U+8E60 蹠 %1B%24%42%6D%28 -PASS U+8E63 蹣 %1B%24%42%6D%2A -PASS U+8E64 蹤 %1B%24%42%6D%27 -PASS U+8E72 蹲 %1B%24%42%6D%2D -PASS U+8E74 蹴 %1B%24%42%3D%33 -PASS U+8E76 蹶 %1B%24%42%6D%2C -PASS U+8E7C 蹼 %1B%24%42%6D%2E -PASS U+8E81 躁 %1B%24%42%6D%2F -PASS U+8E84 躄 %1B%24%42%6D%32 -PASS U+8E85 躅 %1B%24%42%6D%31 -PASS U+8E87 躇 %1B%24%42%6D%30 -PASS U+8E8A 躊 %1B%24%42%6D%34 -PASS U+8E8B 躋 %1B%24%42%6D%33 -PASS U+8E8D 躍 %1B%24%42%4C%76 -PASS U+8E91 躑 %1B%24%42%6D%36 -PASS U+8E93 躓 %1B%24%42%6D%35 -PASS U+8E94 躔 %1B%24%42%6D%37 -PASS U+8E99 躙 %1B%24%42%6D%38 -PASS U+8EA1 躡 %1B%24%42%6D%3A -PASS U+8EAA 躪 %1B%24%42%6D%39 -PASS U+8EAB 身 %1B%24%42%3F%48 -PASS U+8EAC 躬 %1B%24%42%6D%3B -PASS U+8EAF 躯 %1B%24%42%36%6D -PASS U+8EB0 躰 %1B%24%42%6D%3C -PASS U+8EB1 躱 %1B%24%42%6D%3E -PASS U+8EBE 躾 %1B%24%42%6D%3F -PASS U+8EC5 軅 %1B%24%42%6D%40 -PASS U+8EC6 軆 %1B%24%42%6D%3D -PASS U+8EC8 軈 %1B%24%42%6D%41 -PASS U+8ECA 車 %1B%24%42%3C%56 -PASS U+8ECB 軋 %1B%24%42%6D%42 -PASS U+8ECC 軌 %1B%24%42%35%30 -PASS U+8ECD 軍 %1B%24%42%37%33 -FAIL U+8ECF 軏 %1B%24%42%7B%76 assert_equals: expected "%1B%24%42%7B%76" but got "%26%23%33%36%35%35%39%3B" -PASS U+8ED2 軒 %1B%24%42%38%2E -PASS U+8EDB 軛 %1B%24%42%6D%43 -PASS U+8EDF 軟 %1B%24%42%46%70 -PASS U+8EE2 転 %1B%24%42%45%3E -PASS U+8EE3 軣 %1B%24%42%6D%44 -PASS U+8EEB 軫 %1B%24%42%6D%47 -PASS U+8EF8 軸 %1B%24%42%3C%34 -PASS U+8EFB 軻 %1B%24%42%6D%46 -PASS U+8EFC 軼 %1B%24%42%6D%45 -PASS U+8EFD 軽 %1B%24%42%37%5A -PASS U+8EFE 軾 %1B%24%42%6D%48 -PASS U+8F03 較 %1B%24%42%33%53 -PASS U+8F05 輅 %1B%24%42%6D%4A -PASS U+8F09 載 %1B%24%42%3A%5C -PASS U+8F0A 輊 %1B%24%42%6D%49 -PASS U+8F0C 輌 %1B%24%42%6D%52 -PASS U+8F12 輒 %1B%24%42%6D%4C -PASS U+8F13 輓 %1B%24%42%6D%4E -PASS U+8F14 輔 %1B%24%42%4A%65 -PASS U+8F15 輕 %1B%24%42%6D%4B -PASS U+8F19 輙 %1B%24%42%6D%4D -PASS U+8F1B 輛 %1B%24%42%6D%51 -PASS U+8F1C 輜 %1B%24%42%6D%4F -PASS U+8F1D 輝 %1B%24%42%35%31 -PASS U+8F1F 輟 %1B%24%42%6D%50 -PASS U+8F26 輦 %1B%24%42%6D%53 -PASS U+8F29 輩 %1B%24%42%47%5A -PASS U+8F2A 輪 %1B%24%42%4E%58 -PASS U+8F2F 輯 %1B%24%42%3D%34 -PASS U+8F33 輳 %1B%24%42%6D%54 -PASS U+8F38 輸 %1B%24%42%4D%22 -PASS U+8F39 輹 %1B%24%42%6D%56 -PASS U+8F3B 輻 %1B%24%42%6D%55 -PASS U+8F3E 輾 %1B%24%42%6D%59 -PASS U+8F3F 輿 %1B%24%42%4D%41 -PASS U+8F42 轂 %1B%24%42%6D%58 -PASS U+8F44 轄 %1B%24%42%33%6D -PASS U+8F45 轅 %1B%24%42%6D%57 -PASS U+8F46 轆 %1B%24%42%6D%5C -PASS U+8F49 轉 %1B%24%42%6D%5B -PASS U+8F4C 轌 %1B%24%42%6D%5A -PASS U+8F4D 轍 %1B%24%42%45%32 -PASS U+8F4E 轎 %1B%24%42%6D%5D -PASS U+8F57 轗 %1B%24%42%6D%5E -PASS U+8F5C 轜 %1B%24%42%6D%5F -PASS U+8F5F 轟 %1B%24%42%39%6C -PASS U+8F61 轡 %1B%24%42%37%25 -PASS U+8F62 轢 %1B%24%42%6D%60 -PASS U+8F63 轣 %1B%24%42%6D%61 -PASS U+8F64 轤 %1B%24%42%6D%62 -PASS U+8F9B 辛 %1B%24%42%3F%49 -PASS U+8F9C 辜 %1B%24%42%6D%63 -PASS U+8F9E 辞 %1B%24%42%3C%2D -PASS U+8F9F 辟 %1B%24%42%6D%64 -PASS U+8FA3 辣 %1B%24%42%6D%65 -PASS U+8FA7 辧 %1B%24%42%52%21 -PASS U+8FA8 辨 %1B%24%42%51%7E -PASS U+8FAD 辭 %1B%24%42%6D%66 -PASS U+8FAE 辮 %1B%24%42%65%70 -PASS U+8FAF 辯 %1B%24%42%6D%67 -PASS U+8FB0 辰 %1B%24%42%43%24 -PASS U+8FB1 辱 %1B%24%42%3F%2B -PASS U+8FB2 農 %1B%24%42%47%40 -PASS U+8FB7 辷 %1B%24%42%6D%68 -PASS U+8FBA 辺 %1B%24%42%4A%55 -PASS U+8FBB 辻 %1B%24%42%44%54 -PASS U+8FBC 込 %1B%24%42%39%7E -PASS U+8FBF 辿 %1B%24%42%43%29 -PASS U+8FC2 迂 %1B%24%42%31%2A -PASS U+8FC4 迄 %1B%24%42%4B%78 -PASS U+8FC5 迅 %1B%24%42%3F%57 -PASS U+8FCE 迎 %1B%24%42%37%5E -PASS U+8FD1 近 %1B%24%42%36%61 -PASS U+8FD4 返 %1B%24%42%4A%56 -PASS U+8FDA 迚 %1B%24%42%6D%69 -PASS U+8FE2 迢 %1B%24%42%6D%6B -PASS U+8FE5 迥 %1B%24%42%6D%6A -PASS U+8FE6 迦 %1B%24%42%32%60 -PASS U+8FE9 迩 %1B%24%42%46%76 -PASS U+8FEA 迪 %1B%24%42%6D%6C -PASS U+8FEB 迫 %1B%24%42%47%77 -PASS U+8FED 迭 %1B%24%42%45%33 -PASS U+8FEF 迯 %1B%24%42%6D%6D -PASS U+8FF0 述 %1B%24%42%3D%52 -PASS U+8FF4 迴 %1B%24%42%6D%6F -PASS U+8FF7 迷 %1B%24%42%4C%42 -PASS U+8FF8 迸 %1B%24%42%6D%7E -PASS U+8FF9 迹 %1B%24%42%6D%71 -PASS U+8FFA 迺 %1B%24%42%6D%72 -PASS U+8FFD 追 %1B%24%42%44%49 -PASS U+9000 退 %1B%24%42%42%60 -PASS U+9001 送 %1B%24%42%41%77 -PASS U+9003 逃 %1B%24%42%46%28 -PASS U+9005 逅 %1B%24%42%6D%70 -PASS U+9006 逆 %1B%24%42%35%55 -PASS U+900B 逋 %1B%24%42%6D%79 -PASS U+900D 逍 %1B%24%42%6D%76 -PASS U+900E 逎 %1B%24%42%6E%25 -PASS U+900F 透 %1B%24%42%46%29 -PASS U+9010 逐 %1B%24%42%43%60 -PASS U+9011 逑 %1B%24%42%6D%73 -PASS U+9013 逓 %1B%24%42%44%7E -PASS U+9014 途 %1B%24%42%45%53 -PASS U+9015 逕 %1B%24%42%6D%74 -PASS U+9016 逖 %1B%24%42%6D%78 -PASS U+9017 逗 %1B%24%42%3F%60 -PASS U+9019 這 %1B%24%42%47%67 -PASS U+901A 通 %1B%24%42%44%4C -PASS U+901D 逝 %1B%24%42%40%42 -PASS U+901E 逞 %1B%24%42%6D%77 -PASS U+901F 速 %1B%24%42%42%2E -PASS U+9020 造 %1B%24%42%42%24 -PASS U+9021 逡 %1B%24%42%6D%75 -PASS U+9022 逢 %1B%24%42%30%29 -PASS U+9023 連 %1B%24%42%4F%22 -PASS U+9027 逧 %1B%24%42%6D%7A -PASS U+902E 逮 %1B%24%42%42%61 -PASS U+9031 週 %1B%24%42%3D%35 -PASS U+9032 進 %1B%24%42%3F%4A -PASS U+9035 逵 %1B%24%42%6D%7C -PASS U+9036 逶 %1B%24%42%6D%7B -PASS U+9038 逸 %1B%24%42%30%6F -PASS U+9039 逹 %1B%24%42%6D%7D -PASS U+903C 逼 %1B%24%42%49%2F -PASS U+903E 逾 %1B%24%42%6E%27 -PASS U+9041 遁 %1B%24%42%46%5B -PASS U+9042 遂 %1B%24%42%3F%6B -PASS U+9045 遅 %1B%24%42%43%59 -PASS U+9047 遇 %1B%24%42%36%78 -PASS U+9049 遉 %1B%24%42%6E%26 -PASS U+904A 遊 %1B%24%42%4D%37 -PASS U+904B 運 %1B%24%42%31%3F -PASS U+904D 遍 %1B%24%42%4A%57 -PASS U+904E 過 %1B%24%42%32%61 -PASS U+904F 遏 %1B%24%42%6E%21 -PASS U+9050 遐 %1B%24%42%6E%22 -PASS U+9051 遑 %1B%24%42%6E%23 -PASS U+9052 遒 %1B%24%42%6E%24 -PASS U+9053 道 %1B%24%42%46%3B -PASS U+9054 達 %1B%24%42%43%23 -PASS U+9055 違 %1B%24%42%30%63 -PASS U+9056 遖 %1B%24%42%6E%28 -PASS U+9058 遘 %1B%24%42%6E%29 -PASS U+9059 遙 %1B%24%42%74%23 -PASS U+905C 遜 %1B%24%42%42%3D -PASS U+905E 遞 %1B%24%42%6E%2A -PASS U+9060 遠 %1B%24%42%31%73 -PASS U+9061 遡 %1B%24%42%41%4C -PASS U+9063 遣 %1B%24%42%38%2F -PASS U+9065 遥 %1B%24%42%4D%5A -FAIL U+9067 遧 %1B%24%42%7B%79 assert_equals: expected "%1B%24%42%7B%79" but got "%26%23%33%36%39%36%37%3B" -PASS U+9068 遨 %1B%24%42%6E%2B -PASS U+9069 適 %1B%24%42%45%2C -PASS U+906D 遭 %1B%24%42%41%78 -PASS U+906E 遮 %1B%24%42%3C%57 -PASS U+906F 遯 %1B%24%42%6E%2C -PASS U+9072 遲 %1B%24%42%6E%2F -PASS U+9075 遵 %1B%24%42%3D%65 -PASS U+9076 遶 %1B%24%42%6E%2D -PASS U+9077 遷 %1B%24%42%41%2B -PASS U+9078 選 %1B%24%42%41%2A -PASS U+907A 遺 %1B%24%42%30%64 -PASS U+907C 遼 %1B%24%42%4E%4B -PASS U+907D 遽 %1B%24%42%6E%31 -PASS U+907F 避 %1B%24%42%48%72 -PASS U+9080 邀 %1B%24%42%6E%33 -PASS U+9081 邁 %1B%24%42%6E%32 -PASS U+9082 邂 %1B%24%42%6E%30 -PASS U+9083 邃 %1B%24%42%63%64 -PASS U+9084 還 %1B%24%42%34%54 -PASS U+9087 邇 %1B%24%42%6D%6E -PASS U+9089 邉 %1B%24%42%6E%35 -PASS U+908A 邊 %1B%24%42%6E%34 -PASS U+908F 邏 %1B%24%42%6E%36 -PASS U+9091 邑 %1B%24%42%4D%38 -PASS U+90A3 那 %1B%24%42%46%61 -PASS U+90A6 邦 %1B%24%42%4B%2E -PASS U+90A8 邨 %1B%24%42%6E%37 -PASS U+90AA 邪 %1B%24%42%3C%59 -PASS U+90AF 邯 %1B%24%42%6E%38 -PASS U+90B1 邱 %1B%24%42%6E%39 -PASS U+90B5 邵 %1B%24%42%6E%3A -PASS U+90B8 邸 %1B%24%42%45%21 -PASS U+90C1 郁 %1B%24%42%30%6A -PASS U+90CA 郊 %1B%24%42%39%59 -PASS U+90CE 郎 %1B%24%42%4F%3A -PASS U+90DB 郛 %1B%24%42%6E%3E -FAIL U+90DE 郞 %1B%24%42%7B%7A assert_equals: expected "%1B%24%42%7B%7A" but got "%26%23%33%37%30%38%36%3B" -PASS U+90E1 郡 %1B%24%42%37%34 -PASS U+90E2 郢 %1B%24%42%6E%3B -PASS U+90E4 郤 %1B%24%42%6E%3C -PASS U+90E8 部 %1B%24%42%49%74 -PASS U+90ED 郭 %1B%24%42%33%54 -PASS U+90F5 郵 %1B%24%42%4D%39 -PASS U+90F7 郷 %1B%24%42%36%3F -PASS U+90FD 都 %1B%24%42%45%54 -PASS U+9102 鄂 %1B%24%42%6E%3F -PASS U+9112 鄒 %1B%24%42%6E%40 -FAIL U+9115 鄕 %1B%24%42%7B%7C assert_equals: expected "%1B%24%42%7B%7C" but got "%26%23%33%37%31%34%31%3B" -PASS U+9119 鄙 %1B%24%42%6E%41 -FAIL U+9127 鄧 %1B%24%42%7B%7D assert_equals: expected "%1B%24%42%7B%7D" but got "%26%23%33%37%31%35%39%3B" -PASS U+912D 鄭 %1B%24%42%45%22 -PASS U+9130 鄰 %1B%24%42%6E%43 -PASS U+9132 鄲 %1B%24%42%6E%42 -PASS U+9149 酉 %1B%24%42%46%53 -PASS U+914A 酊 %1B%24%42%6E%44 -PASS U+914B 酋 %1B%24%42%3D%36 -PASS U+914C 酌 %1B%24%42%3C%60 -PASS U+914D 配 %1B%24%42%47%5B -PASS U+914E 酎 %1B%24%42%43%71 -PASS U+9152 酒 %1B%24%42%3C%72 -PASS U+9154 酔 %1B%24%42%3F%6C -PASS U+9156 酖 %1B%24%42%6E%45 -PASS U+9158 酘 %1B%24%42%6E%46 -PASS U+9162 酢 %1B%24%42%3F%5D -PASS U+9163 酣 %1B%24%42%6E%47 -PASS U+9165 酥 %1B%24%42%6E%48 -PASS U+9169 酩 %1B%24%42%6E%49 -PASS U+916A 酪 %1B%24%42%4D%6F -PASS U+916C 酬 %1B%24%42%3D%37 -PASS U+9172 酲 %1B%24%42%6E%4B -PASS U+9173 酳 %1B%24%42%6E%4A -PASS U+9175 酵 %1B%24%42%39%5A -PASS U+9177 酷 %1B%24%42%39%73 -PASS U+9178 酸 %1B%24%42%3B%40 -PASS U+9182 醂 %1B%24%42%6E%4E -PASS U+9187 醇 %1B%24%42%3D%66 -PASS U+9189 醉 %1B%24%42%6E%4D -PASS U+918B 醋 %1B%24%42%6E%4C -PASS U+918D 醍 %1B%24%42%42%69 -PASS U+9190 醐 %1B%24%42%38%6F -PASS U+9192 醒 %1B%24%42%40%43 -PASS U+9197 醗 %1B%24%42%48%30 -PASS U+919C 醜 %1B%24%42%3D%39 -PASS U+91A2 醢 %1B%24%42%6E%4F -PASS U+91A4 醤 %1B%24%42%3E%5F -PASS U+91AA 醪 %1B%24%42%6E%52 -PASS U+91AB 醫 %1B%24%42%6E%50 -PASS U+91AF 醯 %1B%24%42%6E%51 -PASS U+91B4 醴 %1B%24%42%6E%54 -PASS U+91B5 醵 %1B%24%42%6E%53 -PASS U+91B8 醸 %1B%24%42%3E%7A -PASS U+91BA 醺 %1B%24%42%6E%55 -PASS U+91C0 釀 %1B%24%42%6E%56 -PASS U+91C1 釁 %1B%24%42%6E%57 -PASS U+91C6 釆 %1B%24%42%48%50 -PASS U+91C7 采 %1B%24%42%3A%53 -PASS U+91C8 釈 %1B%24%42%3C%61 -PASS U+91C9 釉 %1B%24%42%6E%58 -PASS U+91CB 釋 %1B%24%42%6E%59 -PASS U+91CC 里 %1B%24%42%4E%24 -PASS U+91CD 重 %1B%24%42%3D%45 -PASS U+91CE 野 %1B%24%42%4C%6E -PASS U+91CF 量 %1B%24%42%4E%4C -PASS U+91D0 釐 %1B%24%42%6E%5A -PASS U+91D1 金 %1B%24%42%36%62 -PASS U+91D6 釖 %1B%24%42%6E%5B -FAIL U+91D7 釗 %1B%24%42%7C%21 assert_equals: expected "%1B%24%42%7C%21" but got "%26%23%33%37%33%33%35%3B" -PASS U+91D8 釘 %1B%24%42%45%23 -FAIL U+91DA 釚 %1B%24%42%7B%7E assert_equals: expected "%1B%24%42%7B%7E" but got "%26%23%33%37%33%33%38%3B" -PASS U+91DB 釛 %1B%24%42%6E%5E -PASS U+91DC 釜 %1B%24%42%33%78 -PASS U+91DD 針 %1B%24%42%3F%4B -FAIL U+91DE 釞 %1B%24%42%7C%22 assert_equals: expected "%1B%24%42%7C%22" but got "%26%23%33%37%33%34%32%3B" -PASS U+91DF 釟 %1B%24%42%6E%5C -PASS U+91E1 釡 %1B%24%42%6E%5D -PASS U+91E3 釣 %1B%24%42%44%60 -FAIL U+91E4 釤 %1B%24%42%7C%25 assert_equals: expected "%1B%24%42%7C%25" but got "%26%23%33%37%33%34%38%3B" -FAIL U+91E5 釥 %1B%24%42%7C%26 assert_equals: expected "%1B%24%42%7C%26" but got "%26%23%33%37%33%34%39%3B" -PASS U+91E6 釦 %1B%24%42%4B%55 -PASS U+91E7 釧 %1B%24%42%36%7C -FAIL U+91ED 釭 %1B%24%42%7C%23 assert_equals: expected "%1B%24%42%7C%23" but got "%26%23%33%37%33%35%37%3B" -FAIL U+91EE 釮 %1B%24%42%7C%24 assert_equals: expected "%1B%24%42%7C%24" but got "%26%23%33%37%33%35%38%3B" -PASS U+91F5 釵 %1B%24%42%6E%60 -PASS U+91F6 釶 %1B%24%42%6E%61 -PASS U+91FC 釼 %1B%24%42%6E%5F -PASS U+91FF 釿 %1B%24%42%6E%63 -FAIL U+9206 鈆 %1B%24%42%7C%27 assert_equals: expected "%1B%24%42%7C%27" but got "%26%23%33%37%33%38%32%3B" -FAIL U+920A 鈊 %1B%24%42%7C%29 assert_equals: expected "%1B%24%42%7C%29" but got "%26%23%33%37%33%38%36%3B" -PASS U+920D 鈍 %1B%24%42%46%5F -PASS U+920E 鈎 %1B%24%42%33%43 -FAIL U+9210 鈐 %1B%24%42%7C%28 assert_equals: expected "%1B%24%42%7C%28" but got "%26%23%33%37%33%39%32%3B" -PASS U+9211 鈑 %1B%24%42%6E%67 -PASS U+9214 鈔 %1B%24%42%6E%64 -PASS U+9215 鈕 %1B%24%42%6E%66 -PASS U+921E 鈞 %1B%24%42%6E%62 -PASS U+9229 鈩 %1B%24%42%6F%4F -PASS U+922C 鈬 %1B%24%42%6E%65 -PASS U+9234 鈴 %1B%24%42%4E%6B -PASS U+9237 鈷 %1B%24%42%38%5A -FAIL U+9239 鈹 %1B%24%42%7C%30 assert_equals: expected "%1B%24%42%7C%30" but got "%26%23%33%37%34%33%33%3B" -FAIL U+923A 鈺 %1B%24%42%7C%2A assert_equals: expected "%1B%24%42%7C%2A" but got "%26%23%33%37%34%33%34%3B" -FAIL U+923C 鈼 %1B%24%42%7C%2C assert_equals: expected "%1B%24%42%7C%2C" but got "%26%23%33%37%34%33%36%3B" -PASS U+923F 鈿 %1B%24%42%6E%6F -FAIL U+9240 鉀 %1B%24%42%7C%2B assert_equals: expected "%1B%24%42%7C%2B" but got "%26%23%33%37%34%34%30%3B" -PASS U+9244 鉄 %1B%24%42%45%34 -PASS U+9245 鉅 %1B%24%42%6E%6A -PASS U+9248 鉈 %1B%24%42%6E%6D -PASS U+9249 鉉 %1B%24%42%6E%6B -PASS U+924B 鉋 %1B%24%42%6E%70 -FAIL U+924E 鉎 %1B%24%42%7C%2D assert_equals: expected "%1B%24%42%7C%2D" but got "%26%23%33%37%34%35%34%3B" -PASS U+9250 鉐 %1B%24%42%6E%71 -FAIL U+9251 鉑 %1B%24%42%7C%2F assert_equals: expected "%1B%24%42%7C%2F" but got "%26%23%33%37%34%35%37%3B" -PASS U+9257 鉗 %1B%24%42%6E%69 -FAIL U+9259 鉙 %1B%24%42%7C%2E assert_equals: expected "%1B%24%42%7C%2E" but got "%26%23%33%37%34%36%35%3B" -PASS U+925A 鉚 %1B%24%42%6E%76 -PASS U+925B 鉛 %1B%24%42%31%74 -PASS U+925E 鉞 %1B%24%42%6E%68 -PASS U+9262 鉢 %1B%24%42%48%2D -PASS U+9264 鉤 %1B%24%42%6E%6C -PASS U+9266 鉦 %1B%24%42%3E%60 -FAIL U+9267 鉧 %1B%24%42%7C%31 assert_equals: expected "%1B%24%42%7C%31" but got "%26%23%33%37%34%37%39%3B" -PASS U+9271 鉱 %1B%24%42%39%5B -FAIL U+9277 鉷 %1B%24%42%7C%33 assert_equals: expected "%1B%24%42%7C%33" but got "%26%23%33%37%34%39%35%3B" -FAIL U+9278 鉸 %1B%24%42%7C%34 assert_equals: expected "%1B%24%42%7C%34" but got "%26%23%33%37%34%39%36%3B" -PASS U+927E 鉾 %1B%24%42%4B%48 -PASS U+9280 銀 %1B%24%42%36%64 -PASS U+9283 銃 %1B%24%42%3D%46 -PASS U+9285 銅 %1B%24%42%46%3C -FAIL U+9288 銈 %1B%24%42%79%24 assert_equals: expected "%1B%24%42%79%24" but got "%26%23%33%37%35%31%32%3B" -PASS U+9291 銑 %1B%24%42%41%2D -PASS U+9293 銓 %1B%24%42%6E%74 -PASS U+9295 銕 %1B%24%42%6E%6E -PASS U+9296 銖 %1B%24%42%6E%73 -PASS U+9298 銘 %1B%24%42%4C%43 -PASS U+929A 銚 %1B%24%42%44%38 -PASS U+929B 銛 %1B%24%42%6E%75 -PASS U+929C 銜 %1B%24%42%6E%72 -FAIL U+92A7 銧 %1B%24%42%7C%32 assert_equals: expected "%1B%24%42%7C%32" but got "%26%23%33%37%35%34%33%3B" -PASS U+92AD 銭 %1B%24%42%41%2C -PASS U+92B7 銷 %1B%24%42%6E%79 -PASS U+92B9 銹 %1B%24%42%6E%78 -PASS U+92CF 鋏 %1B%24%42%6E%77 -FAIL U+92D0 鋐 %1B%24%42%7C%38 assert_equals: expected "%1B%24%42%7C%38" but got "%26%23%33%37%35%38%34%3B" -PASS U+92D2 鋒 %1B%24%42%4B%2F -FAIL U+92D3 鋓 %1B%24%42%7C%3C assert_equals: expected "%1B%24%42%7C%3C" but got "%26%23%33%37%35%38%37%3B" -FAIL U+92D5 鋕 %1B%24%42%7C%3A assert_equals: expected "%1B%24%42%7C%3A" but got "%26%23%33%37%35%38%39%3B" -FAIL U+92D7 鋗 %1B%24%42%7C%36 assert_equals: expected "%1B%24%42%7C%36" but got "%26%23%33%37%35%39%31%3B" -FAIL U+92D9 鋙 %1B%24%42%7C%37 assert_equals: expected "%1B%24%42%7C%37" but got "%26%23%33%37%35%39%33%3B" -FAIL U+92E0 鋠 %1B%24%42%7C%3B assert_equals: expected "%1B%24%42%7C%3B" but got "%26%23%33%37%36%30%30%3B" -PASS U+92E4 鋤 %1B%24%42%3D%7B -FAIL U+92E7 鋧 %1B%24%42%7C%35 assert_equals: expected "%1B%24%42%7C%35" but got "%26%23%33%37%36%30%37%3B" -PASS U+92E9 鋩 %1B%24%42%6E%7A -PASS U+92EA 鋪 %1B%24%42%4A%5F -PASS U+92ED 鋭 %1B%24%42%31%54 -PASS U+92F2 鋲 %1B%24%42%49%46 -PASS U+92F3 鋳 %1B%24%42%43%72 -PASS U+92F8 鋸 %1B%24%42%35%78 -FAIL U+92F9 鋹 %1B%24%42%79%2A assert_equals: expected "%1B%24%42%79%2A" but got "%26%23%33%37%36%32%35%3B" -PASS U+92FA 鋺 %1B%24%42%6E%7C -FAIL U+92FB 鋻 %1B%24%42%7C%3F assert_equals: expected "%1B%24%42%7C%3F" but got "%26%23%33%37%36%32%37%3B" -PASS U+92FC 鋼 %1B%24%42%39%5D -FAIL U+92FF 鋿 %1B%24%42%7C%42 assert_equals: expected "%1B%24%42%7C%42" but got "%26%23%33%37%36%33%31%3B" -FAIL U+9302 錂 %1B%24%42%7C%44 assert_equals: expected "%1B%24%42%7C%44" but got "%26%23%33%37%36%33%34%3B" -PASS U+9306 錆 %1B%24%42%3B%2C -PASS U+930F 錏 %1B%24%42%6E%7B -PASS U+9310 錐 %1B%24%42%3F%6D -PASS U+9318 錘 %1B%24%42%3F%6E -PASS U+9319 錙 %1B%24%42%6F%21 -PASS U+931A 錚 %1B%24%42%6F%23 -FAIL U+931D 錝 %1B%24%42%7C%43 assert_equals: expected "%1B%24%42%7C%43" but got "%26%23%33%37%36%36%31%3B" -FAIL U+931E 錞 %1B%24%42%7C%41 assert_equals: expected "%1B%24%42%7C%41" but got "%26%23%33%37%36%36%32%3B" -PASS U+9320 錠 %1B%24%42%3E%7B -FAIL U+9321 錡 %1B%24%42%7C%3E assert_equals: expected "%1B%24%42%7C%3E" but got "%26%23%33%37%36%36%35%3B" -PASS U+9322 錢 %1B%24%42%6F%22 -PASS U+9323 錣 %1B%24%42%6F%24 -FAIL U+9325 錥 %1B%24%42%7C%3D assert_equals: expected "%1B%24%42%7C%3D" but got "%26%23%33%37%36%36%39%3B" -PASS U+9326 錦 %1B%24%42%36%53 -PASS U+9328 錨 %1B%24%42%49%45 -PASS U+932B 錫 %1B%24%42%3C%62 -PASS U+932C 錬 %1B%24%42%4F%23 -PASS U+932E 錮 %1B%24%42%6E%7E -PASS U+932F 錯 %1B%24%42%3A%78 -PASS U+9332 録 %1B%24%42%4F%3F -PASS U+9335 錵 %1B%24%42%6F%26 -PASS U+933A 錺 %1B%24%42%6F%25 -PASS U+933B 錻 %1B%24%42%6F%27 -PASS U+9344 鍄 %1B%24%42%6E%7D -FAIL U+9348 鍈 %1B%24%42%79%23 assert_equals: expected "%1B%24%42%79%23" but got "%26%23%33%37%37%30%34%3B" -PASS U+934B 鍋 %1B%24%42%46%69 -PASS U+934D 鍍 %1B%24%42%45%55 -PASS U+9354 鍔 %1B%24%42%44%57 -PASS U+9356 鍖 %1B%24%42%6F%2C -FAIL U+9357 鍗 %1B%24%42%7C%46 assert_equals: expected "%1B%24%42%7C%46" but got "%26%23%33%37%37%31%39%3B" -PASS U+935B 鍛 %1B%24%42%43%43 -PASS U+935C 鍜 %1B%24%42%6F%28 -PASS U+9360 鍠 %1B%24%42%6F%29 -PASS U+936C 鍬 %1B%24%42%37%2D -PASS U+936E 鍮 %1B%24%42%6F%2B -FAIL U+9370 鍰 %1B%24%42%7C%45 assert_equals: expected "%1B%24%42%7C%45" but got "%26%23%33%37%37%34%34%3B" -PASS U+9375 鍵 %1B%24%42%38%30 -PASS U+937C 鍼 %1B%24%42%6F%2A -PASS U+937E 鍾 %1B%24%42%3E%61 -PASS U+938C 鎌 %1B%24%42%33%79 -PASS U+9394 鎔 %1B%24%42%6F%30 -PASS U+9396 鎖 %1B%24%42%3A%3F -PASS U+9397 鎗 %1B%24%42%41%79 -PASS U+939A 鎚 %1B%24%42%44%4A -FAIL U+93A4 鎤 %1B%24%42%7C%47 assert_equals: expected "%1B%24%42%7C%47" but got "%26%23%33%37%37%39%36%3B" -PASS U+93A7 鎧 %1B%24%42%33%3B -PASS U+93AC 鎬 %1B%24%42%6F%2E -PASS U+93AD 鎭 %1B%24%42%6F%2F -PASS U+93AE 鎮 %1B%24%42%44%43 -PASS U+93B0 鎰 %1B%24%42%6F%2D -PASS U+93B9 鎹 %1B%24%42%6F%31 -PASS U+93C3 鏃 %1B%24%42%6F%37 -FAIL U+93C6 鏆 %1B%24%42%7C%48 assert_equals: expected "%1B%24%42%7C%48" but got "%26%23%33%37%38%33%30%3B" -PASS U+93C8 鏈 %1B%24%42%6F%3A -PASS U+93D0 鏐 %1B%24%42%6F%39 -PASS U+93D1 鏑 %1B%24%42%45%2D -PASS U+93D6 鏖 %1B%24%42%6F%32 -PASS U+93D7 鏗 %1B%24%42%6F%33 -PASS U+93D8 鏘 %1B%24%42%6F%36 -PASS U+93DD 鏝 %1B%24%42%6F%38 -FAIL U+93DE 鏞 %1B%24%42%7C%49 assert_equals: expected "%1B%24%42%7C%49" but got "%26%23%33%37%38%35%34%3B" -PASS U+93E1 鏡 %1B%24%42%36%40 -PASS U+93E4 鏤 %1B%24%42%6F%3B -PASS U+93E5 鏥 %1B%24%42%6F%35 -PASS U+93E8 鏨 %1B%24%42%6F%34 -FAIL U+93F8 鏸 %1B%24%42%7C%4A assert_equals: expected "%1B%24%42%7C%4A" but got "%26%23%33%37%38%38%30%3B" -PASS U+9403 鐃 %1B%24%42%6F%3F -PASS U+9407 鐇 %1B%24%42%6F%40 -PASS U+9410 鐐 %1B%24%42%6F%41 -PASS U+9413 鐓 %1B%24%42%6F%3E -PASS U+9414 鐔 %1B%24%42%6F%3D -PASS U+9418 鐘 %1B%24%42%3E%62 -PASS U+9419 鐙 %1B%24%42%46%2A -PASS U+941A 鐚 %1B%24%42%6F%3C -PASS U+9421 鐡 %1B%24%42%6F%45 -PASS U+942B 鐫 %1B%24%42%6F%43 -FAIL U+9431 鐱 %1B%24%42%7C%4B assert_equals: expected "%1B%24%42%7C%4B" but got "%26%23%33%37%39%33%37%3B" -PASS U+9435 鐵 %1B%24%42%6F%44 -PASS U+9436 鐶 %1B%24%42%6F%42 -PASS U+9438 鐸 %1B%24%42%42%78 -PASS U+943A 鐺 %1B%24%42%6F%46 -PASS U+9441 鑁 %1B%24%42%6F%47 -PASS U+9444 鑄 %1B%24%42%6F%49 -FAIL U+9445 鑅 %1B%24%42%7C%4C assert_equals: expected "%1B%24%42%7C%4C" but got "%26%23%33%37%39%35%37%3B" -FAIL U+9448 鑈 %1B%24%42%7C%4D assert_equals: expected "%1B%24%42%7C%4D" but got "%26%23%33%37%39%36%30%3B" -PASS U+9451 鑑 %1B%24%42%34%55 -PASS U+9452 鑒 %1B%24%42%6F%48 -PASS U+9453 鑓 %1B%24%42%4C%7A -PASS U+945A 鑚 %1B%24%42%6F%54 -PASS U+945B 鑛 %1B%24%42%6F%4A -PASS U+945E 鑞 %1B%24%42%6F%4D -PASS U+9460 鑠 %1B%24%42%6F%4B -PASS U+9462 鑢 %1B%24%42%6F%4C -PASS U+946A 鑪 %1B%24%42%6F%4E -PASS U+9470 鑰 %1B%24%42%6F%50 -PASS U+9475 鑵 %1B%24%42%6F%51 -PASS U+9477 鑷 %1B%24%42%6F%52 -PASS U+947C 鑼 %1B%24%42%6F%55 -PASS U+947D 鑽 %1B%24%42%6F%53 -PASS U+947E 鑾 %1B%24%42%6F%56 -PASS U+947F 鑿 %1B%24%42%6F%58 -PASS U+9481 钁 %1B%24%42%6F%57 -PASS U+9577 長 %1B%24%42%44%39 -PASS U+9580 門 %1B%24%42%4C%67 -PASS U+9582 閂 %1B%24%42%6F%59 -PASS U+9583 閃 %1B%24%42%41%2E -PASS U+9587 閇 %1B%24%42%6F%5A -PASS U+9589 閉 %1B%24%42%4A%44 -PASS U+958A 閊 %1B%24%42%6F%5B -PASS U+958B 開 %1B%24%42%33%2B -PASS U+958F 閏 %1B%24%42%31%3C -PASS U+9591 閑 %1B%24%42%34%57 -FAIL U+9592 閒 %1B%24%42%7C%4E assert_equals: expected "%1B%24%42%7C%4E" but got "%26%23%33%38%32%39%30%3B" -PASS U+9593 間 %1B%24%42%34%56 -PASS U+9594 閔 %1B%24%42%6F%5C -PASS U+9596 閖 %1B%24%42%6F%5D -PASS U+9598 閘 %1B%24%42%6F%5E -PASS U+9599 閙 %1B%24%42%6F%5F -PASS U+95A0 閠 %1B%24%42%6F%60 -PASS U+95A2 関 %1B%24%42%34%58 -PASS U+95A3 閣 %1B%24%42%33%55 -PASS U+95A4 閤 %1B%24%42%39%5E -PASS U+95A5 閥 %1B%24%42%48%36 -PASS U+95A7 閧 %1B%24%42%6F%62 -PASS U+95A8 閨 %1B%24%42%6F%61 -PASS U+95AD 閭 %1B%24%42%6F%63 -PASS U+95B2 閲 %1B%24%42%31%5C -PASS U+95B9 閹 %1B%24%42%6F%66 -PASS U+95BB 閻 %1B%24%42%6F%65 -PASS U+95BC 閼 %1B%24%42%6F%64 -PASS U+95BE 閾 %1B%24%42%6F%67 -PASS U+95C3 闃 %1B%24%42%6F%6A -PASS U+95C7 闇 %1B%24%42%30%47 -PASS U+95CA 闊 %1B%24%42%6F%68 -PASS U+95CC 闌 %1B%24%42%6F%6C -PASS U+95CD 闍 %1B%24%42%6F%6B -PASS U+95D4 闔 %1B%24%42%6F%6E -PASS U+95D5 闕 %1B%24%42%6F%6D -PASS U+95D6 闖 %1B%24%42%6F%6F -PASS U+95D8 闘 %1B%24%42%46%2E -PASS U+95DC 關 %1B%24%42%6F%70 -PASS U+95E1 闡 %1B%24%42%6F%71 -PASS U+95E2 闢 %1B%24%42%6F%73 -PASS U+95E5 闥 %1B%24%42%6F%72 -PASS U+961C 阜 %1B%24%42%49%6C -PASS U+9621 阡 %1B%24%42%6F%74 -PASS U+9628 阨 %1B%24%42%6F%75 -PASS U+962A 阪 %1B%24%42%3A%65 -PASS U+962E 阮 %1B%24%42%6F%76 -PASS U+962F 阯 %1B%24%42%6F%77 -PASS U+9632 防 %1B%24%42%4B%49 -PASS U+963B 阻 %1B%24%42%41%4B -PASS U+963F 阿 %1B%24%42%30%24 -PASS U+9640 陀 %1B%24%42%42%4B -PASS U+9642 陂 %1B%24%42%6F%78 -PASS U+9644 附 %1B%24%42%49%6D -PASS U+964B 陋 %1B%24%42%6F%7B -PASS U+964C 陌 %1B%24%42%6F%79 -PASS U+964D 降 %1B%24%42%39%5F -PASS U+964F 陏 %1B%24%42%6F%7A -PASS U+9650 限 %1B%24%42%38%42 -PASS U+965B 陛 %1B%24%42%4A%45 -PASS U+965C 陜 %1B%24%42%6F%7D -PASS U+965D 陝 %1B%24%42%70%21 -PASS U+965E 陞 %1B%24%42%6F%7E -PASS U+965F 陟 %1B%24%42%70%22 -PASS U+9662 院 %1B%24%42%31%21 -PASS U+9663 陣 %1B%24%42%3F%58 -PASS U+9664 除 %1B%24%42%3D%7C -PASS U+9665 陥 %1B%24%42%34%59 -PASS U+9666 陦 %1B%24%42%70%23 -PASS U+966A 陪 %1B%24%42%47%66 -PASS U+966C 陬 %1B%24%42%70%25 -PASS U+9670 陰 %1B%24%42%31%22 -PASS U+9672 陲 %1B%24%42%70%24 -PASS U+9673 陳 %1B%24%42%44%44 -PASS U+9675 陵 %1B%24%42%4E%4D -PASS U+9676 陶 %1B%24%42%46%2B -PASS U+9677 陷 %1B%24%42%6F%7C -PASS U+9678 陸 %1B%24%42%4E%26 -PASS U+967A 険 %1B%24%42%38%31 -PASS U+967D 陽 %1B%24%42%4D%5B -PASS U+9685 隅 %1B%24%42%36%79 -PASS U+9686 隆 %1B%24%42%4E%34 -PASS U+9688 隈 %1B%24%42%37%28 -PASS U+968A 隊 %1B%24%42%42%62 -PASS U+968B 隋 %1B%24%42%67%21 -PASS U+968D 隍 %1B%24%42%70%26 -PASS U+968E 階 %1B%24%42%33%2C -PASS U+968F 随 %1B%24%42%3F%6F -PASS U+9694 隔 %1B%24%42%33%56 -PASS U+9695 隕 %1B%24%42%70%28 -PASS U+9697 隗 %1B%24%42%70%29 -PASS U+9698 隘 %1B%24%42%70%27 -PASS U+9699 隙 %1B%24%42%37%64 -PASS U+969B 際 %1B%24%42%3A%5D -PASS U+969C 障 %1B%24%42%3E%63 -FAIL U+969D 隝 %1B%24%42%7C%51 assert_equals: expected "%1B%24%42%7C%51" but got "%26%23%33%38%35%35%37%3B" -PASS U+96A0 隠 %1B%24%42%31%23 -PASS U+96A3 隣 %1B%24%42%4E%59 -PASS U+96A7 隧 %1B%24%42%70%2B -PASS U+96A8 隨 %1B%24%42%6E%2E -PASS U+96AA 險 %1B%24%42%70%2A -FAIL U+96AF 隯 %1B%24%42%7C%52 assert_equals: expected "%1B%24%42%7C%52" but got "%26%23%33%38%35%37%35%3B" -PASS U+96B0 隰 %1B%24%42%70%2E -PASS U+96B1 隱 %1B%24%42%70%2C -PASS U+96B2 隲 %1B%24%42%70%2D -PASS U+96B4 隴 %1B%24%42%70%2F -PASS U+96B6 隶 %1B%24%42%70%30 -PASS U+96B7 隷 %1B%24%42%4E%6C -PASS U+96B8 隸 %1B%24%42%70%31 -PASS U+96B9 隹 %1B%24%42%70%32 -PASS U+96BB 隻 %1B%24%42%40%49 -PASS U+96BC 隼 %1B%24%42%48%3B -PASS U+96C0 雀 %1B%24%42%3F%7D -PASS U+96C1 雁 %1B%24%42%34%67 -PASS U+96C4 雄 %1B%24%42%4D%3A -PASS U+96C5 雅 %1B%24%42%32%6D -PASS U+96C6 集 %1B%24%42%3D%38 -PASS U+96C7 雇 %1B%24%42%38%5B -PASS U+96C9 雉 %1B%24%42%70%35 -PASS U+96CB 雋 %1B%24%42%70%34 -PASS U+96CC 雌 %1B%24%42%3B%73 -PASS U+96CD 雍 %1B%24%42%70%36 -PASS U+96CE 雎 %1B%24%42%70%33 -PASS U+96D1 雑 %1B%24%42%3B%28 -PASS U+96D5 雕 %1B%24%42%70%3A -PASS U+96D6 雖 %1B%24%42%6A%2D -PASS U+96D9 雙 %1B%24%42%52%56 -PASS U+96DB 雛 %1B%24%42%3F%77 -PASS U+96DC 雜 %1B%24%42%70%38 -PASS U+96E2 離 %1B%24%42%4E%25 -PASS U+96E3 難 %1B%24%42%46%71 -PASS U+96E8 雨 %1B%24%42%31%2B -PASS U+96EA 雪 %1B%24%42%40%63 -PASS U+96EB 雫 %1B%24%42%3C%36 -PASS U+96F0 雰 %1B%24%42%4A%37 -PASS U+96F2 雲 %1B%24%42%31%40 -PASS U+96F6 零 %1B%24%42%4E%6D -PASS U+96F7 雷 %1B%24%42%4D%6B -PASS U+96F9 雹 %1B%24%42%70%3B -PASS U+96FB 電 %1B%24%42%45%45 -PASS U+9700 需 %1B%24%42%3C%7B -PASS U+9704 霄 %1B%24%42%70%3C -PASS U+9706 霆 %1B%24%42%70%3D -PASS U+9707 震 %1B%24%42%3F%4C -PASS U+9708 霈 %1B%24%42%70%3E -PASS U+970A 霊 %1B%24%42%4E%6E -PASS U+970D 霍 %1B%24%42%70%39 -PASS U+970E 霎 %1B%24%42%70%40 -PASS U+970F 霏 %1B%24%42%70%42 -PASS U+9711 霑 %1B%24%42%70%41 -PASS U+9713 霓 %1B%24%42%70%3F -PASS U+9716 霖 %1B%24%42%70%43 -PASS U+9719 霙 %1B%24%42%70%44 -PASS U+971C 霜 %1B%24%42%41%7A -PASS U+971E 霞 %1B%24%42%32%62 -PASS U+9724 霤 %1B%24%42%70%45 -PASS U+9727 霧 %1B%24%42%4C%38 -PASS U+972A 霪 %1B%24%42%70%46 -PASS U+9730 霰 %1B%24%42%70%47 -PASS U+9732 露 %1B%24%42%4F%2A -FAIL U+9733 霳 %1B%24%42%7C%53 assert_equals: expected "%1B%24%42%7C%53" but got "%26%23%33%38%37%30%37%3B" -PASS U+9738 霸 %1B%24%42%5B%31 -PASS U+9739 霹 %1B%24%42%70%48 -FAIL U+973B 霻 %1B%24%42%7C%54 assert_equals: expected "%1B%24%42%7C%54" but got "%26%23%33%38%37%31%35%3B" -PASS U+973D 霽 %1B%24%42%70%49 -PASS U+973E 霾 %1B%24%42%70%4A -PASS U+9742 靂 %1B%24%42%70%4E -FAIL U+9743 靃 %1B%24%42%7C%55 assert_equals: expected "%1B%24%42%7C%55" but got "%26%23%33%38%37%32%33%3B" -PASS U+9744 靄 %1B%24%42%70%4B -PASS U+9746 靆 %1B%24%42%70%4C -PASS U+9748 靈 %1B%24%42%70%4D -PASS U+9749 靉 %1B%24%42%70%4F -FAIL U+974D 靍 %1B%24%42%7C%56 assert_equals: expected "%1B%24%42%7C%56" but got "%26%23%33%38%37%33%33%3B" -FAIL U+974F 靏 %1B%24%42%7C%57 assert_equals: expected "%1B%24%42%7C%57" but got "%26%23%33%38%37%33%35%3B" -FAIL U+9751 靑 %1B%24%42%7C%58 assert_equals: expected "%1B%24%42%7C%58" but got "%26%23%33%38%37%33%37%3B" -PASS U+9752 青 %1B%24%42%40%44 -FAIL U+9755 靕 %1B%24%42%7C%59 assert_equals: expected "%1B%24%42%7C%59" but got "%26%23%33%38%37%34%31%3B" -PASS U+9756 靖 %1B%24%42%4C%77 -PASS U+9759 静 %1B%24%42%40%45 -PASS U+975C 靜 %1B%24%42%70%50 -PASS U+975E 非 %1B%24%42%48%73 -PASS U+9760 靠 %1B%24%42%70%51 -PASS U+9761 靡 %1B%24%42%73%53 -PASS U+9762 面 %1B%24%42%4C%4C -PASS U+9764 靤 %1B%24%42%70%52 -PASS U+9766 靦 %1B%24%42%70%53 -PASS U+9768 靨 %1B%24%42%70%54 -PASS U+9769 革 %1B%24%42%33%57 -PASS U+976B 靫 %1B%24%42%70%56 -PASS U+976D 靭 %1B%24%42%3F%59 -PASS U+9771 靱 %1B%24%42%70%57 -PASS U+9774 靴 %1B%24%42%37%24 -PASS U+9779 靹 %1B%24%42%70%58 -PASS U+977A 靺 %1B%24%42%70%5C -PASS U+977C 靼 %1B%24%42%70%5A -PASS U+9781 鞁 %1B%24%42%70%5B -PASS U+9784 鞄 %1B%24%42%33%73 -PASS U+9785 鞅 %1B%24%42%70%59 -PASS U+9786 鞆 %1B%24%42%70%5D -PASS U+978B 鞋 %1B%24%42%70%5E -PASS U+978D 鞍 %1B%24%42%30%48 -PASS U+978F 鞏 %1B%24%42%70%5F -PASS U+9790 鞐 %1B%24%42%70%60 -PASS U+9798 鞘 %1B%24%42%3E%64 -PASS U+979C 鞜 %1B%24%42%70%61 -PASS U+97A0 鞠 %1B%24%42%35%47 -PASS U+97A3 鞣 %1B%24%42%70%64 -PASS U+97A6 鞦 %1B%24%42%70%63 -PASS U+97A8 鞨 %1B%24%42%70%62 -PASS U+97AB 鞫 %1B%24%42%6B%71 -PASS U+97AD 鞭 %1B%24%42%4A%5C -PASS U+97B3 鞳 %1B%24%42%70%65 -PASS U+97B4 鞴 %1B%24%42%70%66 -PASS U+97C3 韃 %1B%24%42%70%67 -PASS U+97C6 韆 %1B%24%42%70%68 -PASS U+97C8 韈 %1B%24%42%70%69 -PASS U+97CB 韋 %1B%24%42%70%6A -PASS U+97D3 韓 %1B%24%42%34%5A -PASS U+97DC 韜 %1B%24%42%70%6B -PASS U+97ED 韭 %1B%24%42%70%6C -PASS U+97EE 韮 %1B%24%42%47%23 -PASS U+97F2 韲 %1B%24%42%70%6E -PASS U+97F3 音 %1B%24%42%32%3B -PASS U+97F5 韵 %1B%24%42%70%71 -PASS U+97F6 韶 %1B%24%42%70%70 -PASS U+97FB 韻 %1B%24%42%31%24 -PASS U+97FF 響 %1B%24%42%36%41 -PASS U+9801 頁 %1B%24%42%4A%47 -PASS U+9802 頂 %1B%24%42%44%3A -PASS U+9803 頃 %1B%24%42%3A%22 -PASS U+9805 項 %1B%24%42%39%60 -PASS U+9806 順 %1B%24%42%3D%67 -PASS U+9808 須 %1B%24%42%3F%5C -PASS U+980C 頌 %1B%24%42%70%73 -PASS U+980F 頏 %1B%24%42%70%72 -PASS U+9810 預 %1B%24%42%4D%42 -PASS U+9811 頑 %1B%24%42%34%68 -PASS U+9812 頒 %1B%24%42%48%52 -PASS U+9813 頓 %1B%24%42%46%5C -PASS U+9817 頗 %1B%24%42%3F%7C -PASS U+9818 領 %1B%24%42%4E%4E -PASS U+981A 頚 %1B%24%42%37%5B -PASS U+9821 頡 %1B%24%42%70%76 -PASS U+9824 頤 %1B%24%42%70%75 -PASS U+982C 頬 %1B%24%42%4B%4B -PASS U+982D 頭 %1B%24%42%46%2C -PASS U+9834 頴 %1B%24%42%31%50 -PASS U+9837 頷 %1B%24%42%70%77 -PASS U+9838 頸 %1B%24%42%70%74 -PASS U+983B 頻 %1B%24%42%49%51 -PASS U+983C 頼 %1B%24%42%4D%6A -PASS U+983D 頽 %1B%24%42%70%78 -PASS U+9846 顆 %1B%24%42%70%79 -PASS U+984B 顋 %1B%24%42%70%7B -PASS U+984C 題 %1B%24%42%42%6A -PASS U+984D 額 %1B%24%42%33%5B -PASS U+984E 顎 %1B%24%42%33%5C -PASS U+984F 顏 %1B%24%42%70%7A -PASS U+9854 顔 %1B%24%42%34%69 -PASS U+9855 顕 %1B%24%42%38%32 -FAIL U+9857 顗 %1B%24%42%7C%5A assert_equals: expected "%1B%24%42%7C%5A" but got "%26%23%33%38%39%39%39%3B" -PASS U+9858 願 %1B%24%42%34%6A -PASS U+985B 顛 %1B%24%42%45%3F -PASS U+985E 類 %1B%24%42%4E%60 -FAIL U+9865 顥 %1B%24%42%7C%5B assert_equals: expected "%1B%24%42%7C%5B" but got "%26%23%33%39%30%31%33%3B" -PASS U+9867 顧 %1B%24%42%38%5C -PASS U+986B 顫 %1B%24%42%70%7C -PASS U+986F 顯 %1B%24%42%70%7D -PASS U+9870 顰 %1B%24%42%70%7E -PASS U+9871 顱 %1B%24%42%71%21 -PASS U+9873 顳 %1B%24%42%71%23 -PASS U+9874 顴 %1B%24%42%71%22 -PASS U+98A8 風 %1B%24%42%49%77 -PASS U+98AA 颪 %1B%24%42%71%24 -PASS U+98AF 颯 %1B%24%42%71%25 -PASS U+98B1 颱 %1B%24%42%71%26 -PASS U+98B6 颶 %1B%24%42%71%27 -PASS U+98C3 飃 %1B%24%42%71%29 -PASS U+98C4 飄 %1B%24%42%71%28 -PASS U+98C6 飆 %1B%24%42%71%2A -PASS U+98DB 飛 %1B%24%42%48%74 -PASS U+98DC 飜 %1B%24%42%66%4C -PASS U+98DF 食 %1B%24%42%3F%29 -PASS U+98E2 飢 %1B%24%42%35%32 -PASS U+98E9 飩 %1B%24%42%71%2B -PASS U+98EB 飫 %1B%24%42%71%2C -PASS U+98ED 飭 %1B%24%42%52%2C -PASS U+98EE 飮 %1B%24%42%5D%3B -PASS U+98EF 飯 %1B%24%42%48%53 -PASS U+98F2 飲 %1B%24%42%30%7B -PASS U+98F4 飴 %1B%24%42%30%3B -PASS U+98FC 飼 %1B%24%42%3B%74 -PASS U+98FD 飽 %1B%24%42%4B%30 -PASS U+98FE 飾 %1B%24%42%3E%7E -PASS U+9903 餃 %1B%24%42%71%2D -PASS U+9905 餅 %1B%24%42%4C%5F -PASS U+9909 餉 %1B%24%42%71%2E -PASS U+990A 養 %1B%24%42%4D%5C -PASS U+990C 餌 %1B%24%42%31%42 -PASS U+9910 餐 %1B%24%42%3B%41 -PASS U+9912 餒 %1B%24%42%71%2F -PASS U+9913 餓 %1B%24%42%32%6E -PASS U+9914 餔 %1B%24%42%71%30 -PASS U+9918 餘 %1B%24%42%71%31 -PASS U+991D 餝 %1B%24%42%71%33 -PASS U+991E 餞 %1B%24%42%71%34 -PASS U+9920 餠 %1B%24%42%71%36 -PASS U+9921 餡 %1B%24%42%71%32 -PASS U+9924 餤 %1B%24%42%71%35 -FAIL U+9927 餧 %1B%24%42%7C%5E assert_equals: expected "%1B%24%42%7C%5E" but got "%26%23%33%39%32%30%37%3B" -PASS U+9928 館 %1B%24%42%34%5B -PASS U+992C 餬 %1B%24%42%71%37 -PASS U+992E 餮 %1B%24%42%71%38 -PASS U+993D 餽 %1B%24%42%71%39 -PASS U+993E 餾 %1B%24%42%71%3A -PASS U+9942 饂 %1B%24%42%71%3B -PASS U+9945 饅 %1B%24%42%71%3D -PASS U+9949 饉 %1B%24%42%71%3C -PASS U+994B 饋 %1B%24%42%71%3F -PASS U+994C 饌 %1B%24%42%71%42 -PASS U+9950 饐 %1B%24%42%71%3E -PASS U+9951 饑 %1B%24%42%71%40 -PASS U+9952 饒 %1B%24%42%71%41 -PASS U+9955 饕 %1B%24%42%71%43 -PASS U+9957 饗 %1B%24%42%36%42 -PASS U+9996 首 %1B%24%42%3C%73 -PASS U+9997 馗 %1B%24%42%71%44 -PASS U+9998 馘 %1B%24%42%71%45 -PASS U+9999 香 %1B%24%42%39%61 -FAIL U+999E 馞 %1B%24%42%7C%60 assert_equals: expected "%1B%24%42%7C%60" but got "%26%23%33%39%33%32%36%3B" -PASS U+99A5 馥 %1B%24%42%71%46 -PASS U+99A8 馨 %1B%24%42%33%3E -PASS U+99AC 馬 %1B%24%42%47%4F -PASS U+99AD 馭 %1B%24%42%71%47 -PASS U+99AE 馮 %1B%24%42%71%48 -PASS U+99B3 馳 %1B%24%42%43%5A -PASS U+99B4 馴 %1B%24%42%46%6B -PASS U+99BC 馼 %1B%24%42%71%49 -PASS U+99C1 駁 %1B%24%42%47%7D -PASS U+99C4 駄 %1B%24%42%42%4C -PASS U+99C5 駅 %1B%24%42%31%58 -PASS U+99C6 駆 %1B%24%42%36%6E -PASS U+99C8 駈 %1B%24%42%36%6F -PASS U+99D0 駐 %1B%24%42%43%73 -PASS U+99D1 駑 %1B%24%42%71%4E -PASS U+99D2 駒 %1B%24%42%36%70 -PASS U+99D5 駕 %1B%24%42%32%6F -PASS U+99D8 駘 %1B%24%42%71%4D -PASS U+99DB 駛 %1B%24%42%71%4B -PASS U+99DD 駝 %1B%24%42%71%4C -PASS U+99DF 駟 %1B%24%42%71%4A -PASS U+99E2 駢 %1B%24%42%71%58 -PASS U+99ED 駭 %1B%24%42%71%4F -PASS U+99EE 駮 %1B%24%42%71%50 -PASS U+99F1 駱 %1B%24%42%71%51 -PASS U+99F2 駲 %1B%24%42%71%52 -PASS U+99F8 駸 %1B%24%42%71%54 -PASS U+99FB 駻 %1B%24%42%71%53 -PASS U+99FF 駿 %1B%24%42%3D%59 -PASS U+9A01 騁 %1B%24%42%71%55 -PASS U+9A05 騅 %1B%24%42%71%57 -PASS U+9A0E 騎 %1B%24%42%35%33 -PASS U+9A0F 騏 %1B%24%42%71%56 -PASS U+9A12 騒 %1B%24%42%41%7B -PASS U+9A13 験 %1B%24%42%38%33 -PASS U+9A19 騙 %1B%24%42%71%59 -PASS U+9A28 騨 %1B%24%42%42%4D -PASS U+9A2B 騫 %1B%24%42%71%5A -PASS U+9A30 騰 %1B%24%42%46%2D -PASS U+9A37 騷 %1B%24%42%71%5B -PASS U+9A3E 騾 %1B%24%42%71%60 -PASS U+9A40 驀 %1B%24%42%71%5E -PASS U+9A42 驂 %1B%24%42%71%5D -PASS U+9A43 驃 %1B%24%42%71%5F -PASS U+9A45 驅 %1B%24%42%71%5C -PASS U+9A4D 驍 %1B%24%42%71%62 -FAIL U+9A4E 驎 %1B%24%42%7C%61 assert_equals: expected "%1B%24%42%7C%61" but got "%26%23%33%39%35%30%32%3B" -PASS U+9A55 驕 %1B%24%42%71%61 -PASS U+9A57 驗 %1B%24%42%71%64 -PASS U+9A5A 驚 %1B%24%42%36%43 -PASS U+9A5B 驛 %1B%24%42%71%63 -PASS U+9A5F 驟 %1B%24%42%71%65 -PASS U+9A62 驢 %1B%24%42%71%66 -PASS U+9A64 驤 %1B%24%42%71%68 -PASS U+9A65 驥 %1B%24%42%71%67 -PASS U+9A69 驩 %1B%24%42%71%69 -PASS U+9A6A 驪 %1B%24%42%71%6B -PASS U+9A6B 驫 %1B%24%42%71%6A -PASS U+9AA8 骨 %1B%24%42%39%7C -PASS U+9AAD 骭 %1B%24%42%71%6C -PASS U+9AB0 骰 %1B%24%42%71%6D -PASS U+9AB8 骸 %1B%24%42%33%3C -PASS U+9ABC 骼 %1B%24%42%71%6E -PASS U+9AC0 髀 %1B%24%42%71%6F -PASS U+9AC4 髄 %1B%24%42%3F%71 -PASS U+9ACF 髏 %1B%24%42%71%70 -PASS U+9AD1 髑 %1B%24%42%71%71 -PASS U+9AD3 髓 %1B%24%42%71%72 -PASS U+9AD4 體 %1B%24%42%71%73 -PASS U+9AD8 高 %1B%24%42%39%62 -FAIL U+9AD9 髙 %1B%24%42%7C%62 assert_equals: expected "%1B%24%42%7C%62" but got "%26%23%33%39%36%34%31%3B" -FAIL U+9ADC 髜 %1B%24%42%7C%63 assert_equals: expected "%1B%24%42%7C%63" but got "%26%23%33%39%36%34%34%3B" -PASS U+9ADE 髞 %1B%24%42%71%74 -PASS U+9ADF 髟 %1B%24%42%71%75 -PASS U+9AE2 髢 %1B%24%42%71%76 -PASS U+9AE3 髣 %1B%24%42%71%77 -PASS U+9AE6 髦 %1B%24%42%71%78 -PASS U+9AEA 髪 %1B%24%42%48%31 -PASS U+9AEB 髫 %1B%24%42%71%7A -PASS U+9AED 髭 %1B%24%42%49%26 -PASS U+9AEE 髮 %1B%24%42%71%7B -PASS U+9AEF 髯 %1B%24%42%71%79 -PASS U+9AF1 髱 %1B%24%42%71%7D -PASS U+9AF4 髴 %1B%24%42%71%7C -PASS U+9AF7 髷 %1B%24%42%71%7E -PASS U+9AFB 髻 %1B%24%42%72%21 -PASS U+9B06 鬆 %1B%24%42%72%22 -PASS U+9B18 鬘 %1B%24%42%72%23 -PASS U+9B1A 鬚 %1B%24%42%72%24 -PASS U+9B1F 鬟 %1B%24%42%72%25 -PASS U+9B22 鬢 %1B%24%42%72%26 -PASS U+9B23 鬣 %1B%24%42%72%27 -PASS U+9B25 鬥 %1B%24%42%72%28 -PASS U+9B27 鬧 %1B%24%42%72%29 -PASS U+9B28 鬨 %1B%24%42%72%2A -PASS U+9B29 鬩 %1B%24%42%72%2B -PASS U+9B2A 鬪 %1B%24%42%72%2C -PASS U+9B2E 鬮 %1B%24%42%72%2D -PASS U+9B2F 鬯 %1B%24%42%72%2E -PASS U+9B31 鬱 %1B%24%42%5D%35 -PASS U+9B32 鬲 %1B%24%42%72%2F -PASS U+9B3B 鬻 %1B%24%42%64%78 -PASS U+9B3C 鬼 %1B%24%42%35%34 -PASS U+9B41 魁 %1B%24%42%33%21 -PASS U+9B42 魂 %1B%24%42%3A%32 -PASS U+9B43 魃 %1B%24%42%72%31 -PASS U+9B44 魄 %1B%24%42%72%30 -PASS U+9B45 魅 %1B%24%42%4C%25 -PASS U+9B4D 魍 %1B%24%42%72%33 -PASS U+9B4E 魎 %1B%24%42%72%34 -PASS U+9B4F 魏 %1B%24%42%72%32 -PASS U+9B51 魑 %1B%24%42%72%35 -PASS U+9B54 魔 %1B%24%42%4B%62 -PASS U+9B58 魘 %1B%24%42%72%36 -PASS U+9B5A 魚 %1B%24%42%35%7B -PASS U+9B6F 魯 %1B%24%42%4F%25 -FAIL U+9B72 魲 %1B%24%42%7C%65 assert_equals: expected "%1B%24%42%7C%65" but got "%26%23%33%39%37%39%34%3B" -PASS U+9B74 魴 %1B%24%42%72%37 -FAIL U+9B75 魵 %1B%24%42%7C%64 assert_equals: expected "%1B%24%42%7C%64" but got "%26%23%33%39%37%39%37%3B" -PASS U+9B83 鮃 %1B%24%42%72%39 -PASS U+9B8E 鮎 %1B%24%42%30%3E -FAIL U+9B8F 鮏 %1B%24%42%7C%66 assert_equals: expected "%1B%24%42%7C%66" but got "%26%23%33%39%38%32%33%3B" -PASS U+9B91 鮑 %1B%24%42%72%3A -PASS U+9B92 鮒 %1B%24%42%4A%2B -PASS U+9B93 鮓 %1B%24%42%72%38 -PASS U+9B96 鮖 %1B%24%42%72%3B -PASS U+9B97 鮗 %1B%24%42%72%3C -PASS U+9B9F 鮟 %1B%24%42%72%3D -PASS U+9BA0 鮠 %1B%24%42%72%3E -PASS U+9BA8 鮨 %1B%24%42%72%3F -PASS U+9BAA 鮪 %1B%24%42%4B%6E -PASS U+9BAB 鮫 %1B%24%42%3B%2D -PASS U+9BAD 鮭 %1B%24%42%3A%7A -PASS U+9BAE 鮮 %1B%24%42%41%2F -FAIL U+9BB1 鮱 %1B%24%42%7C%67 assert_equals: expected "%1B%24%42%7C%67" but got "%26%23%33%39%38%35%37%3B" -PASS U+9BB4 鮴 %1B%24%42%72%40 -PASS U+9BB9 鮹 %1B%24%42%72%43 -FAIL U+9BBB 鮻 %1B%24%42%7C%68 assert_equals: expected "%1B%24%42%7C%68" but got "%26%23%33%39%38%36%37%3B" -PASS U+9BC0 鯀 %1B%24%42%72%41 -PASS U+9BC6 鯆 %1B%24%42%72%44 -PASS U+9BC9 鯉 %1B%24%42%38%71 -PASS U+9BCA 鯊 %1B%24%42%72%42 -PASS U+9BCF 鯏 %1B%24%42%72%45 -PASS U+9BD1 鯑 %1B%24%42%72%46 -PASS U+9BD2 鯒 %1B%24%42%72%47 -PASS U+9BD4 鯔 %1B%24%42%72%4B -PASS U+9BD6 鯖 %1B%24%42%3B%2A -PASS U+9BDB 鯛 %1B%24%42%42%64 -PASS U+9BE1 鯡 %1B%24%42%72%4C -PASS U+9BE2 鯢 %1B%24%42%72%49 -PASS U+9BE3 鯣 %1B%24%42%72%48 -PASS U+9BE4 鯤 %1B%24%42%72%4A -PASS U+9BE8 鯨 %1B%24%42%37%5F -PASS U+9BF0 鯰 %1B%24%42%72%50 -PASS U+9BF1 鯱 %1B%24%42%72%4F -PASS U+9BF2 鯲 %1B%24%42%72%4E -PASS U+9BF5 鯵 %1B%24%42%30%33 -FAIL U+9C00 鰀 %1B%24%42%7C%69 assert_equals: expected "%1B%24%42%7C%69" but got "%26%23%33%39%39%33%36%3B" -PASS U+9C04 鰄 %1B%24%42%72%5A -PASS U+9C06 鰆 %1B%24%42%72%56 -PASS U+9C08 鰈 %1B%24%42%72%57 -PASS U+9C09 鰉 %1B%24%42%72%53 -PASS U+9C0A 鰊 %1B%24%42%72%59 -PASS U+9C0C 鰌 %1B%24%42%72%55 -PASS U+9C0D 鰍 %1B%24%42%33%62 -PASS U+9C10 鰐 %1B%24%42%4F%4C -PASS U+9C12 鰒 %1B%24%42%72%58 -PASS U+9C13 鰓 %1B%24%42%72%54 -PASS U+9C14 鰔 %1B%24%42%72%52 -PASS U+9C15 鰕 %1B%24%42%72%51 -PASS U+9C1B 鰛 %1B%24%42%72%5C -PASS U+9C21 鰡 %1B%24%42%72%5F -PASS U+9C24 鰤 %1B%24%42%72%5E -PASS U+9C25 鰥 %1B%24%42%72%5D -PASS U+9C2D 鰭 %1B%24%42%49%49 -PASS U+9C2E 鰮 %1B%24%42%72%5B -PASS U+9C2F 鰯 %1B%24%42%30%73 -PASS U+9C30 鰰 %1B%24%42%72%60 -PASS U+9C32 鰲 %1B%24%42%72%62 -PASS U+9C39 鰹 %1B%24%42%33%6F -PASS U+9C3A 鰺 %1B%24%42%72%4D -PASS U+9C3B 鰻 %1B%24%42%31%37 -PASS U+9C3E 鰾 %1B%24%42%72%64 -PASS U+9C46 鱆 %1B%24%42%72%63 -PASS U+9C47 鱇 %1B%24%42%72%61 -PASS U+9C48 鱈 %1B%24%42%43%2D -PASS U+9C52 鱒 %1B%24%42%4B%70 -PASS U+9C57 鱗 %1B%24%42%4E%5A -PASS U+9C5A 鱚 %1B%24%42%72%65 -PASS U+9C60 鱠 %1B%24%42%72%66 -PASS U+9C67 鱧 %1B%24%42%72%67 -PASS U+9C76 鱶 %1B%24%42%72%68 -PASS U+9C78 鱸 %1B%24%42%72%69 -PASS U+9CE5 鳥 %1B%24%42%44%3B -PASS U+9CE7 鳧 %1B%24%42%72%6A -PASS U+9CE9 鳩 %1B%24%42%48%37 -PASS U+9CEB 鳫 %1B%24%42%72%6F -PASS U+9CEC 鳬 %1B%24%42%72%6B -PASS U+9CF0 鳰 %1B%24%42%72%6C -PASS U+9CF3 鳳 %1B%24%42%4B%31 -PASS U+9CF4 鳴 %1B%24%42%4C%44 -PASS U+9CF6 鳶 %1B%24%42%46%50 -PASS U+9D03 鴃 %1B%24%42%72%70 -PASS U+9D06 鴆 %1B%24%42%72%71 -PASS U+9D07 鴇 %1B%24%42%46%3E -PASS U+9D08 鴈 %1B%24%42%72%6E -PASS U+9D09 鴉 %1B%24%42%72%6D -PASS U+9D0E 鴎 %1B%24%42%32%2A -PASS U+9D12 鴒 %1B%24%42%72%79 -PASS U+9D15 鴕 %1B%24%42%72%78 -PASS U+9D1B 鴛 %1B%24%42%31%75 -PASS U+9D1F 鴟 %1B%24%42%72%76 -PASS U+9D23 鴣 %1B%24%42%72%75 -PASS U+9D26 鴦 %1B%24%42%72%73 -PASS U+9D28 鴨 %1B%24%42%33%7B -PASS U+9D2A 鴪 %1B%24%42%72%72 -PASS U+9D2B 鴫 %1B%24%42%3C%32 -PASS U+9D2C 鴬 %1B%24%42%32%29 -PASS U+9D3B 鴻 %1B%24%42%39%63 -PASS U+9D3E 鴾 %1B%24%42%72%7C -PASS U+9D3F 鴿 %1B%24%42%72%7B -PASS U+9D41 鵁 %1B%24%42%72%7A -PASS U+9D44 鵄 %1B%24%42%72%77 -PASS U+9D46 鵆 %1B%24%42%72%7D -PASS U+9D48 鵈 %1B%24%42%72%7E -PASS U+9D50 鵐 %1B%24%42%73%25 -PASS U+9D51 鵑 %1B%24%42%73%24 -PASS U+9D59 鵙 %1B%24%42%73%26 -PASS U+9D5C 鵜 %1B%24%42%31%2D -PASS U+9D5D 鵝 %1B%24%42%73%21 -PASS U+9D5E 鵞 %1B%24%42%73%22 -PASS U+9D60 鵠 %1B%24%42%39%74 -PASS U+9D61 鵡 %1B%24%42%4C%39 -PASS U+9D64 鵤 %1B%24%42%73%23 -FAIL U+9D6B 鵫 %1B%24%42%7C%6B assert_equals: expected "%1B%24%42%7C%6B" but got "%26%23%34%30%32%39%39%3B" -PASS U+9D6C 鵬 %1B%24%42%4B%32 -PASS U+9D6F 鵯 %1B%24%42%73%2B -FAIL U+9D70 鵰 %1B%24%42%7C%6A assert_equals: expected "%1B%24%42%7C%6A" but got "%26%23%34%30%33%30%34%3B" -PASS U+9D72 鵲 %1B%24%42%73%27 -PASS U+9D7A 鵺 %1B%24%42%73%2C -PASS U+9D87 鶇 %1B%24%42%73%29 -PASS U+9D89 鶉 %1B%24%42%73%28 -PASS U+9D8F 鶏 %1B%24%42%37%5C -PASS U+9D9A 鶚 %1B%24%42%73%2D -PASS U+9DA4 鶤 %1B%24%42%73%2E -PASS U+9DA9 鶩 %1B%24%42%73%2F -PASS U+9DAB 鶫 %1B%24%42%73%2A -PASS U+9DAF 鶯 %1B%24%42%72%74 -PASS U+9DB2 鶲 %1B%24%42%73%30 -PASS U+9DB4 鶴 %1B%24%42%44%61 -PASS U+9DB8 鶸 %1B%24%42%73%34 -PASS U+9DBA 鶺 %1B%24%42%73%35 -PASS U+9DBB 鶻 %1B%24%42%73%33 -PASS U+9DC1 鷁 %1B%24%42%73%32 -PASS U+9DC2 鷂 %1B%24%42%73%38 -PASS U+9DC4 鷄 %1B%24%42%73%31 -PASS U+9DC6 鷆 %1B%24%42%73%36 -PASS U+9DCF 鷏 %1B%24%42%73%37 -PASS U+9DD3 鷓 %1B%24%42%73%3A -PASS U+9DD9 鷙 %1B%24%42%73%39 -PASS U+9DE6 鷦 %1B%24%42%73%3C -PASS U+9DED 鷭 %1B%24%42%73%3D -PASS U+9DEF 鷯 %1B%24%42%73%3E -PASS U+9DF2 鷲 %1B%24%42%4F%49 -PASS U+9DF8 鷸 %1B%24%42%73%3B -PASS U+9DF9 鷹 %1B%24%42%42%6B -PASS U+9DFA 鷺 %1B%24%42%3A%6D -PASS U+9DFD 鷽 %1B%24%42%73%3F -FAIL U+9E19 鸙 %1B%24%42%7C%6D assert_equals: expected "%1B%24%42%7C%6D" but got "%26%23%34%30%34%37%33%3B" -PASS U+9E1A 鸚 %1B%24%42%73%40 -PASS U+9E1B 鸛 %1B%24%42%73%41 -PASS U+9E1E 鸞 %1B%24%42%73%42 -PASS U+9E75 鹵 %1B%24%42%73%43 -PASS U+9E78 鹸 %1B%24%42%38%34 -PASS U+9E79 鹹 %1B%24%42%73%44 -PASS U+9E7D 鹽 %1B%24%42%73%45 -PASS U+9E7F 鹿 %1B%24%42%3C%2F -PASS U+9E81 麁 %1B%24%42%73%46 -PASS U+9E88 麈 %1B%24%42%73%47 -PASS U+9E8B 麋 %1B%24%42%73%48 -PASS U+9E8C 麌 %1B%24%42%73%49 -PASS U+9E91 麑 %1B%24%42%73%4C -PASS U+9E92 麒 %1B%24%42%73%4A -PASS U+9E93 麓 %1B%24%42%4F%3C -PASS U+9E95 麕 %1B%24%42%73%4B -PASS U+9E97 麗 %1B%24%42%4E%6F -PASS U+9E9D 麝 %1B%24%42%73%4D -PASS U+9E9F 麟 %1B%24%42%4E%5B -PASS U+9EA5 麥 %1B%24%42%73%4E -PASS U+9EA6 麦 %1B%24%42%47%7E -PASS U+9EA9 麩 %1B%24%42%73%4F -PASS U+9EAA 麪 %1B%24%42%73%51 -PASS U+9EAD 麭 %1B%24%42%73%52 -PASS U+9EB8 麸 %1B%24%42%73%50 -PASS U+9EB9 麹 %1B%24%42%39%6D -PASS U+9EBA 麺 %1B%24%42%4C%4D -PASS U+9EBB 麻 %1B%24%42%4B%63 -PASS U+9EBC 麼 %1B%24%42%56%77 -PASS U+9EBE 麾 %1B%24%42%5D%60 -PASS U+9EBF 麿 %1B%24%42%4B%7B -PASS U+9EC4 黄 %1B%24%42%32%2B -PASS U+9ECC 黌 %1B%24%42%73%54 -PASS U+9ECD 黍 %1B%24%42%35%50 -PASS U+9ECE 黎 %1B%24%42%73%55 -PASS U+9ECF 黏 %1B%24%42%73%56 -PASS U+9ED0 黐 %1B%24%42%73%57 -FAIL U+9ED1 黑 %1B%24%42%7C%6E assert_equals: expected "%1B%24%42%7C%6E" but got "%26%23%34%30%36%35%37%3B" -PASS U+9ED2 黒 %1B%24%42%39%75 -PASS U+9ED4 黔 %1B%24%42%73%58 -PASS U+9ED8 默 %1B%24%42%60%54 -PASS U+9ED9 黙 %1B%24%42%4C%5B -PASS U+9EDB 黛 %1B%24%42%42%63 -PASS U+9EDC 黜 %1B%24%42%73%59 -PASS U+9EDD 黝 %1B%24%42%73%5B -PASS U+9EDE 點 %1B%24%42%73%5A -PASS U+9EE0 黠 %1B%24%42%73%5C -PASS U+9EE5 黥 %1B%24%42%73%5D -PASS U+9EE8 黨 %1B%24%42%73%5E -PASS U+9EEF 黯 %1B%24%42%73%5F -PASS U+9EF4 黴 %1B%24%42%73%60 -PASS U+9EF6 黶 %1B%24%42%73%61 -PASS U+9EF7 黷 %1B%24%42%73%62 -PASS U+9EF9 黹 %1B%24%42%73%63 -PASS U+9EFB 黻 %1B%24%42%73%64 -PASS U+9EFC 黼 %1B%24%42%73%65 -PASS U+9EFD 黽 %1B%24%42%73%66 -PASS U+9F07 鼇 %1B%24%42%73%67 -PASS U+9F08 鼈 %1B%24%42%73%68 -PASS U+9F0E 鼎 %1B%24%42%45%24 -PASS U+9F13 鼓 %1B%24%42%38%5D -PASS U+9F15 鼕 %1B%24%42%73%6A -PASS U+9F20 鼠 %1B%24%42%41%4D -PASS U+9F21 鼡 %1B%24%42%73%6B -PASS U+9F2C 鼬 %1B%24%42%73%6C -PASS U+9F3B 鼻 %1B%24%42%49%21 -PASS U+9F3E 鼾 %1B%24%42%73%6D -PASS U+9F4A 齊 %1B%24%42%73%6E -PASS U+9F4B 齋 %1B%24%42%63%37 -PASS U+9F4E 齎 %1B%24%42%6C%5A -PASS U+9F4F 齏 %1B%24%42%70%6D -PASS U+9F52 齒 %1B%24%42%73%6F -PASS U+9F54 齔 %1B%24%42%73%70 -PASS U+9F5F 齟 %1B%24%42%73%72 -PASS U+9F60 齠 %1B%24%42%73%73 -PASS U+9F61 齡 %1B%24%42%73%74 -PASS U+9F62 齢 %1B%24%42%4E%70 -PASS U+9F63 齣 %1B%24%42%73%71 -PASS U+9F66 齦 %1B%24%42%73%75 -PASS U+9F67 齧 %1B%24%42%73%76 -PASS U+9F6A 齪 %1B%24%42%73%78 -PASS U+9F6C 齬 %1B%24%42%73%77 -PASS U+9F72 齲 %1B%24%42%73%7A -PASS U+9F76 齶 %1B%24%42%73%7B -PASS U+9F77 齷 %1B%24%42%73%79 -PASS U+9F8D 龍 %1B%24%42%4E%36 -PASS U+9F95 龕 %1B%24%42%73%7C -PASS U+9F9C 龜 %1B%24%42%73%7D -PASS U+9F9D 龝 %1B%24%42%63%54 -PASS U+9FA0 龠 %1B%24%42%73%7E -FAIL U+F929 朗 %1B%24%42%7A%46 assert_equals: expected "%1B%24%42%7A%46" but got "%26%23%36%33%37%38%35%3B" -FAIL U+F9DC 隆 %1B%24%42%7C%4F assert_equals: expected "%1B%24%42%7C%4F" but got "%26%23%36%33%39%36%34%3B" -FAIL U+FA0E 﨎 %1B%24%42%79%54 assert_equals: expected "%1B%24%42%79%54" but got "%26%23%36%34%30%31%34%3B" -FAIL U+FA0F 﨏 %1B%24%42%79%5F assert_equals: expected "%1B%24%42%79%5F" but got "%26%23%36%34%30%31%35%3B" -FAIL U+FA10 塚 %1B%24%42%79%60 assert_equals: expected "%1B%24%42%79%60" but got "%26%23%36%34%30%31%36%3B" -FAIL U+FA11 﨑 %1B%24%42%79%75 assert_equals: expected "%1B%24%42%79%75" but got "%26%23%36%34%30%31%37%3B" -FAIL U+FA12 晴 %1B%24%42%7A%3E assert_equals: expected "%1B%24%42%7A%3E" but got "%26%23%36%34%30%31%38%3B" -FAIL U+FA13 﨓 %1B%24%42%7A%4E assert_equals: expected "%1B%24%42%7A%4E" but got "%26%23%36%34%30%31%39%3B" -FAIL U+FA14 﨔 %1B%24%42%7A%50 assert_equals: expected "%1B%24%42%7A%50" but got "%26%23%36%34%30%32%30%3B" -FAIL U+FA15 凞 %1B%24%42%7A%7B assert_equals: expected "%1B%24%42%7A%7B" but got "%26%23%36%34%30%32%31%3B" -FAIL U+FA16 猪 %1B%24%42%7B%23 assert_equals: expected "%1B%24%42%7B%23" but got "%26%23%36%34%30%32%32%3B" -FAIL U+FA17 益 %1B%24%42%7B%3A assert_equals: expected "%1B%24%42%7B%3A" but got "%26%23%36%34%30%32%33%3B" -FAIL U+FA18 礼 %1B%24%42%7B%42 assert_equals: expected "%1B%24%42%7B%42" but got "%26%23%36%34%30%32%34%3B" -FAIL U+FA19 神 %1B%24%42%7B%43 assert_equals: expected "%1B%24%42%7B%43" but got "%26%23%36%34%30%32%35%3B" -FAIL U+FA1A 祥 %1B%24%42%7B%44 assert_equals: expected "%1B%24%42%7B%44" but got "%26%23%36%34%30%32%36%3B" -FAIL U+FA1B 福 %1B%24%42%7B%46 assert_equals: expected "%1B%24%42%7B%46" but got "%26%23%36%34%30%32%37%3B" -FAIL U+FA1C 靖 %1B%24%42%7B%4A assert_equals: expected "%1B%24%42%7B%4A" but got "%26%23%36%34%30%32%38%3B" -FAIL U+FA1D 精 %1B%24%42%7B%4D assert_equals: expected "%1B%24%42%7B%4D" but got "%26%23%36%34%30%32%39%3B" -FAIL U+FA1E 羽 %1B%24%42%7B%56 assert_equals: expected "%1B%24%42%7B%56" but got "%26%23%36%34%30%33%30%3B" -FAIL U+FA1F 﨟 %1B%24%42%7B%61 assert_equals: expected "%1B%24%42%7B%61" but got "%26%23%36%34%30%33%31%3B" -FAIL U+FA20 蘒 %1B%24%42%7B%63 assert_equals: expected "%1B%24%42%7B%63" but got "%26%23%36%34%30%33%32%3B" -FAIL U+FA21 﨡 %1B%24%42%7B%64 assert_equals: expected "%1B%24%42%7B%64" but got "%26%23%36%34%30%33%33%3B" -FAIL U+FA22 諸 %1B%24%42%7B%6D assert_equals: expected "%1B%24%42%7B%6D" but got "%26%23%36%34%30%33%34%3B" -FAIL U+FA23 﨣 %1B%24%42%7B%75 assert_equals: expected "%1B%24%42%7B%75" but got "%26%23%36%34%30%33%35%3B" -FAIL U+FA24 﨤 %1B%24%42%7B%77 assert_equals: expected "%1B%24%42%7B%77" but got "%26%23%36%34%30%33%36%3B" -FAIL U+FA25 逸 %1B%24%42%7B%78 assert_equals: expected "%1B%24%42%7B%78" but got "%26%23%36%34%30%33%37%3B" -FAIL U+FA26 都 %1B%24%42%7B%7B assert_equals: expected "%1B%24%42%7B%7B" but got "%26%23%36%34%30%33%38%3B" -FAIL U+FA27 﨧 %1B%24%42%7C%39 assert_equals: expected "%1B%24%42%7C%39" but got "%26%23%36%34%30%33%39%3B" -FAIL U+FA28 﨨 %1B%24%42%7C%40 assert_equals: expected "%1B%24%42%7C%40" but got "%26%23%36%34%30%34%30%3B" -FAIL U+FA29 﨩 %1B%24%42%7C%50 assert_equals: expected "%1B%24%42%7C%50" but got "%26%23%36%34%30%34%31%3B" -FAIL U+FA2A 飯 %1B%24%42%7C%5C assert_equals: expected "%1B%24%42%7C%5C" but got "%26%23%36%34%30%34%32%3B" -FAIL U+FA2B 飼 %1B%24%42%7C%5D assert_equals: expected "%1B%24%42%7C%5D" but got "%26%23%36%34%30%34%33%3B" -FAIL U+FA2C 館 %1B%24%42%7C%5F assert_equals: expected "%1B%24%42%7C%5F" but got "%26%23%36%34%30%34%34%3B" -FAIL U+FA2D 鶴 %1B%24%42%7C%6C assert_equals: expected "%1B%24%42%7C%6C" but got "%26%23%36%34%30%34%35%3B" -PASS U+FF01 ! %1B%24%42%21%2A -FAIL U+FF02 " %1B%24%42%7C%7E assert_equals: expected "%1B%24%42%7C%7E" but got "%26%23%36%35%32%38%32%3B" -PASS U+FF03 # %1B%24%42%21%74 -PASS U+FF04 $ %1B%24%42%21%70 -PASS U+FF05 % %1B%24%42%21%73 -PASS U+FF06 & %1B%24%42%21%75 -FAIL U+FF07 ' %1B%24%42%7C%7D assert_equals: expected "%1B%24%42%7C%7D" but got "%26%23%36%35%32%38%37%3B" -PASS U+FF08 ( %1B%24%42%21%4A -PASS U+FF09 ) %1B%24%42%21%4B -PASS U+FF0A * %1B%24%42%21%76 -PASS U+FF0B + %1B%24%42%21%5C -PASS U+FF0C , %1B%24%42%21%24 -PASS U+FF0D - %1B%24%42%21%5D -PASS U+FF0E . %1B%24%42%21%25 -PASS U+FF0F / %1B%24%42%21%3F -PASS U+FF10 0 %1B%24%42%23%30 -PASS U+FF11 1 %1B%24%42%23%31 -PASS U+FF12 2 %1B%24%42%23%32 -PASS U+FF13 3 %1B%24%42%23%33 -PASS U+FF14 4 %1B%24%42%23%34 -PASS U+FF15 5 %1B%24%42%23%35 -PASS U+FF16 6 %1B%24%42%23%36 -PASS U+FF17 7 %1B%24%42%23%37 -PASS U+FF18 8 %1B%24%42%23%38 -PASS U+FF19 9 %1B%24%42%23%39 -PASS U+FF1A : %1B%24%42%21%27 -PASS U+FF1B ; %1B%24%42%21%28 -PASS U+FF1C < %1B%24%42%21%63 -PASS U+FF1D = %1B%24%42%21%61 -PASS U+FF1E > %1B%24%42%21%64 -PASS U+FF1F ? %1B%24%42%21%29 -PASS U+FF20 @ %1B%24%42%21%77 -PASS U+FF21 A %1B%24%42%23%41 -PASS U+FF22 B %1B%24%42%23%42 -PASS U+FF23 C %1B%24%42%23%43 -PASS U+FF24 D %1B%24%42%23%44 -PASS U+FF25 E %1B%24%42%23%45 -PASS U+FF26 F %1B%24%42%23%46 -PASS U+FF27 G %1B%24%42%23%47 -PASS U+FF28 H %1B%24%42%23%48 -PASS U+FF29 I %1B%24%42%23%49 -PASS U+FF2A J %1B%24%42%23%4A -PASS U+FF2B K %1B%24%42%23%4B -PASS U+FF2C L %1B%24%42%23%4C -PASS U+FF2D M %1B%24%42%23%4D -PASS U+FF2E N %1B%24%42%23%4E -PASS U+FF2F O %1B%24%42%23%4F -PASS U+FF30 P %1B%24%42%23%50 -PASS U+FF31 Q %1B%24%42%23%51 -PASS U+FF32 R %1B%24%42%23%52 -PASS U+FF33 S %1B%24%42%23%53 -PASS U+FF34 T %1B%24%42%23%54 -PASS U+FF35 U %1B%24%42%23%55 -PASS U+FF36 V %1B%24%42%23%56 -PASS U+FF37 W %1B%24%42%23%57 -PASS U+FF38 X %1B%24%42%23%58 -PASS U+FF39 Y %1B%24%42%23%59 -PASS U+FF3A Z %1B%24%42%23%5A -PASS U+FF3B [ %1B%24%42%21%4E -PASS U+FF3C \ %1B%24%42%21%40 -PASS U+FF3D ] %1B%24%42%21%4F -PASS U+FF3E ^ %1B%24%42%21%30 -PASS U+FF3F _ %1B%24%42%21%32 -PASS U+FF40 ` %1B%24%42%21%2E -PASS U+FF41 a %1B%24%42%23%61 -PASS U+FF42 b %1B%24%42%23%62 -PASS U+FF43 c %1B%24%42%23%63 -PASS U+FF44 d %1B%24%42%23%64 -PASS U+FF45 e %1B%24%42%23%65 -PASS U+FF46 f %1B%24%42%23%66 -PASS U+FF47 g %1B%24%42%23%67 -PASS U+FF48 h %1B%24%42%23%68 -PASS U+FF49 i %1B%24%42%23%69 -PASS U+FF4A j %1B%24%42%23%6A -PASS U+FF4B k %1B%24%42%23%6B -PASS U+FF4C l %1B%24%42%23%6C -PASS U+FF4D m %1B%24%42%23%6D -PASS U+FF4E n %1B%24%42%23%6E -PASS U+FF4F o %1B%24%42%23%6F -PASS U+FF50 p %1B%24%42%23%70 -PASS U+FF51 q %1B%24%42%23%71 -PASS U+FF52 r %1B%24%42%23%72 -PASS U+FF53 s %1B%24%42%23%73 -PASS U+FF54 t %1B%24%42%23%74 -PASS U+FF55 u %1B%24%42%23%75 -PASS U+FF56 v %1B%24%42%23%76 -PASS U+FF57 w %1B%24%42%23%77 -PASS U+FF58 x %1B%24%42%23%78 -PASS U+FF59 y %1B%24%42%23%79 -PASS U+FF5A z %1B%24%42%23%7A -PASS U+FF5B { %1B%24%42%21%50 -PASS U+FF5C | %1B%24%42%21%43 -PASS U+FF5D } %1B%24%42%21%51 -PASS U+FF5E ~ %1B%24%42%21%41 -PASS U+FFE0 ¢ %1B%24%42%21%71 -PASS U+FFE1 £ %1B%24%42%21%72 -PASS U+FFE2 ¬ %1B%24%42%22%4C -PASS U+FFE3  ̄ %1B%24%42%21%31 -FAIL U+FFE4 ¦ %1B%24%42%7C%7C assert_equals: expected "%1B%24%42%7C%7C" but got "%26%23%36%35%35%30%38%3B" -PASS U+FFE5 ¥ %1B%24%42%21%6F -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt deleted file mode 100644 index 6a8a733a..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/encoding/legacy-mb-tchinese/big5/big5-enc-ascii-expected.txt +++ /dev/null
@@ -1,126 +0,0 @@ -This is a testharness.js-based test. -PASS big5 encoder: test for ASCII codepoint 0x0 \0 -PASS big5 encoder: test for ASCII codepoint 0x1 -PASS big5 encoder: test for ASCII codepoint 0x2 -PASS big5 encoder: test for ASCII codepoint 0x3 -PASS big5 encoder: test for ASCII codepoint 0x4 -PASS big5 encoder: test for ASCII codepoint 0x5 -PASS big5 encoder: test for ASCII codepoint 0x6 -PASS big5 encoder: test for ASCII codepoint 0x7 -PASS big5 encoder: test for ASCII codepoint 0x8 -PASS big5 encoder: test for ASCII codepoint 0xb -PASS big5 encoder: test for ASCII codepoint 0xc -PASS big5 encoder: test for ASCII codepoint 0xe -PASS big5 encoder: test for ASCII codepoint 0xf -PASS big5 encoder: test for ASCII codepoint 0x10 -PASS big5 encoder: test for ASCII codepoint 0x11 -PASS big5 encoder: test for ASCII codepoint 0x12 -PASS big5 encoder: test for ASCII codepoint 0x13 -PASS big5 encoder: test for ASCII codepoint 0x14 -PASS big5 encoder: test for ASCII codepoint 0x15 -PASS big5 encoder: test for ASCII codepoint 0x16 -PASS big5 encoder: test for ASCII codepoint 0x17 -PASS big5 encoder: test for ASCII codepoint 0x18 -PASS big5 encoder: test for ASCII codepoint 0x19 -PASS big5 encoder: test for ASCII codepoint 0x1a -PASS big5 encoder: test for ASCII codepoint 0x1b -PASS big5 encoder: test for ASCII codepoint 0x1c -PASS big5 encoder: test for ASCII codepoint 0x1d -PASS big5 encoder: test for ASCII codepoint 0x1e -PASS big5 encoder: test for ASCII codepoint 0x1f -PASS big5 encoder: test for ASCII codepoint 0x20 -PASS big5 encoder: test for ASCII codepoint 0x21 ! -PASS big5 encoder: test for ASCII codepoint 0x22 " -PASS big5 encoder: test for ASCII codepoint 0x24 $ -PASS big5 encoder: test for ASCII codepoint 0x25 % -PASS big5 encoder: test for ASCII codepoint 0x26 & -PASS big5 encoder: test for ASCII codepoint 0x27 ' -PASS big5 encoder: test for ASCII codepoint 0x28 ( -PASS big5 encoder: test for ASCII codepoint 0x29 ) -PASS big5 encoder: test for ASCII codepoint 0x2a * -PASS big5 encoder: test for ASCII codepoint 0x2b + -PASS big5 encoder: test for ASCII codepoint 0x2c , -PASS big5 encoder: test for ASCII codepoint 0x2d - -PASS big5 encoder: test for ASCII codepoint 0x2e . -PASS big5 encoder: test for ASCII codepoint 0x2f / -PASS big5 encoder: test for ASCII codepoint 0x30 0 -PASS big5 encoder: test for ASCII codepoint 0x31 1 -PASS big5 encoder: test for ASCII codepoint 0x32 2 -PASS big5 encoder: test for ASCII codepoint 0x33 3 -PASS big5 encoder: test for ASCII codepoint 0x34 4 -PASS big5 encoder: test for ASCII codepoint 0x35 5 -PASS big5 encoder: test for ASCII codepoint 0x36 6 -PASS big5 encoder: test for ASCII codepoint 0x37 7 -PASS big5 encoder: test for ASCII codepoint 0x38 8 -PASS big5 encoder: test for ASCII codepoint 0x39 9 -PASS big5 encoder: test for ASCII codepoint 0x3a : -PASS big5 encoder: test for ASCII codepoint 0x3b ; -PASS big5 encoder: test for ASCII codepoint 0x3c < -PASS big5 encoder: test for ASCII codepoint 0x3d = -PASS big5 encoder: test for ASCII codepoint 0x3e > -PASS big5 encoder: test for ASCII codepoint 0x3f ? -PASS big5 encoder: test for ASCII codepoint 0x40 @ -PASS big5 encoder: test for ASCII codepoint 0x41 A -PASS big5 encoder: test for ASCII codepoint 0x42 B -PASS big5 encoder: test for ASCII codepoint 0x43 C -PASS big5 encoder: test for ASCII codepoint 0x44 D -PASS big5 encoder: test for ASCII codepoint 0x45 E -PASS big5 encoder: test for ASCII codepoint 0x46 F -PASS big5 encoder: test for ASCII codepoint 0x47 G -PASS big5 encoder: test for ASCII codepoint 0x48 H -PASS big5 encoder: test for ASCII codepoint 0x49 I -PASS big5 encoder: test for ASCII codepoint 0x4a J -PASS big5 encoder: test for ASCII codepoint 0x4b K -PASS big5 encoder: test for ASCII codepoint 0x4c L -PASS big5 encoder: test for ASCII codepoint 0x4d M -PASS big5 encoder: test for ASCII codepoint 0x4e N -PASS big5 encoder: test for ASCII codepoint 0x4f O -PASS big5 encoder: test for ASCII codepoint 0x50 P -PASS big5 encoder: test for ASCII codepoint 0x51 Q -PASS big5 encoder: test for ASCII codepoint 0x52 R -PASS big5 encoder: test for ASCII codepoint 0x53 S -PASS big5 encoder: test for ASCII codepoint 0x54 T -PASS big5 encoder: test for ASCII codepoint 0x55 U -PASS big5 encoder: test for ASCII codepoint 0x56 V -PASS big5 encoder: test for ASCII codepoint 0x57 W -PASS big5 encoder: test for ASCII codepoint 0x58 X -PASS big5 encoder: test for ASCII codepoint 0x59 Y -PASS big5 encoder: test for ASCII codepoint 0x5a Z -PASS big5 encoder: test for ASCII codepoint 0x5b [ -PASS big5 encoder: test for ASCII codepoint 0x5c \ -PASS big5 encoder: test for ASCII codepoint 0x5d ] -PASS big5 encoder: test for ASCII codepoint 0x5e ^ -PASS big5 encoder: test for ASCII codepoint 0x5f _ -PASS big5 encoder: test for ASCII codepoint 0x60 ` -PASS big5 encoder: test for ASCII codepoint 0x61 a -PASS big5 encoder: test for ASCII codepoint 0x62 b -PASS big5 encoder: test for ASCII codepoint 0x63 c -PASS big5 encoder: test for ASCII codepoint 0x64 d -PASS big5 encoder: test for ASCII codepoint 0x65 e -PASS big5 encoder: test for ASCII codepoint 0x66 f -PASS big5 encoder: test for ASCII codepoint 0x67 g -PASS big5 encoder: test for ASCII codepoint 0x68 h -PASS big5 encoder: test for ASCII codepoint 0x69 i -PASS big5 encoder: test for ASCII codepoint 0x6a j -PASS big5 encoder: test for ASCII codepoint 0x6b k -PASS big5 encoder: test for ASCII codepoint 0x6c l -PASS big5 encoder: test for ASCII codepoint 0x6d m -PASS big5 encoder: test for ASCII codepoint 0x6e n -PASS big5 encoder: test for ASCII codepoint 0x6f o -PASS big5 encoder: test for ASCII codepoint 0x70 p -PASS big5 encoder: test for ASCII codepoint 0x71 q -PASS big5 encoder: test for ASCII codepoint 0x72 r -PASS big5 encoder: test for ASCII codepoint 0x73 s -PASS big5 encoder: test for ASCII codepoint 0x74 t -PASS big5 encoder: test for ASCII codepoint 0x75 u -PASS big5 encoder: test for ASCII codepoint 0x76 v -PASS big5 encoder: test for ASCII codepoint 0x77 w -PASS big5 encoder: test for ASCII codepoint 0x78 x -PASS big5 encoder: test for ASCII codepoint 0x79 y -PASS big5 encoder: test for ASCII codepoint 0x7a z -PASS big5 encoder: test for ASCII codepoint 0x7b { -PASS big5 encoder: test for ASCII codepoint 0x7c | -PASS big5 encoder: test for ASCII codepoint 0x7d } -PASS big5 encoder: test for ASCII codepoint 0x7e ~ -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt index 50152a3..ba72fa6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt
@@ -18,12 +18,12 @@ "backgroundColor": "#FFFFFF", "paintInvalidations": [ { - "object": "InlineTextBox 'Second cell'", + "object": "LayoutText #text", "rect": [11, 33, 75, 18], "reason": "selection" }, { - "object": "InlineTextBox 'First cell'", + "object": "LayoutText #text", "rect": [11, 11, 61, 18], "reason": "geometry" } @@ -32,6 +32,22 @@ ], "objectPaintInvalidations": [ { + "object": "LayoutBlockFlow HTML", + "reason": "selection" + }, + { + "object": "LayoutBlockFlow BODY", + "reason": "selection" + }, + { + "object": "LayoutTable TABLE", + "reason": "selection" + }, + { + "object": "LayoutTableCell TD id='firstCell'", + "reason": "selection" + }, + { "object": "LayoutText #text", "reason": "geometry" }, @@ -40,6 +56,10 @@ "reason": "geometry" }, { + "object": "LayoutTableCell TD id='secondCell'", + "reason": "selection" + }, + { "object": "LayoutText #text", "reason": "selection" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt index 2058ff6c..2807eed 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-partial-invalidation-between-blocks-expected.txt
@@ -18,12 +18,12 @@ "backgroundColor": "#FFFFFF", "paintInvalidations": [ { - "object": "InlineTextBox 'Second cell'", + "object": "LayoutText #text", "rect": [11, 35, 70, 19], "reason": "selection" }, { - "object": "InlineTextBox 'First cell'", + "object": "LayoutText #text", "rect": [11, 11, 55, 19], "reason": "geometry" } @@ -32,6 +32,22 @@ ], "objectPaintInvalidations": [ { + "object": "LayoutBlockFlow HTML", + "reason": "selection" + }, + { + "object": "LayoutBlockFlow BODY", + "reason": "selection" + }, + { + "object": "LayoutTable TABLE", + "reason": "selection" + }, + { + "object": "LayoutTableCell TD id='firstCell'", + "reason": "selection" + }, + { "object": "LayoutText #text", "reason": "geometry" }, @@ -40,6 +56,10 @@ "reason": "geometry" }, { + "object": "LayoutTableCell TD id='secondCell'", + "reason": "selection" + }, + { "object": "LayoutText #text", "reason": "selection" },
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/fast/multicol/border-radius-clipped-layer-second-column-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/fast/multicol/border-radius-clipped-layer-second-column-expected.txt new file mode 100644 index 0000000..424c730b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/fast/multicol/border-radius-clipped-layer-second-column-expected.txt
@@ -0,0 +1,24 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x228 + LayoutNGBlockFlow {HTML} at (0,0) size 800x228 + LayoutNGBlockFlow {BODY} at (8,16) size 784x204 + LayoutNGBlockFlow {P} at (0,0) size 784x20 + LayoutText {#text} at (0,0) size 766x19 + text run at (0,0) width 766: "The word 'PASS' should be seen below on the right hand side, inside a rounded box with black border and yellow background." +layer at (8,52) size 784x168 + LayoutNGBlockFlow {DIV} at (0,36) size 784x168 + LayoutMultiColumnSet (anonymous) at (0,0) size 784x168 +layer at (8,52) size 392x264 backgroundClip at (0,0) size 400x220 clip at (0,0) size 400x220 + LayoutMultiColumnFlowThread (anonymous) at (0,0) size 392x264 + LayoutNGBlockFlow (anonymous) at (0,0) size 392x200 + LayoutBR {BR} at (0,6) size 0x0 + LayoutBR {BR} at (0,38) size 0x0 + LayoutBR {BR} at (0,70) size 0x0 + LayoutBR {BR} at (0,102) size 0x0 + LayoutBR {BR} at (0,134) size 0x0 + LayoutBR {BR} at (0,0) size 0x0 +layer at (400,84) size 392x64 clip at (416,100) size 360x32 + LayoutNGBlockFlow (relative positioned) {DIV} at (0,200) size 392x64 [bgcolor=#FFFF00] [border: (16px solid #000000)] + LayoutText {#text} at (177,22) size 38x19 + text run at (177,22) width 38: "PASS"
diff --git a/third_party/WebKit/LayoutTests/platform/win7/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt deleted file mode 100644 index c67c2fb..0000000 --- a/third_party/WebKit/LayoutTests/platform/win7/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL W3C WebSocket API - Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown assert_throws: function "function () { wsocket = CreateWebSocketWithRepeatedProtocolsCaseInsensitive() }" threw object "ReferenceError: CreateWebSocketWithRepeatedProtocolsCaseInsensitive is not defined" that is not a DOMException SYNTAX_ERR: property "code" is equal to undefined, expected 12 -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/rootscroller/gesture-scroll-document-not-root-scroller.html b/third_party/WebKit/LayoutTests/rootscroller/gesture-scroll-document-not-root-scroller.html new file mode 100644 index 0000000..84b7e63a --- /dev/null +++ b/third_party/WebKit/LayoutTests/rootscroller/gesture-scroll-document-not-root-scroller.html
@@ -0,0 +1,105 @@ +<!DOCTYPE html> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<style> + ::-webkit-scrollbar { + width: 0px; + height: 0px; + } + + body, html { + width: 100%; + height: 100%; + margin: 0; + } + + #rootscroller { + width: 100%; + height: 100%; + overflow: auto; + position: absolute; + left: 0; + top: 0; + z-index: -1; + background-color: red; + } + + .spacer { + width: 200%; + height: 200%; + } + + #inflow { + width: 200%; + height: 200%; + background-color: lightgreen; + z-index: 1; + } +</style> + +<div id="rootscroller"> + <div class="spacer"> + </div> +</div> + +<!--This element overflows the viewport and appears over the root scroller. + Therefore, gesture scrolls will target it and should scroll the document rather + than the root scroller --> +<div id="inflow"> + This test ensures that when the document is not the root scroller, i.e. + another element has been set as document.rootScroller, it can still be + scrolled via gesture scrolls. +</div> + +<script> + function scrollDown(pixels_to_scroll, start_x, start_y, + gesture_source_type, speed_in_pixels_s) { + return new Promise((resolve, reject) => { + chrome.gpuBenchmarking.smoothScrollBy(pixels_to_scroll, + resolve, + start_x, + start_y, + gesture_source_type, + 'down', + speed_in_pixels_s); + }); + } + + function waitForScrollAndCheck() { + const MAX_FRAME = 500; + return new Promise((resolve, reject) => { + function tick(frames) { + // We requestAnimationFrame either for 500 frames or until scrollable + // scrolls. + if (frames >= MAX_FRAME || document.scrollingElement.scrollTop > 0) + resolve(); + else + requestAnimationFrame(tick.bind(this, frames + 1)); + } + tick(0); + }); + } + + window.onload = function() { + var rootscroller = document.querySelector('#rootscroller'); + document.rootScroller = rootscroller; + + promise_test( t => { + const GESTURE_SOURCE_TYPE = 2; // MOUSE_INPUT from synthetic_gesture_params.h + return scrollDown(500, 100, 100, GESTURE_SOURCE_TYPE, 1000) + .then(waitForScrollAndCheck) + .then(() => { + if (window.internals) { + assert_equals(window.internals.effectiveRootScroller(document), + rootscroller, + "Failed to set root scroller"); + } + + assert_greater_than( + document.scrollingElement.scrollTop, 0, "Document must be scrolled"); + assert_equals( + rootscroller.scrollTop, 0, "RootScroller must not be scrolled"); + }); + }, "Gesture scrolling should scroll document."); + } +</script>
diff --git a/third_party/WebKit/LayoutTests/rootscroller/keyboard-scroll-document-not-root-scroller.html b/third_party/WebKit/LayoutTests/rootscroller/keyboard-scroll-document-not-root-scroller.html new file mode 100644 index 0000000..6ce0683 --- /dev/null +++ b/third_party/WebKit/LayoutTests/rootscroller/keyboard-scroll-document-not-root-scroller.html
@@ -0,0 +1,93 @@ +<!DOCTYPE html> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<script> + if (window.internals) + window.internals.settings.setScrollAnimatorEnabled(false); +</script> +<style> + ::-webkit-scrollbar { + width: 0px; + height: 0px; + } + + body, html { + width: 100%; + height: 100%; + margin: 0; + } + + #rootscroller { + width: 100%; + height: 100%; + overflow: auto; + position: absolute; + left: 0; + top: 0; + z-index: -1; + background-color: red; + } + + .spacer { + width: 200%; + height: 200%; + } + + #inflow { + width: 200%; + height: 200%; + background-color: lightgreen; + z-index: 1; + } +</style> + +<div id="rootscroller"> + <div class="spacer"> + </div> +</div> + +<!--This element overflows the viewport and appears over the root scroller. + Therefore, scrolling it should scroll the document rather than the root + scroller --> +<div id="inflow"> + This test ensures that when the document is not the root scroller, i.e. + another element has been set as document.rootScroller, it can still be + scrolled via the keyboard. +</div> + +<script> + function click(x, y) { + return new Promise((resolve, reject) => { + var pointerActions = + [{source: "mouse", + actions: [ + { name: "pointerMove", x: x, y: y }, + { name: "pointerDown", x: x, y: y, button: 'left'}, + { name: "pointerUp", x: x, y: y, button: 'left' }]}]; + chrome.gpuBenchmarking.pointerActionSequence(pointerActions, resolve); + }); + } + + window.onload = function() { + var rootscroller = document.querySelector('#rootscroller'); + document.rootScroller = rootscroller; + + promise_test( t => { + // Ensure the "inflow" element/document is focused to receive keyboard + // events. + return click(100, 100).then( () => { + if (window.internals) { + assert_equals(window.internals.effectiveRootScroller(document), + rootscroller, + "Failed to set root scroller"); + } + eventSender.keyDown('ArrowDown'); + + assert_greater_than( + document.scrollingElement.scrollTop, 0, "Document must be scrolled"); + assert_equals( + rootscroller.scrollTop, 0, "RootScroller must not be scrolled"); + }) + }, "Arrow keys should scroll document."); + } +</script>
diff --git a/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document-expected.txt b/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document-expected.txt deleted file mode 100644 index 52cb599..0000000 --- a/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -Reinserting SVG animation into document should continue the animation -PASS successfullyParsed is true - -TEST COMPLETE -PASS rect.x.animVal.value is 30 -PASS rect.x.animVal.value is 60 -
diff --git a/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document.html b/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document.html index e41c1fb6..f766b3a 100644 --- a/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document.html +++ b/third_party/WebKit/LayoutTests/svg/animations/reinserting-svg-into-document.html
@@ -1,45 +1,40 @@ <html> -<script src="../../resources/js-test.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="resources/SVGAnimationTestCase.js"></script> + +<h1>Reinserting SVG animation into document should continue the animation</h1> +<p id="description"></p> +<div id="console"></div> +<svg id="svg" xmlns="http://www.w3.org/2000/svg"> + <rect id="rect" x="0" y="0" width="20" height="20"> + <animate attributeName="x" begin="0" from="0" to="90" dur="3s" fill="freeze" /> + </rect> +</svg> + <script> -function load() { - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } +async_test(t => { + svg = document.getElementById('svg'); + rect = document.getElementById('rect'); - svg = document.getElementById("svg"); - rect = document.getElementById("rect"); + // To ensure that a full lifecycle runs between the remove/append and the + // later asserts, we need to do both of them in successive rAFs. + window.requestAnimationFrame(() => { + svg.setCurrentTime(1); - // FIXME: we need a better way of waiting for chromium events to happen - setTimeout(function () { - svg.setCurrentTime(1); + // Removing and re-adding the SVG shouldn't change anything about the + // underlying animation. + document.body.removeChild(svg); + document.body.appendChild(svg); - document.body.removeChild(svg); - document.body.appendChild(svg); - - // FIXME: we need a better way of waiting for chromium events to happen - setTimeout(function () { - shouldBeCloseEnough("rect.x.animVal.value", "30", 1); - - svg.setCurrentTime(2); - - shouldBeCloseEnough("rect.x.animVal.value", "60", 1); - - if (window.testRunner) - testRunner.notifyDone(); - }, 1); - }, 1); -}; + // The SVG animation will continue after the next frame runs. + window.requestAnimationFrame(t.step_func_done(() => { + assert_greater_than_equal(svg.getCurrentTime(), 1); + assert_greater_than_equal(rect.x.animVal.value, 30); + svg.setCurrentTime(2); + assert_approx_equals(rect.x.animVal.value, 60, 1); + })); + }); +}, 'Reinserting SVG animation into document should continue the animation'); </script> -<body onload="load()"> - <h1>Reinserting SVG animation into document should continue the animation</h1> - <p id="description"></p> - <div id="console"></div> - <svg id="svg" xmlns="http://www.w3.org/2000/svg"> - <rect id="rect" x="0" y="0" width="20" height="20"> - <animate attributeName="x" begin="0" from="0" to="90" dur="3s" fill="freeze" /> - </rect> - </svg> -</body> </html>
diff --git a/third_party/WebKit/LayoutTests/virtual/off-main-thread-websocket/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt b/third_party/WebKit/LayoutTests/virtual/off-main-thread-websocket/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt deleted file mode 100644 index c3b58c7..0000000 --- a/third_party/WebKit/LayoutTests/virtual/off-main-thread-websocket/external/wpt/websockets/Create-protocols-repeated-case-insensitive.any-expected.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This is a testharness.js-based test. -FAIL W3C WebSocket API - Create WebSocket - Pass a valid URL and an array of protocol strings with repeated values but different case - SYNTAX_ERR is thrown assert_throws: function "function () { wsocket = CreateWebSocketWithRepeatedProtocolsCaseInsensitive() }" did not throw -Harness: the test ran to completion. -
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/ctor-audiobuffer.html b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/ctor-audiobuffer.html index 4bbfdca..478973a 100644 --- a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/ctor-audiobuffer.html +++ b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/ctor-audiobuffer.html
@@ -188,9 +188,13 @@ let buffer = new AudioBuffer({length: 128, sampleRate: context.sampleRate}); - let data = buffer.getChannelData(0); + // Don't use getChannelData here because we want to be able to use + // |data| to compare the final results of playing out this buffer. (If + // we did, |data| gets detached when the sources play.) + let data = new Float32Array(buffer.length); for (let k = 0; k < data.length; ++k) data[k] = 1 + k; + buffer.copyToChannel(data, 0); let c1 = new OfflineAudioContext(1, 128, context.sampleRate); let c2 = new OfflineAudioContext(1, 128, context.sampleRate);
diff --git a/third_party/arcore-android-sdk/BUILD.gn b/third_party/arcore-android-sdk/BUILD.gn new file mode 100644 index 0000000..83160ba7 --- /dev/null +++ b/third_party/arcore-android-sdk/BUILD.gn
@@ -0,0 +1,37 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +java_prebuilt("libdynamite_client_java") { + supports_android = true + proguard_configs = [ "proguard-arcore.txt" ] + jar_path = "libarcore_client_c.jar" +} + +source_set("libarcore_sdk") { + deps = [ + ":libarcore_library", + ] + libs = [ "${root_out_dir}/libarcore_sdk_c_minimal.so" ] +} + +copy("libarcore_library") { + if (target_cpu == "arm") { + sources = [ + "libraries/android_arm/libarcore_sdk_c_minimal.so", + ] + } else if (target_cpu == "arm64") { + sources = [ + "libraries/android_arm64/libarcore_sdk_c_minimal.so", + ] + } + outputs = [ + "${root_out_dir}/libarcore_sdk_c_minimal.so", + ] +} + +config("libarcore_config") { + include_dirs = [ "src/libraries/include/" ] +}
diff --git a/third_party/arcore-android-sdk/LICENSE b/third_party/arcore-android-sdk/LICENSE new file mode 100644 index 0000000..64483561 --- /dev/null +++ b/third_party/arcore-android-sdk/LICENSE
@@ -0,0 +1,158 @@ +Copyright (c) 2017, Google Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. +END OF TERMS AND CONDITIONS
diff --git a/third_party/arcore-android-sdk/OWNERS b/third_party/arcore-android-sdk/OWNERS new file mode 100644 index 0000000..d36245b --- /dev/null +++ b/third_party/arcore-android-sdk/OWNERS
@@ -0,0 +1,4 @@ +lincolnfrog@chromium.org +vollick@chromium.org + +# COMPONENT: Internals>VR
diff --git a/third_party/arcore-android-sdk/README.chromium b/third_party/arcore-android-sdk/README.chromium new file mode 100644 index 0000000..8b5b896 --- /dev/null +++ b/third_party/arcore-android-sdk/README.chromium
@@ -0,0 +1,20 @@ +Name: ARCore SDK +Short Name: arcore +URL: https://github.com/google-ar/arcore-android-sdk +Version: 1.1 +Date: 19 March 2018 +License: Apache 2.0 +License File: LICENSE +Security Critical: yes + +Description: +The ARCore Android SDK provides augmented reality capabilities to Android +devices. + +Local Modifications: +To address binary size concerns, we are using a minimal shim produced for +Chromium that is not publicly distributed elsewhere. We have also stripped the +.so's to minimize their size. + +The LICENSE file is taken from + * https://github.com/google-ar/arcore-unity-sdk/blob/master/LICENSE
diff --git a/third_party/arcore-android-sdk/libarcore_client_c.jar b/third_party/arcore-android-sdk/libarcore_client_c.jar new file mode 100644 index 0000000..335fe92 --- /dev/null +++ b/third_party/arcore-android-sdk/libarcore_client_c.jar Binary files differ
diff --git a/third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so b/third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so new file mode 100755 index 0000000..ce3ab2df --- /dev/null +++ b/third_party/arcore-android-sdk/libraries/android_arm/libarcore_sdk_c_minimal.so Binary files differ
diff --git a/third_party/arcore-android-sdk/libraries/android_arm64/libarcore_sdk_c_minimal.so b/third_party/arcore-android-sdk/libraries/android_arm64/libarcore_sdk_c_minimal.so new file mode 100755 index 0000000..cfd9654f --- /dev/null +++ b/third_party/arcore-android-sdk/libraries/android_arm64/libarcore_sdk_c_minimal.so Binary files differ
diff --git a/third_party/arcore-android-sdk/proguard-arcore.txt b/third_party/arcore-android-sdk/proguard-arcore.txt new file mode 100644 index 0000000..b1c85042 --- /dev/null +++ b/third_party/arcore-android-sdk/proguard-arcore.txt
@@ -0,0 +1,41 @@ +# Keep ARCore public-facing classes +-keepparameternames +-renamesourcefileattribute SourceFile + +# These are part of the Java <-> native interfaces for ARCore. +-keepclasseswithmembernames,includedescriptorclasses class com.google.ar.** { + native <methods>; +} + +-keep public class com.google.ar.core.** {*;} + +-keep class com.google.ar.core.annotations.UsedByNative +-keep @com.google.ar.core.annotations.UsedByNative class * +-keepclassmembers class * { + @com.google.ar.core.annotations.UsedByNative *; +} + +-keep class com.google.ar.core.annotations.UsedByReflection +-keep @com.google.ar.core.annotations.UsedByReflection class * +-keepclassmembers class * { + @com.google.ar.core.annotations.UsedByReflection *; +} +# Keep Dynamite classes + +# .aidl file will be proguarded, we should keep all Aidls. +-keep class com.google.vr.dynamite.client.IObjectWrapper { *; } +-keep class com.google.vr.dynamite.client.ILoadedInstanceCreator { *; } +-keep class com.google.vr.dynamite.client.INativeLibraryLoader { *; } + +# Keep annotation files and the file got annotated. +-keep class com.google.vr.dynamite.client.UsedByNative +-keep @com.google.vr.dynamite.client.UsedByNative class * +-keepclassmembers class * { + @com.google.vr.dynamite.client.UsedByNative *; +} + +-keep class com.google.vr.dynamite.client.UsedByReflection +-keep @com.google.vr.dynamite.client.UsedByReflection class * +-keepclassmembers class * { + @com.google.vr.dynamite.client.UsedByReflection *; +}
diff --git a/third_party/blink/common/origin_trials/OWNERS b/third_party/blink/common/origin_trials/OWNERS index 0d67a41..7627713 100644 --- a/third_party/blink/common/origin_trials/OWNERS +++ b/third_party/blink/common/origin_trials/OWNERS
@@ -1 +1,11 @@ -file://content/common/origin_trials/OWNERS +# This file also covers ownership of the following directories: +# //chrome/common/origin_trials/ +# //third_party/blink/public/common/origin_trials/ +# //tools/origin_trials/ + +chasej@chromium.org +iclelland@chromium.org +mek@chromium.org + +# TEAM: experimentation-dev@chromium.org +# COMPONENT: Internals>OriginTrials
diff --git a/third_party/blink/common/origin_trials/trial_token.cc b/third_party/blink/common/origin_trials/trial_token.cc index a2e19a3..1f17fcb 100644 --- a/third_party/blink/common/origin_trials/trial_token.cc +++ b/third_party/blink/common/origin_trials/trial_token.cc
@@ -56,7 +56,7 @@ // static std::unique_ptr<TrialToken> TrialToken::From( - const std::string& token_text, + base::StringPiece token_text, base::StringPiece public_key, OriginTrialTokenStatus* out_status) { DCHECK(out_status); @@ -91,7 +91,7 @@ } // static -OriginTrialTokenStatus TrialToken::Extract(const std::string& token_text, +OriginTrialTokenStatus TrialToken::Extract(base::StringPiece token_text, base::StringPiece public_key, std::string* out_token_payload, std::string* out_token_signature) {
diff --git a/third_party/blink/common/origin_trials/trial_token_validator.cc b/third_party/blink/common/origin_trials/trial_token_validator.cc index 75ef89f6..14359c25 100644 --- a/third_party/blink/common/origin_trials/trial_token_validator.cc +++ b/third_party/blink/common/origin_trials/trial_token_validator.cc
@@ -6,31 +6,57 @@ #include <memory> #include "base/feature_list.h" +#include "base/no_destructor.h" #include "base/time/time.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" -#include "third_party/blink/public/common/origin_trials/trial_policy.h" +#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h" #include "third_party/blink/public/common/origin_trials/trial_token.h" +namespace { + +static base::RepeatingCallback<blink::OriginTrialPolicy*()>& PolicyGetter() { + static base::NoDestructor< + base::RepeatingCallback<blink::OriginTrialPolicy*()>> + policy(base::BindRepeating( + []() -> blink::OriginTrialPolicy* { return nullptr; })); + return *policy; +} +} + namespace blink { -TrialTokenValidator::TrialTokenValidator(std::unique_ptr<TrialPolicy> policy) - : policy_(std::move(policy)) { - DCHECK(policy_.get()); -} +TrialTokenValidator::TrialTokenValidator() {} + TrialTokenValidator::~TrialTokenValidator() = default; +void TrialTokenValidator::SetOriginTrialPolicyGetter( + base::RepeatingCallback<OriginTrialPolicy*()> policy_getter) { + PolicyGetter() = policy_getter; +} + +void TrialTokenValidator::ResetOriginTrialPolicyGetter() { + SetOriginTrialPolicyGetter(base::BindRepeating( + []() -> blink::OriginTrialPolicy* { return nullptr; })); +} + +OriginTrialPolicy* TrialTokenValidator::Policy() { + return PolicyGetter().Run(); +} + OriginTrialTokenStatus TrialTokenValidator::ValidateToken( - const std::string& token, + base::StringPiece token, const url::Origin& origin, std::string* feature_name, base::Time current_time) const { - if (!policy_->IsOriginTrialsSupported()) + OriginTrialPolicy* policy = Policy(); + + if (!policy->IsOriginTrialsSupported()) return OriginTrialTokenStatus::kNotSupported; // TODO(iclelland): Allow for multiple signing keys, and iterate over all // active keys here. https://crbug.com/543220 - base::StringPiece public_key = policy_->GetPublicKey(); + base::StringPiece public_key = policy->GetPublicKey(); OriginTrialTokenStatus status; std::unique_ptr<TrialToken> trial_token = @@ -42,10 +68,10 @@ if (status != OriginTrialTokenStatus::kSuccess) return status; - if (policy_->IsFeatureDisabled(trial_token->feature_name())) + if (policy->IsFeatureDisabled(trial_token->feature_name())) return OriginTrialTokenStatus::kFeatureDisabled; - if (policy_->IsTokenDisabled(trial_token->signature())) + if (policy->IsTokenDisabled(trial_token->signature())) return OriginTrialTokenStatus::kTokenDisabled; *feature_name = trial_token->feature_name(); @@ -130,7 +156,9 @@ } bool TrialTokenValidator::IsTrialPossibleOnOrigin(const GURL& url) const { - return policy_->IsOriginTrialsSupported() && policy_->IsOriginSecure(url); + OriginTrialPolicy* policy = Policy(); + return policy && policy->IsOriginTrialsSupported() && + policy->IsOriginSecure(url); } bool TrialTokenValidator::IsTrialPossibleOnOrigin(
diff --git a/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc b/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc index b45d847..682d131 100644 --- a/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc +++ b/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc
@@ -8,6 +8,7 @@ #include <set> #include <string> +#include "base/bind.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" @@ -15,7 +16,7 @@ #include "base/time/time.h" #include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/origin_trials/trial_policy.h" +#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h" #include "third_party/blink/public/common/origin_trials/trial_token.h" #include "url/gurl.h" @@ -115,13 +116,12 @@ // but before the expiry timestamp of kValidToken. double kNowTimestamp = 1500000000; -class TestOriginTrialPolicy : public TrialPolicy { +class TestOriginTrialPolicy : public OriginTrialPolicy { public: bool IsOriginTrialsSupported() const override { return true; } bool IsOriginSecure(const GURL& url) const override { return url.SchemeIs("https"); } - base::StringPiece GetPublicKey() const override { return base::StringPiece(reinterpret_cast<const char*>(key_), arraysize(kTestPublicKey)); @@ -163,12 +163,16 @@ std::string(reinterpret_cast<const char*>(kExpiredTokenSignature), arraysize(kExpiredTokenSignature))), response_headers_(new net::HttpResponseHeaders("")), - policy_(new TestOriginTrialPolicy), - validator_(base::WrapUnique(policy_)) { + policy_(new TestOriginTrialPolicy) { + TrialTokenValidator::SetOriginTrialPolicyGetter( + base::BindRepeating([](OriginTrialPolicy* policy) { return policy; }, + base::Unretained(policy_))); SetPublicKey(kTestPublicKey); } - ~TrialTokenValidatorTest() override = default; + ~TrialTokenValidatorTest() override { + TrialTokenValidator::ResetOriginTrialPolicyGetter(); + } void SetPublicKey(const uint8_t* key) { policy_->SetPublicKey(key); }
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn index c03f9e5..bf219627 100644 --- a/third_party/blink/public/BUILD.gn +++ b/third_party/blink/public/BUILD.gn
@@ -422,7 +422,6 @@ "platform/web_touch_action.h", "platform/web_touch_event.h", "platform/web_touch_point.h", - "platform/web_trial_token_validator.h", "platform/web_url.h", "platform/web_url_error.h", "platform/web_url_load_timing.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn index 52b399d..ceff049 100644 --- a/third_party/blink/public/common/BUILD.gn +++ b/third_party/blink/public/common/BUILD.gn
@@ -44,6 +44,7 @@ "message_port/transferable_message.h", "mime_util/mime_util.h", "origin_manifest/origin_manifest.h", + "origin_trials/origin_trial_policy.h", "origin_trials/trial_token.h", "origin_trials/trial_token_validator.h", "page/launching_process_state.h",
diff --git a/third_party/blink/public/common/origin_trials/OWNERS b/third_party/blink/public/common/origin_trials/OWNERS index 0d67a41..73686a7 100644 --- a/third_party/blink/public/common/origin_trials/OWNERS +++ b/third_party/blink/public/common/origin_trials/OWNERS
@@ -1 +1 @@ -file://content/common/origin_trials/OWNERS +file://third_party/blink/common/origin_trials/OWNERS
diff --git a/third_party/blink/public/common/origin_trials/origin_trial_policy.h b/third_party/blink/public/common/origin_trials/origin_trial_policy.h new file mode 100644 index 0000000..6b1c602 --- /dev/null +++ b/third_party/blink/public/common/origin_trials/origin_trial_policy.h
@@ -0,0 +1,32 @@ +// 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 THIRD_PARTY_BLINK_PUBLIC_COMMON_ORIGIN_TRIALS_ORIGIN_TRIAL_POLICY_H_ +#define THIRD_PARTY_BLINK_PUBLIC_COMMON_ORIGIN_TRIALS_ORIGIN_TRIAL_POLICY_H_ + +#include "base/strings/string_piece.h" +#include "url/gurl.h" + +namespace blink { + +// The OriginTrialPolicy provides an interface to the TrialTokenValidator used +// to check for disabled features or tokens. +class OriginTrialPolicy { + public: + virtual ~OriginTrialPolicy() = default; + + virtual bool IsOriginTrialsSupported() const { return false; } + virtual base::StringPiece GetPublicKey() const { return base::StringPiece(); } + virtual bool IsFeatureDisabled(base::StringPiece feature) const { + return false; + } + virtual bool IsTokenDisabled(base::StringPiece token_signature) const { + return false; + } + virtual bool IsOriginSecure(const GURL& url) const { return false; } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_ORIGIN_TRIALS_ORIGIN_TRIAL_POLICY_H_
diff --git a/third_party/blink/public/common/origin_trials/trial_policy.h b/third_party/blink/public/common/origin_trials/trial_policy.h deleted file mode 100644 index 56dd7ab..0000000 --- a/third_party/blink/public/common/origin_trials/trial_policy.h +++ /dev/null
@@ -1,28 +0,0 @@ -// 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 THIRD_PARTY_BLINK_PUBLIC_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_H_ -#define THIRD_PARTY_BLINK_PUBLIC_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_H_ - -#include "base/strings/string_piece.h" -#include "third_party/blink/common/common_export.h" -#include "url/gurl.h" - -namespace blink { - -class BLINK_COMMON_EXPORT TrialPolicy { - public: - virtual ~TrialPolicy() = default; - - virtual bool IsOriginTrialsSupported() const = 0; - - virtual base::StringPiece GetPublicKey() const = 0; - virtual bool IsFeatureDisabled(base::StringPiece feature) const = 0; - virtual bool IsTokenDisabled(base::StringPiece token_signature) const = 0; - virtual bool IsOriginSecure(const GURL& url) const = 0; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_H_
diff --git a/third_party/blink/public/common/origin_trials/trial_token.h b/third_party/blink/public/common/origin_trials/trial_token.h index 38dcf971..154e56d 100644 --- a/third_party/blink/public/common/origin_trials/trial_token.h +++ b/third_party/blink/public/common/origin_trials/trial_token.h
@@ -57,7 +57,7 @@ // appropriate for a given origin / feature. It only means that it is // correctly formatted and signed by the supplied public key, and can be // parsed. - static std::unique_ptr<TrialToken> From(const std::string& token_text, + static std::unique_ptr<TrialToken> From(base::StringPiece token_text, base::StringPiece public_key, OriginTrialTokenStatus* out_status); @@ -86,7 +86,7 @@ // |out_token_payload| and |out_token_signature| parameters, respectively. // Otherwise,the return code indicates what was wrong with the string, and // |out_token_payload| and |out_token_signature| are unchanged. - static OriginTrialTokenStatus Extract(const std::string& token_text, + static OriginTrialTokenStatus Extract(base::StringPiece token_text, base::StringPiece public_key, std::string* out_token_payload, std::string* out_token_signature);
diff --git a/third_party/blink/public/common/origin_trials/trial_token_validator.h b/third_party/blink/public/common/origin_trials/trial_token_validator.h index 0bd8d60..6a7b164 100644 --- a/third_party/blink/public/common/origin_trials/trial_token_validator.h +++ b/third_party/blink/public/common/origin_trials/trial_token_validator.h
@@ -9,6 +9,7 @@ #include <memory> #include <string> #include <vector> +#include "base/callback.h" #include "base/strings/string_piece.h" #include "base/time/time.h" #include "third_party/blink/common/common_export.h" @@ -21,7 +22,7 @@ namespace blink { -class TrialPolicy; +class OriginTrialPolicy; enum class OriginTrialTokenStatus; // TrialTokenValidator checks that a page's OriginTrial token enables a certain @@ -31,7 +32,7 @@ // should be enabled or not for a specific document. class BLINK_COMMON_EXPORT TrialTokenValidator { public: - explicit TrialTokenValidator(std::unique_ptr<TrialPolicy> policy); + TrialTokenValidator(); virtual ~TrialTokenValidator(); using FeatureToTokensMap = std::map<std::string /* feature_name */, @@ -40,40 +41,41 @@ // If token validates, |*feature_name| is set to the name of the feature the // token enables. // This method is thread-safe. - virtual OriginTrialTokenStatus ValidateToken(const std::string& token, + virtual OriginTrialTokenStatus ValidateToken(base::StringPiece token, const url::Origin& origin, std::string* feature_name, base::Time current_time) const; - virtual bool RequestEnablesFeature(const net::URLRequest* request, - base::StringPiece feature_name, - base::Time current_time) const; + bool RequestEnablesFeature(const net::URLRequest* request, + base::StringPiece feature_name, + base::Time current_time) const; - virtual bool RequestEnablesFeature( - const GURL& request_url, - const net::HttpResponseHeaders* response_headers, - base::StringPiece feature_name, - base::Time current_time) const; + bool RequestEnablesFeature(const GURL& request_url, + const net::HttpResponseHeaders* response_headers, + base::StringPiece feature_name, + base::Time current_time) const; // Returns all valid tokens in |headers|. - virtual std::unique_ptr<FeatureToTokensMap> GetValidTokensFromHeaders( + std::unique_ptr<FeatureToTokensMap> GetValidTokensFromHeaders( const url::Origin& origin, const net::HttpResponseHeaders* headers, base::Time current_time) const; // Returns all valid tokens in |tokens|. This method is used to re-validate // previously stored tokens. - virtual std::unique_ptr<FeatureToTokensMap> GetValidTokens( + std::unique_ptr<FeatureToTokensMap> GetValidTokens( const url::Origin& origin, const FeatureToTokensMap& tokens, base::Time current_time) const; + static void SetOriginTrialPolicyGetter( + base::RepeatingCallback<OriginTrialPolicy*()> policy); + static void ResetOriginTrialPolicyGetter(); + static OriginTrialPolicy* Policy(); + private: bool IsTrialPossibleOnOrigin(const url::Origin& origin) const; bool IsTrialPossibleOnOrigin(const GURL& url) const; - - std::unique_ptr<TrialPolicy> policy_; - }; // class TrialTokenValidator } // namespace blink
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn index 33ce7e2..3a90358 100644 --- a/third_party/blink/public/mojom/BUILD.gn +++ b/third_party/blink/public/mojom/BUILD.gn
@@ -18,6 +18,7 @@ "clipboard/clipboard.mojom", "color_chooser/color_chooser.mojom", "feature_policy/feature_policy.mojom", + "file/file_utilities.mojom", "leak_detector/leak_detector.mojom", "loader/prefetch_url_loader_service.mojom", "net/ip_address_space.mojom",
diff --git a/third_party/blink/public/mojom/file/OWNERS b/third_party/blink/public/mojom/file/OWNERS new file mode 100644 index 0000000..08850f4 --- /dev/null +++ b/third_party/blink/public/mojom/file/OWNERS
@@ -0,0 +1,2 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/content/common/file_utilities.mojom b/third_party/blink/public/mojom/file/file_utilities.mojom similarity index 95% rename from content/common/file_utilities.mojom rename to third_party/blink/public/mojom/file/file_utilities.mojom index 7ab8354..a894971 100644 --- a/content/common/file_utilities.mojom +++ b/third_party/blink/public/mojom/file/file_utilities.mojom
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -module content.mojom; +module blink.mojom; import "mojo/public/mojom/base/file_info.mojom"; import "mojo/public/mojom/base/file_path.mojom";
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h index 81c92786c..f832a11 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h
@@ -101,7 +101,6 @@ class WebPlatformEventListener; class WebFallbackThemeEngine; class WebFileSystem; -class WebFileUtilities; class WebGestureCurve; class WebGraphicsContext3DProvider; class WebIDBFactory; @@ -136,7 +135,6 @@ class WebThemeEngine; class WebThread; struct WebThreadCreationParams; -class WebTrialTokenValidator; class WebURLLoaderMockFactory; class WebURLResponse; class WebURLResponse; @@ -167,9 +165,6 @@ // Must return non-null. virtual WebClipboard* Clipboard(); - // Must return non-null. - virtual WebFileUtilities* GetFileUtilities() { return nullptr; } - // May return null if sandbox support is not necessary virtual WebSandboxSupport* GetSandboxSupport() { return nullptr; } @@ -720,12 +715,6 @@ virtual WebSyncProvider* BackgroundSyncProvider() { return nullptr; } - // Origin Trials ------------------------------------------------------ - - // TODO(crbug.com/738505): Remove the Web layer and return a - // blink::TrialTokenValidator directly. - virtual std::unique_ptr<WebTrialTokenValidator> CreateTrialTokenValidator(); - // Media Capabilities -------------------------------------------------- virtual WebMediaCapabilitiesClient* MediaCapabilitiesClient() {
diff --git a/third_party/blink/public/platform/web_file_utilities.h b/third_party/blink/public/platform/web_file_utilities.h deleted file mode 100644 index cc5ed6a3..0000000 --- a/third_party/blink/public/platform/web_file_utilities.h +++ /dev/null
@@ -1,51 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_FILE_UTILITIES_H_ -#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_FILE_UTILITIES_H_ - -namespace blink { - -class WebString; -struct WebFileInfo; - -class WebFileUtilities { - public: - virtual bool GetFileInfo(const WebString& path, WebFileInfo& result) { - return false; - } - - protected: - ~WebFileUtilities() = default; -}; - -} // namespace blink - -#endif
diff --git a/third_party/blink/public/platform/web_trial_token_validator.h b/third_party/blink/public/platform/web_trial_token_validator.h deleted file mode 100644 index c002b70..0000000 --- a/third_party/blink/public/platform/web_trial_token_validator.h +++ /dev/null
@@ -1,37 +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 THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_TRIAL_TOKEN_VALIDATOR_H_ -#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_TRIAL_TOKEN_VALIDATOR_H_ - -#include "third_party/blink/public/platform/web_callbacks.h" -#include "third_party/blink/public/platform/web_security_origin.h" -#include "third_party/blink/public/platform/web_string.h" - -namespace blink { - -enum class OriginTrialTokenStatus; - -// This interface abstracts the task of validating a token for an experimental -// feature. Experimental features can be turned on and off at runtime for a -// specific renderer, depending on the presence of a valid token provided by -// the origin. -// -// For more information, see https://github.com/jpchase/OriginTrials. - -class WebTrialTokenValidator { - public: - virtual ~WebTrialTokenValidator() = default; - - // Returns whether the given token is valid for the specified origin. If the - // token is valid, it also returns the feature the token is valid for in - // |*feature_name|. - virtual OriginTrialTokenStatus ValidateToken(const WebString& token, - const WebSecurityOrigin&, - WebString* feature_name) = 0; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_TRIAL_TOKEN_VALIDATOR_H_
diff --git a/third_party/blink/public/web/web_print_params.h b/third_party/blink/public/web/web_print_params.h index ede7a1ef..ac54f2ad 100644 --- a/third_party/blink/public/web/web_print_params.h +++ b/third_party/blink/public/web/web_print_params.h
@@ -61,10 +61,14 @@ // Specifies whether printing layout needs to be applied. bool use_printing_layout; + // Specifies how many pages per sheet. This parameter is for N-up mode. + size_t num_pages_per_sheet; + WebPrintParams() : printer_dpi(72), print_scaling_option(kWebPrintScalingOptionFitToPrintableArea), - use_printing_layout(true) {} + use_printing_layout(true), + num_pages_per_sheet(1) {} WebPrintParams(const WebSize& paper_size) : WebPrintParams(paper_size, true) {} @@ -75,20 +79,23 @@ paper_size(paper_size), printer_dpi(72), print_scaling_option(kWebPrintScalingOptionSourceSize), - use_printing_layout(use_printing_layout) {} + use_printing_layout(use_printing_layout), + num_pages_per_sheet(1) {} WebPrintParams(const WebRect& print_content_area, const WebRect& printable_area, const WebSize& paper_size, int printer_dpi, WebPrintScalingOption print_scaling_option, - bool use_printing_layout) + bool use_printing_layout, + int num_pages_per_sheet) : print_content_area(print_content_area), printable_area(printable_area), paper_size(paper_size), printer_dpi(printer_dpi), print_scaling_option(print_scaling_option), - use_printing_layout(use_printing_layout) {} + use_printing_layout(use_printing_layout), + num_pages_per_sheet(num_pages_per_sheet) {} }; } // namespace blink
diff --git a/third_party/blink/public/web/web_remote_frame.h b/third_party/blink/public/web/web_remote_frame.h index 5420f8e4..85b44de 100644 --- a/third_party/blink/public/web/web_remote_frame.h +++ b/third_party/blink/public/web/web_remote_frame.h
@@ -126,6 +126,8 @@ virtual void IntrinsicSizingInfoChanged(const WebIntrinsicSizingInfo&) = 0; + virtual WebRect GetCompositingRect() = 0; + protected: explicit WebRemoteFrame(WebTreeScopeType scope) : WebFrame(scope) {}
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h index db2e2e8..e02eac9b 100644 --- a/third_party/blink/public/web/web_view.h +++ b/third_party/blink/public/web/web_view.h
@@ -446,6 +446,8 @@ // The valid flags are defined in // third_party/blink/public/platform/autoplay.mojom virtual void AddAutoplayFlags(int32_t flags) = 0; + virtual void ClearAutoplayFlags() = 0; + virtual int32_t AutoplayFlagsForTest() = 0; // Suspend and resume ---------------------------------------------------
diff --git a/third_party/blink/renderer/bindings/scripts/v8_methods.py b/third_party/blink/renderer/bindings/scripts/v8_methods.py index f26edfdf..37f20d1 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_methods.py +++ b/third_party/blink/renderer/bindings/scripts/v8_methods.py
@@ -185,7 +185,7 @@ return { 'activity_logging_world_list': v8_utilities.activity_logging_world_list(method), # [ActivityLogging] 'arguments': argument_contexts, - 'cpp_type': (v8_types.cpp_template_type('Optional', idl_type.cpp_type) + 'cpp_type': (v8_types.cpp_template_type('base::Optional', idl_type.cpp_type) if idl_type.is_explicit_nullable else idl_type.cpp_type), 'cpp_value': this_cpp_value, 'cpp_type_initializer': idl_type.cpp_type_initializer, @@ -272,7 +272,7 @@ used_as_variadic_argument=argument.is_variadic) context = { 'cpp_type': ( - v8_types.cpp_template_type('Optional', this_cpp_type) + v8_types.cpp_template_type('base::Optional', this_cpp_type) if idl_type.is_explicit_nullable and not argument.is_variadic else this_cpp_type), 'cpp_value': this_cpp_value,
diff --git a/third_party/blink/renderer/bindings/scripts/v8_union.py b/third_party/blink/renderer/bindings/scripts/v8_union.py index a57824e..978f697f 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_union.py +++ b/third_party/blink/renderer/bindings/scripts/v8_union.py
@@ -12,12 +12,12 @@ ]) UNION_H_INCLUDES = frozenset([ + 'base/optional.h', 'bindings/core/v8/dictionary.h', 'bindings/core/v8/exception_state.h', 'bindings/core/v8/native_value_traits.h', 'bindings/core/v8/v8_binding_for_core.h', 'platform/heap/handle.h', - 'platform/wtf/optional.h', ])
diff --git a/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.h b/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.h index ffe5bae31..9f7bd03 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.h +++ b/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.h
@@ -11,6 +11,7 @@ #ifndef ArrayBufferOrArrayBufferViewOrDictionary_h #define ArrayBufferOrArrayBufferViewOrDictionary_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" @@ -20,7 +21,6 @@ #include "core/typed_arrays/array_buffer_view_helpers.h" #include "core/typed_arrays/flexible_array_buffer_view.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.h index f46acec..dfcf711 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.h
@@ -11,13 +11,13 @@ #ifndef BooleanOrElementSequence_h #define BooleanOrElementSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.h b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.h index 4566bb1..fb108d3c 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.h +++ b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.h
@@ -11,13 +11,13 @@ #ifndef BooleanOrStringOrUnrestrictedDouble_h #define BooleanOrStringOrUnrestrictedDouble_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.h b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.h index deffc29..df5a881 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.h +++ b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.h
@@ -11,13 +11,13 @@ #ifndef BooleanOrTestCallbackInterface_h #define BooleanOrTestCallbackInterface_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.h b/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.h index 07c937af..cc009e9 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.h +++ b/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.h
@@ -11,13 +11,13 @@ #ifndef ByteStringOrNodeList_h #define ByteStringOrNodeList_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.h b/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.h index 7626491..61ee35ed 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.h +++ b/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.h
@@ -11,13 +11,13 @@ #ifndef ByteStringSequenceSequenceOrByteStringByteStringRecord_h #define ByteStringSequenceSequenceOrByteStringByteStringRecord_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.h index a7c9336..8737721 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.h
@@ -11,13 +11,13 @@ #ifndef DoubleOrDoubleOrNullSequence_h #define DoubleOrDoubleOrNullSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.h index 33b2874..02242909 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.h
@@ -11,13 +11,13 @@ #ifndef DoubleOrDoubleSequence_h #define DoubleOrDoubleSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.h index fc73de5..bac263a 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.h
@@ -11,13 +11,13 @@ #ifndef DoubleOrLongOrBooleanSequence_h #define DoubleOrLongOrBooleanSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_string.h b/third_party/blink/renderer/bindings/tests/results/core/double_or_string.h index 88a5594..a5c640b6 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/double_or_string.h +++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_string.h
@@ -11,13 +11,13 @@ #ifndef DoubleOrString_h #define DoubleOrString_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.h index af10623e..5969e55 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.h
@@ -11,13 +11,13 @@ #ifndef DoubleOrStringOrDoubleOrStringSequence_h #define DoubleOrStringOrDoubleOrStringSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.h b/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.h index f8d293e..a8fcd45 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.h +++ b/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.h
@@ -11,13 +11,13 @@ #ifndef ElementSequenceOrByteStringDoubleOrStringRecord_h #define ElementSequenceOrByteStringDoubleOrStringRecord_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.h b/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.h index 352e144..4002327 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.h +++ b/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.h
@@ -11,13 +11,13 @@ #ifndef FloatOrBoolean_h #define FloatOrBoolean_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.h b/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.h index 00cbccee..58ead6c 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.h +++ b/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.h
@@ -11,13 +11,13 @@ #ifndef LongOrBoolean_h #define LongOrBoolean_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.h b/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.h index 012843a..ec6177c 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.h +++ b/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.h
@@ -11,6 +11,7 @@ #ifndef LongOrTestDictionary_h #define LongOrTestDictionary_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" @@ -18,7 +19,6 @@ #include "bindings/core/v8/v8_test_dictionary.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.h b/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.h index 77ce2d0..577985b7 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.h +++ b/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.h
@@ -11,13 +11,13 @@ #ifndef LongSequenceOrEvent_h #define LongSequenceOrEvent_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.h b/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.h index 74c9852a..79eac2fb 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.h +++ b/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.h
@@ -11,13 +11,13 @@ #ifndef NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord_h #define NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.h b/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.h index af17a0b..ddf4f63 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.h +++ b/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.h
@@ -11,13 +11,13 @@ #ifndef NodeOrNodeList_h #define NodeOrNodeList_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.h b/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.h index 5fee539..49ed3efb 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.h +++ b/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.h
@@ -11,6 +11,7 @@ #ifndef StringOrArrayBufferOrArrayBufferView_h #define StringOrArrayBufferOrArrayBufferView_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" @@ -20,7 +21,6 @@ #include "core/typed_arrays/array_buffer_view_helpers.h" #include "core/typed_arrays/flexible_array_buffer_view.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/string_or_double.h b/third_party/blink/renderer/bindings/tests/results/core/string_or_double.h index 7b005b56..06b0bf5a 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/string_or_double.h +++ b/third_party/blink/renderer/bindings/tests/results/core/string_or_double.h
@@ -11,13 +11,13 @@ #ifndef StringOrDouble_h #define StringOrDouble_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.h index 10ddc80..e1ae193 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.h
@@ -11,13 +11,13 @@ #ifndef StringOrStringSequence_h #define StringOrStringSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.h b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.h index c333831..673fc4e2 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.h
@@ -11,13 +11,13 @@ #ifndef TestEnumOrDouble_h #define TestEnumOrDouble_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.h index 3c28b5d9..786fa75 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.h
@@ -11,13 +11,13 @@ #ifndef TestEnumOrTestEnumOrNullSequence_h #define TestEnumOrTestEnumOrNullSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.h b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.h index ae050b7..5f9b2f7 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.h
@@ -11,13 +11,13 @@ #ifndef TestEnumOrTestEnumSequence_h #define TestEnumOrTestEnumSequence_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.h b/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.h index eda86fc..eaf6ed3 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.h
@@ -11,6 +11,7 @@ #ifndef TestInterface2OrUint8Array_h #define TestInterface2OrUint8Array_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" @@ -21,7 +22,6 @@ #include "core/typed_arrays/array_buffer_view_helpers.h" #include "core/typed_arrays/flexible_array_buffer_view.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_garbage_collected_or_string.h b/third_party/blink/renderer/bindings/tests/results/core/test_interface_garbage_collected_or_string.h index d53c14da..f598d2c 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_garbage_collected_or_string.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_garbage_collected_or_string.h
@@ -11,13 +11,13 @@ #ifndef TestInterfaceGarbageCollectedOrString_h #define TestInterfaceGarbageCollectedOrString_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.h b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.h index 85ec0c4d..5cf3dce 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.h
@@ -11,13 +11,13 @@ #ifndef TestInterfaceOrLong_h #define TestInterfaceOrLong_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.h b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.h index b0352cd9..241667ea 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.h +++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.h
@@ -11,13 +11,13 @@ #ifndef TestInterfaceOrTestInterfaceEmpty_h #define TestInterfaceOrTestInterfaceEmpty_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.h b/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.h index a3331f1..63f3f1a 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.h +++ b/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.h
@@ -11,13 +11,13 @@ #ifndef UnrestrictedDoubleOrString_h #define UnrestrictedDoubleOrString_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.h b/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.h index debf7243b..4d35f4e3 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.h +++ b/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.h
@@ -11,13 +11,13 @@ #ifndef UnsignedLongLongOrBooleanOrTestCallbackInterface_h #define UnsignedLongLongOrBooleanOrTestCallbackInterface_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc index e04954f..0be1593 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -5283,7 +5283,7 @@ return; } - Optional<Vector<int32_t>> longSequenceArg; + base::Optional<Vector<int32_t>> longSequenceArg; if (!info[0]->IsNullOrUndefined()) { longSequenceArg = NativeValueTraits<IDLSequence<IDLLong>>::NativeValue(info.GetIsolate(), info[0], exceptionState); if (exceptionState.HadException()) @@ -5338,7 +5338,7 @@ static void nullableLongMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { TestObject* impl = V8TestObject::ToImpl(info.Holder()); - Optional<int32_t> result = impl->nullableLongMethod(); + base::Optional<int32_t> result = impl->nullableLongMethod(); if (!result) V8SetReturnValueNull(info); else @@ -5360,7 +5360,7 @@ static void nullableLongSequenceMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { TestObject* impl = V8TestObject::ToImpl(info.Holder()); - Optional<Vector<int32_t>> result = impl->nullableLongSequenceMethod(); + base::Optional<Vector<int32_t>> result = impl->nullableLongSequenceMethod(); if (!result) V8SetReturnValueNull(info); else @@ -5719,7 +5719,7 @@ static void nullableTestDictionaryMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { TestObject* impl = V8TestObject::ToImpl(info.Holder()); - Optional<TestDictionary> result; + base::Optional<TestDictionary> result; impl->nullableTestDictionaryMethod(result); if (!result) V8SetReturnValueNull(info); @@ -5734,7 +5734,7 @@ } static void staticNullableTestDictionaryMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { - Optional<TestDictionary> result; + base::Optional<TestDictionary> result; TestObject::staticNullableTestDictionaryMethod(result); if (!result) V8SetReturnValueNull(info);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.h b/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.h index 9fefed31..769a1c0 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.h +++ b/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.h
@@ -11,13 +11,13 @@ #ifndef XMLHttpRequestOrString_h #define XMLHttpRequestOrString_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "core/core_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.h b/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.h index 7e777fd4..523309d 100644 --- a/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.h +++ b/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.h
@@ -11,13 +11,13 @@ #ifndef BooleanOrString_h #define BooleanOrString_h +#include "base/optional.h" #include "bindings/core/v8/dictionary.h" #include "bindings/core/v8/exception_state.h" #include "bindings/core/v8/native_value_traits.h" #include "bindings/core/v8/v8_binding_for_core.h" #include "modules/modules_export.h" #include "platform/heap/handle.h" -#include "platform/wtf/optional.h" namespace blink {
diff --git a/third_party/blink/renderer/core/css/html.css b/third_party/blink/renderer/core/css/html.css index ae34705..0322f50 100644 --- a/third_party/blink/renderer/core/css/html.css +++ b/third_party/blink/renderer/core/css/html.css
@@ -599,6 +599,7 @@ } input[type="range" i]::-webkit-slider-container, input[type="range" i]::-webkit-media-slider-container { + -webkit-appearance: inherit; flex: 1; min-width: 0; box-sizing: border-box;
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc index f9582ed..d20b066 100644 --- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc +++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -356,7 +356,7 @@ current++; // Clamp negative values at zero. - value = negative ? 0 : static_cast<int>(roundf(local_value)); + value = negative ? 0 : static_cast<int>(round(local_value)); string = current; return true; } @@ -427,8 +427,7 @@ double alpha = 0; if (!ParseDouble(string, end, terminator, false, alpha)) return false; - value = - negative ? 0 : static_cast<int>(roundf(std::min(alpha, 1.0) * 255.0f)); + value = negative ? 0 : static_cast<int>(round(std::min(alpha, 1.0) * 255.0)); string = end; return true; }
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc index 3c03fb8..e43688d 100644 --- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc +++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -539,9 +539,11 @@ static int ClampRGBComponent(const CSSPrimitiveValue& value) { double result = value.GetDoubleValue(); - if (value.IsPercentage()) - result *= 2.55; - return clampTo<int>(roundf(result), 0, 255); + if (value.IsPercentage()) { + // 2.55 cannot be precisely represented as a double + result = (result / 100.0) * 255.0; + } + return clampTo<int>(round(result), 0, 255); } static bool ParseRGBParameters(CSSParserTokenRange& range, RGBA32& result) { @@ -584,11 +586,11 @@ if (!alpha_percent) return false; else - alpha = alpha_percent->GetDoubleValue() / 100.0f; + alpha = alpha_percent->GetDoubleValue() / 100.0; } // W3 standard stipulates a 2.55 alpha value multiplication factor. int alpha_component = - static_cast<int>(lroundf(clampTo<double>(alpha, 0.0, 1.0) * 255.0f)); + static_cast<int>(lround(clampTo<double>(alpha, 0.0, 1.0) * 255.0)); result = MakeRGBA(color_array[0], color_array[1], color_array[2], alpha_component); } else { @@ -642,7 +644,7 @@ if (!alpha_percent) return false; else - alpha = alpha_percent->GetDoubleValue() / 100.0f; + alpha = alpha_percent->GetDoubleValue() / 100.0; } alpha = clampTo<double>(alpha, 0.0, 1.0); }
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/overflow_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/overflow_custom.cc index cdffd05..fac92046 100644 --- a/third_party/blink/renderer/core/css/properties/shorthands/overflow_custom.cc +++ b/third_party/blink/renderer/core/css/properties/shorthands/overflow_custom.cc
@@ -19,25 +19,35 @@ const CSSParserContext& context, const CSSParserLocalContext&, HeapVector<CSSPropertyValue, 256>& properties) const { - CSSValueID id = range.ConsumeIncludingWhitespace().Id(); + CSSValueID x_id = CSSValueInvalid; + // Per the FIXME below, if only one value is specified, it could be a valid + // value for overflow-y but not overflow-x. Thus, we first assume it is the + // overflow-y value. + CSSValueID y_id = range.ConsumeIncludingWhitespace().Id(); + if (!range.AtEnd()) { + x_id = y_id; + y_id = range.ConsumeIncludingWhitespace().Id(); + if (!CSSParserFastPaths::IsValidKeywordPropertyAndValue( + CSSPropertyOverflowX, x_id, context.Mode())) + return false; + } if (!CSSParserFastPaths::IsValidKeywordPropertyAndValue(CSSPropertyOverflowY, - id, context.Mode())) + y_id, context.Mode())) return false; if (!range.AtEnd()) return false; - CSSValue* overflow_y_value = CSSIdentifierValue::Create(id); - CSSValue* overflow_x_value = nullptr; + CSSValue* overflow_y_value = CSSIdentifierValue::Create(y_id); // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. - // If - // this value has been set using the shorthand, then for now overflow-x + // If this value has been set using the shorthand, then for now overflow-x // will default to auto, but once we implement pagination controls, it // should default to hidden. If the overflow-y value is anything but // paged-x or paged-y, then overflow-x and overflow-y should have the - // same - // value. - if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY) + // same value. + if (x_id) + overflow_x_value = CSSIdentifierValue::Create(x_id); + else if (y_id == CSSValueWebkitPagedX || y_id == CSSValueWebkitPagedY) overflow_x_value = CSSIdentifierValue::Create(CSSValueAuto); else overflow_x_value = overflow_y_value; @@ -56,9 +66,12 @@ const LayoutObject*, Node* styled_node, bool allow_visited_style) const { - if (style.OverflowX() == style.OverflowY()) - return CSSIdentifierValue::Create(style.OverflowX()); - return nullptr; + CSSValueList* list = CSSValueList::CreateSpaceSeparated(); + list->Append(*CSSIdentifierValue::Create(style.OverflowX())); + if (style.OverflowX() != style.OverflowY()) + list->Append(*CSSIdentifierValue::Create(style.OverflowY())); + + return list; } } // namespace CSSShorthand
diff --git a/third_party/blink/renderer/core/css/style_change_reason.cc b/third_party/blink/renderer/core/css/style_change_reason.cc index 8c2e2cd..474c784 100644 --- a/third_party/blink/renderer/core/css/style_change_reason.cc +++ b/third_party/blink/renderer/core/css/style_change_reason.cc
@@ -20,6 +20,7 @@ const char kDesignMode[] = "DesignMode"; const char kFontSizeChange[] = "FontSizeChange"; const char kFonts[] = "Fonts"; +const char kFrame[] = "Frame"; const char kFullScreen[] = "FullScreen"; const char kInheritedStyleChangeFromParentFrame[] = "InheritedStyleChangeFromParentFrame";
diff --git a/third_party/blink/renderer/core/css/style_change_reason.h b/third_party/blink/renderer/core/css/style_change_reason.h index f080680..a42cca3 100644 --- a/third_party/blink/renderer/core/css/style_change_reason.h +++ b/third_party/blink/renderer/core/css/style_change_reason.h
@@ -22,6 +22,7 @@ extern const char kControl[]; extern const char kDeclarativeContent[]; extern const char kDesignMode[]; +extern const char kFrame[]; extern const char kFontSizeChange[]; extern const char kFonts[]; extern const char kFullScreen[];
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc index 9416c98f..18e48dc16 100644 --- a/third_party/blink/renderer/core/css/style_property_serializer.cc +++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -478,7 +478,7 @@ case CSSPropertyWebkitMarginCollapse: return GetShorthandValue(webkitMarginCollapseShorthand()); case CSSPropertyOverflow: - return GetCommonValue(overflowShorthand()); + return Get2Values(overflowShorthand()); case CSSPropertyOverscrollBehavior: return GetShorthandValue(overscrollBehaviorShorthand()); case CSSPropertyPadding:
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index c75f6d5..c0a9b0f8 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -681,7 +681,7 @@ LayoutBox* box_to_scroll = nullptr; - if (GetDocument().GetRootScrollerController().ScrollsViewport(*this)) + if (this == GetDocument().documentElement()) box_to_scroll = GetDocument().GetLayoutView(); else if (GetLayoutObject()) box_to_scroll = ToLayoutBox(GetLayoutObject()); @@ -689,7 +689,13 @@ if (!box_to_scroll) return; - ScrollResult result = box_to_scroll->EnclosingBox()->Scroll( + ScrollableArea* scrollable_area = + box_to_scroll->EnclosingBox()->GetScrollableArea(); + + if (!scrollable_area) + return; + + ScrollResult result = scrollable_area->UserScroll( ScrollGranularity(static_cast<int>(scroll_state.deltaGranularity())), delta);
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc index 3ea67822..9bade3a 100644 --- a/third_party/blink/renderer/core/exported/web_frame_test.cc +++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -10667,6 +10667,12 @@ } TEST_P(WebFrameOverscrollTest, RootLayerOverscrolledOnInnerIFrameOverScroll) { + // TODO(bokan): This test will fail without root-layer-scrolls but that's ok + // because it's already shipped and non-root-layer-scrolls is no longer + // supported. https://crbug.com/823365. + if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) + return; + OverscrollWebViewClient client; RegisterMockedHttpURLLoad("overscroll/iframe-overscroll.html"); RegisterMockedHttpURLLoad("overscroll/scrollable-iframe.html");
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc index 29a886fa6..db660a3 100644 --- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc +++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
@@ -418,6 +418,10 @@ ->GlobalProxyIfNotDetached(); } +WebRect WebRemoteFrameImpl::GetCompositingRect() { + return GetFrame()->View()->GetCompositingRect(); +} + WebRemoteFrameImpl::WebRemoteFrameImpl(WebTreeScopeType scope, WebRemoteFrameClient* client) : WebRemoteFrame(scope),
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h index 901f385..dcdccee 100644 --- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h +++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
@@ -84,6 +84,7 @@ void IntrinsicSizingInfoChanged(const WebIntrinsicSizingInfo&) override; void SetHasReceivedUserGestureBeforeNavigation(bool value) override; v8::Local<v8::Object> GlobalProxy() const override; + WebRect GetCompositingRect() override; void InitializeCoreFrame(Page&, FrameOwner*, const AtomicString& name); RemoteFrame* GetFrame() const { return frame_.Get(); }
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc index f2a2a4d..4a883e14 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -3849,4 +3849,12 @@ page_->AddAutoplayFlags(value); } +void WebViewImpl::ClearAutoplayFlags() { + page_->ClearAutoplayFlags(); +} + +int32_t WebViewImpl::AutoplayFlagsForTest() { + return page_->AutoplayFlags(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h index f2a35c0..8962955 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.h +++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -436,6 +436,8 @@ void ForceNextDrawingBufferCreationToFail() override; void AddAutoplayFlags(int32_t) override; + void ClearAutoplayFlags() override; + int32_t AutoplayFlagsForTest() override; IntSize MainFrameSize(); WebDisplayMode DisplayMode() const { return display_mode_; }
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc index b3954d7..61a3935 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3064,6 +3064,12 @@ } TEST_P(WebViewTest, KeyDownScrollsHandled) { + // TODO(bokan): This test will fail without root-layer-scrolls but that's ok + // because it's already shipped and non-root-layer-scrolls is no longer + // supported. https://crbug.com/823365. + if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) + return; + RegisterMockedHttpURLLoad("content-width-1000.html"); WebViewImpl* web_view =
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc index 6544ed7..ff642e209 100644 --- a/third_party/blink/renderer/core/frame/remote_frame_view.cc +++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h" #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" @@ -90,15 +91,75 @@ // Translate the intersection rect from the root frame's coordinate space // to the remote frame's coordinate space. - viewport_intersection = ConvertFromRootFrame(intersected_rect); + FloatRect viewport_intersection_float = + remote_frame_->OwnerLayoutObject() + ->AncestorToLocalQuad(local_root_view->GetLayoutView(), + FloatQuad(intersected_rect), + kTraverseDocumentBoundaries | kUseTransforms) + .BoundingBox(); + viewport_intersection_float.Move( + -remote_frame_->OwnerLayoutObject()->ContentBoxOffset()); + viewport_intersection = EnclosingIntRect(viewport_intersection_float); } - if (viewport_intersection != last_viewport_intersection_) { - remote_frame_->Client()->UpdateRemoteViewportIntersection( - viewport_intersection); - } + if (viewport_intersection == last_viewport_intersection_) + return; last_viewport_intersection_ = viewport_intersection; + remote_frame_->Client()->UpdateRemoteViewportIntersection( + viewport_intersection); +} + +IntRect RemoteFrameView::GetCompositingRect() { + LocalFrameView* local_root_view = + ToLocalFrame(remote_frame_->Tree().Parent())->LocalFrameRoot().View(); + if (!local_root_view || !remote_frame_->OwnerLayoutObject()) + return IntRect(); + + // For main frames we constrain the rect that gets painted to the viewport. + // If the local frame root is an OOPIF itself, then we use the root's + // intersection rect. This represents a conservative maximum for the area + // that needs to be rastered by the OOPIF compositor. + IntSize viewport_size = local_root_view->FrameRect().Size(); + if (local_root_view->GetPage()->MainFrame() != local_root_view->GetFrame()) { + viewport_size = local_root_view->RemoteViewportIntersection().Size(); + } + + // The viewport size needs to account for intermediate CSS transforms before + // being compared to the frame size. + FloatQuad viewport_quad = + remote_frame_->OwnerLayoutObject()->AncestorToLocalQuad( + local_root_view->GetLayoutView(), + FloatRect(FloatPoint(), FloatSize(viewport_size)), + kTraverseDocumentBoundaries | kUseTransforms); + IntSize converted_viewport_size = + EnclosingIntRect(viewport_quad.BoundingBox()).Size(); + + IntSize frame_size = FrameRect().Size(); + + // Iframes that fit within the window viewport get fully rastered. For + // iframes that are larger than the window viewport, add a 30% buffer to the + // draw area to try to prevent guttering during scroll. + // TODO(kenrb): The 30% value is arbitrary, it gives 15% overdraw in both + // directions when the iframe extends beyond both edges of the viewport, and + // it seems to make guttering rare with slow to medium speed wheel scrolling. + // Can we collect UMA data to estimate how much extra rastering this causes, + // and possibly how common guttering is? + converted_viewport_size.Scale(1.3f); + converted_viewport_size.SetWidth( + std::min(frame_size.Width(), converted_viewport_size.Width())); + converted_viewport_size.SetHeight( + std::min(frame_size.Height(), converted_viewport_size.Height())); + IntPoint expanded_origin; + if (!last_viewport_intersection_.IsEmpty()) { + IntSize expanded_size = + last_viewport_intersection_.Size().ExpandedTo(converted_viewport_size); + expanded_size -= last_viewport_intersection_.Size(); + expanded_size.Scale(0.5f, 0.5f); + expanded_origin = last_viewport_intersection_.Location() - expanded_size; + expanded_origin.ClampNegativeToZero(); + } + return IntRect(expanded_origin, converted_viewport_size); } void RemoteFrameView::Dispose() { @@ -210,17 +271,6 @@ remote_frame_->Client()->VisibilityChanged(self_visible_ && parent_visible_); } -IntRect RemoteFrameView::ConvertFromRootFrame( - const IntRect& rect_in_root_frame) const { - if (LocalFrameView* parent = ParentFrameView()) { - IntRect parent_rect = parent->ConvertFromRootFrame(rect_in_root_frame); - parent_rect.SetLocation( - parent->ConvertSelfToChild(*this, parent_rect.Location())); - return parent_rect; - } - return rect_in_root_frame; -} - void RemoteFrameView::SetupRenderThrottling() { if (visibility_observer_) return;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h index 4730289..a57f63b 100644 --- a/third_party/blink/renderer/core/frame/remote_frame_view.h +++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -60,6 +60,11 @@ void SetIntrinsicSizeInfo(const IntrinsicSizingInfo& size_info); bool HasIntrinsicSizingInfo() const override; + // Compute the interest rect of this frame in its unscrolled space. This may + // be used by the OOPIF's compositor to limit the amount of rastered tiles, + // and reduce the number of paint-ops generated. + IntRect GetCompositingRect(); + uint32_t Print(const IntRect&, WebCanvas*) const; void Trace(blink::Visitor*) override; @@ -68,7 +73,6 @@ explicit RemoteFrameView(RemoteFrame*); LocalFrameView* ParentFrameView() const; - IntRect ConvertFromRootFrame(const IntRect&) const; void UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled); bool CanThrottleRendering() const;
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc index 771acd0..8f80f9c8 100644 --- a/third_party/blink/renderer/core/html/forms/range_input_type.cc +++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -250,7 +250,6 @@ HTMLElement* container = SliderContainerElement::Create(document); container->AppendChild(track); GetElement().UserAgentShadowRoot()->AppendChild(container); - container->setAttribute(styleAttr, "-webkit-appearance:inherit"); } LayoutObject* RangeInputType::CreateLayoutObject(const ComputedStyle&) const {
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc index 3aaeef9..34921fd 100644 --- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc +++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -32,6 +32,7 @@ #include "third_party/blink/renderer/core/html/forms/slider_thumb_element.h" #include "third_party/blink/renderer/core/dom/events/event.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/events/mouse_event.h" #include "third_party/blink/renderer/core/events/touch_event.h" @@ -58,7 +59,9 @@ } inline SliderThumbElement::SliderThumbElement(Document& document) - : HTMLDivElement(document), in_drag_mode_(false) {} + : HTMLDivElement(document), in_drag_mode_(false) { + SetHasCustomStyleCallbacks(); +} SliderThumbElement* SliderThumbElement::Create(Document& document) { SliderThumbElement* element = new SliderThumbElement(document); @@ -315,6 +318,26 @@ } } +scoped_refptr<ComputedStyle> SliderThumbElement::CustomStyleForLayoutObject() { + Element* host = OwnerShadowHost(); + DCHECK(host); + const ComputedStyle& host_style = host->ComputedStyleRef(); + scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject(); + + if (host_style.Appearance() == kSliderVerticalPart) + style->SetAppearance(kSliderThumbVerticalPart); + else if (host_style.Appearance() == kSliderHorizontalPart) + style->SetAppearance(kSliderThumbHorizontalPart); + else if (host_style.Appearance() == kMediaSliderPart) + style->SetAppearance(kMediaSliderThumbPart); + else if (host_style.Appearance() == kMediaVolumeSliderPart) + style->SetAppearance(kMediaVolumeSliderThumbPart); + if (style->HasAppearance()) + LayoutTheme::GetTheme().AdjustSliderThumbSize(*style); + + return style; +} + // -------------------------------- inline SliderContainerElement::SliderContainerElement(Document& document)
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.h b/third_party/blink/renderer/core/html/forms/slider_thumb_element.h index e4e6923..43a2f1c4 100644 --- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.h +++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.h
@@ -60,6 +60,7 @@ private: SliderThumbElement(Document&); LayoutObject* CreateLayoutObject(const ComputedStyle&) override; + scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() final; Element* CloneWithoutAttributesAndChildren(Document&) const override; bool IsDisabledFormControl() const override; bool MatchesReadOnlyPseudoClass() const override;
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc index 8e0c611..4491976 100644 --- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc +++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -25,6 +25,7 @@ #include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h" #include "third_party/blink/renderer/bindings/core/v8/exception_messages.h" #include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/css/style_change_reason.h" #include "third_party/blink/renderer/core/dom/ax_object_cache.h" #include "third_party/blink/renderer/core/dom/events/event.h" #include "third_party/blink/renderer/core/dom/exception_code.h" @@ -147,6 +148,9 @@ content_frame_ = &frame; + SetNeedsStyleRecalc(kLocalStyleChange, StyleChangeReasonForTracing::Create( + StyleChangeReason::kFrame)); + for (ContainerNode* node = this; node; node = node->ParentOrShadowHostNode()) node->IncrementConnectedSubframeCount(); }
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc index 973b9389..1df04e2 100644 --- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc +++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -338,10 +338,8 @@ auto sk_image = image->PaintImageForCurrentFrame().GetSkImage(); auto image_info = GetSkImageInfo(image); // Avoid sRGB transfer function by setting the color space to nullptr. - if (SkColorSpace::Equals(image_info.colorSpace(), - SkColorSpace::MakeSRGB().get())) { + if (image_info.colorSpace()->isSRGB()) image_info = image_info.makeColorSpace(nullptr); - } SkImageInfo resized_info = image_info.makeWH(parsed_options.resize_width, parsed_options.resize_height);
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc index 97023a3..b9a98e2 100644 --- a/third_party/blink/renderer/core/input/scroll_manager.cc +++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -20,6 +20,7 @@ #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/scrolling/overscroll_controller.h" #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h" +#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h" #include "third_party/blink/renderer/core/page/scrolling/scroll_state.h" #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -148,22 +149,18 @@ bool ScrollManager::CanScroll(const ScrollState& scroll_state, const Element& current_element) { - double delta_x = scroll_state.isBeginning() ? scroll_state.deltaXHint() - : scroll_state.deltaX(); - double delta_y = scroll_state.isBeginning() ? scroll_state.deltaYHint() - : scroll_state.deltaY(); - if (!delta_x && !delta_y) - return true; - ScrollableArea* scrollable_area = nullptr; - if (IsViewportScrollingElement(current_element) || current_element == *(frame_->GetDocument()->documentElement())) { + if (!current_element.GetLayoutObject()) + return false; + if (frame_->IsMainFrame()) return true; // For subframes, the viewport is added to the scroll chain only if it can - // actually consume some delta hints. + // actually consume some delta hints. The main frame always gets added + // since it produces overscroll effects. scrollable_area = frame_->View() ? frame_->View()->GetScrollableArea() : nullptr; } @@ -174,6 +171,13 @@ if (!scrollable_area) return false; + double delta_x = scroll_state.isBeginning() ? scroll_state.deltaXHint() + : scroll_state.deltaX(); + double delta_y = scroll_state.isBeginning() ? scroll_state.deltaYHint() + : scroll_state.deltaY(); + if (!delta_x && !delta_y) + return true; + if (!scrollable_area->UserInputScrollable(kHorizontalScrollbar)) delta_x = 0; if (!scrollable_area->UserInputScrollable(kVerticalScrollbar)) @@ -205,21 +209,50 @@ if (!node) return false; - frame_->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); + Document& document = node->GetDocument(); - LayoutBox* cur_box = node->GetLayoutObject()->EnclosingBox(); - while (cur_box) { + document.UpdateStyleAndLayoutIgnorePendingStylesheets(); + + std::deque<int> scroll_chain; + std::unique_ptr<ScrollStateData> scroll_state_data = + std::make_unique<ScrollStateData>(); + ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data)); + RecomputeScrollChain(*node, *scroll_state, scroll_chain); + + while (!scroll_chain.empty()) { + Node* node = DOMNodeIds::NodeForId(scroll_chain.back()); + scroll_chain.pop_back(); + DCHECK(node); + + LayoutBox* box = ToLayoutBox(node->GetLayoutObject()); + DCHECK(box); + ScrollDirectionPhysical physical_direction = - ToPhysicalDirection(direction, cur_box->IsHorizontalWritingMode(), - cur_box->Style()->IsFlippedBlocksWritingMode()); + ToPhysicalDirection(direction, box->IsHorizontalWritingMode(), + box->Style()->IsFlippedBlocksWritingMode()); - ScrollResult result = - cur_box->Scroll(granularity, ToScrollDelta(physical_direction, 1)); + ScrollableArea* scrollable_area = nullptr; + + // The global root scroller must be scrolled by the RootFrameViewport. + if (RootScrollerUtil::IsGlobal(*box)) { + LocalFrame& main_frame = frame_->LocalFrameRoot(); + // The local root must be the main frame if we have the global root + // scroller since it can't yet be set on OOPIFs. + DCHECK(main_frame.IsMainFrame()); + + scrollable_area = main_frame.View()->GetScrollableArea(); + } else if (node == document.documentElement()) { + scrollable_area = document.GetLayoutView()->GetScrollableArea(); + } else { + scrollable_area = box->GetScrollableArea(); + } + + DCHECK(scrollable_area); + ScrollResult result = scrollable_area->UserScroll( + granularity, ToScrollDelta(physical_direction, 1)); if (result.DidScroll()) return true; - - cur_box = cur_box->ContainingBlock(); } return false;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index e650e09..9aff140 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -998,17 +998,6 @@ return width; } -ScrollResult LayoutBox::Scroll(ScrollGranularity granularity, - const FloatSize& delta) { - // Presumably the same issue as in setScrollTop. See crbug.com/343132. - DisableCompositingQueryAsserts disabler; - - if (!GetScrollableArea()) - return ScrollResult(); - - return GetScrollableArea()->UserScroll(granularity, delta); -} - bool LayoutBox::CanBeScrolledAndHasScrollableArea() const { return CanBeProgramaticallyScrolled() && (PixelSnappedScrollHeight() != PixelSnappedClientHeight() ||
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index 9b5ea9a..18faa17 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1000,7 +1000,6 @@ // scrollWidth. For the full story, visit crbug.com/724255 LayoutUnit VerticalScrollbarWidthClampedToContentBox() const; - virtual ScrollResult Scroll(ScrollGranularity, const FloatSize&); bool CanBeScrolledAndHasScrollableArea() const; virtual bool CanBeProgramaticallyScrolled() const; virtual void Autoscroll(const IntPoint&);
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.cc b/third_party/blink/renderer/core/layout/layout_embedded_object.cc index 9ee0e68..ca6ad03 100644 --- a/third_party/blink/renderer/core/layout/layout_embedded_object.cc +++ b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
@@ -142,11 +142,6 @@ ClearNeedsLayout(); } -ScrollResult LayoutEmbeddedObject::Scroll(ScrollGranularity granularity, - const FloatSize&) { - return ScrollResult(); -} - CompositingReasons LayoutEmbeddedObject::AdditionalCompositingReasons() const { if (RequiresAcceleratedCompositing()) return CompositingReason::kPlugin;
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.h b/third_party/blink/renderer/core/layout/layout_embedded_object.h index 180fb40..5905650b 100644 --- a/third_party/blink/renderer/core/layout/layout_embedded_object.h +++ b/third_party/blink/renderer/core/layout/layout_embedded_object.h
@@ -67,8 +67,6 @@ PaintLayerType LayerTypeRequired() const final; - ScrollResult Scroll(ScrollGranularity, const FloatSize&) final; - CompositingReasons AdditionalCompositingReasons() const override; PluginAvailability plugin_availability_ = kPluginAvailable;
diff --git a/third_party/blink/renderer/core/layout/layout_slider.cc b/third_party/blink/renderer/core/layout/layout_slider.cc index 3cdee413..13b12de9 100644 --- a/third_party/blink/renderer/core/layout/layout_slider.cc +++ b/third_party/blink/renderer/core/layout/layout_slider.cc
@@ -64,16 +64,6 @@ ShadowElementNames::SliderThumb())); } -void LayoutSlider::UpdateLayout() { - // FIXME: Find a way to cascade appearance. - // http://webkit.org/b/62535 - LayoutBox* thumb_box = GetSliderThumbElement()->GetLayoutBox(); - if (thumb_box && thumb_box->IsSliderThumb()) - ToLayoutSliderThumb(thumb_box)->UpdateAppearance(StyleRef()); - - LayoutFlexibleBox::UpdateLayout(); -} - bool LayoutSlider::InDragMode() const { return GetSliderThumbElement()->IsActive(); }
diff --git a/third_party/blink/renderer/core/layout/layout_slider.h b/third_party/blink/renderer/core/layout/layout_slider.h index 10236f4..6bfcca8 100644 --- a/third_party/blink/renderer/core/layout/layout_slider.h +++ b/third_party/blink/renderer/core/layout/layout_slider.h
@@ -53,7 +53,6 @@ void ComputeIntrinsicLogicalWidths( LayoutUnit& min_logical_width, LayoutUnit& max_logical_width) const override; - void UpdateLayout() override; SliderThumbElement* GetSliderThumbElement() const; };
diff --git a/third_party/blink/renderer/core/layout/layout_slider_thumb.cc b/third_party/blink/renderer/core/layout/layout_slider_thumb.cc index 4c7b354..9868b24 100644 --- a/third_party/blink/renderer/core/layout/layout_slider_thumb.cc +++ b/third_party/blink/renderer/core/layout/layout_slider_thumb.cc
@@ -40,17 +40,4 @@ LayoutSliderThumb::LayoutSliderThumb(SliderThumbElement* element) : LayoutBlockFlow(element) {} -void LayoutSliderThumb::UpdateAppearance(const ComputedStyle& parent_style) { - if (parent_style.Appearance() == kSliderVerticalPart) - MutableStyleRef().SetAppearance(kSliderThumbVerticalPart); - else if (parent_style.Appearance() == kSliderHorizontalPart) - MutableStyleRef().SetAppearance(kSliderThumbHorizontalPart); - else if (parent_style.Appearance() == kMediaSliderPart) - MutableStyleRef().SetAppearance(kMediaSliderThumbPart); - else if (parent_style.Appearance() == kMediaVolumeSliderPart) - MutableStyleRef().SetAppearance(kMediaVolumeSliderThumbPart); - if (StyleRef().HasAppearance()) - LayoutTheme::GetTheme().AdjustSliderThumbSize(MutableStyleRef()); -} - } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_slider_thumb.h b/third_party/blink/renderer/core/layout/layout_slider_thumb.h index fa0dc42..a26341b 100644 --- a/third_party/blink/renderer/core/layout/layout_slider_thumb.h +++ b/third_party/blink/renderer/core/layout/layout_slider_thumb.h
@@ -41,7 +41,6 @@ class LayoutSliderThumb final : public LayoutBlockFlow { public: LayoutSliderThumb(SliderThumbElement*); - void UpdateAppearance(const ComputedStyle& parent_style); private: bool IsOfType(LayoutObjectType type) const override {
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc index 1ce91caf..6307baeb 100644 --- a/third_party/blink/renderer/core/layout/layout_view.cc +++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -896,20 +896,6 @@ return RuntimeEnabledFeatures::RootLayerScrollingEnabled(); } -ScrollResult LayoutView::Scroll(ScrollGranularity granularity, - const FloatSize& delta) { - // TODO(bokan): We shouldn't need this specialization but we currently do - // because of the Windows pan scrolling path. That should go through a more - // normalized ScrollManager-like scrolling path and we should get rid of - // of this override. All frame scrolling should be handled by - // ViewportScrollCallback. - - if (!GetFrameView()) - return ScrollResult(false, false, delta.Width(), delta.Height()); - - return GetFrameView()->GetScrollableArea()->UserScroll(granularity, delta); -} - LayoutRect LayoutView::DebugRect() const { LayoutRect rect; LayoutBlock* block = ContainingBlock();
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h index 9b08868..df11eaf 100644 --- a/third_party/blink/renderer/core/layout/layout_view.h +++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -243,10 +243,6 @@ return false; } - // The rootLayerScrolls setting will ultimately determine whether - // LocalFrameView or PaintLayerScrollableArea handle the scroll. - ScrollResult Scroll(ScrollGranularity, const FloatSize&) override; - LayoutRect DebugRect() const override; IntSize ScrolledContentOffset() const override;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc index 23910f7..8d39450 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -346,9 +346,12 @@ } logical_height += fragment_logical_size.block_size; intrinsic_content_logical_height += layout_result.IntrinsicBlockSize(); - NGBoxStrut border_scrollbar_padding = - ComputeBorders(constraint_space, Style()) + - ComputePadding(constraint_space, Style()) + GetScrollbarSizes(); + + NGBoxStrut borders = ComputeBorders(constraint_space, Style()); + NGBoxStrut scrollbars = GetScrollbarSizes(); + NGBoxStrut padding = ComputePadding(constraint_space, Style()); + NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding; + if (IsLastFragment(physical_fragment)) intrinsic_content_logical_height -= border_scrollbar_padding.BlockSum(); box_->SetLogicalHeight(logical_height); @@ -390,9 +393,9 @@ } // |ComputeOverflow()| below calls |AddOverflowFromChildren()|, which - // computes visual overflow from |RootInlineBox| if |ChildrenInline()|. - block->ComputeOverflow(intrinsic_block_size - - border_scrollbar_padding.block_end); + // computes visual overflow from |RootInlineBox| if |ChildrenInline()| + block->ComputeOverflow(intrinsic_block_size - borders.block_end - + scrollbars.BlockSum()); } box_->UpdateAfterLayout(); @@ -471,7 +474,9 @@ // LegacyLayout flips vertical-rl horizontal coordinates before paint. // NGLayout flips X location for LegacyLayout compatibility. if (containing_block->StyleRef().IsFlippedBlocksWritingMode()) { - LayoutUnit container_width = containing_block->Size().Width(); + NGBoxStrut scrollbars = GetScrollbarSizes(); + LayoutUnit container_width = + containing_block->Size().Width() - scrollbars.block_start; layout_box->SetX(container_width - fragment.Offset().left - additional_offset.left - fragment.Size().width); } else {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc index d0c6e53..7457f97 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -214,6 +214,18 @@ } } +AffineTransform LayoutSVGResourceClipper::CalculateClipTransform( + const FloatRect& reference_box) const { + AffineTransform transform = + ToSVGClipPathElement(GetElement()) + ->CalculateTransform(SVGElement::kIncludeMotionTransform); + if (ClipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { + transform.Translate(reference_box.X(), reference_box.Y()); + transform.ScaleNonUniform(reference_box.Width(), reference_box.Height()); + } + return transform; +} + bool LayoutSVGResourceClipper::HitTestClipContent( const FloatRect& object_bounding_box, const FloatPoint& node_at_point) { @@ -221,21 +233,12 @@ if (!SVGLayoutSupport::PointInClippingArea(*this, point)) return false; - if (ClipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { - AffineTransform transform; - transform.Translate(object_bounding_box.X(), object_bounding_box.Y()); - transform.ScaleNonUniform(object_bounding_box.Width(), - object_bounding_box.Height()); - point = transform.Inverse().MapPoint(point); - } - - AffineTransform animated_local_transform = - ToSVGClipPathElement(GetElement()) - ->CalculateTransform(SVGElement::kIncludeMotionTransform); - if (!animated_local_transform.IsInvertible()) + AffineTransform user_space_transform = + CalculateClipTransform(object_bounding_box); + if (!user_space_transform.IsInvertible()) return false; - point = animated_local_transform.Inverse().MapPoint(point); + point = user_space_transform.Inverse().MapPoint(point); for (const SVGElement& child_element : Traversal<SVGElement>::ChildrenOf(*GetElement())) { @@ -264,14 +267,7 @@ if (local_clip_bounds_.IsEmpty()) CalculateLocalClipBounds(); - AffineTransform transform = - ToSVGClipPathElement(GetElement()) - ->CalculateTransform(SVGElement::kIncludeMotionTransform); - if (ClipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { - transform.Translate(reference_box.X(), reference_box.Y()); - transform.ScaleNonUniform(reference_box.Width(), reference_box.Height()); - } - return transform.MapRect(local_clip_bounds_); + return CalculateClipTransform(reference_box).MapRect(local_clip_bounds_); } void LayoutSVGResourceClipper::WillBeDestroyed() {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h index e5b354d..dd32bc66 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
@@ -48,6 +48,7 @@ ->CurrentValue() ->EnumValue(); } + AffineTransform CalculateClipTransform(const FloatRect& reference_box) const; base::Optional<Path> AsPath(); sk_sp<const PaintRecord> CreatePaintRecord();
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc index bcaadc7..27af825 100644 --- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc +++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -4,11 +4,11 @@ #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h" +#include "base/time/time.h" #include "third_party/blink/public/common/origin_trials/trial_token.h" #include "third_party/blink/public/common/origin_trials/trial_token_validator.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_security_origin.h" -#include "third_party/blink/public/platform/web_trial_token_validator.h" #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h" @@ -23,6 +23,7 @@ #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "v8/include/v8.h" @@ -88,7 +89,7 @@ OriginTrialContext::OriginTrialContext( ExecutionContext& context, - std::unique_ptr<WebTrialTokenValidator> validator) + std::unique_ptr<TrialTokenValidator> validator) : Supplement<ExecutionContext>(context), trial_token_validator_(std::move(validator)) {} @@ -108,7 +109,9 @@ Supplement<ExecutionContext>::From<OriginTrialContext>(context); if (!origin_trials) { origin_trials = new OriginTrialContext( - *context, Platform::Current()->CreateTrialTokenValidator()); + *context, TrialTokenValidator::Policy() + ? std::make_unique<TrialTokenValidator>() + : nullptr); Supplement<ExecutionContext>::ProvideTo(*context, origin_trials); } return origin_trials; @@ -251,20 +254,22 @@ return false; } - WebSecurityOrigin origin; - if (context->IsWorkletGlobalScope()) { - origin = WebSecurityOrigin( - ToWorkletGlobalScope(context)->DocumentSecurityOrigin()); - } else { - origin = WebSecurityOrigin(context->GetSecurityOrigin()); - } + const SecurityOrigin* origin; + if (context->IsWorkletGlobalScope()) + origin = ToWorkletGlobalScope(context)->DocumentSecurityOrigin(); + else + origin = context->GetSecurityOrigin(); - WebString trial_name; bool valid = false; - OriginTrialTokenStatus token_result = - trial_token_validator_->ValidateToken(token, origin, &trial_name); + StringUTF8Adaptor token_string(token); + std::string trial_name_str; + OriginTrialTokenStatus token_result = trial_token_validator_->ValidateToken( + token_string.AsStringPiece(), origin->ToUrlOrigin(), &trial_name_str, + base::Time::Now()); if (token_result == OriginTrialTokenStatus::kSuccess) { valid = true; + String trial_name = + String::FromUTF8(trial_name_str.data(), trial_name_str.size()); enabled_trials_.insert(trial_name); // Also enable any trials implied by this trial Vector<AtomicString> implied_trials =
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.h b/third_party/blink/renderer/core/origin_trials/origin_trial_context.h index 4d4b19f3..f5fc607 100644 --- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.h +++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ORIGIN_TRIALS_ORIGIN_TRIAL_CONTEXT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_ORIGIN_TRIALS_ORIGIN_TRIAL_CONTEXT_H_ -#include "third_party/blink/public/platform/web_trial_token_validator.h" +#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/platform/supplementable.h" @@ -39,8 +39,7 @@ public: static const char kSupplementName[]; - OriginTrialContext(ExecutionContext&, - std::unique_ptr<WebTrialTokenValidator>); + OriginTrialContext(ExecutionContext&, std::unique_ptr<TrialTokenValidator>); // Returns the OriginTrialContext for a specific ExecutionContext, if one // exists. @@ -98,7 +97,7 @@ Vector<String> tokens_; HashSet<String> enabled_trials_; HashSet<String> installed_trials_; - std::unique_ptr<WebTrialTokenValidator> trial_token_validator_; + std::unique_ptr<TrialTokenValidator> trial_token_validator_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc index 1fd590e..fbc748d 100644 --- a/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc +++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc
@@ -7,7 +7,7 @@ #include <memory> #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/origin_trials/trial_token.h" -#include "third_party/blink/public/platform/web_trial_token_validator.h" +#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" #include "third_party/blink/renderer/core/dom/dom_exception.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/html/html_head_element.h" @@ -35,23 +35,25 @@ // Trial token placeholder for mocked calls to validator const char kTokenPlaceholder[] = "The token contents are not used"; -class MockTokenValidator : public WebTrialTokenValidator { +class MockTokenValidator : public TrialTokenValidator { public: MockTokenValidator() : response_(OriginTrialTokenStatus::kNotSupported), call_count_(0) {} ~MockTokenValidator() override = default; // blink::WebTrialTokenValidator implementation - OriginTrialTokenStatus ValidateToken(const WebString& token, - const WebSecurityOrigin& origin, - WebString* feature_name) override { + OriginTrialTokenStatus ValidateToken(base::StringPiece token, + const url::Origin& origin, + std::string* feature_name, + base::Time current_time) const override { call_count_++; *feature_name = feature_; return response_; } // Useful methods for controlling the validator - void SetResponse(OriginTrialTokenStatus response, const WebString& feature) { + void SetResponse(OriginTrialTokenStatus response, + const std::string& feature) { response_ = response; feature_ = feature; } @@ -59,8 +61,8 @@ private: OriginTrialTokenStatus response_; - WebString feature_; - int call_count_; + std::string feature_; + mutable int call_count_; DISALLOW_COPY_AND_ASSIGN(MockTokenValidator); };
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc index 9eae0f4..3f4d3f06 100644 --- a/third_party/blink/renderer/core/page/page.cc +++ b/third_party/blink/renderer/core/page/page.cc
@@ -834,7 +834,11 @@ } void Page::AddAutoplayFlags(int32_t value) { - autoplay_flags_ = value; + autoplay_flags_ |= value; +} + +void Page::ClearAutoplayFlags() { + autoplay_flags_ = 0; } int32_t Page::AutoplayFlags() const {
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h index 4ef2efee0..0f51ac4d 100644 --- a/third_party/blink/renderer/core/page/page.h +++ b/third_party/blink/renderer/core/page/page.h
@@ -322,6 +322,7 @@ int64_t GetUkmSourceId() override; void AddAutoplayFlags(int32_t flags); + void ClearAutoplayFlags(); int32_t AutoplayFlags() const;
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc index e467960..4148cf54 100644 --- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc +++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -903,6 +903,7 @@ ASSERT_EQ(MainFrame()->GetDocument()->documentElement(), global_controller.GlobalRootScroller()); + MainFrameView()->UpdateAllLifecyclePhases(); GraphicsLayer* scroll_layer = global_controller.RootScrollerLayer(); GraphicsLayer* container_layer = global_controller.RootContainerLayer();
diff --git a/third_party/blink/renderer/core/paint/clip_path_clipper.cc b/third_party/blink/renderer/core/paint/clip_path_clipper.cc index e7c8d37..21b120ddc 100644 --- a/third_party/blink/renderer/core/paint/clip_path_clipper.cc +++ b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
@@ -190,15 +190,7 @@ } mask_to_content.Multiply( - ToSVGClipPathElement(resource_clipper.GetElement()) - ->CalculateTransform(SVGElement::kIncludeMotionTransform)); - - if (resource_clipper.ClipPathUnits() == - SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { - mask_to_content.Translate(reference_box.X(), reference_box.Y()); - mask_to_content.ScaleNonUniform(reference_box.Width(), - reference_box.Height()); - } + resource_clipper.CalculateClipTransform(reference_box)); return mask_to_content; }
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.cc b/third_party/blink/renderer/core/paint/paint_invalidator.cc index e7cecbd..f734d37 100644 --- a/third_party/blink/renderer/core/paint/paint_invalidator.cc +++ b/third_party/blink/renderer/core/paint/paint_invalidator.cc
@@ -61,6 +61,13 @@ if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) return; + // One of the following conditions happened in crbug.com/837226. + if (!context.paint_invalidation_container || + !context.paint_invalidation_container->FirstFragment() + .HasLocalBorderBoxProperties() || + !context.tree_builder_context_) + return; + if (!(context.paint_invalidation_container->Layer()->GetCompositingReasons() & CompositingReason::kComboAllDirectReasons)) return;
diff --git a/third_party/blink/renderer/devtools/front_end/jsconfig.json b/third_party/blink/renderer/devtools/front_end/jsconfig.json index 9e26dfe..119d02d 100644 --- a/third_party/blink/renderer/devtools/front_end/jsconfig.json +++ b/third_party/blink/renderer/devtools/front_end/jsconfig.json
@@ -1 +1,22 @@ -{} \ No newline at end of file +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext", "dom"] + }, + "include": [ + "**/*" + ], + "exclude": [ + ".eslintrc.js", + "audits2_worker/lighthouse/", + "audits2/lighthouse/", + "cm/", + "cm_headless/", + "cm_modes/", + "cm_web_modes/", + "diff/diff_match_patch.js", + "formatter_worker/acorn/", + "terminal/xterm.js/", + "protocol_externs.js" + ] +} \ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js b/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js index 2b5bdd70..44db82d 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js +++ b/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
@@ -56,6 +56,7 @@ this._currentTabLocked = false; this._autoSelectFirstItemOnShow = true; + this._triggerDropDownTimeout = null; this._dropDownButton = this._createDropDownButton(); UI.zoomManager.addEventListener(UI.ZoomManager.Events.ZoomChanged, this._zoomChanged, this); this.makeTabSlider(); @@ -560,17 +561,25 @@ const dropDownContainer = createElementWithClass('div', 'tabbed-pane-header-tabs-drop-down-container'); const chevronIcon = UI.Icon.create('largeicon-chevron', 'chevron-icon'); dropDownContainer.appendChild(chevronIcon); - dropDownContainer.addEventListener('click', this._onDropDownMouseDown.bind(this)); - dropDownContainer.addEventListener('mousedown', this._onDropDownMouseDown.bind(this)); + dropDownContainer.addEventListener('click', this._dropDownClicked.bind(this)); + dropDownContainer.addEventListener('mousedown', event => { + if (event.which !== 1 || this._triggerDropDownTimeout) + return; + this._triggerDropDownTimeout = setTimeout(this._dropDownClicked.bind(this, event), 200); + }); return dropDownContainer; } /** * @param {!Event} event */ - _onDropDownMouseDown(event) { + _dropDownClicked(event) { if (event.which !== 1) return; + if (this._triggerDropDownTimeout) { + clearTimeout(this._triggerDropDownTimeout); + this._triggerDropDownTimeout = null; + } const rect = this._dropDownButton.getBoundingClientRect(); const menu = new UI.ContextMenu(event, false, rect.left, rect.bottom); for (let i = 0; i < this._tabs.length; ++i) {
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h index a7332ab..c42d3365 100644 --- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h +++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_WORKLET_ANIMATION_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_ANIMATIONWORKLET_WORKLET_ANIMATION_H_ +#include "base/optional.h" #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" #include "third_party/blink/renderer/bindings/modules/v8/document_timeline_or_scroll_timeline.h" #include "third_party/blink/renderer/core/animation/animation.h" @@ -112,7 +113,7 @@ const String animator_name_; Animation::AnimationPlayState play_state_; // Start time in ms. - WTF::Optional<double> start_time_; + base::Optional<double> start_time_; Member<Document> document_;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc index fe5b2ee..53856ef7 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -34,6 +34,7 @@ #include "third_party/blink/renderer/platform/histogram.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/checked_numeric.h" +#include "third_party/blink/renderer/platform/wtf/math_extras.h" namespace blink { @@ -259,7 +260,7 @@ return; if (GetState().LineWidth() == width) return; - ModifiableState().SetLineWidth(width); + ModifiableState().SetLineWidth(clampTo<float>(width)); } String BaseRenderingContext2D::lineCap() const { @@ -297,7 +298,7 @@ return; if (GetState().MiterLimit() == limit) return; - ModifiableState().SetMiterLimit(limit); + ModifiableState().SetMiterLimit(clampTo<float>(limit)); } double BaseRenderingContext2D::shadowOffsetX() const { @@ -309,7 +310,7 @@ return; if (GetState().ShadowOffset().Width() == x) return; - ModifiableState().SetShadowOffsetX(x); + ModifiableState().SetShadowOffsetX(clampTo<float>(x)); } double BaseRenderingContext2D::shadowOffsetY() const { @@ -321,7 +322,7 @@ return; if (GetState().ShadowOffset().Height() == y) return; - ModifiableState().SetShadowOffsetY(y); + ModifiableState().SetShadowOffsetY(clampTo<float>(y)); } double BaseRenderingContext2D::shadowBlur() const { @@ -333,7 +334,7 @@ return; if (GetState().ShadowBlur() == blur) return; - ModifiableState().SetShadowBlur(blur); + ModifiableState().SetShadowBlur(clampTo<float>(blur)); } String BaseRenderingContext2D::shadowColor() const { @@ -374,7 +375,7 @@ void BaseRenderingContext2D::setLineDashOffset(double offset) { if (!std::isfinite(offset) || GetState().LineDashOffset() == offset) return; - ModifiableState().SetLineDashOffset(offset); + ModifiableState().SetLineDashOffset(clampTo<float>(offset)); } double BaseRenderingContext2D::globalAlpha() const { @@ -450,7 +451,9 @@ return; AffineTransform new_transform = GetState().Transform(); - new_transform.ScaleNonUniform(sx, sy); + float fsx = clampTo<float>(sx); + float fsy = clampTo<float>(sy); + new_transform.ScaleNonUniform(fsx, fsy); if (GetState().Transform() == new_transform) return; @@ -458,8 +461,8 @@ if (!GetState().IsTransformInvertible()) return; - c->scale(sx, sy); - path_.Transform(AffineTransform().ScaleNonUniform(1.0 / sx, 1.0 / sy)); + c->scale(fsx, fsy); + path_.Transform(AffineTransform().ScaleNonUniform(1.0 / fsx, 1.0 / fsy)); } void BaseRenderingContext2D::rotate(double angle_in_radians) { @@ -478,7 +481,7 @@ ModifiableState().SetTransform(new_transform); if (!GetState().IsTransformInvertible()) return; - c->rotate(angle_in_radians * (180.0 / piFloat)); + c->rotate(clampTo<float>(angle_in_radians * (180.0 / piFloat))); path_.Transform(AffineTransform().RotateRadians(-angle_in_radians)); } @@ -493,15 +496,18 @@ return; AffineTransform new_transform = GetState().Transform(); - new_transform.Translate(tx, ty); + // clamp to float to avoid float cast overflow when used as SkScalar + float ftx = clampTo<float>(tx); + float fty = clampTo<float>(ty); + new_transform.Translate(ftx, fty); if (GetState().Transform() == new_transform) return; ModifiableState().SetTransform(new_transform); if (!GetState().IsTransformInvertible()) return; - c->translate(tx, ty); - path_.Transform(AffineTransform().Translate(-tx, -ty)); + c->translate(ftx, fty); + path_.Transform(AffineTransform().Translate(-ftx, -fty)); } void BaseRenderingContext2D::transform(double m11, @@ -518,7 +524,15 @@ !std::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy)) return; - AffineTransform transform(m11, m12, m21, m22, dx, dy); + // clamp to float to avoid float cast overflow when used as SkScalar + float fm11 = clampTo<float>(m11); + float fm12 = clampTo<float>(m12); + float fm21 = clampTo<float>(m21); + float fm22 = clampTo<float>(m22); + float fdx = clampTo<float>(dx); + float fdy = clampTo<float>(dy); + + AffineTransform transform(fm11, fm12, fm21, fm22, fdx, fdy); AffineTransform new_transform = GetState().Transform() * transform; if (GetState().Transform() == new_transform) return; @@ -570,7 +584,15 @@ return; resetTransform(); - transform(m11, m12, m21, m22, dx, dy); + // clamp to float to avoid float cast overflow when used as SkScalar + float fm11 = clampTo<float>(m11); + float fm12 = clampTo<float>(m12); + float fm21 = clampTo<float>(m21); + float fm22 = clampTo<float>(m22); + float fdx = clampTo<float>(dx); + float fdy = clampTo<float>(dy); + + transform(fm11, fm12, fm21, fm22, fdx, fdy); } void BaseRenderingContext2D::beginPath() { @@ -677,7 +699,13 @@ if (!DrawingCanvas()) return; - SkRect rect = SkRect::MakeXYWH(x, y, width, height); + // clamp to float to avoid float cast overflow when used as SkScalar + float fx = clampTo<float>(x); + float fy = clampTo<float>(y); + float fwidth = clampTo<float>(width); + float fheight = clampTo<float>(height); + + SkRect rect = SkRect::MakeXYWH(fx, fy, fwidth, fheight); Draw([&rect](PaintCanvas* c, const PaintFlags* flags) // draw lambda { c->drawRect(rect, *flags); }, [&rect, this](const SkIRect& clip_bounds) // overdraw test lambda @@ -711,7 +739,13 @@ if (!DrawingCanvas()) return; - SkRect rect = SkRect::MakeXYWH(x, y, width, height); + // clamp to float to avoid float cast overflow when used as SkScalar + float fx = clampTo<float>(x); + float fy = clampTo<float>(y); + float fwidth = clampTo<float>(width); + float fheight = clampTo<float>(height); + + SkRect rect = SkRect::MakeXYWH(fx, fy, fwidth, fheight); FloatRect bounds = rect; InflateStrokeRect(bounds); Draw([&rect](PaintCanvas* c, const PaintFlags* flags) // draw lambda @@ -771,9 +805,9 @@ if (!GetState().IsTransformInvertible()) return false; - FloatPoint point(x, y); - if (!std::isfinite(point.X()) || !std::isfinite(point.Y())) + if (!std::isfinite(x) || !std::isfinite(y)) return false; + FloatPoint point(clampTo<float>(x), clampTo<float>(y)); AffineTransform ctm = GetState().Transform(); FloatPoint transformed_point = ctm.Inverse().MapPoint(point); @@ -800,9 +834,9 @@ if (!GetState().IsTransformInvertible()) return false; - FloatPoint point(x, y); - if (!std::isfinite(point.X()) || !std::isfinite(point.Y())) + if (!std::isfinite(x) || !std::isfinite(y)) return false; + FloatPoint point(clampTo<float>(x), clampTo<float>(y)); AffineTransform ctm = GetState().Transform(); FloatPoint transformed_point = ctm.Inverse().MapPoint(point); @@ -840,8 +874,14 @@ PaintFlags clear_flags; clear_flags.setBlendMode(SkBlendMode::kClear); clear_flags.setStyle(PaintFlags::kFill_Style); - FloatRect rect(x, y, width, height); + // clamp to float to avoid float cast overflow when used as SkScalar + float fx = clampTo<float>(x); + float fy = clampTo<float>(y); + float fwidth = clampTo<float>(width); + float fheight = clampTo<float>(height); + + FloatRect rect(fx, fy, fwidth, fheight); if (RectContainsTransformedRect(rect, clip_bounds)) { CheckOverdraw(rect, &clear_flags, CanvasRenderingContext2DState::kNoImage, kClipFill); @@ -1243,8 +1283,18 @@ !std::isfinite(sw) || !std::isfinite(sh) || !dw || !dh || !sw || !sh) return; - FloatRect src_rect = NormalizeRect(FloatRect(sx, sy, sw, sh)); - FloatRect dst_rect = NormalizeRect(FloatRect(dx, dy, dw, dh)); + // clamp to float to avoid float cast overflow when used as SkScalar + float fsx = clampTo<float>(sx); + float fsy = clampTo<float>(sy); + float fsw = clampTo<float>(sw); + float fsh = clampTo<float>(sh); + float fdx = clampTo<float>(dx); + float fdy = clampTo<float>(dy); + float fdw = clampTo<float>(dw); + float fdh = clampTo<float>(dh); + + FloatRect src_rect = NormalizeRect(FloatRect(fsx, fsy, fsw, fsh)); + FloatRect dst_rect = NormalizeRect(FloatRect(fdx, fdy, fdw, fdh)); FloatSize image_size = image_source->ElementSize(default_object_size); ClipRectsToImageRect(FloatRect(FloatPoint(), image_size), &src_rect, @@ -1342,8 +1392,18 @@ double y0, double x1, double y1) { + if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || + !std::isfinite(y1)) + return nullptr; + + // clamp to float to avoid float cast overflow + float fx0 = clampTo<float>(x0); + float fy0 = clampTo<float>(y0); + float fx1 = clampTo<float>(x1); + float fy1 = clampTo<float>(y1); + CanvasGradient* gradient = - CanvasGradient::Create(FloatPoint(x0, y0), FloatPoint(x1, y1)); + CanvasGradient::Create(FloatPoint(fx0, fy0), FloatPoint(fx1, fy1)); return gradient; } @@ -1362,8 +1422,20 @@ return nullptr; } - CanvasGradient* gradient = - CanvasGradient::Create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); + if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) || + !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1)) + return nullptr; + + // clamp to float to avoid float cast overflow + float fx0 = clampTo<float>(x0); + float fy0 = clampTo<float>(y0); + float fr0 = clampTo<float>(r0); + float fx1 = clampTo<float>(x1); + float fy1 = clampTo<float>(y1); + float fr1 = clampTo<float>(r1); + + CanvasGradient* gradient = CanvasGradient::Create(FloatPoint(fx0, fy0), fr0, + FloatPoint(fx1, fy1), fr1); return gradient; } @@ -1453,7 +1525,7 @@ if (AlphaChannel(GetState().ShadowColor())) { FloatRect shadow_rect(canvas_rect); shadow_rect.Move(GetState().ShadowOffset()); - shadow_rect.Inflate(GetState().ShadowBlur()); + shadow_rect.Inflate(clampTo<float>(GetState().ShadowBlur())); canvas_rect.Unite(shadow_rect); }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc index 08f1406..b52c2b5 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -380,8 +380,10 @@ double height) { BaseRenderingContext2D::clearRect(x, y, width, height); - if (hit_region_manager_) { - FloatRect rect(x, y, width, height); + if (hit_region_manager_ && std::isfinite(x) && std::isfinite(y) && + std::isfinite(width) && std::isfinite(height)) { + FloatRect rect(clampTo<float>(x), clampTo<float>(y), clampTo<float>(width), + clampTo<float>(height)); hit_region_manager_->RemoveHitRegionsInRect(rect, GetState().Transform()); } } @@ -832,7 +834,8 @@ override); text_run.SetNormalizeSpace(true); // Draw the item text at the correct point. - FloatPoint location(x, y + GetFontBaseline(font_metrics)); + FloatPoint location(clampTo<float>(x), + clampTo<float>(y + GetFontBaseline(font_metrics))); double font_width = font.Width(text_run); bool use_max_width = (max_width && *max_width < font_width); @@ -861,7 +864,8 @@ text_run_paint_info.bounds = FloatRect(location.X() - font_metrics.Height() / 2, location.Y() - font_metrics.Ascent() - font_metrics.LineGap(), - width + font_metrics.Height(), font_metrics.LineSpacing()); + clampTo<float>(width + font_metrics.Height()), + font_metrics.LineSpacing()); if (paint_type == CanvasRenderingContext2DState::kStrokePaintType) InflateStrokeRect(text_run_paint_info.bounds); @@ -871,7 +875,8 @@ DrawingCanvas()->translate(location.X(), location.Y()); // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) // still work. - DrawingCanvas()->scale((font_width > 0 ? (width / font_width) : 0), 1); + DrawingCanvas()->scale( + (font_width > 0 ? clampTo<float>(width / font_width) : 0), 1); location = FloatPoint(); }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc index 0df1100..1183be5 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
@@ -137,7 +137,7 @@ } void CanvasRenderingContext2DState::SetLineDashOffset(double offset) { - line_dash_offset_ = offset; + line_dash_offset_ = clampTo<float>(offset); line_dash_dirty_ = true; } @@ -147,6 +147,9 @@ // number of elements is odd if (dash.size() % 2) line_dash_.AppendVector(dash); + // clamp the double values to float + std::transform(line_dash_.begin(), line_dash_.end(), line_dash_.begin(), + [](double d) { return clampTo<float>(d); }); line_dash_dirty_ = true; } @@ -402,7 +405,8 @@ SkDrawLooper* CanvasRenderingContext2DState::ShadowOnlyDrawLooper() const { if (!shadow_only_draw_looper_) { DrawLooperBuilder draw_looper_builder; - draw_looper_builder.AddShadow(shadow_offset_, shadow_blur_, shadow_color_, + draw_looper_builder.AddShadow(shadow_offset_, clampTo<float>(shadow_blur_), + shadow_color_, DrawLooperBuilder::kShadowIgnoresTransforms, DrawLooperBuilder::kShadowRespectsAlpha); shadow_only_draw_looper_ = draw_looper_builder.DetachDrawLooper(); @@ -414,7 +418,8 @@ const { if (!shadow_and_foreground_draw_looper_) { DrawLooperBuilder draw_looper_builder; - draw_looper_builder.AddShadow(shadow_offset_, shadow_blur_, shadow_color_, + draw_looper_builder.AddShadow(shadow_offset_, clampTo<float>(shadow_blur_), + shadow_color_, DrawLooperBuilder::kShadowIgnoresTransforms, DrawLooperBuilder::kShadowRespectsAlpha); draw_looper_builder.AddUnmodifiedContent(); @@ -455,17 +460,17 @@ } void CanvasRenderingContext2DState::SetShadowOffsetX(double x) { - shadow_offset_.SetWidth(x); + shadow_offset_.SetWidth(clampTo<float>(x)); ShadowParameterChanged(); } void CanvasRenderingContext2DState::SetShadowOffsetY(double y) { - shadow_offset_.SetHeight(y); + shadow_offset_.SetHeight(clampTo<float>(y)); ShadowParameterChanged(); } void CanvasRenderingContext2DState::SetShadowBlur(double shadow_blur) { - shadow_blur_ = shadow_blur; + shadow_blur_ = clampTo<float>(shadow_blur); ShadowParameterChanged(); }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h index 122c85f..92afb24 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/platform/graphics/paint/paint_filter.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h" #include "third_party/blink/renderer/platform/transforms/affine_transform.h" +#include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -127,7 +128,7 @@ TextBaseline GetTextBaseline() const { return text_baseline_; } void SetLineWidth(double line_width) { - stroke_flags_.setStrokeWidth(line_width); + stroke_flags_.setStrokeWidth(clampTo<float>(line_width)); } double LineWidth() const { return stroke_flags_.getStrokeWidth(); } @@ -146,7 +147,7 @@ } void SetMiterLimit(double miter_limit) { - stroke_flags_.setStrokeMiter(miter_limit); + stroke_flags_.setStrokeMiter(clampTo<float>(miter_limit)); } double MiterLimit() const { return stroke_flags_.getStrokeMiter(); }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc index 44918ed..e345cb0 100644 --- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc +++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
@@ -41,6 +41,9 @@ // The number of seconds to jump when double tapping. constexpr int kNumberOfSecondsToJump = 10; +// The CSS class to add to hide the element. +const char kHiddenClassName[] = "hidden"; + } // namespace. namespace blink { @@ -166,6 +169,10 @@ MediaElement().TogglePlayState(); + // If we triggered a play event then we should quickly hide the button. + if (!MediaElement().paused()) + SetIsDisplayed(false); + MaybeRecordInteracted(); UpdateDisplayType(); } @@ -284,6 +291,14 @@ *internal_button_, WebSize(kInnerButtonSize, kInnerButtonSize)); } +void MediaControlOverlayPlayButtonElement::SetIsDisplayed(bool displayed) { + if (displayed == displayed_) + return; + + SetClass(kHiddenClassName, !displayed); + displayed_ = displayed; +} + void MediaControlOverlayPlayButtonElement::TapTimerFired(TimerBase*) { if (tap_was_touch_event_.value()) GetMediaControls().MaybeToggleControlsFromTap();
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h index c833650..6b429b0 100644 --- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h +++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h
@@ -32,6 +32,8 @@ WebSize GetSizeOrDefault() const final; + void SetIsDisplayed(bool); + void Trace(blink::Visitor*) override; protected: @@ -88,6 +90,8 @@ Member<HTMLDivElement> internal_button_; Member<AnimatedArrow> left_jump_arrow_; Member<AnimatedArrow> right_jump_arrow_; + + bool displayed_ = true; }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc index 824842e3..181f760 100644 --- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc +++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -902,12 +902,23 @@ return panel_->IsOpaque(); } +void MediaControlsImpl::MaybeShowOverlayPlayButton() { + if (overlay_play_button_) + overlay_play_button_->SetIsDisplayed(true); +} + void MediaControlsImpl::MakeOpaque() { ShowCursor(); panel_->MakeOpaque(); + MaybeShowOverlayPlayButton(); } void MediaControlsImpl::MakeOpaqueFromPointerEvent() { + // If we have quickly hidden the controls we should always show them when we + // have a pointer event. If the controls are hidden the play button will + // remain hidden. + MaybeShowOverlayPlayButton(); + if (IsVisible()) return;
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h index 9ff2a7d..6adad597 100644 --- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h +++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
@@ -234,6 +234,9 @@ void MakeTransparent(); bool IsVisible() const; + // If the overlay play button is present then make sure it is displayed. + void MaybeShowOverlayPlayButton(); + void UpdatePlayState(); enum HideBehaviorFlags {
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css index 3c9a6fe..2a1db59 100644 --- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css +++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
@@ -298,6 +298,13 @@ background: transparent; margin-bottom: -40px; position: relative; + opacity: 1; + transition: opacity 0.25s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +video::-webkit-media-controls-overlay-play-button.hidden { + opacity: 0; + transition: opacity 0.75s cubic-bezier(0.25, 0.1, 0.25, 1); } input[pseudo="-webkit-media-controls-overlay-play-button" i]::-internal-media-controls-overlay-play-button-internal {
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css index 8d5d498..612d467 100644 --- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css +++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css
@@ -8,7 +8,7 @@ width: 56px; top: 50%; left: 50%; - margin-top: -36px; + margin-top: -37px; margin-left: -28px; overflow: hidden; } @@ -146,19 +146,19 @@ #cutoff-1 { top: 0; bottom: 50%; - margin-bottom: 36px; + margin-bottom: 37px; } #cutoff-2 { top: 50%; - bottom: 19px; - margin-top: 20px; + bottom: 21px; + margin-top: 19px; } #cutoff-3, #cutoff-4 { top: 0; - bottom: 19px; + bottom: 21px; } #cutoff-3 {
diff --git a/third_party/blink/renderer/platform/OWNERS b/third_party/blink/renderer/platform/OWNERS index 55624d9..7e45d47 100644 --- a/third_party/blink/renderer/platform/OWNERS +++ b/third_party/blink/renderer/platform/OWNERS
@@ -1,3 +1,4 @@ +bokan@chromium.org dgozman@chromium.org drott@chromium.org eae@chromium.org
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc index eb851ac..20bc2116 100644 --- a/third_party/blink/renderer/platform/exported/platform.cc +++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -37,7 +37,6 @@ #include "base/trace_event/memory_dump_manager.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/interface_provider.h" -#include "third_party/blink/public/common/origin_trials/trial_policy.h" #include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_cache_storage.h" #include "third_party/blink/public/platform/modules/webmidi/web_midi_accessor.h" @@ -53,7 +52,6 @@ #include "third_party/blink/public/platform/web_socket_handshake_throttle.h" #include "third_party/blink/public/platform/web_storage_namespace.h" #include "third_party/blink/public/platform/web_thread.h" -#include "third_party/blink/public/platform/web_trial_token_validator.h" #include "third_party/blink/renderer/platform/exported/web_clipboard_impl.h" #include "third_party/blink/renderer/platform/font_family_names.h" #include "third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h" @@ -278,10 +276,6 @@ return nullptr; } -std::unique_ptr<WebTrialTokenValidator> Platform::CreateTrialTokenValidator() { - return nullptr; -} - // TODO(slangley): Remove this once we can get pepper to use mojo directly. WebClipboard* Platform::Clipboard() { DEFINE_STATIC_LOCAL(WebClipboardImpl, clipboard, ());
diff --git a/third_party/blink/renderer/platform/file_metadata.cc b/third_party/blink/renderer/platform/file_metadata.cc index bb4b665..31a9f9a 100644 --- a/third_party/blink/renderer/platform/file_metadata.cc +++ b/third_party/blink/renderer/platform/file_metadata.cc
@@ -30,11 +30,15 @@ #include "third_party/blink/renderer/platform/file_metadata.h" +#include "base/optional.h" #include "net/base/filename_util.h" +#include "third_party/blink/public/mojom/file/file_utilities.mojom-blink.h" #include "third_party/blink/public/platform/file_path_conversion.h" +#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_file_info.h" -#include "third_party/blink/public/platform/web_file_utilities.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" +#include "third_party/blink/renderer/platform/wtf/thread_specific.h" #include "url/gurl.h" namespace blink { @@ -56,13 +60,26 @@ } bool GetFileMetadata(const String& path, FileMetadata& metadata) { - WebFileInfo web_file_info; - if (!Platform::Current()->GetFileUtilities()->GetFileInfo(path, - web_file_info)) + DEFINE_THREAD_SAFE_STATIC_LOCAL( + ThreadSpecific<mojom::blink::FileUtilitiesHostPtr>, thread_specific_host, + ()); + auto& host = *thread_specific_host; + if (!host) { + Platform::Current()->GetInterfaceProvider()->GetInterface( + mojo::MakeRequest(&host)); + } + + base::Optional<base::File::Info> file_info; + if (!host->GetFileInfo(WebStringToFilePath(path), &file_info) || !file_info) return false; - metadata.modification_time = web_file_info.modification_time; - metadata.length = web_file_info.length; - metadata.type = static_cast<FileMetadata::Type>(web_file_info.type); + + // Blink now expects NaN as uninitialized/null Date. + metadata.modification_time = file_info->last_modified.is_null() + ? std::numeric_limits<double>::quiet_NaN() + : file_info->last_modified.ToJsTime(); + metadata.length = file_info->size; + metadata.type = file_info->is_directory ? FileMetadata::kTypeDirectory + : FileMetadata::kTypeFile; return true; }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc index 8d0601a..0c1b065 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
@@ -51,13 +51,13 @@ // format). if (!info.colorSpace()) return; - if (SkColorSpace::Equals(info.colorSpace(), SkColorSpace::MakeSRGB().get())) - color_space_ = kSRGBCanvasColorSpace; - else if (SkColorSpace::Equals( - info.colorSpace(), - SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, - SkColorSpace::kRec2020_Gamut) - .get())) + // kSRGBCanvasColorSpace covers sRGB and linear-rgb. We need to check for + // Rec2020 and P3. + if (SkColorSpace::Equals( + info.colorSpace(), + SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, + SkColorSpace::kRec2020_Gamut) + .get())) color_space_ = kRec2020CanvasColorSpace; else if (SkColorSpace::Equals( info.colorSpace(),
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc index c068b64..33f93792 100644 --- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc +++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -1548,30 +1548,35 @@ IncrementalMarkingScope scope(ThreadState::Current()); ObjectRegistry registry; RegisteringObject* object = new RegisteringObject(®istry); + + // Clear any objects that have been added to the regular marking worklist in + // the process of calling the constructor. EXPECT_FALSE(scope.marking_worklist()->IsGlobalEmpty()); - MarkingItem item; - EXPECT_TRUE(scope.marking_worklist()->Pop(WorklistTaskId::MainThread, &item)); - RegisteringObject* recorded_object = - reinterpret_cast<RegisteringObject*>(item.object); - // In this case, the Member write barrier will also add the object to the - // regular marking stack. The not-fully-constructed object marking stack is - // only needed when going through custom off-heap data structures that require - // eager tracing. - EXPECT_EQ(object, recorded_object); - // In this example, there are two more objects on the callback stack: the - // backing store for which a write barrier triggers after rehashing, and - // a write barrier for the Member assignment (which might not always happen). - scope.marking_worklist()->Pop(WorklistTaskId::MainThread, &item); - scope.marking_worklist()->Pop(WorklistTaskId::MainThread, &item); - // The mixin object should be on the not-fully-constructed object marking - // stack. + MarkingItem marking_item; + while (scope.marking_worklist()->Pop(WorklistTaskId::MainThread, + &marking_item)) { + HeapObjectHeader* header = + HeapObjectHeader::FromPayload(marking_item.object); + if (header->IsMarked()) + header->Unmark(); + } + EXPECT_TRUE(scope.marking_worklist()->IsGlobalEmpty()); + EXPECT_FALSE(scope.not_fully_constructed_worklist()->IsGlobalEmpty()); - NotFullyConstructedItem item2; - EXPECT_TRUE(scope.not_fully_constructed_worklist()->Pop( - WorklistTaskId::MainThread, &item2)); - RegisteringObject* recorded_object2 = - reinterpret_cast<RegisteringObject*>(item2); - EXPECT_EQ(object, recorded_object2); + NotFullyConstructedItem partial_item; + bool found_mixin_object = false; + // The same object may be on the marking work list because of expanding + // and rehashing of the backing store in the registry. + while (scope.not_fully_constructed_worklist()->Pop(WorklistTaskId::MainThread, + &partial_item)) { + if (object == partial_item) + found_mixin_object = true; + HeapObjectHeader* header = HeapObjectHeader::FromPayload(partial_item); + if (header->IsMarked()) + header->Unmark(); + } + EXPECT_TRUE(found_mixin_object); + EXPECT_TRUE(scope.not_fully_constructed_worklist()->IsGlobalEmpty()); } TEST(IncrementalMarkingTest, OverrideAfterMixinConstruction) {
diff --git a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni index 8c86c58..b69eb6f9 100644 --- a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni +++ b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -3,6 +3,7 @@ # found in the LICENSE file. typemaps = [ + "//mojo/public/cpp/base/file_info.typemap", "//mojo/public/cpp/base/file_path.typemap", "//mojo/common/values.typemap", "//third_party/blink/renderer/core/messaging/blink_cloneable_message.typemap",
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index 03891aa..a4e33638 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -371,37 +371,37 @@ kShortIdlePeriodDurationSampleCount, kShortIdlePeriodDurationPercentile), current_use_case(UseCase::kNone, - "MainThreadScheduler.UseCase", + "Scheduler.UseCase", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, UseCaseToString), longest_jank_free_task_duration( base::TimeDelta(), - "MainThreadScheduler.LongestJankFreeTaskDuration", + "Scheduler.LongestJankFreeTaskDuration", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, TimeDeltaToMilliseconds), renderer_pause_count(0, - "MainThreadScheduler.PauseCount", + "Scheduler.PauseCount", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_), navigation_task_expected_count( 0, - "MainThreadScheduler.NavigationTaskExpectedCount", + "Scheduler.NavigationTaskExpectedCount", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_), expensive_task_policy(ExpensiveTaskPolicy::kRun, - "MainThreadScheduler.ExpensiveTaskPolicy", + "Scheduler.ExpensiveTaskPolicy", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, ExpensiveTaskPolicyToString), rail_mode_for_tracing(current_policy.rail_mode(), - "MainThreadScheduler.RAILMode", + "Scheduler.RAILMode", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, RAILModeToString), renderer_hidden(false, - "MainThreadScheduler.Hidden", + "Scheduler.Hidden", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, HiddenStateToString), @@ -412,7 +412,7 @@ BackgroundStateToString), keep_active_fetch_or_worker( false, - "MainThreadScheduler.KeepRendererActive", + "Scheduler.KeepRendererActive", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), @@ -429,71 +429,71 @@ YesNoStateToString), loading_task_estimated_cost( base::TimeDelta(), - "MainThreadScheduler.LoadingTaskEstimatedCostMs", + "Scheduler.LoadingTaskEstimatedCostMs", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, TimeDeltaToMilliseconds), timer_task_estimated_cost( base::TimeDelta(), - "MainThreadScheduler.TimerTaskEstimatedCostMs", + "Scheduler.TimerTaskEstimatedCostMs", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, TimeDeltaToMilliseconds), loading_tasks_seem_expensive( false, - "MainThreadScheduler.LoadingTasksSeemExpensive", + "Scheduler.LoadingTasksSeemExpensive", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), timer_tasks_seem_expensive( false, - "MainThreadScheduler.TimerTasksSeemExpensive", + "Scheduler.TimerTasksSeemExpensive", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), touchstart_expected_soon(false, - "MainThreadScheduler.TouchstartExpectedSoon", + "Scheduler.TouchstartExpectedSoon", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), have_seen_a_begin_main_frame( false, - "MainThreadScheduler.HasSeenBeginMainFrame", + "Scheduler.HasSeenBeginMainFrame", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), have_reported_blocking_intervention_in_current_policy( false, - "MainThreadScheduler.HasReportedBlockingInterventionInCurrentPolicy", + "Scheduler.HasReportedBlockingInterventionInCurrentPolicy", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), have_reported_blocking_intervention_since_navigation( false, - "MainThreadScheduler.HasReportedBlockingInterventionSinceNavigation", + "Scheduler.HasReportedBlockingInterventionSinceNavigation", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), has_visible_render_widget_with_touch_handler( false, - "MainThreadScheduler.HasVisibleRenderWidgetWithTouchHandler", + "Scheduler.HasVisibleRenderWidgetWithTouchHandler", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), begin_frame_not_expected_soon( false, - "MainThreadScheduler.BeginFrameNotExpectedSoon", + "Scheduler.BeginFrameNotExpectedSoon", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), in_idle_period_for_testing( false, - "MainThreadScheduler.InIdlePeriod", + "Scheduler.InIdlePeriod", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), use_virtual_time(false, - "MainThreadScheduler.UseVirtualTime", + "Scheduler.UseVirtualTime", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), @@ -504,17 +504,17 @@ AudioPlayingStateToString), compositor_will_send_main_frame_not_expected( false, - "MainThreadScheduler.CompositorWillSendMainFrameNotExpected", + "Scheduler.CompositorWillSendMainFrameNotExpected", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), has_navigated(false, - "MainThreadScheduler.HasNavigated", + "Scheduler.HasNavigated", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), pause_timers_for_webview(false, - "MainThreadScheduler.PauseTimersForWebview", + "Scheduler.PauseTimersForWebview", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), @@ -529,7 +529,7 @@ RendererProcessTypeToString), task_description_for_tracing( base::nullopt, - "MainThreadScheduler.MainThreadTask", + "Scheduler.MainThreadTask", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, OptionalTaskDescriptionToString), @@ -546,48 +546,48 @@ MainThreadSchedulerImpl* main_thread_scheduler_impl) : awaiting_touch_start_response( false, - "MainThreadScheduler.AwaitingTouchstartResponse", + "Scheduler.AwaitingTouchstartResponse", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), in_idle_period(false, - "MainThreadScheduler.InIdlePeriod", + "Scheduler.InIdlePeriod", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), begin_main_frame_on_critical_path( false, - "MainThreadScheduler.BeginMainFrameOnCriticalPath", + "Scheduler.BeginMainFrameOnCriticalPath", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), last_gesture_was_compositor_driven( false, - "MainThreadScheduler.LastGestureWasCompositorDriven", + "Scheduler.LastGestureWasCompositorDriven", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), default_gesture_prevented( true, - "MainThreadScheduler.DefaultGesturePrevented", + "Scheduler.DefaultGesturePrevented", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), have_seen_a_potentially_blocking_gesture( false, - "MainThreadScheduler.HaveSeenPotentiallyBlockingGesture", + "Scheduler.HaveSeenPotentiallyBlockingGesture", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), waiting_for_meaningful_paint( false, - "MainThreadScheduler.WaitingForMeaningfulPaint", + "Scheduler.WaitingForMeaningfulPaint", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString), have_seen_input_since_navigation( false, - "MainThreadScheduler.HaveSeenInputSinceNavigation", + "Scheduler.HaveSeenInputSinceNavigation", main_thread_scheduler_impl, &main_thread_scheduler_impl->tracing_controller_, YesNoStateToString) {}
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc index 50eab23..d812f284f 100644 --- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc +++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -174,10 +174,6 @@ return old_platform_ ? old_platform_->Clipboard() : nullptr; } -WebFileUtilities* TestingPlatformSupport::GetFileUtilities() { - return old_platform_ ? old_platform_->GetFileUtilities() : nullptr; -} - WebIDBFactory* TestingPlatformSupport::IdbFactory() { return old_platform_ ? old_platform_->IdbFactory() : nullptr; }
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.h b/third_party/blink/renderer/platform/testing/testing_platform_support.h index 33ad9466..b0447e34 100644 --- a/third_party/blink/renderer/platform/testing/testing_platform_support.h +++ b/third_party/blink/renderer/platform/testing/testing_platform_support.h
@@ -96,7 +96,6 @@ WebThread* CurrentThread() override; WebBlobRegistry* GetBlobRegistry() override; WebClipboard* Clipboard() override; - WebFileUtilities* GetFileUtilities() override; WebIDBFactory* IdbFactory() override; WebURLLoaderMockFactory* GetURLLoaderMockFactory() override; std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory()
diff --git a/third_party/blink/renderer/platform/wtf/BUILD.gn b/third_party/blink/renderer/platform/wtf/BUILD.gn index e550bd2..d160ed34 100644 --- a/third_party/blink/renderer/platform/wtf/BUILD.gn +++ b/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -116,7 +116,6 @@ "math_extras.h", "noncopyable.h", "not_found.h", - "optional.h", "process_metrics.h", "ref_counted.h", "ref_vector.h", @@ -321,7 +320,6 @@ "hash_set_test.cc", "list_hash_set_test.cc", "math_extras_test.cc", - "optional_test.cc", "ref_ptr_test.cc", "saturated_arithmetic_test.cc", "scoped_logger_test.cc",
diff --git a/third_party/blink/renderer/platform/wtf/README.md b/third_party/blink/renderer/platform/wtf/README.md index be3012ef..92a96a90 100644 --- a/third_party/blink/renderer/platform/wtf/README.md +++ b/third_party/blink/renderer/platform/wtf/README.md
@@ -72,7 +72,6 @@ [Time.h], [CryptographicallyRandomNumber.h], [AutoReset.h], - [Optional.h] ## History @@ -115,7 +114,6 @@ [Time.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/time.h [CryptographicallyRandomNumber.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/cryptographically_random_number.h [AutoReset.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/auto_reset.h -[Optional.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/optional.h [1]: https://chromium.googlesource.com/chromium/src/+/e372c152fc6e57743ebc508fe17f6eb131b4ff8d [2]: https://chromium.googlesource.com/chromium/src/+/547a6ca360a56fbee3d5ea4a71ba18f91622455c [3]: https://chromium.googlesource.com/chromium/src/+/478890427ee03fd88e6f0f58ee8220512044bed9/third_party/WebKit/WebCore/kwq/KWQAssertions.h
diff --git a/third_party/blink/renderer/platform/wtf/optional.h b/third_party/blink/renderer/platform/wtf/optional.h deleted file mode 100644 index 83184cd..0000000 --- a/third_party/blink/renderer/platform/wtf/optional.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_OPTIONAL_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_OPTIONAL_H_ - -#include "base/optional.h" -#include "base/stl_util.h" - -namespace WTF { - -// WTF::Optional is base::Optional. See base/optional.h for documentation. -// -// A clang plugin enforces that garbage collected types are not allocated -// outside of the heap. GC containers such as HeapVector are allowed though. -template <typename T> -using Optional = base::Optional<T>; - -constexpr base::nullopt_t nullopt = base::nullopt; -constexpr base::in_place_t in_place = base::in_place; - -template <typename T> -constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) { - return base::make_optional(std::forward<T>(value)); -} - -template <typename T> -T* OptionalOrNullptr(Optional<T>& optional) { - return base::OptionalOrNullptr(optional); -} - -} // namespace WTF - -using WTF::Optional; - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_OPTIONAL_H_
diff --git a/third_party/blink/renderer/platform/wtf/optional_test.cc b/third_party/blink/renderer/platform/wtf/optional_test.cc deleted file mode 100644 index 8f8424a..0000000 --- a/third_party/blink/renderer/platform/wtf/optional_test.cc +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/platform/wtf/optional.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace WTF { -namespace { - -struct IntBox { - IntBox(int n) : number(n) {} - int number; -}; - -class DestructionNotifier { - public: - DestructionNotifier(bool& flag) : flag_(flag) {} - ~DestructionNotifier() { flag_ = true; } - - private: - bool& flag_; -}; - -TEST(OptionalTest, BooleanTest) { - Optional<int> optional; - EXPECT_FALSE(optional); - optional.emplace(0); - EXPECT_TRUE(optional); -} - -TEST(OptionalTest, Dereference) { - Optional<int> optional; - optional.emplace(1); - EXPECT_EQ(1, *optional); - - Optional<IntBox> optional_intbox; - optional_intbox.emplace(42); - EXPECT_EQ(42, optional_intbox->number); -} - -TEST(OptionalTest, DestructorCalled) { - // Destroying a disengaged optional shouldn't do anything. - { Optional<DestructionNotifier> optional; } - - // Destroying an engaged optional should call the destructor. - bool is_destroyed = false; - { - Optional<DestructionNotifier> optional; - optional.emplace(is_destroyed); - EXPECT_FALSE(is_destroyed); - } - EXPECT_TRUE(is_destroyed); -} - -} // namespace -} // namespace WTF
diff --git a/third_party/blink/tools/blinkpy/common/system/executive.py b/third_party/blink/tools/blinkpy/common/system/executive.py index 833ac9d..73db9a9 100644 --- a/third_party/blink/tools/blinkpy/common/system/executive.py +++ b/third_party/blink/tools/blinkpy/common/system/executive.py
@@ -111,13 +111,8 @@ Will fail silently if pid does not exist or insufficient permissions. """ - # This method behaves differently on Windows and Linux. On Windows, it - # kills the process as well as all of its subprocesses (because of the - # '/t' flag). Some call sites depend on this behaviour (e.g. to kill all - # worker processes of wptserve on Windows). - # TODO(robertma): Replicate the behaviour on POSIX by calling setsid() - # in Popen's preexec_fn hook, and perhaps rename the method to - # kill_process_tree. + # According to http://docs.python.org/library/os.html + # os.kill isn't available on Windows. if sys.platform == 'win32': command = ['taskkill.exe', '/f', '/t', '/pid', pid] # taskkill will exit 128 if the process is not found. We should log.
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json b/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json index a6f6d37..d595213 100644 --- a/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json +++ b/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json
@@ -3,7 +3,6 @@ "ws_doc_root": null, "check_subdomains": false, "server_host": "127.0.0.1", - "log_level": "info", "ports": { "http": [8001, 8081], "https": [8444],
diff --git a/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py b/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py index 9cd0ec1..a2fd36d 100644 --- a/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py +++ b/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py
@@ -395,10 +395,6 @@ test_inputs = [] for _ in xrange(iterations): - # TODO(crbug.com/650747): We may want to switch the two loops below - # to make the behavior consistent with gtest runner (--gtest_repeat - # is an alias for --repeat-each now), which looks like "ABCABCABC". - # And remember to update the help text when we do so. for test in tests_to_run: for _ in xrange(repeat_each): test_inputs.append(self._test_input_for_file(test))
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py index d80c5df..b50962b 100644 --- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py +++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests.py
@@ -342,6 +342,7 @@ "'unexpected' == Ignore any tests that had unexpected results on the bot.")), optparse.make_option( '--iterations', + '--gtest_repeat', type='int', default=1, help='Number of times to run the set of tests (e.g. ABCABCABC)'), @@ -378,7 +379,6 @@ help='Output per-test profile information, using the specified profiler.'), optparse.make_option( '--repeat-each', - '--gtest_repeat', type='int', default=1, help='Number of times to run each test (e.g. AAABBBCCC)'),
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py index e5359bbf..cab57c4 100644 --- a/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py +++ b/third_party/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
@@ -401,12 +401,12 @@ def test_gtest_repeat(self): tests_to_run = ['passes/image.html', 'passes/text.html'] tests_run = get_tests_run(['--gtest_repeat', '2', '--order', 'natural'] + tests_to_run) - self.assertEqual(tests_run, ['passes/image.html', 'passes/image.html', 'passes/text.html', 'passes/text.html']) + self.assertEqual(tests_run, ['passes/image.html', 'passes/text.html', 'passes/image.html', 'passes/text.html']) - def test_gtest_repeat_overrides_repeat_each(self): + def test_gtest_repeat_overrides_iterations(self): tests_to_run = ['passes/image.html', 'passes/text.html'] - tests_run = get_tests_run(['--repeat-each', '4', '--gtest_repeat', '2', '--order', 'natural'] + tests_to_run) - self.assertEqual(tests_run, ['passes/image.html', 'passes/image.html', 'passes/text.html', 'passes/text.html']) + tests_run = get_tests_run(['--iterations', '4', '--gtest_repeat', '2', '--order', 'natural'] + tests_to_run) + self.assertEqual(tests_run, ['passes/image.html', 'passes/text.html', 'passes/image.html', 'passes/text.html']) def test_ignore_flag(self): # Note that passes/image.html is expected to be run since we specified it directly.
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py b/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py index 1785b705..9f96f04 100644 --- a/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py +++ b/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py
@@ -149,8 +149,8 @@ def _spawn_process(self): _log.debug('Starting %s server, cmd="%s"', self._name, str(self._start_cmd)) - # Apache2 (or httpd) spawns a background daemon and immediately exits. - retval = self._executive.run_command(self._start_cmd, return_exit_code=True) + self._process = self._executive.popen(self._start_cmd) + retval = self._process.returncode if retval: raise server_base.ServerError('Failed to start %s: %s' % (self._name, retval))
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py index 074aaa0..3129e9bf 100644 --- a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py +++ b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
@@ -50,6 +50,7 @@ if self._port_obj.host.filesystem.exists(path_to_ws_handlers): start_cmd += ['--ws_doc_root', path_to_ws_handlers] + self._stdout = self._stderr = self._executive.DEVNULL # TODO(burnik): We should stop setting the CWD once WPT can be run without it. self._cwd = path_to_wpt_root self._env = port_obj.host.environ.copy() @@ -60,41 +61,36 @@ if datetime.date.today() > expiration_date - datetime.timedelta(30): logging.getLogger(__name__).error( 'Pre-generated keys and certificates are going to be expired at %s.' - ' Please re-generate them by following steps in %s/README.chromium.', - expiration_date.strftime('%b %d %Y'), path_to_wpt_support) + ' Please re-generate them by following steps in %s/README.chromium.' + % (expiration_date.strftime('%b %d %Y'), path_to_wpt_support)) def _stop_running_server(self): - if not self._wait_for_action(self._check_and_kill): - # This is mostly for POSIX systems. We send SIGINT in - # _check_and_kill() and here we use SIGKILL. - self._executive.kill_process(self._pid) - + self._wait_for_action(self._check_and_kill_wptserve) if self._filesystem.exists(self._pid_file): self._filesystem.remove(self._pid_file) - def _check_and_kill(self): + def _check_and_kill_wptserve(self): """Tries to kill wptserve. Returns True if it appears to be not running. Or, if it appears to be running, tries to kill the process and returns False. """ - if not (self._pid and self._process): - _log.warning('No process object or PID. wptserve has not started.') - return True - - # Polls the process in case it has died; otherwise, the process might be - # defunct and check_running_pid can still succeed. - if self._process.poll() or not self._executive.check_running_pid(self._pid): + if not (self._pid and self._executive.check_running_pid(self._pid)): _log.debug('pid %d is not running', self._pid) return True _log.debug('pid %d is running, killing it', self._pid) - # kill_process() kills the whole process tree on Windows, but not on - # POSIX, so we send SIGINT instead to allow wptserve to exit gracefully. + # Executive.kill_process appears to not to effectively kill the + # wptserve processes on Linux (and presumably other platforms). if self._platform.is_win(): self._executive.kill_process(self._pid) else: self._executive.interrupt(self._pid) + # According to Popen.wait(), this can deadlock when using stdout=PIPE or + # stderr=PIPE. We're using DEVNULL for both so that should not occur. + if self._process is not None: + self._process.wait() + return False
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py index 4306605..a49487f 100644 --- a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py +++ b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
@@ -4,9 +4,8 @@ import logging -from blinkpy.common.host_mock import MockHost -from blinkpy.common.system.executive_mock import MockProcess from blinkpy.common.system.log_testing import LoggingTestCase +from blinkpy.common.host_mock import MockHost from blinkpy.web_tests.port import test from blinkpy.web_tests.servers.wptserve import WPTServe @@ -55,7 +54,6 @@ server = WPTServe(test_port, '/log_file_dir') server._pid_file = '/tmp/pidfile' server._spawn_process = lambda: 4 - server._process = MockProcess() server._is_server_running_on_all_ports = lambda: True # Simulate a process that never gets killed.
diff --git a/third_party/closure_compiler/externs/pending.js b/third_party/closure_compiler/externs/pending.js index 8c11821c..0f0e2f3b 100644 --- a/third_party/closure_compiler/externs/pending.js +++ b/third_party/closure_compiler/externs/pending.js
@@ -9,13 +9,6 @@ */ /** - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture - * @param {number} pointerId - * TODO(dpapad): Remove this once it is added to Closure Compiler itself. - */ -Element.prototype.setPointerCapture = function(pointerId) {}; - -/** * @see https://drafts.fxtf.org/geometry-1/#domrectreadonly * TODO(scottchen): Remove this once it is added to Closure Compiler itself. */ @@ -77,4 +70,4 @@ /** @param {Element} target */ unobserve(target) {} -} \ No newline at end of file +}
diff --git a/third_party/fuchsia-sdk/BUILD.gn b/third_party/fuchsia-sdk/BUILD.gn index b25fb8c..d1cc8ac 100644 --- a/third_party/fuchsia-sdk/BUILD.gn +++ b/third_party/fuchsia-sdk/BUILD.gn
@@ -12,6 +12,12 @@ fuchsia_sdk_pkg("async") { sources = [ + "include/lib/async/dispatcher.h", + "include/lib/async/receiver.h", + "include/lib/async/task.h", + "include/lib/async/time.h", + "include/lib/async/trap.h", + "include/lib/async/wait.h", "ops.c", ] } @@ -20,6 +26,9 @@ fuchsia_sdk_pkg("async_default") { name = "async-default" libs = [ "async-default" ] + sources = [ + "include/lib/async/default.h", + ] } fuchsia_sdk_pkg("zx") { @@ -29,6 +38,29 @@ "eventpair.cpp", "fifo.cpp", "guest.cpp", + "include/lib/zx/bti.h", + "include/lib/zx/channel.h", + "include/lib/zx/event.h", + "include/lib/zx/eventpair.h", + "include/lib/zx/fifo.h", + "include/lib/zx/guest.h", + "include/lib/zx/handle.h", + "include/lib/zx/interrupt.h", + "include/lib/zx/job.h", + "include/lib/zx/log.h", + "include/lib/zx/object.h", + "include/lib/zx/object_traits.h", + "include/lib/zx/pmt.h", + "include/lib/zx/port.h", + "include/lib/zx/process.h", + "include/lib/zx/resource.h", + "include/lib/zx/socket.h", + "include/lib/zx/task.h", + "include/lib/zx/thread.h", + "include/lib/zx/time.h", + "include/lib/zx/timer.h", + "include/lib/zx/vmar.h", + "include/lib/zx/vmo.h", "interrupt.cpp", "job.cpp", "log.cpp", @@ -55,6 +87,16 @@ "decoding.cpp", "encoding.cpp", "formatting.cpp", + "include/lib/fidl/coding.h", + "include/lib/fidl/cpp", + "include/lib/fidl/cpp/builder.h", + "include/lib/fidl/cpp/message.h", + "include/lib/fidl/cpp/message_buffer.h", + "include/lib/fidl/cpp/message_builder.h", + "include/lib/fidl/cpp/message_part.h", + "include/lib/fidl/cpp/string_view.h", + "include/lib/fidl/cpp/vector_view.h", + "include/lib/fidl/internal.h", "message.cpp", "message_buffer.cpp", "message_builder.cpp", @@ -72,6 +114,37 @@ "coding_traits.cc", "decoder.cc", "encoder.cc", + "include/lib/fidl/cpp/array.h", + "include/lib/fidl/cpp/binding.h", + "include/lib/fidl/cpp/binding_set.h", + "include/lib/fidl/cpp/clone.h", + "include/lib/fidl/cpp/coding_traits.h", + "include/lib/fidl/cpp/comparison.h", + "include/lib/fidl/cpp/decoder.h", + "include/lib/fidl/cpp/encoder.h", + "include/lib/fidl/cpp/interface_handle.h", + "include/lib/fidl/cpp/interface_ptr.h", + "include/lib/fidl/cpp/interface_ptr_set.h", + "include/lib/fidl/cpp/interface_request.h", + "include/lib/fidl/cpp/internal", + "include/lib/fidl/cpp/internal/header.h", + "include/lib/fidl/cpp/internal/implementation.h", + "include/lib/fidl/cpp/internal/logging.h", + "include/lib/fidl/cpp/internal/message_handler.h", + "include/lib/fidl/cpp/internal/message_reader.h", + "include/lib/fidl/cpp/internal/pending_response.h", + "include/lib/fidl/cpp/internal/proxy.h", + "include/lib/fidl/cpp/internal/proxy_controller.h", + "include/lib/fidl/cpp/internal/stub.h", + "include/lib/fidl/cpp/internal/stub_controller.h", + "include/lib/fidl/cpp/internal/synchronous_proxy.h", + "include/lib/fidl/cpp/internal/weak_stub_controller.h", + "include/lib/fidl/cpp/optional.h", + "include/lib/fidl/cpp/string.h", + "include/lib/fidl/cpp/synchronous_interface_ptr.h", + "include/lib/fidl/cpp/thread_safe_binding_set.h", + "include/lib/fidl/cpp/traits.h", + "include/lib/fidl/cpp/vector.h", "internal", "internal/logging.cc", "internal/message_handler.cc", @@ -85,3 +158,20 @@ "string.cc", ] } + +fuchsia_sdk_pkg("fdio") { + sources = [ + "include/fdio/debug.h", + "include/fdio/io.fidl.h", + "include/fdio/io.h", + "include/fdio/limits.h", + "include/fdio/module.modulemap", + "include/fdio/namespace.h", + "include/fdio/private.h", + "include/fdio/remoteio.h", + "include/fdio/socket.h", + "include/fdio/util.h", + "include/fdio/vfs.h", + "include/fdio/watcher.h", + ] +}
diff --git a/third_party/sqlite/amalgamation/rename_exports.h b/third_party/sqlite/amalgamation/rename_exports.h index ea018ac..44a75daf6 100644 --- a/third_party/sqlite/amalgamation/rename_exports.h +++ b/third_party/sqlite/amalgamation/rename_exports.h
@@ -7,347 +7,360 @@ #ifndef THIRD_PARTY_SQLITE_AMALGAMATION_RENAME_EXPORTS_H_ #define THIRD_PARTY_SQLITE_AMALGAMATION_RENAME_EXPORTS_H_ -#define sqlite3_activate_cerod chrome_sqlite3_activate_cerod // Lines 5424-5426 -#define sqlite3_activate_see chrome_sqlite3_activate_see // Lines 5414-5416 -#define sqlite3_aggregate_context chrome_sqlite3_aggregate_context // Line 4961 -#define sqlite3_aggregate_count chrome_sqlite3_aggregate_count // Line 4766 -#define sqlite3_auto_extension chrome_sqlite3_auto_extension // Line 6055 -#define sqlite3_backup_finish chrome_sqlite3_backup_finish // Line 7840 -#define sqlite3_backup_init chrome_sqlite3_backup_init // Lines 7833-7838 -#define sqlite3_backup_pagecount chrome_sqlite3_backup_pagecount // Line 7842 -#define sqlite3_backup_remaining chrome_sqlite3_backup_remaining // Line 7841 -#define sqlite3_backup_step chrome_sqlite3_backup_step // Line 7839 -#define sqlite3_bind_blob chrome_sqlite3_bind_blob // Line 3979 -#define sqlite3_bind_blob64 chrome_sqlite3_bind_blob64 // Lines 3980-3981 -#define sqlite3_bind_double chrome_sqlite3_bind_double // Line 3982 -#define sqlite3_bind_int chrome_sqlite3_bind_int // Line 3983 -#define sqlite3_bind_int64 chrome_sqlite3_bind_int64 // Line 3984 -#define sqlite3_bind_null chrome_sqlite3_bind_null // Line 3985 +#define sqlite3_activate_cerod chrome_sqlite3_activate_cerod // Lines 5376-5378 +#define sqlite3_activate_see chrome_sqlite3_activate_see // Lines 5366-5368 +#define sqlite3_aggregate_context chrome_sqlite3_aggregate_context // Line 4913 +#define sqlite3_aggregate_count chrome_sqlite3_aggregate_count // Line 4718 +#define sqlite3_auto_extension chrome_sqlite3_auto_extension // Line 6007 +#define sqlite3_backup_finish chrome_sqlite3_backup_finish // Line 7802 +#define sqlite3_backup_init chrome_sqlite3_backup_init // Lines 7795-7800 +#define sqlite3_backup_pagecount chrome_sqlite3_backup_pagecount // Line 7804 +#define sqlite3_backup_remaining chrome_sqlite3_backup_remaining // Line 7803 +#define sqlite3_backup_step chrome_sqlite3_backup_step // Line 7801 +#define sqlite3_bind_blob chrome_sqlite3_bind_blob // Line 3931 +#define sqlite3_bind_blob64 chrome_sqlite3_bind_blob64 // Lines 3932-3933 +#define sqlite3_bind_double chrome_sqlite3_bind_double // Line 3934 +#define sqlite3_bind_int chrome_sqlite3_bind_int // Line 3935 +#define sqlite3_bind_int64 chrome_sqlite3_bind_int64 // Line 3936 +#define sqlite3_bind_null chrome_sqlite3_bind_null // Line 3937 #define sqlite3_bind_parameter_count \ - chrome_sqlite3_bind_parameter_count // Line 4014 + chrome_sqlite3_bind_parameter_count // Line 3966 #define sqlite3_bind_parameter_index \ - chrome_sqlite3_bind_parameter_index // Line 4060 + chrome_sqlite3_bind_parameter_index // Line 4012 #define sqlite3_bind_parameter_name \ - chrome_sqlite3_bind_parameter_name // Line 4042 -#define sqlite3_bind_pointer chrome_sqlite3_bind_pointer // Line 3991 -#define sqlite3_bind_text chrome_sqlite3_bind_text // Line 3986 -#define sqlite3_bind_text16 chrome_sqlite3_bind_text16 // Line 3987 -#define sqlite3_bind_text64 chrome_sqlite3_bind_text64 // Lines 3988-3989 -#define sqlite3_bind_value chrome_sqlite3_bind_value // Line 3990 -#define sqlite3_bind_zeroblob chrome_sqlite3_bind_zeroblob // Line 3992 -#define sqlite3_bind_zeroblob64 chrome_sqlite3_bind_zeroblob64 // Line 3993 -#define sqlite3_blob_bytes chrome_sqlite3_blob_bytes // Line 6596 -#define sqlite3_blob_close chrome_sqlite3_blob_close // Line 6580 -#define sqlite3_blob_open chrome_sqlite3_blob_open // Lines 6524-6532 -#define sqlite3_blob_read chrome_sqlite3_blob_read // Line 6625 -#define sqlite3_blob_reopen chrome_sqlite3_blob_reopen // Line 6557 -#define sqlite3_blob_write chrome_sqlite3_blob_write // Line 6667 -#define sqlite3_busy_handler chrome_sqlite3_busy_handler // Line 2386 -#define sqlite3_busy_timeout chrome_sqlite3_busy_timeout // Line 2409 + chrome_sqlite3_bind_parameter_name // Line 3994 +#define sqlite3_bind_pointer chrome_sqlite3_bind_pointer // Line 3943 +#define sqlite3_bind_text chrome_sqlite3_bind_text // Line 3938 +#define sqlite3_bind_text16 chrome_sqlite3_bind_text16 // Line 3939 +#define sqlite3_bind_text64 chrome_sqlite3_bind_text64 // Lines 3940-3941 +#define sqlite3_bind_value chrome_sqlite3_bind_value // Line 3942 +#define sqlite3_bind_zeroblob chrome_sqlite3_bind_zeroblob // Line 3944 +#define sqlite3_bind_zeroblob64 chrome_sqlite3_bind_zeroblob64 // Line 3945 +#define sqlite3_blob_bytes chrome_sqlite3_blob_bytes // Line 6548 +#define sqlite3_blob_close chrome_sqlite3_blob_close // Line 6532 +#define sqlite3_blob_open chrome_sqlite3_blob_open // Lines 6476-6484 +#define sqlite3_blob_read chrome_sqlite3_blob_read // Line 6577 +#define sqlite3_blob_reopen chrome_sqlite3_blob_reopen // Line 6509 +#define sqlite3_blob_write chrome_sqlite3_blob_write // Line 6619 +#define sqlite3_busy_handler chrome_sqlite3_busy_handler // Line 2402 +#define sqlite3_busy_timeout chrome_sqlite3_busy_timeout // Line 2425 #define sqlite3_cancel_auto_extension \ - chrome_sqlite3_cancel_auto_extension // Line 6067 -#define sqlite3_changes chrome_sqlite3_changes // Line 2228 -#define sqlite3_clear_bindings chrome_sqlite3_clear_bindings // Line 4070 + chrome_sqlite3_cancel_auto_extension // Line 6019 +#define sqlite3_changes chrome_sqlite3_changes // Line 2244 +#define sqlite3_clear_bindings chrome_sqlite3_clear_bindings // Line 4022 #define sqlite3_close chrome_sqlite3_close // Line 331 #define sqlite3_close_v2 chrome_sqlite3_close_v2 // Line 332 #define sqlite3_collation_needed \ - chrome_sqlite3_collation_needed // Lines 5363-5367 + chrome_sqlite3_collation_needed // Lines 5315-5319 #define sqlite3_collation_needed16 \ - chrome_sqlite3_collation_needed16 // Lines 5368-5372 -#define sqlite3_column_blob chrome_sqlite3_column_blob // Line 4536 -#define sqlite3_column_bytes chrome_sqlite3_column_bytes // Line 4543 -#define sqlite3_column_bytes16 chrome_sqlite3_column_bytes16 // Line 4544 -#define sqlite3_column_count chrome_sqlite3_column_count // Line 4086 + chrome_sqlite3_collation_needed16 // Lines 5320-5324 +#define sqlite3_column_blob chrome_sqlite3_column_blob // Line 4488 +#define sqlite3_column_bytes chrome_sqlite3_column_bytes // Line 4495 +#define sqlite3_column_bytes16 chrome_sqlite3_column_bytes16 // Line 4496 +#define sqlite3_column_count chrome_sqlite3_column_count // Line 4038 #define sqlite3_column_database_name \ - chrome_sqlite3_column_database_name // Line 4164 + chrome_sqlite3_column_database_name // Line 4116 #define sqlite3_column_database_name16 \ - chrome_sqlite3_column_database_name16 // Line 4165 -#define sqlite3_column_decltype chrome_sqlite3_column_decltype // Line 4201 -#define sqlite3_column_decltype16 chrome_sqlite3_column_decltype16 // Line 4202 -#define sqlite3_column_double chrome_sqlite3_column_double // Line 4537 -#define sqlite3_column_int chrome_sqlite3_column_int // Line 4538 -#define sqlite3_column_int64 chrome_sqlite3_column_int64 // Line 4539 -#define sqlite3_column_name chrome_sqlite3_column_name // Line 4115 -#define sqlite3_column_name16 chrome_sqlite3_column_name16 // Line 4116 + chrome_sqlite3_column_database_name16 // Line 4117 +#define sqlite3_column_decltype chrome_sqlite3_column_decltype // Line 4153 +#define sqlite3_column_decltype16 chrome_sqlite3_column_decltype16 // Line 4154 +#define sqlite3_column_double chrome_sqlite3_column_double // Line 4489 +#define sqlite3_column_int chrome_sqlite3_column_int // Line 4490 +#define sqlite3_column_int64 chrome_sqlite3_column_int64 // Line 4491 +#define sqlite3_column_name chrome_sqlite3_column_name // Line 4067 +#define sqlite3_column_name16 chrome_sqlite3_column_name16 // Line 4068 #define sqlite3_column_origin_name \ - chrome_sqlite3_column_origin_name // Line 4168 + chrome_sqlite3_column_origin_name // Line 4120 #define sqlite3_column_origin_name16 \ - chrome_sqlite3_column_origin_name16 // Line 4169 -#define sqlite3_column_table_name chrome_sqlite3_column_table_name // Line 4166 + chrome_sqlite3_column_origin_name16 // Line 4121 +#define sqlite3_column_table_name chrome_sqlite3_column_table_name // Line 4118 #define sqlite3_column_table_name16 \ - chrome_sqlite3_column_table_name16 // Line 4167 -#define sqlite3_column_text chrome_sqlite3_column_text // Line 4540 -#define sqlite3_column_text16 chrome_sqlite3_column_text16 // Line 4541 -#define sqlite3_column_type chrome_sqlite3_column_type // Line 4545 -#define sqlite3_column_value chrome_sqlite3_column_value // Line 4542 -#define sqlite3_commit_hook chrome_sqlite3_commit_hook // Line 5670 + chrome_sqlite3_column_table_name16 // Line 4119 +#define sqlite3_column_text chrome_sqlite3_column_text // Line 4492 +#define sqlite3_column_text16 chrome_sqlite3_column_text16 // Line 4493 +#define sqlite3_column_type chrome_sqlite3_column_type // Line 4497 +#define sqlite3_column_value chrome_sqlite3_column_value // Line 4494 +#define sqlite3_commit_hook chrome_sqlite3_commit_hook // Line 5622 #define sqlite3_compileoption_get chrome_sqlite3_compileoption_get // Line 191 #define sqlite3_compileoption_used \ chrome_sqlite3_compileoption_used // Line 190 -#define sqlite3_complete chrome_sqlite3_complete // Line 2324 -#define sqlite3_complete16 chrome_sqlite3_complete16 // Line 2325 -#define sqlite3_config chrome_sqlite3_config // Line 1507 -#define sqlite3_context_db_handle chrome_sqlite3_context_db_handle // Line 4988 +#define sqlite3_complete chrome_sqlite3_complete // Line 2340 +#define sqlite3_complete16 chrome_sqlite3_complete16 // Line 2341 +#define sqlite3_config chrome_sqlite3_config // Line 1514 +#define sqlite3_context_db_handle chrome_sqlite3_context_db_handle // Line 4940 #define sqlite3_create_collation \ - chrome_sqlite3_create_collation // Lines 5313-5319 + chrome_sqlite3_create_collation // Lines 5265-5271 #define sqlite3_create_collation16 \ - chrome_sqlite3_create_collation16 // Lines 5328-5334 + chrome_sqlite3_create_collation16 // Lines 5280-5286 #define sqlite3_create_collation_v2 \ - chrome_sqlite3_create_collation_v2 // Lines 5320-5327 + chrome_sqlite3_create_collation_v2 // Lines 5272-5279 #define sqlite3_create_function \ - chrome_sqlite3_create_function // Lines 4700-4709 + chrome_sqlite3_create_function // Lines 4652-4661 #define sqlite3_create_function16 \ - chrome_sqlite3_create_function16 // Lines 4710-4719 + chrome_sqlite3_create_function16 // Lines 4662-4671 #define sqlite3_create_function_v2 \ - chrome_sqlite3_create_function_v2 // Lines 4720-4730 -#define sqlite3_create_module chrome_sqlite3_create_module // Lines 6327-6332 + chrome_sqlite3_create_function_v2 // Lines 4672-4682 +#define sqlite3_create_module chrome_sqlite3_create_module // Lines 6279-6284 #define sqlite3_create_module_v2 \ - chrome_sqlite3_create_module_v2 // Lines 6333-6339 -#define sqlite3_data_count chrome_sqlite3_data_count // Line 4307 -#define sqlite3_data_directory chrome_sqlite3_data_directory // Line 5541 -#define sqlite3_db_cacheflush chrome_sqlite3_db_cacheflush // Line 8524 -#define sqlite3_db_config chrome_sqlite3_db_config // Line 1526 -#define sqlite3_db_filename chrome_sqlite3_db_filename // Line 5595 -#define sqlite3_db_handle chrome_sqlite3_db_handle // Line 5578 -#define sqlite3_db_mutex chrome_sqlite3_db_mutex // Line 6971 -#define sqlite3_db_readonly chrome_sqlite3_db_readonly // Line 5605 -#define sqlite3_db_release_memory chrome_sqlite3_db_release_memory // Line 5793 -#define sqlite3_db_status chrome_sqlite3_db_status // Line 7200 -#define sqlite3_declare_vtab chrome_sqlite3_declare_vtab // Line 6396 + chrome_sqlite3_create_module_v2 // Lines 6285-6291 +#define sqlite3_data_count chrome_sqlite3_data_count // Line 4259 +#define sqlite3_data_directory chrome_sqlite3_data_directory // Line 5493 +#define sqlite3_db_cacheflush chrome_sqlite3_db_cacheflush // Line 8486 +#define sqlite3_db_config chrome_sqlite3_db_config // Line 1533 +#define sqlite3_db_filename chrome_sqlite3_db_filename // Line 5547 +#define sqlite3_db_handle chrome_sqlite3_db_handle // Line 5530 +#define sqlite3_db_mutex chrome_sqlite3_db_mutex // Line 6923 +#define sqlite3_db_readonly chrome_sqlite3_db_readonly // Line 5557 +#define sqlite3_db_release_memory chrome_sqlite3_db_release_memory // Line 5745 +#define sqlite3_db_status chrome_sqlite3_db_status // Line 7152 +#define sqlite3_declare_vtab chrome_sqlite3_declare_vtab // Line 6348 +#define sqlite3_deserialize chrome_sqlite3_deserialize // Lines 8869-8876 #define sqlite3_enable_load_extension \ - chrome_sqlite3_enable_load_extension // Line 6017 + chrome_sqlite3_enable_load_extension // Line 5969 #define sqlite3_enable_shared_cache \ - chrome_sqlite3_enable_shared_cache // Line 5763 -#define sqlite3_errcode chrome_sqlite3_errcode // Line 3403 -#define sqlite3_errmsg chrome_sqlite3_errmsg // Line 3405 -#define sqlite3_errmsg16 chrome_sqlite3_errmsg16 // Line 3406 -#define sqlite3_errstr chrome_sqlite3_errstr // Line 3407 + chrome_sqlite3_enable_shared_cache // Line 5715 +#define sqlite3_errcode chrome_sqlite3_errcode // Line 3355 +#define sqlite3_errmsg chrome_sqlite3_errmsg // Line 3357 +#define sqlite3_errmsg16 chrome_sqlite3_errmsg16 // Line 3358 +#define sqlite3_errstr chrome_sqlite3_errstr // Line 3359 #define sqlite3_exec chrome_sqlite3_exec // Lines 403-409 -#define sqlite3_expanded_sql chrome_sqlite3_expanded_sql // Line 3748 -#define sqlite3_expired chrome_sqlite3_expired // Line 4767 -#define sqlite3_extended_errcode chrome_sqlite3_extended_errcode // Line 3404 +#define sqlite3_expanded_sql chrome_sqlite3_expanded_sql // Line 3700 +#define sqlite3_expired chrome_sqlite3_expired // Line 4719 +#define sqlite3_extended_errcode chrome_sqlite3_extended_errcode // Line 3356 #define sqlite3_extended_result_codes \ - chrome_sqlite3_extended_result_codes // Line 2103 -#define sqlite3_file_control chrome_sqlite3_file_control // Line 7006 -#define sqlite3_finalize chrome_sqlite3_finalize // Line 4573 -#define sqlite3_free chrome_sqlite3_free // Line 2695 -#define sqlite3_free_table chrome_sqlite3_free_table // Line 2492 -#define sqlite3_get_autocommit chrome_sqlite3_get_autocommit // Line 5565 -#define sqlite3_get_auxdata chrome_sqlite3_get_auxdata // Line 5047 -#define sqlite3_get_table chrome_sqlite3_get_table // Lines 2484-2491 -#define sqlite3_global_recover chrome_sqlite3_global_recover // Line 4769 -#define sqlite3_initialize chrome_sqlite3_initialize // Line 1471 -#define sqlite3_interrupt chrome_sqlite3_interrupt // Line 2289 -#define sqlite3_key chrome_sqlite3_key // Lines 5382-5385 -#define sqlite3_key_v2 chrome_sqlite3_key_v2 // Lines 5386-5390 -#define sqlite3_last_insert_rowid chrome_sqlite3_last_insert_rowid // Line 2165 + chrome_sqlite3_extended_result_codes // Line 2119 +#define sqlite3_file_control chrome_sqlite3_file_control // Line 6958 +#define sqlite3_finalize chrome_sqlite3_finalize // Line 4525 +#define sqlite3_free chrome_sqlite3_free // Line 2647 +#define sqlite3_free_table chrome_sqlite3_free_table // Line 2508 +#define sqlite3_get_autocommit chrome_sqlite3_get_autocommit // Line 5517 +#define sqlite3_get_auxdata chrome_sqlite3_get_auxdata // Line 4999 +#define sqlite3_get_table chrome_sqlite3_get_table // Lines 2500-2507 +#define sqlite3_global_recover chrome_sqlite3_global_recover // Line 4721 +#define sqlite3_initialize chrome_sqlite3_initialize // Line 1478 +#define sqlite3_interrupt chrome_sqlite3_interrupt // Line 2305 +#define sqlite3_key chrome_sqlite3_key // Lines 5334-5337 +#define sqlite3_key_v2 chrome_sqlite3_key_v2 // Lines 5338-5342 +#define sqlite3_last_insert_rowid chrome_sqlite3_last_insert_rowid // Line 2181 #define sqlite3_libversion chrome_sqlite3_libversion // Line 163 #define sqlite3_libversion_number chrome_sqlite3_libversion_number // Line 165 -#define sqlite3_limit chrome_sqlite3_limit // Line 3475 -#define sqlite3_load_extension chrome_sqlite3_load_extension // Lines 5985-5990 -#define sqlite3_log chrome_sqlite3_log // Line 8061 -#define sqlite3_malloc chrome_sqlite3_malloc // Line 2691 -#define sqlite3_malloc64 chrome_sqlite3_malloc64 // Line 2692 -#define sqlite3_memory_alarm chrome_sqlite3_memory_alarm // Lines 4771-4772 -#define sqlite3_memory_highwater chrome_sqlite3_memory_highwater // Line 2722 -#define sqlite3_memory_used chrome_sqlite3_memory_used // Line 2721 -#define sqlite3_mprintf chrome_sqlite3_mprintf // Line 2598 -#define sqlite3_msize chrome_sqlite3_msize // Line 2696 -#define sqlite3_mutex_alloc chrome_sqlite3_mutex_alloc // Line 6816 -#define sqlite3_mutex_enter chrome_sqlite3_mutex_enter // Line 6818 -#define sqlite3_mutex_free chrome_sqlite3_mutex_free // Line 6817 -#define sqlite3_mutex_held chrome_sqlite3_mutex_held // Line 6930 -#define sqlite3_mutex_leave chrome_sqlite3_mutex_leave // Line 6820 -#define sqlite3_mutex_notheld chrome_sqlite3_mutex_notheld // Line 6931 -#define sqlite3_mutex_try chrome_sqlite3_mutex_try // Line 6819 -#define sqlite3_next_stmt chrome_sqlite3_next_stmt // Line 5621 -#define sqlite3_open chrome_sqlite3_open // Lines 3303-3306 -#define sqlite3_open16 chrome_sqlite3_open16 // Lines 3307-3310 -#define sqlite3_open_v2 chrome_sqlite3_open_v2 // Lines 3311-3316 -#define sqlite3_os_end chrome_sqlite3_os_end // Line 1474 -#define sqlite3_os_init chrome_sqlite3_os_init // Line 1473 -#define sqlite3_overload_function chrome_sqlite3_overload_function // Line 6415 -#define sqlite3_prepare chrome_sqlite3_prepare // Lines 3670-3676 -#define sqlite3_prepare16 chrome_sqlite3_prepare16 // Lines 3692-3698 -#define sqlite3_prepare16_v2 chrome_sqlite3_prepare16_v2 // Lines 3699-3705 -#define sqlite3_prepare16_v3 chrome_sqlite3_prepare16_v3 // Lines 3706-3713 -#define sqlite3_prepare_v2 chrome_sqlite3_prepare_v2 // Lines 3677-3683 -#define sqlite3_prepare_v3 chrome_sqlite3_prepare_v3 // Lines 3684-3691 -#define sqlite3_preupdate_count chrome_sqlite3_preupdate_count // Line 8623 -#define sqlite3_preupdate_depth chrome_sqlite3_preupdate_depth // Line 8624 -#define sqlite3_preupdate_hook chrome_sqlite3_preupdate_hook // Lines 8609-8621 -#define sqlite3_preupdate_new chrome_sqlite3_preupdate_new // Line 8625 -#define sqlite3_preupdate_old chrome_sqlite3_preupdate_old // Line 8622 -#define sqlite3_profile chrome_sqlite3_profile // Lines 2946-2947 -#define sqlite3_progress_handler chrome_sqlite3_progress_handler // Line 3074 -#define sqlite3_randomness chrome_sqlite3_randomness // Line 2745 -#define sqlite3_realloc chrome_sqlite3_realloc // Line 2693 -#define sqlite3_realloc64 chrome_sqlite3_realloc64 // Line 2694 -#define sqlite3_rekey chrome_sqlite3_rekey // Lines 5400-5403 -#define sqlite3_rekey_v2 chrome_sqlite3_rekey_v2 // Lines 5404-5408 -#define sqlite3_release_memory chrome_sqlite3_release_memory // Line 5779 -#define sqlite3_reset chrome_sqlite3_reset // Line 4600 +#define sqlite3_limit chrome_sqlite3_limit // Line 3427 +#define sqlite3_load_extension chrome_sqlite3_load_extension // Lines 5937-5942 +#define sqlite3_log chrome_sqlite3_log // Line 8023 +#define sqlite3_malloc chrome_sqlite3_malloc // Line 2643 +#define sqlite3_malloc64 chrome_sqlite3_malloc64 // Line 2644 +#define sqlite3_memory_alarm chrome_sqlite3_memory_alarm // Lines 4723-4724 +#define sqlite3_memory_highwater chrome_sqlite3_memory_highwater // Line 2674 +#define sqlite3_memory_used chrome_sqlite3_memory_used // Line 2673 +#define sqlite3_mprintf chrome_sqlite3_mprintf // Line 2550 +#define sqlite3_msize chrome_sqlite3_msize // Line 2648 +#define sqlite3_mutex_alloc chrome_sqlite3_mutex_alloc // Line 6768 +#define sqlite3_mutex_enter chrome_sqlite3_mutex_enter // Line 6770 +#define sqlite3_mutex_free chrome_sqlite3_mutex_free // Line 6769 +#define sqlite3_mutex_held chrome_sqlite3_mutex_held // Line 6882 +#define sqlite3_mutex_leave chrome_sqlite3_mutex_leave // Line 6772 +#define sqlite3_mutex_notheld chrome_sqlite3_mutex_notheld // Line 6883 +#define sqlite3_mutex_try chrome_sqlite3_mutex_try // Line 6771 +#define sqlite3_next_stmt chrome_sqlite3_next_stmt // Line 5573 +#define sqlite3_open chrome_sqlite3_open // Lines 3255-3258 +#define sqlite3_open16 chrome_sqlite3_open16 // Lines 3259-3262 +#define sqlite3_open_v2 chrome_sqlite3_open_v2 // Lines 3263-3268 +#define sqlite3_os_end chrome_sqlite3_os_end // Line 1481 +#define sqlite3_os_init chrome_sqlite3_os_init // Line 1480 +#define sqlite3_overload_function chrome_sqlite3_overload_function // Line 6367 +#define sqlite3_prepare chrome_sqlite3_prepare // Lines 3622-3628 +#define sqlite3_prepare16 chrome_sqlite3_prepare16 // Lines 3644-3650 +#define sqlite3_prepare16_v2 chrome_sqlite3_prepare16_v2 // Lines 3651-3657 +#define sqlite3_prepare16_v3 chrome_sqlite3_prepare16_v3 // Lines 3658-3665 +#define sqlite3_prepare_v2 chrome_sqlite3_prepare_v2 // Lines 3629-3635 +#define sqlite3_prepare_v3 chrome_sqlite3_prepare_v3 // Lines 3636-3643 +#define sqlite3_preupdate_count chrome_sqlite3_preupdate_count // Line 8585 +#define sqlite3_preupdate_depth chrome_sqlite3_preupdate_depth // Line 8586 +#define sqlite3_preupdate_hook chrome_sqlite3_preupdate_hook // Lines 8571-8583 +#define sqlite3_preupdate_new chrome_sqlite3_preupdate_new // Line 8587 +#define sqlite3_preupdate_old chrome_sqlite3_preupdate_old // Line 8584 +#define sqlite3_profile chrome_sqlite3_profile // Lines 2898-2899 +#define sqlite3_progress_handler chrome_sqlite3_progress_handler // Line 3026 +#define sqlite3_randomness chrome_sqlite3_randomness // Line 2697 +#define sqlite3_realloc chrome_sqlite3_realloc // Line 2645 +#define sqlite3_realloc64 chrome_sqlite3_realloc64 // Line 2646 +#define sqlite3_rekey chrome_sqlite3_rekey // Lines 5352-5355 +#define sqlite3_rekey_v2 chrome_sqlite3_rekey_v2 // Lines 5356-5360 +#define sqlite3_release_memory chrome_sqlite3_release_memory // Line 5731 +#define sqlite3_reset chrome_sqlite3_reset // Line 4552 #define sqlite3_reset_auto_extension \ - chrome_sqlite3_reset_auto_extension // Line 6075 -#define sqlite3_result_blob chrome_sqlite3_result_blob // Line 5195 -#define sqlite3_result_blob64 chrome_sqlite3_result_blob64 // Lines 5196-5197 -#define sqlite3_result_double chrome_sqlite3_result_double // Line 5198 -#define sqlite3_result_error chrome_sqlite3_result_error // Line 5199 -#define sqlite3_result_error16 chrome_sqlite3_result_error16 // Line 5200 -#define sqlite3_result_error_code chrome_sqlite3_result_error_code // Line 5203 + chrome_sqlite3_reset_auto_extension // Line 6027 +#define sqlite3_result_blob chrome_sqlite3_result_blob // Line 5147 +#define sqlite3_result_blob64 chrome_sqlite3_result_blob64 // Lines 5148-5149 +#define sqlite3_result_double chrome_sqlite3_result_double // Line 5150 +#define sqlite3_result_error chrome_sqlite3_result_error // Line 5151 +#define sqlite3_result_error16 chrome_sqlite3_result_error16 // Line 5152 +#define sqlite3_result_error_code chrome_sqlite3_result_error_code // Line 5155 #define sqlite3_result_error_nomem \ - chrome_sqlite3_result_error_nomem // Line 5202 + chrome_sqlite3_result_error_nomem // Line 5154 #define sqlite3_result_error_toobig \ - chrome_sqlite3_result_error_toobig // Line 5201 -#define sqlite3_result_int chrome_sqlite3_result_int // Line 5204 -#define sqlite3_result_int64 chrome_sqlite3_result_int64 // Line 5205 -#define sqlite3_result_null chrome_sqlite3_result_null // Line 5206 -#define sqlite3_result_pointer chrome_sqlite3_result_pointer // Line 5214 -#define sqlite3_result_subtype chrome_sqlite3_result_subtype // Line 5231 -#define sqlite3_result_text chrome_sqlite3_result_text // Line 5207 -#define sqlite3_result_text16 chrome_sqlite3_result_text16 // Line 5210 -#define sqlite3_result_text16be chrome_sqlite3_result_text16be // Line 5212 -#define sqlite3_result_text16le chrome_sqlite3_result_text16le // Line 5211 -#define sqlite3_result_text64 chrome_sqlite3_result_text64 // Lines 5208-5209 -#define sqlite3_result_value chrome_sqlite3_result_value // Line 5213 -#define sqlite3_result_zeroblob chrome_sqlite3_result_zeroblob // Line 5215 -#define sqlite3_result_zeroblob64 chrome_sqlite3_result_zeroblob64 // Line 5216 -#define sqlite3_rollback_hook chrome_sqlite3_rollback_hook // Line 5671 + chrome_sqlite3_result_error_toobig // Line 5153 +#define sqlite3_result_int chrome_sqlite3_result_int // Line 5156 +#define sqlite3_result_int64 chrome_sqlite3_result_int64 // Line 5157 +#define sqlite3_result_null chrome_sqlite3_result_null // Line 5158 +#define sqlite3_result_pointer chrome_sqlite3_result_pointer // Line 5166 +#define sqlite3_result_subtype chrome_sqlite3_result_subtype // Line 5183 +#define sqlite3_result_text chrome_sqlite3_result_text // Line 5159 +#define sqlite3_result_text16 chrome_sqlite3_result_text16 // Line 5162 +#define sqlite3_result_text16be chrome_sqlite3_result_text16be // Line 5164 +#define sqlite3_result_text16le chrome_sqlite3_result_text16le // Line 5163 +#define sqlite3_result_text64 chrome_sqlite3_result_text64 // Lines 5160-5161 +#define sqlite3_result_value chrome_sqlite3_result_value // Line 5165 +#define sqlite3_result_zeroblob chrome_sqlite3_result_zeroblob // Line 5167 +#define sqlite3_result_zeroblob64 chrome_sqlite3_result_zeroblob64 // Line 5168 +#define sqlite3_rollback_hook chrome_sqlite3_rollback_hook // Line 5623 #define sqlite3_rtree_geometry_callback \ - chrome_sqlite3_rtree_geometry_callback // Lines 8872-8877 + chrome_sqlite3_rtree_geometry_callback // Lines 8956-8961 #define sqlite3_rtree_query_callback \ - chrome_sqlite3_rtree_query_callback // Lines 8898-8904 -#define sqlite3_set_authorizer chrome_sqlite3_set_authorizer // Lines 2836-2840 -#define sqlite3_set_auxdata chrome_sqlite3_set_auxdata // Line 5048 + chrome_sqlite3_rtree_query_callback // Lines 8982-8988 +#define sqlite3_serialize chrome_sqlite3_serialize // Lines 8817-8822 +#define sqlite3_set_authorizer chrome_sqlite3_set_authorizer // Lines 2788-2792 +#define sqlite3_set_auxdata chrome_sqlite3_set_auxdata // Line 5000 #define sqlite3_set_last_insert_rowid \ - chrome_sqlite3_set_last_insert_rowid // Line 2175 -#define sqlite3_shutdown chrome_sqlite3_shutdown // Line 1472 -#define sqlite3_sleep chrome_sqlite3_sleep // Line 5446 -#define sqlite3_snapshot_cmp chrome_sqlite3_snapshot_cmp // Lines 8792-8795 -#define sqlite3_snapshot_free chrome_sqlite3_snapshot_free // Line 8768 -#define sqlite3_snapshot_get chrome_sqlite3_snapshot_get // Lines 8713-8717 -#define sqlite3_snapshot_open chrome_sqlite3_snapshot_open // Lines 8751-8755 -#define sqlite3_snapshot_recover chrome_sqlite3_snapshot_recover // Line 8817 -#define sqlite3_snprintf chrome_sqlite3_snprintf // Line 2600 -#define sqlite3_soft_heap_limit chrome_sqlite3_soft_heap_limit // Line 5857 -#define sqlite3_soft_heap_limit64 chrome_sqlite3_soft_heap_limit64 // Line 5846 + chrome_sqlite3_set_last_insert_rowid // Line 2191 +#define sqlite3_shutdown chrome_sqlite3_shutdown // Line 1479 +#define sqlite3_sleep chrome_sqlite3_sleep // Line 5398 +#define sqlite3_snapshot_cmp chrome_sqlite3_snapshot_cmp // Lines 8754-8757 +#define sqlite3_snapshot_free chrome_sqlite3_snapshot_free // Line 8730 +#define sqlite3_snapshot_get chrome_sqlite3_snapshot_get // Lines 8675-8679 +#define sqlite3_snapshot_open chrome_sqlite3_snapshot_open // Lines 8713-8717 +#define sqlite3_snapshot_recover chrome_sqlite3_snapshot_recover // Line 8779 +#define sqlite3_snprintf chrome_sqlite3_snprintf // Line 2552 +#define sqlite3_soft_heap_limit chrome_sqlite3_soft_heap_limit // Line 5809 +#define sqlite3_soft_heap_limit64 chrome_sqlite3_soft_heap_limit64 // Line 5798 #define sqlite3_sourceid chrome_sqlite3_sourceid // Line 164 -#define sqlite3_sql chrome_sqlite3_sql // Line 3747 -#define sqlite3_status chrome_sqlite3_status // Line 7090 -#define sqlite3_status64 chrome_sqlite3_status64 // Lines 7091-7096 -#define sqlite3_step chrome_sqlite3_step // Line 4286 -#define sqlite3_stmt_busy chrome_sqlite3_stmt_busy // Line 3805 -#define sqlite3_stmt_readonly chrome_sqlite3_stmt_readonly // Line 3784 +#define sqlite3_sql chrome_sqlite3_sql // Line 3699 +#define sqlite3_status chrome_sqlite3_status // Line 7042 +#define sqlite3_status64 chrome_sqlite3_status64 // Lines 7043-7048 +#define sqlite3_step chrome_sqlite3_step // Line 4238 +#define sqlite3_stmt_busy chrome_sqlite3_stmt_busy // Line 3757 +#define sqlite3_stmt_readonly chrome_sqlite3_stmt_readonly // Line 3736 #define sqlite3_stmt_scanstatus \ - chrome_sqlite3_stmt_scanstatus // Lines 8476-8481 + chrome_sqlite3_stmt_scanstatus // Lines 8438-8443 #define sqlite3_stmt_scanstatus_reset \ - chrome_sqlite3_stmt_scanstatus_reset // Line 8492 -#define sqlite3_stmt_status chrome_sqlite3_stmt_status // Line 7343 -#define sqlite3_strglob chrome_sqlite3_strglob // Line 7992 -#define sqlite3_stricmp chrome_sqlite3_stricmp // Line 7974 -#define sqlite3_strlike chrome_sqlite3_strlike // Line 8038 -#define sqlite3_strnicmp chrome_sqlite3_strnicmp // Line 7975 -#define sqlite3_system_errno chrome_sqlite3_system_errno // Line 8638 + chrome_sqlite3_stmt_scanstatus_reset // Line 8454 +#define sqlite3_stmt_status chrome_sqlite3_stmt_status // Line 7305 +#define sqlite3_strglob chrome_sqlite3_strglob // Line 7954 +#define sqlite3_stricmp chrome_sqlite3_stricmp // Line 7936 +#define sqlite3_strlike chrome_sqlite3_strlike // Line 8000 +#define sqlite3_strnicmp chrome_sqlite3_strnicmp // Line 7937 +#define sqlite3_system_errno chrome_sqlite3_system_errno // Line 8600 #define sqlite3_table_column_metadata \ - chrome_sqlite3_table_column_metadata // Lines 5929-5939 -#define sqlite3_temp_directory chrome_sqlite3_temp_directory // Line 5504 -#define sqlite3_test_control chrome_sqlite3_test_control // Line 7025 -#define sqlite3_thread_cleanup chrome_sqlite3_thread_cleanup // Line 4770 + chrome_sqlite3_table_column_metadata // Lines 5881-5891 +#define sqlite3_temp_directory chrome_sqlite3_temp_directory // Line 5456 +#define sqlite3_test_control chrome_sqlite3_test_control // Line 6977 +#define sqlite3_thread_cleanup chrome_sqlite3_thread_cleanup // Line 4722 #define sqlite3_threadsafe chrome_sqlite3_threadsafe // Line 230 -#define sqlite3_total_changes chrome_sqlite3_total_changes // Line 2252 -#define sqlite3_trace chrome_sqlite3_trace // Lines 2944-2945 -#define sqlite3_trace_v2 chrome_sqlite3_trace_v2 // Lines 3035-3040 -#define sqlite3_transfer_bindings chrome_sqlite3_transfer_bindings // Line 4768 -#define sqlite3_unlock_notify chrome_sqlite3_unlock_notify // Lines 7959-7963 -#define sqlite3_update_hook chrome_sqlite3_update_hook // Lines 5722-5726 -#define sqlite3_uri_boolean chrome_sqlite3_uri_boolean // Line 3358 -#define sqlite3_uri_int64 chrome_sqlite3_uri_int64 // Line 3359 -#define sqlite3_uri_parameter chrome_sqlite3_uri_parameter // Line 3357 -#define sqlite3_user_data chrome_sqlite3_user_data // Line 4976 -#define sqlite3_value_blob chrome_sqlite3_value_blob // Line 4874 -#define sqlite3_value_bytes chrome_sqlite3_value_bytes // Line 4883 -#define sqlite3_value_bytes16 chrome_sqlite3_value_bytes16 // Line 4884 -#define sqlite3_value_double chrome_sqlite3_value_double // Line 4875 -#define sqlite3_value_dup chrome_sqlite3_value_dup // Line 4915 -#define sqlite3_value_free chrome_sqlite3_value_free // Line 4916 -#define sqlite3_value_int chrome_sqlite3_value_int // Line 4876 -#define sqlite3_value_int64 chrome_sqlite3_value_int64 // Line 4877 -#define sqlite3_value_nochange chrome_sqlite3_value_nochange // Line 4887 +#define sqlite3_total_changes chrome_sqlite3_total_changes // Line 2268 +#define sqlite3_trace chrome_sqlite3_trace // Lines 2896-2897 +#define sqlite3_trace_v2 chrome_sqlite3_trace_v2 // Lines 2987-2992 +#define sqlite3_transfer_bindings chrome_sqlite3_transfer_bindings // Line 4720 +#define sqlite3_unlock_notify chrome_sqlite3_unlock_notify // Lines 7921-7925 +#define sqlite3_update_hook chrome_sqlite3_update_hook // Lines 5674-5678 +#define sqlite3_uri_boolean chrome_sqlite3_uri_boolean // Line 3310 +#define sqlite3_uri_int64 chrome_sqlite3_uri_int64 // Line 3311 +#define sqlite3_uri_parameter chrome_sqlite3_uri_parameter // Line 3309 +#define sqlite3_user_data chrome_sqlite3_user_data // Line 4928 +#define sqlite3_value_blob chrome_sqlite3_value_blob // Line 4826 +#define sqlite3_value_bytes chrome_sqlite3_value_bytes // Line 4835 +#define sqlite3_value_bytes16 chrome_sqlite3_value_bytes16 // Line 4836 +#define sqlite3_value_double chrome_sqlite3_value_double // Line 4827 +#define sqlite3_value_dup chrome_sqlite3_value_dup // Line 4867 +#define sqlite3_value_free chrome_sqlite3_value_free // Line 4868 +#define sqlite3_value_int chrome_sqlite3_value_int // Line 4828 +#define sqlite3_value_int64 chrome_sqlite3_value_int64 // Line 4829 +#define sqlite3_value_nochange chrome_sqlite3_value_nochange // Line 4839 #define sqlite3_value_numeric_type \ - chrome_sqlite3_value_numeric_type // Line 4886 -#define sqlite3_value_pointer chrome_sqlite3_value_pointer // Line 4878 -#define sqlite3_value_subtype chrome_sqlite3_value_subtype // Line 4899 -#define sqlite3_value_text chrome_sqlite3_value_text // Line 4879 -#define sqlite3_value_text16 chrome_sqlite3_value_text16 // Line 4880 -#define sqlite3_value_text16be chrome_sqlite3_value_text16be // Line 4882 -#define sqlite3_value_text16le chrome_sqlite3_value_text16le // Line 4881 -#define sqlite3_value_type chrome_sqlite3_value_type // Line 4885 + chrome_sqlite3_value_numeric_type // Line 4838 +#define sqlite3_value_pointer chrome_sqlite3_value_pointer // Line 4830 +#define sqlite3_value_subtype chrome_sqlite3_value_subtype // Line 4851 +#define sqlite3_value_text chrome_sqlite3_value_text // Line 4831 +#define sqlite3_value_text16 chrome_sqlite3_value_text16 // Line 4832 +#define sqlite3_value_text16be chrome_sqlite3_value_text16be // Line 4834 +#define sqlite3_value_text16le chrome_sqlite3_value_text16le // Line 4833 +#define sqlite3_value_type chrome_sqlite3_value_type // Line 4837 #define sqlite3_version chrome_sqlite3_version // Line 162 -#define sqlite3_vfs_find chrome_sqlite3_vfs_find // Line 6698 -#define sqlite3_vfs_register chrome_sqlite3_vfs_register // Line 6699 -#define sqlite3_vfs_unregister chrome_sqlite3_vfs_unregister // Line 6700 -#define sqlite3_vmprintf chrome_sqlite3_vmprintf // Line 2599 -#define sqlite3_vsnprintf chrome_sqlite3_vsnprintf // Line 2601 -#define sqlite3_vtab_collation chrome_sqlite3_vtab_collation // Line 8371 -#define sqlite3_vtab_config chrome_sqlite3_vtab_config // Line 8284 -#define sqlite3_vtab_nochange chrome_sqlite3_vtab_nochange // Line 8356 -#define sqlite3_vtab_on_conflict chrome_sqlite3_vtab_on_conflict // Line 8337 +#define sqlite3_vfs_find chrome_sqlite3_vfs_find // Line 6650 +#define sqlite3_vfs_register chrome_sqlite3_vfs_register // Line 6651 +#define sqlite3_vfs_unregister chrome_sqlite3_vfs_unregister // Line 6652 +#define sqlite3_vmprintf chrome_sqlite3_vmprintf // Line 2551 +#define sqlite3_vsnprintf chrome_sqlite3_vsnprintf // Line 2553 +#define sqlite3_vtab_collation chrome_sqlite3_vtab_collation // Line 8333 +#define sqlite3_vtab_config chrome_sqlite3_vtab_config // Line 8246 +#define sqlite3_vtab_nochange chrome_sqlite3_vtab_nochange // Line 8318 +#define sqlite3_vtab_on_conflict chrome_sqlite3_vtab_on_conflict // Line 8299 #define sqlite3_wal_autocheckpoint \ - chrome_sqlite3_wal_autocheckpoint // Line 8132 -#define sqlite3_wal_checkpoint chrome_sqlite3_wal_checkpoint // Line 8154 + chrome_sqlite3_wal_autocheckpoint // Line 8094 +#define sqlite3_wal_checkpoint chrome_sqlite3_wal_checkpoint // Line 8116 #define sqlite3_wal_checkpoint_v2 \ - chrome_sqlite3_wal_checkpoint_v2 // Lines 8248-8254 -#define sqlite3_wal_hook chrome_sqlite3_wal_hook // Lines 8097-8101 -#define sqlite3changegroup_add chrome_sqlite3changegroup_add // Line 9826 + chrome_sqlite3_wal_checkpoint_v2 // Lines 8210-8216 +#define sqlite3_wal_hook chrome_sqlite3_wal_hook // Lines 8059-8063 +#define sqlite3changegroup_add chrome_sqlite3changegroup_add // Line 9943 #define sqlite3changegroup_add_strm \ - chrome_sqlite3changegroup_add_strm // Lines 10250-10253 -#define sqlite3changegroup_delete chrome_sqlite3changegroup_delete // Line 9861 -#define sqlite3changegroup_new chrome_sqlite3changegroup_new // Line 9749 + chrome_sqlite3changegroup_add_strm // Lines 10593-10596 +#define sqlite3changegroup_delete chrome_sqlite3changegroup_delete // Line 9980 +#define sqlite3changegroup_new chrome_sqlite3changegroup_new // Line 9865 #define sqlite3changegroup_output \ - chrome_sqlite3changegroup_output // Lines 9852-9856 + chrome_sqlite3changegroup_output // Lines 9970-9974 #define sqlite3changegroup_output_strm \ - chrome_sqlite3changegroup_output_strm // Lines 10254-10257 + chrome_sqlite3changegroup_output_strm // Lines 10597-10600 #define sqlite3changeset_apply \ - chrome_sqlite3changeset_apply // Lines 10005-10019 + chrome_sqlite3changeset_apply // Lines 10140-10154 #define sqlite3changeset_apply_strm \ - chrome_sqlite3changeset_apply_strm // Lines 10206-10220 + chrome_sqlite3changeset_apply_strm // Lines 10532-10546 +#define sqlite3changeset_apply_v2 \ + chrome_sqlite3changeset_apply_v2 // Lines 10155-10171 +#define sqlite3changeset_apply_v2_strm \ + chrome_sqlite3changeset_apply_v2_strm // Lines 10547-10563 #define sqlite3changeset_concat \ - chrome_sqlite3changeset_concat // Lines 9699-9706 + chrome_sqlite3changeset_concat // Lines 9811-9818 #define sqlite3changeset_concat_strm \ - chrome_sqlite3changeset_concat_strm // Lines 10221-10228 + chrome_sqlite3changeset_concat_strm // Lines 10564-10571 #define sqlite3changeset_conflict \ - chrome_sqlite3changeset_conflict // Lines 9591-9595 -#define sqlite3changeset_finalize chrome_sqlite3changeset_finalize // Line 9640 + chrome_sqlite3changeset_conflict // Lines 9697-9701 +#define sqlite3changeset_finalize chrome_sqlite3changeset_finalize // Line 9750 #define sqlite3changeset_fk_conflicts \ - chrome_sqlite3changeset_fk_conflicts // Lines 9607-9610 + chrome_sqlite3changeset_fk_conflicts // Lines 9714-9717 #define sqlite3changeset_invert \ - chrome_sqlite3changeset_invert // Lines 9670-9673 + chrome_sqlite3changeset_invert // Lines 9780-9783 #define sqlite3changeset_invert_strm \ - chrome_sqlite3changeset_invert_strm // Lines 10229-10234 -#define sqlite3changeset_new chrome_sqlite3changeset_new // Lines 9564-9568 -#define sqlite3changeset_next chrome_sqlite3changeset_next // Line 9440 -#define sqlite3changeset_old chrome_sqlite3changeset_old // Lines 9531-9535 -#define sqlite3changeset_op chrome_sqlite3changeset_op // Lines 9468-9474 -#define sqlite3changeset_pk chrome_sqlite3changeset_pk // Lines 9501-9505 -#define sqlite3changeset_start chrome_sqlite3changeset_start // Lines 9411-9415 + chrome_sqlite3changeset_invert_strm // Lines 10572-10577 +#define sqlite3changeset_new chrome_sqlite3changeset_new // Lines 9669-9673 +#define sqlite3changeset_next chrome_sqlite3changeset_next // Line 9541 +#define sqlite3changeset_old chrome_sqlite3changeset_old // Lines 9635-9639 +#define sqlite3changeset_op chrome_sqlite3changeset_op // Lines 9570-9576 +#define sqlite3changeset_pk chrome_sqlite3changeset_pk // Lines 9604-9608 +#define sqlite3changeset_start chrome_sqlite3changeset_start // Lines 9511-9515 #define sqlite3changeset_start_strm \ - chrome_sqlite3changeset_start_strm // Lines 10235-10239 -#define sqlite3session_attach chrome_sqlite3session_attach // Lines 9130-9133 + chrome_sqlite3changeset_start_strm // Lines 10578-10582 +#define sqlite3rebaser_configure \ + chrome_sqlite3rebaser_configure // Lines 10407-10410 +#define sqlite3rebaser_create chrome_sqlite3rebaser_create // Line 10396 +#define sqlite3rebaser_delete chrome_sqlite3rebaser_delete // Line 10440 +#define sqlite3rebaser_rebase chrome_sqlite3rebaser_rebase // Lines 10426-10430 +#define sqlite3rebaser_rebase_strm \ + chrome_sqlite3rebaser_rebase_strm // Lines 10601-10607 +#define sqlite3session_attach chrome_sqlite3session_attach // Lines 9225-9228 #define sqlite3session_changeset \ - chrome_sqlite3session_changeset // Lines 9257-9261 + chrome_sqlite3session_changeset // Lines 9354-9358 #define sqlite3session_changeset_strm \ - chrome_sqlite3session_changeset_strm // Lines 10240-10244 -#define sqlite3session_create chrome_sqlite3session_create // Lines 9004-9008 -#define sqlite3session_delete chrome_sqlite3session_delete // Line 9022 -#define sqlite3session_diff chrome_sqlite3session_diff // Lines 9319-9324 -#define sqlite3session_enable chrome_sqlite3session_enable // Line 9042 -#define sqlite3session_indirect chrome_sqlite3session_indirect // Line 9071 -#define sqlite3session_isempty chrome_sqlite3session_isempty // Line 9376 + chrome_sqlite3session_changeset_strm // Lines 10583-10587 +#define sqlite3session_create chrome_sqlite3session_create // Lines 9095-9099 +#define sqlite3session_delete chrome_sqlite3session_delete // Line 9114 +#define sqlite3session_diff chrome_sqlite3session_diff // Lines 9417-9422 +#define sqlite3session_enable chrome_sqlite3session_enable // Line 9135 +#define sqlite3session_indirect chrome_sqlite3session_indirect // Line 9165 +#define sqlite3session_isempty chrome_sqlite3session_isempty // Line 9475 #define sqlite3session_patchset \ - chrome_sqlite3session_patchset // Lines 9355-9359 + chrome_sqlite3session_patchset // Lines 9454-9458 #define sqlite3session_patchset_strm \ - chrome_sqlite3session_patchset_strm // Lines 10245-10249 + chrome_sqlite3session_patchset_strm // Lines 10588-10592 #define sqlite3session_table_filter \ - chrome_sqlite3session_table_filter // Lines 9144-9151 + chrome_sqlite3session_table_filter // Lines 9240-9247 #endif // THIRD_PARTY_SQLITE_AMALGAMATION_RENAME_EXPORTS_H_
diff --git a/third_party/sqlite/amalgamation/shell/shell.c b/third_party/sqlite/amalgamation/shell/shell.c index 3f3ff3ae..0500581 100644 --- a/third_party/sqlite/amalgamation/shell/shell.c +++ b/third_party/sqlite/amalgamation/shell/shell.c
@@ -150,6 +150,9 @@ # ifndef access # define access(f,m) _access((f),(m)) # endif +# ifndef unlink +# define unlink _unlink +# endif # undef popen # define popen _popen # undef pclose @@ -997,7 +1000,10 @@ ** We need several data types from the Windows SDK header. */ +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif + #include "windows.h" /* @@ -2139,6 +2145,9 @@ # include <direct.h> /* # include "test_windirent.h" */ # define dirent DIRENT +# ifndef chmod +# define chmod _chmod +# endif # ifndef stat # define stat _stat # endif @@ -2205,6 +2214,97 @@ va_end(ap); } +#if defined(_WIN32) +/* +** This function is designed to convert a Win32 FILETIME structure into the +** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC). +*/ +static sqlite3_uint64 fileTimeToUnixTime( + LPFILETIME pFileTime +){ + SYSTEMTIME epochSystemTime; + ULARGE_INTEGER epochIntervals; + FILETIME epochFileTime; + ULARGE_INTEGER fileIntervals; + + memset(&epochSystemTime, 0, sizeof(SYSTEMTIME)); + epochSystemTime.wYear = 1970; + epochSystemTime.wMonth = 1; + epochSystemTime.wDay = 1; + SystemTimeToFileTime(&epochSystemTime, &epochFileTime); + epochIntervals.LowPart = epochFileTime.dwLowDateTime; + epochIntervals.HighPart = epochFileTime.dwHighDateTime; + + fileIntervals.LowPart = pFileTime->dwLowDateTime; + fileIntervals.HighPart = pFileTime->dwHighDateTime; + + return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; +} + +/* +** This function attempts to normalize the time values found in the stat() +** buffer to UTC. This is necessary on Win32, where the runtime library +** appears to return these values as local times. +*/ +static void statTimesToUtc( + const char *zPath, + struct stat *pStatBuf +){ + HANDLE hFindFile; + WIN32_FIND_DATAW fd; + LPWSTR zUnicodeName; + extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); + zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath); + if( zUnicodeName ){ + memset(&fd, 0, sizeof(WIN32_FIND_DATA)); + hFindFile = FindFirstFileW(zUnicodeName, &fd); + if( hFindFile!=NULL ){ + pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime); + pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime); + pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime); + FindClose(hFindFile); + } + sqlite3_free(zUnicodeName); + } +} +#endif + +/* +** This function is used in place of stat(). On Windows, special handling +** is required in order for the included time to be returned as UTC. On all +** other systems, this function simply calls stat(). +*/ +static int fileStat( + const char *zPath, + struct stat *pStatBuf +){ +#if defined(_WIN32) + int rc = stat(zPath, pStatBuf); + if( rc==0 ) statTimesToUtc(zPath, pStatBuf); + return rc; +#else + return stat(zPath, pStatBuf); +#endif +} + +/* +** This function is used in place of lstat(). On Windows, special handling +** is required in order for the included time to be returned as UTC. On all +** other systems, this function simply calls lstat(). +*/ +static int fileLinkStat( + const char *zPath, + struct stat *pStatBuf +){ +#if defined(_WIN32) + int rc = lstat(zPath, pStatBuf); + if( rc==0 ) statTimesToUtc(zPath, pStatBuf); + return rc; +#else + return lstat(zPath, pStatBuf); +#endif +} + /* ** Argument zFile is the name of a file that will be created and/or written ** by SQL function writefile(). This function ensures that the directory @@ -2236,7 +2336,7 @@ if( i==nCopy ) break; zCopy[i] = '\0'; - rc2 = stat(zCopy, &sStat); + rc2 = fileStat(zCopy, &sStat); if( rc2!=0 ){ if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR; }else{ @@ -2278,7 +2378,7 @@ ** to do so using chmod(), it is not an error. */ struct stat sStat; if( errno!=EEXIST - || 0!=stat(zFile, &sStat) + || 0!=fileStat(zFile, &sStat) || !S_ISDIR(sStat.st_mode) || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777)) ){ @@ -2316,15 +2416,23 @@ SYSTEMTIME currentTime; LONGLONG intervals; HANDLE hFile; + LPWSTR zUnicodeName; + extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); + GetSystemTime(¤tTime); SystemTimeToFileTime(¤tTime, &lastAccess); intervals = Int32x32To64(mtime, 10000000) + 116444736000000000; lastWrite.dwLowDateTime = (DWORD)intervals; lastWrite.dwHighDateTime = intervals >> 32; - hFile = CreateFile( - zFile, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, + zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile); + if( zUnicodeName==0 ){ + return 1; + } + hFile = CreateFileW( + zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); + sqlite3_free(zUnicodeName); if( hFile!=INVALID_HANDLE_VALUE ){ BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite); CloseHandle(hFile); @@ -2332,7 +2440,7 @@ }else{ return 1; } -#elif defined(AT_FDCWD) && 0 /* utimensat() is not univerally available */ +#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ /* Recent unix */ struct timespec times[2]; times[0].tv_nsec = times[1].tv_nsec = 0; @@ -2532,10 +2640,12 @@ sqlite3_free(pLvl->zDir); } sqlite3_free(pCur->zPath); + sqlite3_free(pCur->aLvl); pCur->aLvl = 0; pCur->zPath = 0; pCur->zBase = 0; pCur->nBase = 0; + pCur->nLvl = 0; pCur->iLvl = -1; pCur->iRowid = 1; } @@ -2547,7 +2657,6 @@ fsdir_cursor *pCur = (fsdir_cursor*)cur; fsdirResetCursor(pCur); - sqlite3_free(pCur->aLvl); sqlite3_free(pCur); return SQLITE_OK; } @@ -2608,7 +2717,7 @@ sqlite3_free(pCur->zPath); pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name); if( pCur->zPath==0 ) return SQLITE_NOMEM; - if( lstat(pCur->zPath, &pCur->sStat) ){ + if( fileLinkStat(pCur->zPath, &pCur->sStat) ){ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); return SQLITE_ERROR; } @@ -2742,7 +2851,7 @@ if( pCur->zPath==0 ){ return SQLITE_NOMEM; } - if( lstat(pCur->zPath, &pCur->sStat) ){ + if( fileLinkStat(pCur->zPath, &pCur->sStat) ){ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); return SQLITE_ERROR; } @@ -2949,7 +3058,7 @@ #define COMPLETION_INDEXES 5 #define COMPLETION_TRIGGERS 6 #define COMPLETION_DATABASES 7 -#define COMPLETION_TABLES 8 +#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */ #define COMPLETION_COLUMNS 9 #define COMPLETION_MODULES 10 #define COMPLETION_EOF 11 @@ -3121,8 +3230,7 @@ const char *zDb = (const char*)sqlite3_column_text(pS2, 1); zSql = sqlite3_mprintf( "%z%s" - "SELECT name FROM \"%w\".sqlite_master" - " WHERE type='table'", + "SELECT name FROM \"%w\".sqlite_master", zSql, zSep, zDb ); if( zSql==0 ) return SQLITE_NOMEM; @@ -3994,29 +4102,48 @@ #include <string.h> #include <assert.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#if !defined(_WIN32) && !defined(WIN32) -# include <unistd.h> -# include <dirent.h> -# include <utime.h> -#else -# include <io.h> -#endif -#include <time.h> -#include <errno.h> - #include <zlib.h> #ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_AMALGAMATION + /* typedef sqlite3_int64 i64; */ /* typedef unsigned char u8; */ typedef unsigned short u16; typedef unsigned long u32; #define MIN(a,b) ((a)<(b) ? (a) : (b)) + +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define ALWAYS(X) (1) +# define NEVER(X) (0) +#elif !defined(NDEBUG) +# define ALWAYS(X) ((X)?1:(assert(0),0)) +# define NEVER(X) ((X)?(assert(0),1):0) +#else +# define ALWAYS(X) (X) +# define NEVER(X) (X) +#endif + +#endif /* SQLITE_AMALGAMATION */ + +/* +** Definitions for mode bitmasks S_IFDIR, S_IFREG and S_IFLNK. +** +** In some ways it would be better to obtain these values from system +** header files. But, the dependency is undesirable and (a) these +** have been stable for decades, (b) the values are part of POSIX and +** are also made explicit in [man stat], and (c) are part of the +** file format for zip archives. +*/ +#ifndef S_IFDIR +# define S_IFDIR 0040000 +#endif +#ifndef S_IFREG +# define S_IFREG 0100000 +#endif +#ifndef S_IFLNK +# define S_IFLNK 0120000 #endif static const char ZIPFILE_SCHEMA[] = @@ -4057,6 +4184,9 @@ ** ** ZIPFILE_SIGNATURE_LFH: ** First 4 bytes of a valid LFH record. +** +** ZIPFILE_SIGNATURE_EOCD +** First 4 bytes of a valid EOCD record. */ #define ZIPFILE_EXTRA_TIMESTAMP 0x5455 #define ZIPFILE_NEWENTRY_MADEBY ((3<<8) + 30) @@ -4065,22 +4195,14 @@ #define ZIPFILE_SIGNATURE_CDS 0x02014b50 #define ZIPFILE_SIGNATURE_LFH 0x04034b50 #define ZIPFILE_SIGNATURE_EOCD 0x06054b50 -#define ZIPFILE_LFH_FIXED_SZ 30 /* -** Set the error message contained in context ctx to the results of -** vprintf(zFmt, ...). +** The sizes of the fixed-size part of each of the three main data +** structures in a zip archive. */ -static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ - char *zMsg = 0; - va_list ap; - va_start(ap, zFmt); - zMsg = sqlite3_vmprintf(zFmt, ap); - sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); - va_end(ap); -} - +#define ZIPFILE_LFH_FIXED_SZ 30 +#define ZIPFILE_EOCD_FIXED_SZ 22 +#define ZIPFILE_CDS_FIXED_SZ 46 /* *** 4.3.16 End of central directory record: @@ -4186,47 +4308,39 @@ typedef struct ZipfileEntry ZipfileEntry; struct ZipfileEntry { - char *zPath; /* Path of zipfile entry */ - u8 *aCdsEntry; /* Buffer containing entire CDS entry */ - int nCdsEntry; /* Size of buffer aCdsEntry[] in bytes */ - int bDeleted; /* True if entry has been deleted */ + ZipfileCDS cds; /* Parsed CDS record */ + u32 mUnixTime; /* Modification time, in UNIX format */ + u8 *aExtra; /* cds.nExtra+cds.nComment bytes of extra data */ + i64 iDataOff; /* Offset to data in file (if aData==0) */ + u8 *aData; /* cds.szCompressed bytes of compressed data */ ZipfileEntry *pNext; /* Next element in in-memory CDS */ }; /* -** Cursor type for recursively iterating through a directory structure. +** Cursor type for zipfile tables. */ typedef struct ZipfileCsr ZipfileCsr; struct ZipfileCsr { sqlite3_vtab_cursor base; /* Base class - must be first */ i64 iId; /* Cursor ID */ - int bEof; /* True when at EOF */ + u8 bEof; /* True when at EOF */ + u8 bNoop; /* If next xNext() call is no-op */ /* Used outside of write transactions */ FILE *pFile; /* Zip file */ i64 iNextOff; /* Offset of next record in central directory */ ZipfileEOCD eocd; /* Parse of central directory record */ - /* Used inside write transactions */ - ZipfileEntry *pCurrent; - - ZipfileCDS cds; /* Central Directory Structure */ - ZipfileLFH lfh; /* Local File Header for current entry */ - i64 iDataOff; /* Offset in zipfile to data */ - u32 mTime; /* Extended mtime value */ - int flags; /* Flags byte (see below for bits) */ + ZipfileEntry *pFreeEntry; /* Free this list when cursor is closed or reset */ + ZipfileEntry *pCurrent; /* Current entry */ ZipfileCsr *pCsrNext; /* Next cursor on same virtual table */ }; -/* -** Values for ZipfileCsr.flags. -*/ -#define ZIPFILE_MTIME_VALID 0x0001 - typedef struct ZipfileTab ZipfileTab; struct ZipfileTab { sqlite3_vtab base; /* Base class - must be first */ char *zFile; /* Zip file this table accesses (may be NULL) */ + sqlite3 *db; /* Host database connection */ u8 *aBuffer; /* Temporary buffer used for various tasks */ ZipfileCsr *pCsrList; /* List of cursors */ @@ -4240,17 +4354,33 @@ i64 szOrig; /* Size of archive at start of transaction */ }; +/* +** Set the error message contained in context ctx to the results of +** vprintf(zFmt, ...). +*/ +static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ + char *zMsg = 0; + va_list ap; + va_start(ap, zFmt); + zMsg = sqlite3_vmprintf(zFmt, ap); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); + va_end(ap); +} + +/* +** If string zIn is quoted, dequote it in place. Otherwise, if the string +** is not quoted, do nothing. +*/ static void zipfileDequote(char *zIn){ char q = zIn[0]; if( q=='"' || q=='\'' || q=='`' || q=='[' ){ - char c; int iIn = 1; int iOut = 0; if( q=='[' ) q = ']'; - while( (c = zIn[iIn++]) ){ - if( c==q ){ - if( zIn[iIn++]!=q ) break; - } + while( ALWAYS(zIn[iIn]) ){ + char c = zIn[iIn++]; + if( c==q && zIn[iIn++]!=q ) break; zIn[iOut++] = c; } zIn[iOut] = '\0'; @@ -4278,6 +4408,21 @@ ZipfileTab *pNew = 0; int rc; + /* If the table name is not "zipfile", require that the argument be + ** specified. This stops zipfile tables from being created as: + ** + ** CREATE VIRTUAL TABLE zzz USING zipfile(); + ** + ** It does not prevent: + ** + ** CREATE VIRTUAL TABLE zipfile USING zipfile(); + */ + assert( 0==sqlite3_stricmp(argv[0], "zipfile") ); + if( (0!=sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){ + *pzErr = sqlite3_mprintf("zipfile constructor requires one argument"); + return SQLITE_ERROR; + } + if( argc>3 ){ zFile = argv[3]; nFile = (int)strlen(zFile)+1; @@ -4288,6 +4433,7 @@ pNew = (ZipfileTab*)sqlite3_malloc(nByte+nFile); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, nByte+nFile); + pNew->db = db; pNew->aBuffer = (u8*)&pNew[1]; if( zFile ){ pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE]; @@ -4300,9 +4446,42 @@ } /* +** Free the ZipfileEntry structure indicated by the only argument. +*/ +static void zipfileEntryFree(ZipfileEntry *p){ + if( p ){ + sqlite3_free(p->cds.zFile); + sqlite3_free(p); + } +} + +/* +** Release resources that should be freed at the end of a write +** transaction. +*/ +static void zipfileCleanupTransaction(ZipfileTab *pTab){ + ZipfileEntry *pEntry; + ZipfileEntry *pNext; + + if( pTab->pWriteFd ){ + fclose(pTab->pWriteFd); + pTab->pWriteFd = 0; + } + for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){ + pNext = pEntry->pNext; + zipfileEntryFree(pEntry); + } + pTab->pFirstEntry = 0; + pTab->pLastEntry = 0; + pTab->szCurrent = 0; + pTab->szOrig = 0; +} + +/* ** This method is the destructor for zipfile vtab objects. */ static int zipfileDisconnect(sqlite3_vtab *pVtab){ + zipfileCleanupTransaction((ZipfileTab*)pVtab); sqlite3_free(pVtab); return SQLITE_OK; } @@ -4330,12 +4509,20 @@ ** by zipfileOpen(). */ static void zipfileResetCursor(ZipfileCsr *pCsr){ - sqlite3_free(pCsr->cds.zFile); - pCsr->cds.zFile = 0; + ZipfileEntry *p; + ZipfileEntry *pNext; + pCsr->bEof = 0; if( pCsr->pFile ){ fclose(pCsr->pFile); pCsr->pFile = 0; + zipfileEntryFree(pCsr->pCurrent); + pCsr->pCurrent = 0; + } + + for(p=pCsr->pFreeEntry; p; p=pNext){ + pNext = p->pNext; + zipfileEntryFree(p); } } @@ -4349,12 +4536,8 @@ zipfileResetCursor(pCsr); /* Remove this cursor from the ZipfileTab.pCsrList list. */ - for(pp=&pTab->pCsrList; *pp; pp=&((*pp)->pCsrNext)){ - if( *pp==pCsr ){ - *pp = pCsr->pCsrNext; - break; - } - } + for(pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext)); + *pp = pCsr->pCsrNext; sqlite3_free(pCsr); return SQLITE_OK; @@ -4364,13 +4547,31 @@ ** Set the error message for the virtual table associated with cursor ** pCsr to the results of vprintf(zFmt, ...). */ -static void zipfileSetErrmsg(ZipfileCsr *pCsr, const char *zFmt, ...){ +static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){ va_list ap; va_start(ap, zFmt); + sqlite3_free(pTab->base.zErrMsg); + pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap); + va_end(ap); +} +static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){ + va_list ap; + va_start(ap, zFmt); + sqlite3_free(pCsr->base.pVtab->zErrMsg); pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap); va_end(ap); } +/* +** Read nRead bytes of data from offset iOff of file pFile into buffer +** aRead[]. Return SQLITE_OK if successful, or an SQLite error code +** otherwise. +** +** If an error does occur, output variable (*pzErrmsg) may be set to point +** to an English language error message. It is the responsibility of the +** caller to eventually free this buffer using +** sqlite3_free(). +*/ static int zipfileReadData( FILE *pFile, /* Read from this file */ u8 *aRead, /* Read into this buffer */ @@ -4404,9 +4605,16 @@ return SQLITE_OK; } +/* +** Read and return a 16-bit little-endian unsigned integer from buffer aBuf. +*/ static u16 zipfileGetU16(const u8 *aBuf){ return (aBuf[1] << 8) + aBuf[0]; } + +/* +** Read and return a 32-bit little-endian unsigned integer from buffer aBuf. +*/ static u32 zipfileGetU32(const u8 *aBuf){ return ((u32)(aBuf[3]) << 24) + ((u32)(aBuf[2]) << 16) @@ -4414,10 +4622,17 @@ + ((u32)(aBuf[0]) << 0); } +/* +** Write a 16-bit little endiate integer into buffer aBuf. +*/ static void zipfilePutU16(u8 *aBuf, u16 val){ aBuf[0] = val & 0xFF; aBuf[1] = (val>>8) & 0xFF; } + +/* +** Write a 32-bit little endiate integer into buffer aBuf. +*/ static void zipfilePutU32(u8 *aBuf, u32 val){ aBuf[0] = val & 0xFF; aBuf[1] = (val>>8) & 0xFF; @@ -4431,15 +4646,11 @@ #define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; } #define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; } -static u8* zipfileCsrBuffer(ZipfileCsr *pCsr){ - return ((ZipfileTab*)(pCsr->base.pVtab))->aBuffer; -} - /* ** Magic numbers used to read CDS records. */ -#define ZIPFILE_CDS_FIXED_SZ 46 #define ZIPFILE_CDS_NFILE_OFF 28 +#define ZIPFILE_CDS_SZCOMPRESSED_OFF 20 /* ** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR @@ -4476,168 +4687,80 @@ } /* -** Read the CDS record for the current entry from disk into pCsr->cds. +** Decode the LFH record in buffer aBuf into (*pLFH). Return SQLITE_ERROR +** if the record is not well-formed, or SQLITE_OK otherwise. */ -static int zipfileCsrReadCDS(ZipfileCsr *pCsr){ - char **pzErr = &pCsr->base.pVtab->zErrMsg; - u8 *aRead; - int rc = SQLITE_OK; - - sqlite3_free(pCsr->cds.zFile); - pCsr->cds.zFile = 0; - - if( pCsr->pCurrent==0 ){ - aRead = zipfileCsrBuffer(pCsr); - rc = zipfileReadData( - pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr - ); - }else{ - aRead = pCsr->pCurrent->aCdsEntry; - } - - if( rc==SQLITE_OK ){ - rc = zipfileReadCDS(aRead, &pCsr->cds); - if( rc!=SQLITE_OK ){ - assert( pCsr->pCurrent==0 ); - zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff); - }else{ - int nRead; - if( pCsr->pCurrent==0 ){ - nRead = pCsr->cds.nFile + pCsr->cds.nExtra; - aRead = zipfileCsrBuffer(pCsr); - pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ; - rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr); - }else{ - aRead = &aRead[ZIPFILE_CDS_FIXED_SZ]; - } - - if( rc==SQLITE_OK ){ - pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead); - pCsr->iNextOff += pCsr->cds.nFile; - pCsr->iNextOff += pCsr->cds.nExtra; - pCsr->iNextOff += pCsr->cds.nComment; - } - - /* Scan the cds.nExtra bytes of "extra" fields for any that can - ** be interpreted. The general format of an extra field is: - ** - ** Header ID 2 bytes - ** Data Size 2 bytes - ** Data N bytes - ** - */ - if( rc==SQLITE_OK ){ - u8 *p = &aRead[pCsr->cds.nFile]; - u8 *pEnd = &p[pCsr->cds.nExtra]; - - while( p<pEnd ){ - u16 id = zipfileRead16(p); - u16 nByte = zipfileRead16(p); - - switch( id ){ - case ZIPFILE_EXTRA_TIMESTAMP: { - u8 b = p[0]; - if( b & 0x01 ){ /* 0x01 -> modtime is present */ - pCsr->mTime = zipfileGetU32(&p[1]); - pCsr->flags |= ZIPFILE_MTIME_VALID; - } - break; - } - } - - p += nByte; - } - } - } - } - - return rc; -} - -static FILE *zipfileGetFd(ZipfileCsr *pCsr){ - if( pCsr->pFile ) return pCsr->pFile; - return ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd; -} - static int zipfileReadLFH( - FILE *pFd, - i64 iOffset, - u8 *aTmp, - ZipfileLFH *pLFH, - char **pzErr + u8 *aBuffer, + ZipfileLFH *pLFH ){ - u8 *aRead = aTmp; - static const int szFix = ZIPFILE_LFH_FIXED_SZ; - int rc; - - rc = zipfileReadData(pFd, aRead, szFix, iOffset, pzErr); - if( rc==SQLITE_OK ){ - u32 sig = zipfileRead32(aRead); - if( sig!=ZIPFILE_SIGNATURE_LFH ){ - *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", (int)iOffset); - rc = SQLITE_ERROR; - }else{ - pLFH->iVersionExtract = zipfileRead16(aRead); - pLFH->flags = zipfileRead16(aRead); - pLFH->iCompression = zipfileRead16(aRead); - pLFH->mTime = zipfileRead16(aRead); - pLFH->mDate = zipfileRead16(aRead); - pLFH->crc32 = zipfileRead32(aRead); - pLFH->szCompressed = zipfileRead32(aRead); - pLFH->szUncompressed = zipfileRead32(aRead); - pLFH->nFile = zipfileRead16(aRead); - pLFH->nExtra = zipfileRead16(aRead); - assert( aRead==&aTmp[szFix] ); - } - } - return rc; -} - -static int zipfileCsrReadLFH(ZipfileCsr *pCsr){ - FILE *pFile = zipfileGetFd(pCsr); - char **pzErr = &pCsr->base.pVtab->zErrMsg; - u8 *aRead = zipfileCsrBuffer(pCsr); - int rc = zipfileReadLFH(pFile, pCsr->cds.iOffset, aRead, &pCsr->lfh, pzErr); - pCsr->iDataOff = pCsr->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; - pCsr->iDataOff += pCsr->lfh.nFile+pCsr->lfh.nExtra; - return rc; -} - - -/* -** Advance an ZipfileCsr to its next row of output. -*/ -static int zipfileNext(sqlite3_vtab_cursor *cur){ - ZipfileCsr *pCsr = (ZipfileCsr*)cur; + u8 *aRead = aBuffer; int rc = SQLITE_OK; - pCsr->flags = 0; - if( pCsr->pCurrent==0 ){ - i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; - if( pCsr->iNextOff>=iEof ){ - pCsr->bEof = 1; - } + u32 sig = zipfileRead32(aRead); + if( sig!=ZIPFILE_SIGNATURE_LFH ){ + rc = SQLITE_ERROR; }else{ - assert( pCsr->pFile==0 ); - do { - pCsr->pCurrent = pCsr->pCurrent->pNext; - }while( pCsr->pCurrent && pCsr->pCurrent->bDeleted ); - if( pCsr->pCurrent==0 ){ - pCsr->bEof = 1; - } + pLFH->iVersionExtract = zipfileRead16(aRead); + pLFH->flags = zipfileRead16(aRead); + pLFH->iCompression = zipfileRead16(aRead); + pLFH->mTime = zipfileRead16(aRead); + pLFH->mDate = zipfileRead16(aRead); + pLFH->crc32 = zipfileRead32(aRead); + pLFH->szCompressed = zipfileRead32(aRead); + pLFH->szUncompressed = zipfileRead32(aRead); + pLFH->nFile = zipfileRead16(aRead); + pLFH->nExtra = zipfileRead16(aRead); } - - if( pCsr->bEof==0 ){ - rc = zipfileCsrReadCDS(pCsr); - if( rc==SQLITE_OK ){ - rc = zipfileCsrReadLFH(pCsr); - } - } - return rc; } + /* +** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields. +** Scan through this buffer to find an "extra-timestamp" field. If one +** exists, extract the 32-bit modification-timestamp from it and store +** the value in output parameter *pmTime. +** +** Zero is returned if no extra-timestamp record could be found (and so +** *pmTime is left unchanged), or non-zero otherwise. +** +** The general format of an extra field is: +** +** Header ID 2 bytes +** Data Size 2 bytes +** Data N bytes +*/ +static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){ + int ret = 0; + u8 *p = aExtra; + u8 *pEnd = &aExtra[nExtra]; + + while( p<pEnd ){ + u16 id = zipfileRead16(p); + u16 nByte = zipfileRead16(p); + + switch( id ){ + case ZIPFILE_EXTRA_TIMESTAMP: { + u8 b = p[0]; + if( b & 0x01 ){ /* 0x01 -> modtime is present */ + *pmTime = zipfileGetU32(&p[1]); + ret = 1; + } + break; + } + } + + p += nByte; + } + return ret; +} + +/* +** Convert the standard MS-DOS timestamp stored in the mTime and mDate +** fields of the CDS structure passed as the only argument to a 32-bit +** UNIX seconds-since-the-epoch timestamp. Return the result. +** ** "Standard" MS-DOS time format: ** ** File modification time: @@ -4648,44 +4771,237 @@ ** Bits 00-04: day ** Bits 05-08: month (1-12) ** Bits 09-15: years from 1980 +** +** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx */ -static time_t zipfileMtime(ZipfileCsr *pCsr){ - struct tm t; - memset(&t, 0, sizeof(t)); - t.tm_sec = (pCsr->cds.mTime & 0x1F)*2; - t.tm_min = (pCsr->cds.mTime >> 5) & 0x2F; - t.tm_hour = (pCsr->cds.mTime >> 11) & 0x1F; +static u32 zipfileMtime(ZipfileCDS *pCDS){ + int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F)); + int M = ((pCDS->mDate >> 5) & 0x0F); + int D = (pCDS->mDate & 0x1F); + int B = -13; - t.tm_mday = (pCsr->cds.mDate & 0x1F); - t.tm_mon = ((pCsr->cds.mDate >> 5) & 0x0F) - 1; - t.tm_year = 80 + ((pCsr->cds.mDate >> 9) & 0x7F); + int sec = (pCDS->mTime & 0x1F)*2; + int min = (pCDS->mTime >> 5) & 0x3F; + int hr = (pCDS->mTime >> 11) & 0x1F; + i64 JD; - return mktime(&t); + /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */ + + /* Calculate the JD in seconds for noon on the day in question */ + if( M<3 ){ + Y = Y-1; + M = M+12; + } + JD = (i64)(24*60*60) * ( + (int)(365.25 * (Y + 4716)) + + (int)(30.6001 * (M + 1)) + + D + B - 1524 + ); + + /* Correct the JD for the time within the day */ + JD += (hr-12) * 3600 + min * 60 + sec; + + /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */ + return (u32)(JD - (i64)(24405875) * 24*60*6); } -static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mTime){ - time_t t = (time_t)mTime; - struct tm res; +/* +** The opposite of zipfileMtime(). This function populates the mTime and +** mDate fields of the CDS structure passed as the first argument according +** to the UNIX timestamp value passed as the second. +*/ +static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){ + /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */ + i64 JD = (i64)2440588 + mUnixTime / (24*60*60); -#if !defined(_WIN32) && !defined(WIN32) - localtime_r(&t, &res); -#else - memcpy(&res, localtime(&t), sizeof(struct tm)); -#endif + int A, B, C, D, E; + int yr, mon, day; + int hr, min, sec; - pCds->mTime = (u16)( - (res.tm_sec / 2) + - (res.tm_min << 5) + - (res.tm_hour << 11)); + A = (int)((JD - 1867216.25)/36524.25); + A = (int)(JD + 1 + A - (A/4)); + B = A + 1524; + C = (int)((B - 122.1)/365.25); + D = (36525*(C&32767))/100; + E = (int)((B-D)/30.6001); - pCds->mDate = (u16)( - (res.tm_mday-1) + - ((res.tm_mon+1) << 5) + - ((res.tm_year-80) << 9)); + day = B - D - (int)(30.6001*E); + mon = (E<14 ? E-1 : E-13); + yr = mon>2 ? C-4716 : C-4715; + + hr = (mUnixTime % (24*60*60)) / (60*60); + min = (mUnixTime % (60*60)) / 60; + sec = (mUnixTime % 60); + + if( yr>=1980 ){ + pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9)); + pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11)); + }else{ + pCds->mDate = pCds->mTime = 0; + } + + assert( mUnixTime<315507600 + || mUnixTime==zipfileMtime(pCds) + || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds)) + /* || (mUnixTime % 2) */ + ); } +/* +** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in +** size) containing an entire zip archive image. Or, if aBlob is NULL, +** then pFile is a file-handle open on a zip file. In either case, this +** function creates a ZipfileEntry object based on the zip archive entry +** for which the CDS record is at offset iOff. +** +** If successful, SQLITE_OK is returned and (*ppEntry) set to point to +** the new object. Otherwise, an SQLite error code is returned and the +** final value of (*ppEntry) undefined. +*/ +static int zipfileGetEntry( + ZipfileTab *pTab, /* Store any error message here */ + const u8 *aBlob, /* Pointer to in-memory file image */ + int nBlob, /* Size of aBlob[] in bytes */ + FILE *pFile, /* If aBlob==0, read from this file */ + i64 iOff, /* Offset of CDS record */ + ZipfileEntry **ppEntry /* OUT: Pointer to new object */ +){ + u8 *aRead; + char **pzErr = &pTab->base.zErrMsg; + int rc = SQLITE_OK; + + if( aBlob==0 ){ + aRead = pTab->aBuffer; + rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr); + }else{ + aRead = (u8*)&aBlob[iOff]; + } + + if( rc==SQLITE_OK ){ + int nAlloc; + ZipfileEntry *pNew; + + int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]); + int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]); + nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]); + + nAlloc = sizeof(ZipfileEntry) + nExtra; + if( aBlob ){ + nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]); + } + + pNew = (ZipfileEntry*)sqlite3_malloc(nAlloc); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(ZipfileEntry)); + rc = zipfileReadCDS(aRead, &pNew->cds); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff); + }else if( aBlob==0 ){ + rc = zipfileReadData( + pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr + ); + }else{ + aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; + } + } + + if( rc==SQLITE_OK ){ + u32 *pt = &pNew->mUnixTime; + pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead); + pNew->aExtra = (u8*)&pNew[1]; + memcpy(pNew->aExtra, &aRead[nFile], nExtra); + if( pNew->cds.zFile==0 ){ + rc = SQLITE_NOMEM; + }else if( 0==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){ + pNew->mUnixTime = zipfileMtime(&pNew->cds); + } + } + + if( rc==SQLITE_OK ){ + static const int szFix = ZIPFILE_LFH_FIXED_SZ; + ZipfileLFH lfh; + if( pFile ){ + rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); + }else{ + aRead = (u8*)&aBlob[pNew->cds.iOffset]; + } + + rc = zipfileReadLFH(aRead, &lfh); + if( rc==SQLITE_OK ){ + pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; + pNew->iDataOff += lfh.nFile + lfh.nExtra; + if( aBlob && pNew->cds.szCompressed ){ + pNew->aData = &pNew->aExtra[nExtra]; + memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed); + } + }else{ + *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", + (int)pNew->cds.iOffset + ); + } + } + + if( rc!=SQLITE_OK ){ + zipfileEntryFree(pNew); + }else{ + *ppEntry = pNew; + } + } + + return rc; +} + +/* +** Advance an ZipfileCsr to its next row of output. +*/ +static int zipfileNext(sqlite3_vtab_cursor *cur){ + ZipfileCsr *pCsr = (ZipfileCsr*)cur; + int rc = SQLITE_OK; + + if( pCsr->pFile ){ + i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; + zipfileEntryFree(pCsr->pCurrent); + pCsr->pCurrent = 0; + if( pCsr->iNextOff>=iEof ){ + pCsr->bEof = 1; + }else{ + ZipfileEntry *p = 0; + ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab); + rc = zipfileGetEntry(pTab, 0, 0, pCsr->pFile, pCsr->iNextOff, &p); + if( rc==SQLITE_OK ){ + pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ; + pCsr->iNextOff += (int)p->cds.nExtra + p->cds.nFile + p->cds.nComment; + } + pCsr->pCurrent = p; + } + }else{ + if( !pCsr->bNoop ){ + pCsr->pCurrent = pCsr->pCurrent->pNext; + } + if( pCsr->pCurrent==0 ){ + pCsr->bEof = 1; + } + } + + pCsr->bNoop = 0; + return rc; +} + +static void zipfileFree(void *p) { + sqlite3_free(p); +} + +/* +** Buffer aIn (size nIn bytes) contains compressed data. Uncompressed, the +** size is nOut bytes. This function uncompresses the data and sets the +** return value in context pCtx to the result (a blob). +** +** If an error occurs, an error code is left in pCtx instead. +*/ static void zipfileInflate( - sqlite3_context *pCtx, /* Store error here, if any */ + sqlite3_context *pCtx, /* Store result here */ const u8 *aIn, /* Compressed data */ int nIn, /* Size of buffer aIn[] in bytes */ int nOut /* Expected output size */ @@ -4711,7 +5027,8 @@ if( err!=Z_STREAM_END ){ zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err); }else{ - sqlite3_result_blob(pCtx, aRes, nOut, SQLITE_TRANSIENT); + sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree); + aRes = 0; } } sqlite3_free(aRes); @@ -4719,10 +5036,22 @@ } } +/* +** Buffer aIn (size nIn bytes) contains uncompressed data. This function +** compresses it and sets (*ppOut) to point to a buffer containing the +** compressed data. The caller is responsible for eventually calling +** sqlite3_free() to release buffer (*ppOut). Before returning, (*pnOut) +** is set to the size of buffer (*ppOut) in bytes. +** +** If no error occurs, SQLITE_OK is returned. Otherwise, an SQLite error +** code is returned and an error message left in virtual-table handle +** pTab. The values of (*ppOut) and (*pnOut) are left unchanged in this +** case. +*/ static int zipfileDeflate( - ZipfileTab *pTab, /* Set error message here */ const u8 *aIn, int nIn, /* Input */ - u8 **ppOut, int *pnOut /* Output */ + u8 **ppOut, int *pnOut, /* Output */ + char **pzErr /* OUT: Error message */ ){ int nAlloc = (int)compressBound(nIn); u8 *aOut; @@ -4748,7 +5077,7 @@ *pnOut = (int)str.total_out; }else{ sqlite3_free(aOut); - pTab->base.zErrMsg = sqlite3_mprintf("zipfile: deflate() error"); + *pzErr = sqlite3_mprintf("zipfile: deflate() error"); rc = SQLITE_ERROR; } deflateEnd(&str); @@ -4768,60 +5097,66 @@ int i /* Which column to return */ ){ ZipfileCsr *pCsr = (ZipfileCsr*)cur; + ZipfileCDS *pCDS = &pCsr->pCurrent->cds; int rc = SQLITE_OK; switch( i ){ case 0: /* name */ - sqlite3_result_text(ctx, pCsr->cds.zFile, -1, SQLITE_TRANSIENT); + sqlite3_result_text(ctx, pCDS->zFile, -1, SQLITE_TRANSIENT); break; case 1: /* mode */ /* TODO: Whether or not the following is correct surely depends on ** the platform on which the archive was created. */ - sqlite3_result_int(ctx, pCsr->cds.iExternalAttr >> 16); + sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16); break; case 2: { /* mtime */ - if( pCsr->flags & ZIPFILE_MTIME_VALID ){ - sqlite3_result_int64(ctx, pCsr->mTime); - }else{ - sqlite3_result_int64(ctx, zipfileMtime(pCsr)); - } + sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime); break; } case 3: { /* sz */ if( sqlite3_vtab_nochange(ctx)==0 ){ - sqlite3_result_int64(ctx, pCsr->cds.szUncompressed); + sqlite3_result_int64(ctx, pCDS->szUncompressed); } break; } case 4: /* rawdata */ if( sqlite3_vtab_nochange(ctx) ) break; case 5: { /* data */ - if( i==4 || pCsr->cds.iCompression==0 || pCsr->cds.iCompression==8 ){ - int sz = pCsr->cds.szCompressed; - int szFinal = pCsr->cds.szUncompressed; + if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){ + int sz = pCDS->szCompressed; + int szFinal = pCDS->szUncompressed; if( szFinal>0 ){ - u8 *aBuf = sqlite3_malloc(sz); - if( aBuf==0 ){ - rc = SQLITE_NOMEM; + u8 *aBuf; + u8 *aFree = 0; + if( pCsr->pCurrent->aData ){ + aBuf = pCsr->pCurrent->aData; }else{ - FILE *pFile = zipfileGetFd(pCsr); - rc = zipfileReadData(pFile, aBuf, sz, pCsr->iDataOff, - &pCsr->base.pVtab->zErrMsg - ); + aBuf = aFree = sqlite3_malloc(sz); + if( aBuf==0 ){ + rc = SQLITE_NOMEM; + }else{ + FILE *pFile = pCsr->pFile; + if( pFile==0 ){ + pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd; + } + rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff, + &pCsr->base.pVtab->zErrMsg + ); + } } if( rc==SQLITE_OK ){ - if( i==5 && pCsr->cds.iCompression ){ + if( i==5 && pCDS->iCompression ){ zipfileInflate(ctx, aBuf, sz, szFinal); }else{ sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT); } - sqlite3_free(aBuf); } + sqlite3_free(aFree); }else{ /* Figure out if this is a directory or a zero-sized file. Consider ** it to be a directory either if the mode suggests so, or if ** the final character in the name is '/'. */ - u32 mode = pCsr->cds.iExternalAttr >> 16; - if( !(mode & S_IFDIR) && pCsr->cds.zFile[pCsr->cds.nFile-1]!='/' ){ + u32 mode = pCDS->iExternalAttr >> 16; + if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){ sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC); } } @@ -4829,9 +5164,10 @@ break; } case 6: /* method */ - sqlite3_result_int(ctx, pCsr->cds.iCompression); + sqlite3_result_int(ctx, pCDS->iCompression); break; - case 7: /* z */ + default: /* z */ + assert( i==7 ); sqlite3_result_int64(ctx, pCsr->iId); break; } @@ -4840,16 +5176,7 @@ } /* -** Return the rowid for the current row. -*/ -static int zipfileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - assert( 0 ); - return SQLITE_OK; -} - -/* -** Return TRUE if the cursor has been moved off of the last -** row of output. +** Return TRUE if the cursor is at EOF. */ static int zipfileEof(sqlite3_vtab_cursor *cur){ ZipfileCsr *pCsr = (ZipfileCsr*)cur; @@ -4857,28 +5184,43 @@ } /* +** If aBlob is not NULL, then it points to a buffer nBlob bytes in size +** containing an entire zip archive image. Or, if aBlob is NULL, then pFile +** is guaranteed to be a file-handle open on a zip file. +** +** This function attempts to locate the EOCD record within the zip archive +** and populate *pEOCD with the results of decoding it. SQLITE_OK is +** returned if successful. Otherwise, an SQLite error code is returned and +** an English language error message may be left in virtual-table pTab. */ static int zipfileReadEOCD( ZipfileTab *pTab, /* Return errors here */ - FILE *pFile, /* Read from this file */ + const u8 *aBlob, /* Pointer to in-memory file image */ + int nBlob, /* Size of aBlob[] in bytes */ + FILE *pFile, /* Read from this file if aBlob==0 */ ZipfileEOCD *pEOCD /* Object to populate */ ){ u8 *aRead = pTab->aBuffer; /* Temporary buffer */ - i64 szFile; /* Total size of file in bytes */ int nRead; /* Bytes to read from file */ - i64 iOff; /* Offset to read from */ - int rc; + int rc = SQLITE_OK; - fseek(pFile, 0, SEEK_END); - szFile = (i64)ftell(pFile); - if( szFile==0 ){ - memset(pEOCD, 0, sizeof(ZipfileEOCD)); - return SQLITE_OK; + if( aBlob==0 ){ + i64 iOff; /* Offset to read from */ + i64 szFile; /* Total size of file in bytes */ + fseek(pFile, 0, SEEK_END); + szFile = (i64)ftell(pFile); + if( szFile==0 ){ + memset(pEOCD, 0, sizeof(ZipfileEOCD)); + return SQLITE_OK; + } + nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); + iOff = szFile - nRead; + rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg); + }else{ + nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE)); + aRead = (u8*)&aBlob[nBlob-nRead]; } - nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); - iOff = szFile - nRead; - rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg); if( rc==SQLITE_OK ){ int i; @@ -4904,17 +5246,59 @@ pEOCD->nEntryTotal = zipfileRead16(aRead); pEOCD->nSize = zipfileRead32(aRead); pEOCD->iOffset = zipfileRead32(aRead); - -#if 0 - printf("iDisk=%d iFirstDisk=%d nEntry=%d " - "nEntryTotal=%d nSize=%d iOffset=%d", - (int)pEOCD->iDisk, (int)pEOCD->iFirstDisk, (int)pEOCD->nEntry, - (int)pEOCD->nEntryTotal, (int)pEOCD->nSize, (int)pEOCD->iOffset - ); -#endif } - return SQLITE_OK; + return rc; +} + +/* +** Add object pNew to the linked list that begins at ZipfileTab.pFirstEntry +** and ends with pLastEntry. If argument pBefore is NULL, then pNew is added +** to the end of the list. Otherwise, it is added to the list immediately +** before pBefore (which is guaranteed to be a part of said list). +*/ +static void zipfileAddEntry( + ZipfileTab *pTab, + ZipfileEntry *pBefore, + ZipfileEntry *pNew +){ + assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) ); + assert( pNew->pNext==0 ); + if( pBefore==0 ){ + if( pTab->pFirstEntry==0 ){ + pTab->pFirstEntry = pTab->pLastEntry = pNew; + }else{ + assert( pTab->pLastEntry->pNext==0 ); + pTab->pLastEntry->pNext = pNew; + pTab->pLastEntry = pNew; + } + }else{ + ZipfileEntry **pp; + for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext)); + pNew->pNext = pBefore; + *pp = pNew; + } +} + +static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){ + ZipfileEOCD eocd; + int rc; + int i; + i64 iOff; + + rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd); + iOff = eocd.iOffset; + for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){ + ZipfileEntry *pNew = 0; + rc = zipfileGetEntry(pTab, aBlob, nBlob, pTab->pWriteFd, iOff, &pNew); + + if( rc==SQLITE_OK ){ + zipfileAddEntry(pTab, 0, pNew); + iOff += ZIPFILE_CDS_FIXED_SZ; + iOff += (int)pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment; + } + } + return rc; } /* @@ -4927,29 +5311,37 @@ ){ ZipfileTab *pTab = (ZipfileTab*)cur->pVtab; ZipfileCsr *pCsr = (ZipfileCsr*)cur; - const char *zFile; /* Zip file to scan */ + const char *zFile = 0; /* Zip file to scan */ int rc = SQLITE_OK; /* Return Code */ + int bInMemory = 0; /* True for an in-memory zipfile */ zipfileResetCursor(pCsr); if( pTab->zFile ){ zFile = pTab->zFile; }else if( idxNum==0 ){ - /* Error. This is an eponymous virtual table and the user has not - ** supplied a file name. */ - zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument"); + zipfileCursorErr(pCsr, "zipfile() function requires an argument"); return SQLITE_ERROR; + }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]); + int nBlob = sqlite3_value_bytes(argv[0]); + assert( pTab->pFirstEntry==0 ); + rc = zipfileLoadDirectory(pTab, aBlob, nBlob); + pCsr->pFreeEntry = pTab->pFirstEntry; + pTab->pFirstEntry = pTab->pLastEntry = 0; + if( rc!=SQLITE_OK ) return rc; + bInMemory = 1; }else{ zFile = (const char*)sqlite3_value_text(argv[0]); } - if( pTab->pWriteFd==0 ){ + if( 0==pTab->pWriteFd && 0==bInMemory ){ pCsr->pFile = fopen(zFile, "rb"); if( pCsr->pFile==0 ){ - zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile); + zipfileCursorErr(pCsr, "cannot open file: %s", zFile); rc = SQLITE_ERROR; }else{ - rc = zipfileReadEOCD(pTab, pCsr->pFile, &pCsr->eocd); + rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd); if( rc==SQLITE_OK ){ if( pCsr->eocd.nEntry==0 ){ pCsr->bEof = 1; @@ -4960,12 +5352,9 @@ } } }else{ - ZipfileEntry e; - memset(&e, 0, sizeof(e)); - e.pNext = pTab->pFirstEntry; - pCsr->pCurrent = &e; + pCsr->bNoop = 1; + pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry; rc = zipfileNext(cur); - assert( pCsr->pCurrent!=&e ); } return rc; @@ -5001,182 +5390,67 @@ return SQLITE_OK; } -/* -** Add object pNew to the end of the linked list that begins at -** ZipfileTab.pFirstEntry and ends with pLastEntry. -*/ -static void zipfileAddEntry( - ZipfileTab *pTab, - ZipfileEntry *pBefore, - ZipfileEntry *pNew -){ - assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) ); - assert( pNew->pNext==0 ); - if( pBefore==0 ){ - if( pTab->pFirstEntry==0 ){ - pTab->pFirstEntry = pTab->pLastEntry = pNew; - }else{ - assert( pTab->pLastEntry->pNext==0 ); - pTab->pLastEntry->pNext = pNew; - pTab->pLastEntry = pNew; - } - }else{ - ZipfileEntry **pp; - for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext)); - pNew->pNext = pBefore; - *pp = pNew; - } -} - -static int zipfileLoadDirectory(ZipfileTab *pTab){ - ZipfileEOCD eocd; - int rc; - - rc = zipfileReadEOCD(pTab, pTab->pWriteFd, &eocd); - if( rc==SQLITE_OK && eocd.nEntry>0 ){ - int i; - int iOff = 0; - u8 *aBuf = sqlite3_malloc(eocd.nSize); - if( aBuf==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = zipfileReadData( - pTab->pWriteFd, aBuf, eocd.nSize, eocd.iOffset, &pTab->base.zErrMsg - ); - } - - for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){ - u16 nFile; - u16 nExtra; - u16 nComment; - ZipfileEntry *pNew; - u8 *aRec = &aBuf[iOff]; - - nFile = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF]); - nExtra = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF+2]); - nComment = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF+4]); - - pNew = sqlite3_malloc( - sizeof(ZipfileEntry) - + nFile+1 - + ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment - ); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(ZipfileEntry)); - pNew->zPath = (char*)&pNew[1]; - memcpy(pNew->zPath, &aRec[ZIPFILE_CDS_FIXED_SZ], nFile); - pNew->zPath[nFile] = '\0'; - pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1]; - pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; - memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry); - zipfileAddEntry(pTab, 0, pNew); - } - - iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; - } - - sqlite3_free(aBuf); - } - - return rc; -} - -static ZipfileEntry *zipfileNewEntry( - ZipfileCDS *pCds, /* Values for fixed size part of CDS */ - const char *zPath, /* Path for new entry */ - int nPath, /* strlen(zPath) */ - u32 mTime /* Modification time (or 0) */ -){ - u8 *aWrite; +static ZipfileEntry *zipfileNewEntry(const char *zPath){ ZipfileEntry *pNew; - pCds->nFile = (u16)nPath; - pCds->nExtra = mTime ? 9 : 0; - pNew = (ZipfileEntry*)sqlite3_malloc( - sizeof(ZipfileEntry) + - nPath+1 + - ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra - ); - + pNew = sqlite3_malloc(sizeof(ZipfileEntry)); if( pNew ){ memset(pNew, 0, sizeof(ZipfileEntry)); - pNew->zPath = (char*)&pNew[1]; - pNew->aCdsEntry = (u8*)&pNew->zPath[nPath+1]; - pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra; - memcpy(pNew->zPath, zPath, nPath+1); - - aWrite = pNew->aCdsEntry; - zipfileWrite32(aWrite, ZIPFILE_SIGNATURE_CDS); - zipfileWrite16(aWrite, pCds->iVersionMadeBy); - zipfileWrite16(aWrite, pCds->iVersionExtract); - zipfileWrite16(aWrite, pCds->flags); - zipfileWrite16(aWrite, pCds->iCompression); - zipfileWrite16(aWrite, pCds->mTime); - zipfileWrite16(aWrite, pCds->mDate); - zipfileWrite32(aWrite, pCds->crc32); - zipfileWrite32(aWrite, pCds->szCompressed); - zipfileWrite32(aWrite, pCds->szUncompressed); - zipfileWrite16(aWrite, pCds->nFile); - zipfileWrite16(aWrite, pCds->nExtra); - zipfileWrite16(aWrite, pCds->nComment); assert( pCds->nComment==0 ); - zipfileWrite16(aWrite, pCds->iDiskStart); - zipfileWrite16(aWrite, pCds->iInternalAttr); - zipfileWrite32(aWrite, pCds->iExternalAttr); - zipfileWrite32(aWrite, pCds->iOffset); - assert( aWrite==&pNew->aCdsEntry[ZIPFILE_CDS_FIXED_SZ] ); - memcpy(aWrite, zPath, nPath); - if( pCds->nExtra ){ - aWrite += nPath; - zipfileWrite16(aWrite, ZIPFILE_EXTRA_TIMESTAMP); - zipfileWrite16(aWrite, 5); - *aWrite++ = 0x01; - zipfileWrite32(aWrite, mTime); + pNew->cds.zFile = sqlite3_mprintf("%s", zPath); + if( pNew->cds.zFile==0 ){ + sqlite3_free(pNew); + pNew = 0; } } - return pNew; } +static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){ + ZipfileCDS *pCds = &pEntry->cds; + u8 *a = aBuf; + + pCds->nExtra = 9; + + /* Write the LFH itself */ + zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH); + zipfileWrite16(a, pCds->iVersionExtract); + zipfileWrite16(a, pCds->flags); + zipfileWrite16(a, pCds->iCompression); + zipfileWrite16(a, pCds->mTime); + zipfileWrite16(a, pCds->mDate); + zipfileWrite32(a, pCds->crc32); + zipfileWrite32(a, pCds->szCompressed); + zipfileWrite32(a, pCds->szUncompressed); + zipfileWrite16(a, (u16)pCds->nFile); + zipfileWrite16(a, pCds->nExtra); + assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] ); + + /* Add the file name */ + memcpy(a, pCds->zFile, (int)pCds->nFile); + a += (int)pCds->nFile; + + /* The "extra" data */ + zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP); + zipfileWrite16(a, 5); + *a++ = 0x01; + zipfileWrite32(a, pEntry->mUnixTime); + + return a-aBuf; +} + static int zipfileAppendEntry( ZipfileTab *pTab, - ZipfileCDS *pCds, - const char *zPath, /* Path for new entry */ - int nPath, /* strlen(zPath) */ + ZipfileEntry *pEntry, const u8 *pData, - int nData, - u32 mTime + int nData ){ u8 *aBuf = pTab->aBuffer; + int nBuf; int rc; - zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_LFH); - zipfileWrite16(aBuf, pCds->iVersionExtract); - zipfileWrite16(aBuf, pCds->flags); - zipfileWrite16(aBuf, pCds->iCompression); - zipfileWrite16(aBuf, pCds->mTime); - zipfileWrite16(aBuf, pCds->mDate); - zipfileWrite32(aBuf, pCds->crc32); - zipfileWrite32(aBuf, pCds->szCompressed); - zipfileWrite32(aBuf, pCds->szUncompressed); - zipfileWrite16(aBuf, (u16)nPath); - zipfileWrite16(aBuf, pCds->nExtra); - assert( aBuf==&pTab->aBuffer[ZIPFILE_LFH_FIXED_SZ] ); - rc = zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer)); + nBuf = zipfileSerializeLFH(pEntry, aBuf); + rc = zipfileAppendData(pTab, aBuf, nBuf); if( rc==SQLITE_OK ){ - rc = zipfileAppendData(pTab, (const u8*)zPath, nPath); - } - - if( rc==SQLITE_OK && pCds->nExtra ){ - aBuf = pTab->aBuffer; - zipfileWrite16(aBuf, ZIPFILE_EXTRA_TIMESTAMP); - zipfileWrite16(aBuf, 5); - *aBuf++ = 0x01; - zipfileWrite32(aBuf, mTime); - rc = zipfileAppendData(pTab, pTab->aBuffer, 9); - } - - if( rc==SQLITE_OK ){ + pEntry->iDataOff = pTab->szCurrent; rc = zipfileAppendData(pTab, pData, nData); } @@ -5184,15 +5458,15 @@ } static int zipfileGetMode( - ZipfileTab *pTab, sqlite3_value *pVal, - u32 defaultMode, /* Value to use if pVal IS NULL */ - u32 *pMode + int bIsDir, /* If true, default to directory */ + u32 *pMode, /* OUT: Mode value */ + char **pzErr /* OUT: Error message */ ){ const char *z = (const char*)sqlite3_value_text(pVal); u32 mode = 0; if( z==0 ){ - mode = defaultMode; + mode = (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)); }else if( z[0]>='0' && z[0]<='9' ){ mode = (unsigned int)sqlite3_value_int(pVal); }else{ @@ -5202,9 +5476,7 @@ switch( z[0] ){ case '-': mode |= S_IFREG; break; case 'd': mode |= S_IFDIR; break; -#if !defined(_WIN32) && !defined(WIN32) case 'l': mode |= S_IFLNK; break; -#endif default: goto parse_error; } for(i=1; i<10; i++){ @@ -5212,11 +5484,17 @@ else if( z[i]!='-' ) goto parse_error; } } + if( ((mode & S_IFDIR)==0)==bIsDir ){ + /* The "mode" attribute is a directory, but data has been specified. + ** Or vice-versa - no data but "mode" is a file or symlink. */ + *pzErr = sqlite3_mprintf("zipfile: mode does not match data"); + return SQLITE_CONSTRAINT; + } *pMode = mode; return SQLITE_OK; parse_error: - pTab->base.zErrMsg = sqlite3_mprintf("zipfile: parse error in mode: %s", z); + *pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s", z); return SQLITE_ERROR; } @@ -5232,6 +5510,81 @@ return 1; } +static int zipfileBegin(sqlite3_vtab *pVtab){ + ZipfileTab *pTab = (ZipfileTab*)pVtab; + int rc = SQLITE_OK; + + assert( pTab->pWriteFd==0 ); + + /* Open a write fd on the file. Also load the entire central directory + ** structure into memory. During the transaction any new file data is + ** appended to the archive file, but the central directory is accumulated + ** in main-memory until the transaction is committed. */ + pTab->pWriteFd = fopen(pTab->zFile, "ab+"); + if( pTab->pWriteFd==0 ){ + pTab->base.zErrMsg = sqlite3_mprintf( + "zipfile: failed to open file %s for writing", pTab->zFile + ); + rc = SQLITE_ERROR; + }else{ + fseek(pTab->pWriteFd, 0, SEEK_END); + pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); + rc = zipfileLoadDirectory(pTab, 0, 0); + } + + if( rc!=SQLITE_OK ){ + zipfileCleanupTransaction(pTab); + } + + return rc; +} + +/* +** Return the current time as a 32-bit timestamp in UNIX epoch format (like +** time(2)). +*/ +static u32 zipfileTime(void){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(0); + u32 ret; + if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ + i64 ms; + pVfs->xCurrentTimeInt64(pVfs, &ms); + ret = (u32)((ms/1000) - ((i64)24405875 * 8640)); + }else{ + double day; + pVfs->xCurrentTime(pVfs, &day); + ret = (u32)((day - 2440587.5) * 86400); + } + return ret; +} + +/* +** Return a 32-bit timestamp in UNIX epoch format. +** +** If the value passed as the only argument is either NULL or an SQL NULL, +** return the current time. Otherwise, return the value stored in (*pVal) +** cast to a 32-bit unsigned integer. +*/ +static u32 zipfileGetTime(sqlite3_value *pVal){ + if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ + return zipfileTime(); + } + return (u32)sqlite3_value_int64(pVal); +} + +/* +** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry +** linked list. Remove it from the list and free the object. +*/ +static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ + if( pOld ){ + ZipfileEntry **pp; + for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext)); + *pp = (*pp)->pNext; + zipfileEntryFree(pOld); + } +} + /* ** xUpdate method. */ @@ -5246,7 +5599,7 @@ ZipfileEntry *pNew = 0; /* New in-memory CDS entry */ u32 mode = 0; /* Mode for new entry */ - i64 mTime = 0; /* Modification time for new entry */ + u32 mTime = 0; /* Modification time for new entry */ i64 sz = 0; /* Uncompressed size */ const char *zPath = 0; /* Path for new entry */ int nPath = 0; /* strlen(zPath) */ @@ -5255,217 +5608,239 @@ int iMethod = 0; /* Compression method for new entry */ u8 *pFree = 0; /* Free this */ char *zFree = 0; /* Also free this */ - ZipfileCDS cds; /* New Central Directory Structure entry */ ZipfileEntry *pOld = 0; + ZipfileEntry *pOld2 = 0; + int bUpdate = 0; /* True for an update that modifies "name" */ int bIsDir = 0; u32 iCrc32 = 0; - assert( pTab->zFile ); - assert( pTab->pWriteFd ); + if( pTab->pWriteFd==0 ){ + rc = zipfileBegin(pVtab); + if( rc!=SQLITE_OK ) return rc; + } + /* If this is a DELETE or UPDATE, find the archive entry to delete. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ const char *zDelete = (const char*)sqlite3_value_text(apVal[0]); int nDelete = (int)strlen(zDelete); + if( nVal>1 ){ + const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]); + if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){ + bUpdate = 1; + } + } for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){ - if( pOld->bDeleted ) continue; - if( zipfileComparePath(pOld->zPath, zDelete, nDelete)==0 ){ - pOld->bDeleted = 1; + if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){ break; } assert( pOld->pNext ); } - if( nVal==1 ) return SQLITE_OK; } - /* Check that "sz" and "rawdata" are both NULL: */ - if( sqlite3_value_type(apVal[5])!=SQLITE_NULL - || sqlite3_value_type(apVal[6])!=SQLITE_NULL - ){ - rc = SQLITE_CONSTRAINT; - } - - if( rc==SQLITE_OK ){ - if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){ - /* data=NULL. A directory */ - bIsDir = 1; - }else{ - /* Value specified for "data", and possibly "method". This must be - ** a regular file or a symlink. */ - const u8 *aIn = sqlite3_value_blob(apVal[7]); - int nIn = sqlite3_value_bytes(apVal[7]); - int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; - - iMethod = sqlite3_value_int(apVal[8]); - sz = nIn; - pData = aIn; - nData = nIn; - if( iMethod!=0 && iMethod!=8 ){ - rc = SQLITE_CONSTRAINT; - }else{ - if( bAuto || iMethod ){ - int nCmp; - rc = zipfileDeflate(pTab, aIn, nIn, &pFree, &nCmp); - if( rc==SQLITE_OK ){ - if( iMethod || nCmp<nIn ){ - iMethod = 8; - pData = pFree; - nData = nCmp; - } - } - } - iCrc32 = crc32(0, aIn, nIn); - } - } - } - - if( rc==SQLITE_OK ){ - rc = zipfileGetMode(pTab, apVal[3], - (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)), &mode - ); - if( rc==SQLITE_OK && (bIsDir == ((mode & S_IFDIR)==0)) ){ - /* The "mode" attribute is a directory, but data has been specified. - ** Or vice-versa - no data but "mode" is a file or symlink. */ + if( nVal>1 ){ + /* Check that "sz" and "rawdata" are both NULL: */ + if( sqlite3_value_type(apVal[5])!=SQLITE_NULL ){ + zipfileTableErr(pTab, "sz must be NULL"); rc = SQLITE_CONSTRAINT; } - } - - if( rc==SQLITE_OK ){ - zPath = (const char*)sqlite3_value_text(apVal[2]); - nPath = (int)strlen(zPath); - if( sqlite3_value_type(apVal[4])==SQLITE_NULL ){ - mTime = (sqlite3_int64)time(0); - }else{ - mTime = sqlite3_value_int64(apVal[4]); + if( sqlite3_value_type(apVal[6])!=SQLITE_NULL ){ + zipfileTableErr(pTab, "rawdata must be NULL"); + rc = SQLITE_CONSTRAINT; } - } - if( rc==SQLITE_OK && bIsDir ){ - /* For a directory, check that the last character in the path is a - ** '/'. This appears to be required for compatibility with info-zip - ** (the unzip command on unix). It does not create directories - ** otherwise. */ - if( zPath[nPath-1]!='/' ){ - zFree = sqlite3_mprintf("%s/", zPath); - if( zFree==0 ){ rc = SQLITE_NOMEM; } - zPath = (const char*)zFree; - nPath++; + if( rc==SQLITE_OK ){ + if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){ + /* data=NULL. A directory */ + bIsDir = 1; + }else{ + /* Value specified for "data", and possibly "method". This must be + ** a regular file or a symlink. */ + const u8 *aIn = sqlite3_value_blob(apVal[7]); + int nIn = sqlite3_value_bytes(apVal[7]); + int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; + + iMethod = sqlite3_value_int(apVal[8]); + sz = nIn; + pData = aIn; + nData = nIn; + if( iMethod!=0 && iMethod!=8 ){ + zipfileTableErr(pTab, "unknown compression method: %d", iMethod); + rc = SQLITE_CONSTRAINT; + }else{ + if( bAuto || iMethod ){ + int nCmp; + rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg); + if( rc==SQLITE_OK ){ + if( iMethod || nCmp<nIn ){ + iMethod = 8; + pData = pFree; + nData = nCmp; + } + } + } + iCrc32 = crc32(0, aIn, nIn); + } + } } - } - /* Check that we're not inserting a duplicate entry */ - if( rc==SQLITE_OK ){ - ZipfileEntry *p; - for(p=pTab->pFirstEntry; p; p=p->pNext){ - if( p->bDeleted ) continue; - if( zipfileComparePath(p->zPath, zPath, nPath)==0 ){ - rc = SQLITE_CONSTRAINT; - break; + if( rc==SQLITE_OK ){ + rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg); + } + + if( rc==SQLITE_OK ){ + zPath = (const char*)sqlite3_value_text(apVal[2]); + nPath = (int)strlen(zPath); + mTime = zipfileGetTime(apVal[4]); + } + + if( rc==SQLITE_OK && bIsDir ){ + /* For a directory, check that the last character in the path is a + ** '/'. This appears to be required for compatibility with info-zip + ** (the unzip command on unix). It does not create directories + ** otherwise. */ + if( zPath[nPath-1]!='/' ){ + zFree = sqlite3_mprintf("%s/", zPath); + if( zFree==0 ){ rc = SQLITE_NOMEM; } + zPath = (const char*)zFree; + nPath++; + } + } + + /* Check that we're not inserting a duplicate entry -OR- updating an + ** entry with a path, thereby making it into a duplicate. */ + if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){ + ZipfileEntry *p; + for(p=pTab->pFirstEntry; p; p=p->pNext){ + if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){ + switch( sqlite3_vtab_on_conflict(pTab->db) ){ + case SQLITE_IGNORE: { + goto zipfile_update_done; + } + case SQLITE_REPLACE: { + pOld2 = p; + break; + } + default: { + zipfileTableErr(pTab, "duplicate name: \"%s\"", zPath); + rc = SQLITE_CONSTRAINT; + break; + } + } + break; + } + } + } + + if( rc==SQLITE_OK ){ + /* Create the new CDS record. */ + pNew = zipfileNewEntry(zPath); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; + pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; + pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS; + pNew->cds.iCompression = (u16)iMethod; + zipfileMtimeToDos(&pNew->cds, mTime); + pNew->cds.crc32 = iCrc32; + pNew->cds.szCompressed = nData; + pNew->cds.szUncompressed = (u32)sz; + pNew->cds.iExternalAttr = (mode<<16); + pNew->cds.iOffset = (u32)pTab->szCurrent; + pNew->cds.nFile = (u16)nPath; + pNew->mUnixTime = (u32)mTime; + rc = zipfileAppendEntry(pTab, pNew, pData, nData); + zipfileAddEntry(pTab, pOld, pNew); } } } - if( rc==SQLITE_OK ){ - /* Create the new CDS record. */ - memset(&cds, 0, sizeof(cds)); - cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; - cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; - cds.flags = ZIPFILE_NEWENTRY_FLAGS; - cds.iCompression = (u16)iMethod; - zipfileMtimeToDos(&cds, (u32)mTime); - cds.crc32 = iCrc32; - cds.szCompressed = nData; - cds.szUncompressed = (u32)sz; - cds.iExternalAttr = (mode<<16); - cds.iOffset = (u32)pTab->szCurrent; - pNew = zipfileNewEntry(&cds, zPath, nPath, (u32)mTime); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - zipfileAddEntry(pTab, pOld, pNew); + if( rc==SQLITE_OK && (pOld || pOld2) ){ + ZipfileCsr *pCsr; + for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){ + if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){ + pCsr->pCurrent = pCsr->pCurrent->pNext; + pCsr->bNoop = 1; + } } + + zipfileRemoveEntryFromList(pTab, pOld); + zipfileRemoveEntryFromList(pTab, pOld2); } - /* Append the new header+file to the archive */ - if( rc==SQLITE_OK ){ - rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime); - } - - if( rc!=SQLITE_OK && pOld ){ - pOld->bDeleted = 0; - } +zipfile_update_done: sqlite3_free(pFree); sqlite3_free(zFree); return rc; } +static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){ + u8 *a = aBuf; + zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD); + zipfileWrite16(a, p->iDisk); + zipfileWrite16(a, p->iFirstDisk); + zipfileWrite16(a, p->nEntry); + zipfileWrite16(a, p->nEntryTotal); + zipfileWrite32(a, p->nSize); + zipfileWrite32(a, p->iOffset); + zipfileWrite16(a, 0); /* Size of trailing comment in bytes*/ + + return a-aBuf; +} + static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){ - u8 *aBuf = pTab->aBuffer; - - zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_EOCD); - zipfileWrite16(aBuf, p->iDisk); - zipfileWrite16(aBuf, p->iFirstDisk); - zipfileWrite16(aBuf, p->nEntry); - zipfileWrite16(aBuf, p->nEntryTotal); - zipfileWrite32(aBuf, p->nSize); - zipfileWrite32(aBuf, p->iOffset); - zipfileWrite16(aBuf, 0); /* Size of trailing comment in bytes*/ - - assert( (aBuf-pTab->aBuffer)==22 ); - return zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer)); + int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer); + assert( nBuf==ZIPFILE_EOCD_FIXED_SZ ); + return zipfileAppendData(pTab, pTab->aBuffer, nBuf); } -static void zipfileCleanupTransaction(ZipfileTab *pTab){ - ZipfileEntry *pEntry; - ZipfileEntry *pNext; +/* +** Serialize the CDS structure into buffer aBuf[]. Return the number +** of bytes written. +*/ +static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){ + u8 *a = aBuf; + ZipfileCDS *pCDS = &pEntry->cds; - for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){ - pNext = pEntry->pNext; - sqlite3_free(pEntry); - } - pTab->pFirstEntry = 0; - pTab->pLastEntry = 0; - fclose(pTab->pWriteFd); - pTab->pWriteFd = 0; - pTab->szCurrent = 0; - pTab->szOrig = 0; -} - -static int zipfileBegin(sqlite3_vtab *pVtab){ - ZipfileTab *pTab = (ZipfileTab*)pVtab; - int rc = SQLITE_OK; - - assert( pTab->pWriteFd==0 ); - - /* This table is only writable if a default archive path was specified - ** as part of the CREATE VIRTUAL TABLE statement. */ - if( pTab->zFile==0 ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "zipfile: writing requires a default archive" - ); - return SQLITE_ERROR; + if( pEntry->aExtra==0 ){ + pCDS->nExtra = 9; } - /* Open a write fd on the file. Also load the entire central directory - ** structure into memory. During the transaction any new file data is - ** appended to the archive file, but the central directory is accumulated - ** in main-memory until the transaction is committed. */ - pTab->pWriteFd = fopen(pTab->zFile, "ab+"); - if( pTab->pWriteFd==0 ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "zipfile: failed to open file %s for writing", pTab->zFile - ); - rc = SQLITE_ERROR; + zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS); + zipfileWrite16(a, pCDS->iVersionMadeBy); + zipfileWrite16(a, pCDS->iVersionExtract); + zipfileWrite16(a, pCDS->flags); + zipfileWrite16(a, pCDS->iCompression); + zipfileWrite16(a, pCDS->mTime); + zipfileWrite16(a, pCDS->mDate); + zipfileWrite32(a, pCDS->crc32); + zipfileWrite32(a, pCDS->szCompressed); + zipfileWrite32(a, pCDS->szUncompressed); + assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] ); + zipfileWrite16(a, pCDS->nFile); + zipfileWrite16(a, pCDS->nExtra); + zipfileWrite16(a, pCDS->nComment); + zipfileWrite16(a, pCDS->iDiskStart); + zipfileWrite16(a, pCDS->iInternalAttr); + zipfileWrite32(a, pCDS->iExternalAttr); + zipfileWrite32(a, pCDS->iOffset); + + memcpy(a, pCDS->zFile, pCDS->nFile); + a += pCDS->nFile; + + if( pEntry->aExtra ){ + int n = (int)pCDS->nExtra + (int)pCDS->nComment; + memcpy(a, pEntry->aExtra, n); + a += n; }else{ - fseek(pTab->pWriteFd, 0, SEEK_END); - pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); - rc = zipfileLoadDirectory(pTab); + assert( pCDS->nExtra==9 ); + zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP); + zipfileWrite16(a, 5); + *a++ = 0x01; + zipfileWrite32(a, pEntry->mUnixTime); } - if( rc!=SQLITE_OK ){ - zipfileCleanupTransaction(pTab); - } - - return rc; + return a-aBuf; } static int zipfileCommit(sqlite3_vtab *pVtab){ @@ -5477,10 +5852,10 @@ ZipfileEOCD eocd; int nEntry = 0; - /* Write out all undeleted entries */ + /* Write out all entries */ for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){ - if( p->bDeleted ) continue; - rc = zipfileAppendData(pTab, p->aCdsEntry, p->nCdsEntry); + int n = zipfileSerializeCDS(p, pTab->aBuffer); + rc = zipfileAppendData(pTab, pTab->aBuffer, n); nEntry++; } @@ -5521,7 +5896,7 @@ pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0])); if( pCsr ){ - ZipfileCDS *p = &pCsr->cds; + ZipfileCDS *p = &pCsr->pCurrent->cds; char *zRes = sqlite3_mprintf("{" "\"version-made-by\" : %u, " "\"version-to-extract\" : %u, " @@ -5558,7 +5933,6 @@ } } - /* ** xFindFunction method. */ @@ -5569,18 +5943,259 @@ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ void **ppArg /* OUT: User data for *pxFunc */ ){ - if( nArg>0 ){ - if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ - *pxFunc = zipfileFunctionCds; - *ppArg = (void*)pVtab; - return 1; + if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ + *pxFunc = zipfileFunctionCds; + *ppArg = (void*)pVtab; + return 1; + } + return 0; +} + +typedef struct ZipfileBuffer ZipfileBuffer; +struct ZipfileBuffer { + u8 *a; /* Pointer to buffer */ + int n; /* Size of buffer in bytes */ + int nAlloc; /* Byte allocated at a[] */ +}; + +typedef struct ZipfileCtx ZipfileCtx; +struct ZipfileCtx { + int nEntry; + ZipfileBuffer body; + ZipfileBuffer cds; +}; + +static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){ + if( pBuf->n+nByte>pBuf->nAlloc ){ + u8 *aNew; + int nNew = pBuf->n ? pBuf->n*2 : 512; + int nReq = pBuf->n + nByte; + + while( nNew<nReq ) nNew = nNew*2; + aNew = sqlite3_realloc(pBuf->a, nNew); + if( aNew==0 ) return SQLITE_NOMEM; + pBuf->a = aNew; + pBuf->nAlloc = nNew; + } + return SQLITE_OK; +} + +/* +** xStep() callback for the zipfile() aggregate. This can be called in +** any of the following ways: +** +** SELECT zipfile(name,data) ... +** SELECT zipfile(name,mode,mtime,data) ... +** SELECT zipfile(name,mode,mtime,data,method) ... +*/ +void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){ + ZipfileCtx *p; /* Aggregate function context */ + ZipfileEntry e; /* New entry to add to zip archive */ + + sqlite3_value *pName = 0; + sqlite3_value *pMode = 0; + sqlite3_value *pMtime = 0; + sqlite3_value *pData = 0; + sqlite3_value *pMethod = 0; + + int bIsDir = 0; + u32 mode; + int rc = SQLITE_OK; + char *zErr = 0; + + int iMethod = -1; /* Compression method to use (0 or 8) */ + + const u8 *aData = 0; /* Possibly compressed data for new entry */ + int nData = 0; /* Size of aData[] in bytes */ + int szUncompressed = 0; /* Size of data before compression */ + u8 *aFree = 0; /* Free this before returning */ + u32 iCrc32 = 0; /* crc32 of uncompressed data */ + + char *zName = 0; /* Path (name) of new entry */ + int nName = 0; /* Size of zName in bytes */ + char *zFree = 0; /* Free this before returning */ + int nByte; + + memset(&e, 0, sizeof(e)); + p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); + if( p==0 ) return; + + /* Martial the arguments into stack variables */ + if( nVal!=2 && nVal!=4 && nVal!=5 ){ + zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()"); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + pName = apVal[0]; + if( nVal==2 ){ + pData = apVal[1]; + }else{ + pMode = apVal[1]; + pMtime = apVal[2]; + pData = apVal[3]; + if( nVal==5 ){ + pMethod = apVal[4]; } } - return 0; + /* Check that the 'name' parameter looks ok. */ + zName = (char*)sqlite3_value_text(pName); + nName = sqlite3_value_bytes(pName); + if( zName==0 ){ + zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL"); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + + /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use + ** deflate compression) or NULL (choose automatically). */ + if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){ + iMethod = (int)sqlite3_value_int64(pMethod); + if( iMethod!=0 && iMethod!=8 ){ + zErr = sqlite3_mprintf("illegal method value: %d", iMethod); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + } + + /* Now inspect the data. If this is NULL, then the new entry must be a + ** directory. Otherwise, figure out whether or not the data should + ** be deflated or simply stored in the zip archive. */ + if( sqlite3_value_type(pData)==SQLITE_NULL ){ + bIsDir = 1; + iMethod = 0; + }else{ + aData = sqlite3_value_blob(pData); + szUncompressed = nData = sqlite3_value_bytes(pData); + iCrc32 = crc32(0, aData, nData); + if( iMethod<0 || iMethod==8 ){ + int nOut = 0; + rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr); + if( rc!=SQLITE_OK ){ + goto zipfile_step_out; + } + if( iMethod==8 || nOut<nData ){ + aData = aFree; + nData = nOut; + iMethod = 8; + }else{ + iMethod = 0; + } + } + } + + /* Decode the "mode" argument. */ + rc = zipfileGetMode(pMode, bIsDir, &mode, &zErr); + if( rc ) goto zipfile_step_out; + + /* Decode the "mtime" argument. */ + e.mUnixTime = zipfileGetTime(pMtime); + + /* If this is a directory entry, ensure that there is exactly one '/' + ** at the end of the path. Or, if this is not a directory and the path + ** ends in '/' it is an error. */ + if( bIsDir==0 ){ + if( zName[nName-1]=='/' ){ + zErr = sqlite3_mprintf("non-directory name must not end with /"); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + }else{ + if( zName[nName-1]!='/' ){ + zName = zFree = sqlite3_mprintf("%s/", zName); + nName++; + if( zName==0 ){ + rc = SQLITE_NOMEM; + goto zipfile_step_out; + } + }else{ + while( nName>1 && zName[nName-2]=='/' ) nName--; + } + } + + /* Assemble the ZipfileEntry object for the new zip archive entry */ + e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; + e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; + e.cds.flags = ZIPFILE_NEWENTRY_FLAGS; + e.cds.iCompression = (u16)iMethod; + zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime); + e.cds.crc32 = iCrc32; + e.cds.szCompressed = nData; + e.cds.szUncompressed = szUncompressed; + e.cds.iExternalAttr = (mode<<16); + e.cds.iOffset = p->body.n; + e.cds.nFile = (u16)nName; + e.cds.zFile = zName; + + /* Append the LFH to the body of the new archive */ + nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9; + if( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out; + p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]); + + /* Append the data to the body of the new archive */ + if( nData>0 ){ + if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out; + memcpy(&p->body.a[p->body.n], aData, nData); + p->body.n += nData; + } + + /* Append the CDS record to the directory of the new archive */ + nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9; + if( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out; + p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]); + + /* Increment the count of entries in the archive */ + p->nEntry++; + + zipfile_step_out: + sqlite3_free(aFree); + sqlite3_free(zFree); + if( rc ){ + if( zErr ){ + sqlite3_result_error(pCtx, zErr, -1); + }else{ + sqlite3_result_error_code(pCtx, rc); + } + } + sqlite3_free(zErr); } /* +** xFinalize() callback for zipfile aggregate function. +*/ +void zipfileFinal(sqlite3_context *pCtx){ + ZipfileCtx *p; + ZipfileEOCD eocd; + int nZip; + u8 *aZip; + + p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); + if( p==0 ) return; + if( p->nEntry>0 ){ + memset(&eocd, 0, sizeof(eocd)); + eocd.nEntry = (u16)p->nEntry; + eocd.nEntryTotal = (u16)p->nEntry; + eocd.nSize = p->cds.n; + eocd.iOffset = p->body.n; + + nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ; + aZip = (u8*)sqlite3_malloc(nZip); + if( aZip==0 ){ + sqlite3_result_error_nomem(pCtx); + }else{ + memcpy(aZip, p->body.a, p->body.n); + memcpy(&aZip[p->body.n], p->cds.a, p->cds.n); + zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]); + sqlite3_result_blob(pCtx, aZip, nZip, zipfileFree); + } + } + + sqlite3_free(p->body.a); + sqlite3_free(p->cds.a); +} + + +/* ** Register the "zipfile" virtual table. */ static int zipfileRegister(sqlite3 *db){ @@ -5597,7 +6212,7 @@ zipfileNext, /* xNext - advance a cursor */ zipfileEof, /* xEof - check for end of scan */ zipfileColumn, /* xColumn - read data */ - zipfileRowid, /* xRowid - read data */ + 0, /* xRowid - read data */ zipfileUpdate, /* xUpdate */ zipfileBegin, /* xBegin */ 0, /* xSync */ @@ -5608,8 +6223,11 @@ }; int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0); + if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1); if( rc==SQLITE_OK ){ - rc = sqlite3_overload_function(db, "zipfile_cds", -1); + rc = sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0, + zipfileStep, zipfileFinal + ); } return rc; } @@ -7978,6 +8596,7 @@ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ +#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */ /* ** These are the allowed shellFlgs values @@ -8084,6 +8703,7 @@ ** ** Also throw an error if the EDITOR program returns a non-zero exit code. */ +#ifndef SQLITE_NOHAVE_SYSTEM static void editFunc( sqlite3_context *context, int argc, @@ -8181,9 +8801,10 @@ goto edit_func_end; } if( bBin ){ - sqlite3_result_blob(context, p, sz, sqlite3_free); + sqlite3_result_blob64(context, p, sz, sqlite3_free); }else{ - sqlite3_result_text(context, (const char*)p, sz, sqlite3_free); + sqlite3_result_text64(context, (const char*)p, sz, + sqlite3_free, SQLITE_UTF8); } p = 0; @@ -8193,6 +8814,7 @@ sqlite3_free(zTempFile); sqlite3_free(p); } +#endif /* SQLITE_NOHAVE_SYSTEM */ /* ** Save or restore the current output mode @@ -9178,29 +9800,54 @@ ){ int iCur; int iHiwtr; + FILE *out; + if( pArg==0 || pArg->out==0 ) return 0; + out = pArg->out; - if( pArg && pArg->out ){ - displayStatLine(pArg, "Memory Used:", - "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); - displayStatLine(pArg, "Number of Outstanding Allocations:", - "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); - if( pArg->shellFlgs & SHFLG_Pagecache ){ - displayStatLine(pArg, "Number of Pcache Pages Used:", - "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); - } - displayStatLine(pArg, "Number of Pcache Overflow Bytes:", - "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine(pArg, "Largest Allocation:", - "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine(pArg, "Largest Pcache Allocation:", - "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); -#ifdef YYTRACKMAXSTACKDEPTH - displayStatLine(pArg, "Deepest Parser Stack:", - "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); + if( pArg->pStmt && (pArg->statsOn & 2) ){ + int nCol, i, x; + sqlite3_stmt *pStmt = pArg->pStmt; + char z[100]; + nCol = sqlite3_column_count(pStmt); + raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol); + for(i=0; i<nCol; i++){ + sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); +#ifndef SQLITE_OMIT_DECLTYPE + sqlite3_snprintf(30, z+x, "declared type:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); #endif +#ifdef SQLITE_ENABLE_COLUMN_METADATA + sqlite3_snprintf(30, z+x, "database name:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_database_name(pStmt,i)); + sqlite3_snprintf(30, z+x, "table name:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); + sqlite3_snprintf(30, z+x, "origin name:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i)); +#endif + } } - if( pArg && pArg->out && db ){ + displayStatLine(pArg, "Memory Used:", + "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); + displayStatLine(pArg, "Number of Outstanding Allocations:", + "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); + if( pArg->shellFlgs & SHFLG_Pagecache ){ + displayStatLine(pArg, "Number of Pcache Pages Used:", + "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); + } + displayStatLine(pArg, "Number of Pcache Overflow Bytes:", + "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); + displayStatLine(pArg, "Largest Allocation:", + "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); + displayStatLine(pArg, "Largest Pcache Allocation:", + "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); +#ifdef YYTRACKMAXSTACKDEPTH + displayStatLine(pArg, "Deepest Parser Stack:", + "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); +#endif + + if( db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, @@ -9235,6 +9882,9 @@ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); raw_printf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; + sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); + raw_printf(pArg->out, "Page cache spills: %d\n", iCur); + iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); @@ -9244,7 +9894,7 @@ iCur); } - if( pArg && pArg->out && db && pArg->pStmt ){ + if( pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); @@ -9254,6 +9904,12 @@ raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE, bReset); + raw_printf(pArg->out, "Reprepare operations: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); + raw_printf(pArg->out, "Number of times run: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); + raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ @@ -9469,8 +10125,7 @@ */ static void exec_prepared_stmt( ShellState *pArg, /* Pointer to ShellState */ - sqlite3_stmt *pStmt, /* Statment to run */ - int (*xCallback)(void*,int,char**,char**,int*) /* Callback function */ + sqlite3_stmt *pStmt /* Statment to run */ ){ int rc; @@ -9480,54 +10135,47 @@ rc = sqlite3_step(pStmt); /* if we have a result set... */ if( SQLITE_ROW == rc ){ - /* if we have a callback... */ - if( xCallback ){ - /* allocate space for col name ptr, value ptr, and type */ - int nCol = sqlite3_column_count(pStmt); - void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); - if( !pData ){ - rc = SQLITE_NOMEM; - }else{ - char **azCols = (char **)pData; /* Names of result columns */ - char **azVals = &azCols[nCol]; /* Results */ - int *aiTypes = (int *)&azVals[nCol]; /* Result types */ - int i, x; - assert(sizeof(int) <= sizeof(char *)); - /* save off ptrs to column names */ - for(i=0; i<nCol; i++){ - azCols[i] = (char *)sqlite3_column_name(pStmt, i); - } - do{ - /* extract the data and data types */ - for(i=0; i<nCol; i++){ - aiTypes[i] = x = sqlite3_column_type(pStmt, i); - if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ - azVals[i] = ""; - }else{ - azVals[i] = (char*)sqlite3_column_text(pStmt, i); - } - if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ - rc = SQLITE_NOMEM; - break; /* from for */ - } - } /* end for */ - - /* if data and types extracted successfully... */ - if( SQLITE_ROW == rc ){ - /* call the supplied callback with the result row data */ - if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){ - rc = SQLITE_ABORT; - }else{ - rc = sqlite3_step(pStmt); - } - } - } while( SQLITE_ROW == rc ); - sqlite3_free(pData); - } + /* allocate space for col name ptr, value ptr, and type */ + int nCol = sqlite3_column_count(pStmt); + void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); + if( !pData ){ + rc = SQLITE_NOMEM; }else{ + char **azCols = (char **)pData; /* Names of result columns */ + char **azVals = &azCols[nCol]; /* Results */ + int *aiTypes = (int *)&azVals[nCol]; /* Result types */ + int i, x; + assert(sizeof(int) <= sizeof(char *)); + /* save off ptrs to column names */ + for(i=0; i<nCol; i++){ + azCols[i] = (char *)sqlite3_column_name(pStmt, i); + } do{ - rc = sqlite3_step(pStmt); - } while( rc == SQLITE_ROW ); + /* extract the data and data types */ + for(i=0; i<nCol; i++){ + aiTypes[i] = x = sqlite3_column_type(pStmt, i); + if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ + azVals[i] = ""; + }else{ + azVals[i] = (char*)sqlite3_column_text(pStmt, i); + } + if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ + rc = SQLITE_NOMEM; + break; /* from for */ + } + } /* end for */ + + /* if data and types extracted successfully... */ + if( SQLITE_ROW == rc ){ + /* call the supplied callback with the result row data */ + if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){ + rc = SQLITE_ABORT; + }else{ + rc = sqlite3_step(pStmt); + } + } + } while( SQLITE_ROW == rc ); + sqlite3_free(pData); } } } @@ -9673,17 +10321,15 @@ ** and callback data argument. */ static int shell_exec( - sqlite3 *db, /* An open database */ - const char *zSql, /* SQL to be evaluated */ - int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ - /* (not the same as sqlite3_exec) */ ShellState *pArg, /* Pointer to ShellState */ + const char *zSql, /* SQL to be evaluated */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ int rc2; const char *zLeftover; /* Tail of unprocessed SQL */ + sqlite3 *db = pArg->db; if( pzErrMsg ){ *pzErrMsg = NULL; @@ -9754,13 +10400,18 @@ if( rc==SQLITE_OK ){ pArg->cMode = MODE_Explain; explain_data_prepare(pArg, pExplain); - exec_prepared_stmt(pArg, pExplain, xCallback); + exec_prepared_stmt(pArg, pExplain); explain_data_delete(pArg); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); } - sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, triggerEQP, 0); + if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ + sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); + /* Reprepare pStmt before reactiving trace modes */ + sqlite3_finalize(pStmt); + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + } restore_debug_trace_modes(); } @@ -9780,7 +10431,7 @@ } } - exec_prepared_stmt(pArg, pStmt, xCallback); + exec_prepared_stmt(pArg, pStmt); explain_data_delete(pArg); /* print usage stats if stats on */ @@ -10043,11 +10694,11 @@ savedMode = p->mode; p->zDestTable = sTable.z; p->mode = p->cMode = MODE_Insert; - rc = shell_exec(p->db, sSelect.z, shell_callback, p, 0); + rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); - shell_exec(p->db, sSelect.z, shell_callback, p, 0); + shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); } p->zDestTable = savedDestTable; @@ -10164,6 +10815,7 @@ " on the output.\n" ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE\n" " The --new option starts with an empty file\n" + " Other options: --readonly --append --zip\n" ".output ?FILE? Send output to FILE or stdout\n" ".print STRING... Print literal STRING\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" @@ -10181,10 +10833,14 @@ ".session CMD ... Create or control sessions\n" #endif ".sha3sum ?OPTIONS...? Compute a SHA3 hash of database content\n" +#ifndef SQLITE_NOHAVE_SYSTEM ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" +#endif ".show Show the current values for various settings\n" ".stats ?on|off? Show stats or turn stats on or off\n" +#ifndef SQLITE_NOHAVE_SYSTEM ".system CMD ARGS... Run CMD ARGS... in a system shell\n" +#endif ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" @@ -10313,13 +10969,21 @@ /* ** Try to deduce the type of file for zName based on its content. Return ** one of the SHELL_OPEN_* constants. +** +** If the file does not exist or is empty but its name looks like a ZIP +** archive and the dfltZip flag is true, then assume it is a ZIP archive. +** Otherwise, assume an ordinary database regardless of the filename if +** the type cannot be determined from content. */ -static int deduceDatabaseType(const char *zName){ +static int deduceDatabaseType(const char *zName, int dfltZip){ FILE *f = fopen(zName, "rb"); size_t n; int rc = SHELL_OPEN_UNSPEC; char zBuf[100]; - if( f==0 ) return SHELL_OPEN_NORMAL; + if( f==0 ){ + if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ) return SHELL_OPEN_ZIPFILE; + return SHELL_OPEN_NORMAL; + } fseek(f, -25, SEEK_END); n = fread(zBuf, 25, 1, f); if( n==1 && memcmp(zBuf, "Start-Of-SQLite3-", 17)==0 ){ @@ -10330,6 +10994,8 @@ if( n==1 && zBuf[0]==0x50 && zBuf[1]==0x4b && zBuf[2]==0x05 && zBuf[3]==0x06 ){ rc = SHELL_OPEN_ZIPFILE; + }else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ + return SHELL_OPEN_ZIPFILE; } } fclose(f); @@ -10344,7 +11010,7 @@ if( p->db==0 ){ sqlite3_initialize(); if( p->openMode==SHELL_OPEN_UNSPEC && access(p->zDbFilename,0)==0 ){ - p->openMode = deduceDatabaseType(p->zDbFilename); + p->openMode = (u8)deduceDatabaseType(p->zDbFilename, 0); } switch( p->openMode ){ case SHELL_OPEN_APPENDVFS: { @@ -10356,6 +11022,10 @@ sqlite3_open(":memory:", &p->db); break; } + case SHELL_OPEN_READONLY: { + sqlite3_open_v2(p->zDbFilename, &p->db, SQLITE_OPEN_READONLY, 0); + break; + } case SHELL_OPEN_UNSPEC: case SHELL_OPEN_NORMAL: { sqlite3_open(p->zDbFilename, &p->db); @@ -10385,10 +11055,12 @@ shellModuleSchema, 0, 0); sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, shellPutsFunc, 0, 0); +#ifndef SQLITE_NOHAVE_SYSTEM sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, editFunc, 0, 0); sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0, editFunc, 0, 0); +#endif if( p->openMode==SHELL_OPEN_ZIPFILE ){ char *zSql = sqlite3_mprintf( "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename); @@ -10585,7 +11257,6 @@ return f; } -#if !defined(SQLITE_UNTESTABLE) #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) /* ** A routine for handling output from sqlite3_trace(). @@ -10608,7 +11279,6 @@ return 0; } #endif -#endif /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is @@ -11003,6 +11673,7 @@ #endif }else{ output_file_close(p->out); +#ifndef SQLITE_NOHAVE_SYSTEM if( p->doXdgOpen ){ const char *zXdgOpenCmd = #if defined(_WIN32) @@ -11021,6 +11692,7 @@ outputModePop(p); p->doXdgOpen = 0; } +#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ } p->outfile[0] = 0; p->out = stdout; @@ -12131,8 +12803,8 @@ " data BLOB -- compressed content\n" ")"; const char *zDrop = "DROP TABLE IF EXISTS sqlar"; - const char *zInsertFmt = - "REPLACE INTO sqlar(name,mode,mtime,sz,data)\n" + const char *zInsertFmt[2] = { + "REPLACE INTO %s(name,mode,mtime,sz,data)\n" " SELECT\n" " %s,\n" " mode,\n" @@ -12141,30 +12813,70 @@ " WHEN '-' THEN length(data)\n" " WHEN 'd' THEN 0\n" " ELSE -1 END,\n" - " CASE WHEN lsmode(mode) LIKE 'd%%' THEN NULL else data END\n" + " sqlar_compress(data)\n" " FROM fsdir(%Q,%Q)\n" - " WHERE lsmode(mode) NOT LIKE '?%%';"; + " WHERE lsmode(mode) NOT LIKE '?%%';", + "REPLACE INTO %s(name,mode,mtime,data)\n" + " SELECT\n" + " %s,\n" + " mode,\n" + " mtime,\n" + " data\n" + " FROM fsdir(%Q,%Q)\n" + " WHERE lsmode(mode) NOT LIKE '?%%';" + }; int i; /* For iterating through azFile[] */ int rc; /* Return code */ + const char *zTab = 0; /* SQL table into which to insert */ + char *zSql; + char zTemp[50]; + arExecSql(pAr, "PRAGMA page_size=512"); rc = arExecSql(pAr, "SAVEPOINT ar;"); if( rc!=SQLITE_OK ) return rc; - if( bUpdate==0 ){ - rc = arExecSql(pAr, zDrop); - if( rc!=SQLITE_OK ) return rc; + zTemp[0] = 0; + if( pAr->bZip ){ + /* Initialize the zipfile virtual table, if necessary */ + if( pAr->zFile ){ + sqlite3_uint64 r; + sqlite3_randomness(sizeof(r),&r); + sqlite3_snprintf(sizeof(zTemp),zTemp,"zip%016llx",r); + zTab = zTemp; + zSql = sqlite3_mprintf( + "CREATE VIRTUAL TABLE temp.%s USING zipfile(%Q)", + zTab, pAr->zFile + ); + rc = arExecSql(pAr, zSql); + sqlite3_free(zSql); + }else{ + zTab = "zip"; + } + }else{ + /* Initialize the table for an SQLAR */ + zTab = "sqlar"; + if( bUpdate==0 ){ + rc = arExecSql(pAr, zDrop); + if( rc!=SQLITE_OK ) goto end_ar_transaction; + } + rc = arExecSql(pAr, zCreate); } - rc = arExecSql(pAr, zCreate); for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ - char *zSql = sqlite3_mprintf(zInsertFmt, + char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab, pAr->bVerbose ? "shell_putsnl(name)" : "name", pAr->azArg[i], pAr->zDir); - rc = arExecSql(pAr, zSql); - sqlite3_free(zSql); + rc = arExecSql(pAr, zSql2); + sqlite3_free(zSql2); } +end_ar_transaction: if( rc!=SQLITE_OK ){ arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;"); }else{ rc = arExecSql(pAr, "RELEASE ar;"); + if( pAr->bZip && pAr->zFile ){ + zSql = sqlite3_mprintf("DROP TABLE %s", zTemp); + arExecSql(pAr, zSql); + sqlite3_free(zSql); + } } return rc; } @@ -12186,20 +12898,17 @@ cmd.p = pState; cmd.db = pState->db; if( cmd.zFile ){ - eDbType = deduceDatabaseType(cmd.zFile); + eDbType = deduceDatabaseType(cmd.zFile, 1); }else{ eDbType = pState->openMode; } if( eDbType==SHELL_OPEN_ZIPFILE ){ - if( cmd.zFile==0 ){ - cmd.zSrcTable = sqlite3_mprintf("zip"); - }else{ - cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); - } - if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){ - utf8_printf(stderr, "zip archives are read-only\n"); - rc = SQLITE_ERROR; - goto end_ar_command; + if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){ + if( cmd.zFile==0 ){ + cmd.zSrcTable = sqlite3_mprintf("zip"); + }else{ + cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); + } } cmd.bZip = 1; }else if( cmd.zFile ){ @@ -12224,14 +12933,12 @@ goto end_ar_command; } sqlite3_fileio_init(cmd.db, 0, 0); -#ifdef SQLITE_HAVE_ZLIB sqlite3_sqlar_init(cmd.db, 0, 0); -#endif sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, shellPutsFunc, 0, 0); } - if( cmd.zSrcTable==0 ){ + if( cmd.zSrcTable==0 && cmd.bZip==0 ){ if( cmd.eCmd!=AR_CMD_CREATE && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ @@ -12624,7 +13331,7 @@ }else if( strcmp(azArg[1],"trigger")==0 ){ p->autoEQP = AUTOEQP_trigger; }else{ - p->autoEQP = booleanValue(azArg[1]); + p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ raw_printf(stderr, "Usage: .eqp off|on|trigger|full\n"); @@ -12711,14 +13418,11 @@ callback, &data, &zErrMsg); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; - shell_exec(p->db, "SELECT * FROM sqlite_stat1", - shell_callback, &data,&zErrMsg); + shell_exec(p, "SELECT * FROM sqlite_stat1", &zErrMsg); data.zDestTable = "sqlite_stat3"; - shell_exec(p->db, "SELECT * FROM sqlite_stat3", - shell_callback, &data,&zErrMsg); + shell_exec(p, "SELECT * FROM sqlite_stat3", &zErrMsg); data.zDestTable = "sqlite_stat4"; - shell_exec(p->db, "SELECT * FROM sqlite_stat4", - shell_callback, &data, &zErrMsg); + shell_exec(p, "SELECT * FROM sqlite_stat4", &zErrMsg); raw_printf(p->out, "ANALYZE sqlite_master;\n"); } }else @@ -13199,12 +13903,14 @@ const char *z = azArg[iName]; if( optionMatch(z,"new") ){ newFlag = 1; -#ifdef SQLITE_HAVE_ZIP +#ifdef SQLITE_HAVE_ZLIB }else if( optionMatch(z, "zip") ){ p->openMode = SHELL_OPEN_ZIPFILE; #endif }else if( optionMatch(z, "append") ){ p->openMode = SHELL_OPEN_APPENDVFS; + }else if( optionMatch(z, "readonly") ){ + p->openMode = SHELL_OPEN_READONLY; }else if( z[0]=='-' ){ utf8_printf(stderr, "unknown option: %s\n", z); rc = 1; @@ -13261,6 +13967,7 @@ } output_reset(p); if( zFile[0]=='-' && zFile[1]=='-' ) zFile++; +#ifndef SQLITE_NOHAVE_SYSTEM if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){ p->doXdgOpen = 1; outputModePush(p); @@ -13275,6 +13982,7 @@ } zFile = p->zTempFile; } +#endif /* SQLITE_NOHAVE_SYSTEM */ if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); @@ -13394,10 +14102,9 @@ sqlite3_close(pSrc); }else - if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ - p->scanstatsOn = booleanValue(azArg[1]); + p->scanstatsOn = (u8)booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); #endif @@ -13436,8 +14143,8 @@ } } if( zName!=0 ){ - int isMaster = sqlite3_strlike(zName, "sqlite_master", 0)==0; - if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master",0)==0 ){ + int isMaster = sqlite3_strlike(zName, "sqlite_master", '\\')==0; + if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = sqlite3_mprintf( "CREATE TABLE %s (\n" @@ -13497,13 +14204,18 @@ appendText(&sSelect, ") WHERE ", 0); if( zName ){ char *zQarg = sqlite3_mprintf("%Q", zName); + int bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 || + strchr(zName, '[') != 0; if( strchr(zName, '.') ){ appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); }else{ appendText(&sSelect, "lower(tbl_name)", 0); } - appendText(&sSelect, strchr(zName, '*') ? " GLOB " : " LIKE ", 0); + appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0); appendText(&sSelect, zQarg, 0); + if( !bGlob ){ + appendText(&sSelect, " ESCAPE '\\' ", 0); + } appendText(&sSelect, " AND ", 0); sqlite3_free(zQarg); } @@ -13918,7 +14630,7 @@ }else{ zLike = z; bSeparate = 1; - if( sqlite3_strlike("sqlite_%", zLike, 0)==0 ) bSchema = 1; + if( sqlite3_strlike("sqlite\\_%", zLike, '\\')==0 ) bSchema = 1; } } if( bSchema ){ @@ -13985,11 +14697,12 @@ if( bDebug ){ utf8_printf(p->out, "%s\n", zSql); }else{ - shell_exec(p->db, zSql, shell_callback, p, 0); + shell_exec(p, zSql, 0); } sqlite3_free(zSql); }else +#ifndef SQLITE_NOHAVE_SYSTEM if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ @@ -14009,6 +14722,7 @@ sqlite3_free(zCmd); if( x ) raw_printf(stderr, "System command returns %d\n", x); }else +#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ if( c=='s' && strncmp(azArg[0], "show", n)==0 ){ static const char *azBool[] = { "off", "on", "trigger", "full"}; @@ -14048,7 +14762,7 @@ if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){ if( nArg==2 ){ - p->statsOn = booleanValue(azArg[1]); + p->statsOn = (u8)booleanValue(azArg[1]); }else if( nArg==1 ){ display_stats(p->db, p, 0); }else{ @@ -14589,6 +15303,16 @@ } /* +** We need a default sqlite3_complete() implementation to use in case +** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes +** any arbitrary text is a complete SQL statement. This is not very +** user-friendly, but it does seem to work. +*/ +#ifdef SQLITE_OMIT_COMPLETE +int sqlite3_complete(const char *zSql){ return 1; } +#endif + +/* ** Return true if zSql is a complete SQL statement. Return false if it ** ends in the middle of a string literal or C-style comment. */ @@ -14612,7 +15336,7 @@ open_db(p, 0); if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); BEGIN_TIMER; - rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); + rc = shell_exec(p, zSql, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; @@ -14844,6 +15568,10 @@ ** Show available command line options */ static const char zOptions[] = +#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) + " -A ARGS... run \".archive ARGS\" and exit\n" +#endif + " -append append the database to the end of the file\n" " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" @@ -14870,6 +15598,7 @@ " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" + " -readonly open the database read-only\n" " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" @@ -14877,6 +15606,9 @@ #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif +#ifdef SQLITE_HAVE_ZLIB + " -zip open the file as a ZIP Archive\n" +#endif ; static void usage(int showDetail){ utf8_printf(stderr, @@ -14979,21 +15711,39 @@ } #endif main_init(&data); + + /* On Windows, we must translate command-line arguments into UTF-8. + ** The SQLite memory allocator subsystem has to be enabled in order to + ** do this. But we want to run an sqlite3_shutdown() afterwards so that + ** subsequent sqlite3_config() calls will work. So copy all results into + ** memory that does not come from the SQLite memory allocator. + */ #if !SQLITE_SHELL_IS_UTF8 sqlite3_initialize(); - argv = sqlite3_malloc64(sizeof(argv[0])*argc); + argv = malloc(sizeof(argv[0])*argc); if( argv==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } for(i=0; i<argc; i++){ - argv[i] = sqlite3_win32_unicode_to_utf8(wargv[i]); + char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); + int n; + if( z==0 ){ + raw_printf(stderr, "out of memory\n"); + exit(1); + } + n = (int)strlen(z); + argv[i] = malloc( n+1 ); if( argv[i]==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } + memcpy(argv[i], z, n+1); + sqlite3_free(z); } + sqlite3_shutdown(); #endif + assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; @@ -15123,12 +15873,20 @@ utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]); exit(1); } -#ifdef SQLITE_HAVE_ZIP +#ifdef SQLITE_HAVE_ZLIB }else if( strcmp(z,"-zip")==0 ){ data.openMode = SHELL_OPEN_ZIPFILE; #endif }else if( strcmp(z,"-append")==0 ){ data.openMode = SHELL_OPEN_APPENDVFS; + }else if( strcmp(z,"-readonly")==0 ){ + data.openMode = SHELL_OPEN_READONLY; +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) + }else if( strncmp(z, "-A",2)==0 ){ + /* All remaining command-line arguments are passed to the ".archive" + ** command, so ignore them */ + break; +#endif } } if( data.zDbFilename==0 ){ @@ -15182,12 +15940,14 @@ }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); -#ifdef SQLITE_HAVE_ZIP +#ifdef SQLITE_HAVE_ZLIB }else if( strcmp(z,"-zip")==0 ){ data.openMode = SHELL_OPEN_ZIPFILE; #endif }else if( strcmp(z,"-append")==0 ){ data.openMode = SHELL_OPEN_APPENDVFS; + }else if( strcmp(z,"-readonly")==0 ){ + data.openMode = SHELL_OPEN_READONLY; }else if( strcmp(z,"-ascii")==0 ){ data.mode = MODE_Ascii; sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, @@ -15265,7 +16025,7 @@ if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); - rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg); + rc = shell_exec(&data, z, &zErrMsg); if( zErrMsg!=0 ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; @@ -15274,6 +16034,23 @@ if( bail_on_error ) return rc; } } +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) + }else if( strncmp(z, "-A", 2)==0 ){ + if( nCmd>0 ){ + utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands" + " with \"%s\"\n", z); + return 1; + } + open_db(&data, 0); + if( z[2] ){ + argv[i] = &z[2]; + arDotCommand(&data, argv+(i-1), argc-(i-1)); + }else{ + arDotCommand(&data, argv+i, argc-i); + } + readStdin = 0; + break; +#endif }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); raw_printf(stderr,"Use -help for a list of options.\n"); @@ -15293,7 +16070,7 @@ if( rc ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); - rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg); + rc = shell_exec(&data, azCmd[i], &zErrMsg); if( zErrMsg!=0 ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); return rc!=0 ? rc : 1; @@ -15356,8 +16133,8 @@ data.doXdgOpen = 0; clearTempFile(&data); #if !SQLITE_SHELL_IS_UTF8 - for(i=0; i<argc; i++) sqlite3_free(argv[i]); - sqlite3_free(argv); + for(i=0; i<argc; i++) free(argv[i]); + free(argv); #endif return rc; }
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c index 9cfb97f..1f8981fd 100644 --- a/third_party/sqlite/amalgamation/sqlite3.c +++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.22.0. By combining all the individual C code files into this +** version 3.23.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -213,7 +213,7 @@ "ENABLE_BATCH_ATOMIC_WRITE", #endif #if SQLITE_ENABLE_CEROD - "ENABLE_CEROD", + "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), #endif #if SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", @@ -1147,9 +1147,9 @@ ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.22.0" -#define SQLITE_VERSION_NUMBER 3022000 -#define SQLITE_SOURCE_ID "2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2alt1" +#define SQLITE_VERSION "3.23.1" +#define SQLITE_VERSION_NUMBER 3023001 +#define SQLITE_SOURCE_ID "2018-04-10 17:39:29 4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd14alt1" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2088,6 +2088,12 @@ ** so that all subsequent write operations are independent. ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. +** +** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] +** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain +** a file lock using the xLock or xShmLock methods of the VFS to wait +** for up to M milliseconds before failing, where M is the single +** unsigned integer parameter. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -2122,6 +2128,7 @@ #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 +#define SQLITE_FCNTL_LOCK_TIMEOUT 34 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -3078,11 +3085,13 @@ ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to ** override this behaviour. The first parameter passed to this operation -** is an integer - non-zero to disable checkpoints-on-close, or zero (the -** default) to enable them. The second parameter is a pointer to an integer +** is an integer - positive to disable checkpoints-on-close, or zero (the +** default) to enable them, and negative to leave the setting unchanged. +** The second parameter is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. ** </dd> +** ** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt> ** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, @@ -3092,13 +3101,20 @@ ** slower. But the QPSG has the advantage of more predictable behavior. With ** the QPSG active, SQLite will always use the same query plan in the field as ** was used during testing in the lab. +** The first argument to this setting is an integer which is 0 to disable +** the QPSG, positive to enable QPSG, or negative to leave the setting +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether the QPSG is disabled or enabled +** following this call. ** </dd> +** ** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt> ** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not ** include output for any operations performed by trigger programs. This ** option is used to set or clear (the default) a flag that governs this ** behavior. The first parameter passed to this operation is an integer - -** non-zero to enable output for trigger programs, or zero to disable it. +** positive to enable output for trigger programs, or zero to disable it, +** or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which is written ** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if ** it is not disabled, 1 if it is. @@ -3520,16 +3536,16 @@ ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. -** These routines understand most of the common K&R formatting options, -** plus some additional non-standard formats, detailed below. -** Note that some of the more obscure formatting options from recent -** C-library standards are omitted from this implementation. +** These routines understand most of the common formatting options from +** the standard library printf() +** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]). +** See the [built-in printf()] documentation for details. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their -** results into memory obtained from [sqlite3_malloc()]. +** results into memory obtained from [sqlite3_malloc64()]. ** The strings returned by these two routines should be ** released by [sqlite3_free()]. ^Both routines return a -** NULL pointer if [sqlite3_malloc()] is unable to allocate enough +** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough ** memory to hold the resulting string. ** ** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from @@ -3553,71 +3569,7 @@ ** ** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). ** -** These routines all implement some additional formatting -** options that are useful for constructing SQL statements. -** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", "%w" and "%z" options. -** -** ^(The %q option works like %s in that it substitutes a nul-terminated -** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal.)^ By doubling each '\'' -** character it escapes that character and allows it to be inserted into -** the string. -** -** For example, assume the string variable zText contains text as follows: -** -** <blockquote><pre> -** char *zText = "It's a happy day!"; -** </pre></blockquote> -** -** One can use this text in an SQL statement as follows: -** -** <blockquote><pre> -** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText); -** sqlite3_exec(db, zSQL, 0, 0, 0); -** sqlite3_free(zSQL); -** </pre></blockquote> -** -** Because the %q format string is used, the '\'' character in zText -** is escaped and the SQL generated is as follows: -** -** <blockquote><pre> -** INSERT INTO table1 VALUES('It''s a happy day!') -** </pre></blockquote> -** -** This is correct. Had we used %s instead of %q, the generated SQL -** would have looked like this: -** -** <blockquote><pre> -** INSERT INTO table1 VALUES('It's a happy day!'); -** </pre></blockquote> -** -** This second example is an SQL syntax error. As a general rule you should -** always use %q instead of %s when inserting text into a string literal. -** -** ^(The %Q option works like %q except it also adds single quotes around -** the outside of the total string. Additionally, if the parameter in the -** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes).)^ So, for example, one could say: -** -** <blockquote><pre> -** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText); -** sqlite3_exec(db, zSQL, 0, 0, 0); -** sqlite3_free(zSQL); -** </pre></blockquote> -** -** The code above will render a correct SQL statement in the zSQL -** variable even if the zText variable is a NULL pointer. -** -** ^(The "%w" formatting option is like "%q" except that it expects to -** be contained within double-quotes instead of single quotes, and it -** escapes the double-quote character instead of the single-quote -** character.)^ The "%w" formatting option is intended for safely inserting -** table and column names into a constructed SQL statement. -** -** ^(The "%z" formatting option works like "%s" but with the -** addition that after the string has been read and copied into -** the result, [sqlite3_free()] is called on the input string.)^ +** See also: [built-in printf()], [printf() SQL function] */ SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); @@ -4683,13 +4635,13 @@ ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** </li> +** </ol> ** ** <p>^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having ** the extra prepFlags parameter, which is a bit array consisting of zero or ** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The ** sqlite3_prepare_v2() interface works exactly the same as ** sqlite3_prepare_v3() with a zero prepFlags parameter. -** </ol> */ SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ @@ -8318,6 +8270,15 @@ ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** </dd> ** +** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> +** <dd>This parameter returns the number of dirty cache entries that have +** been written to disk in the middle of a transaction due to the page +** cache overflowing. Transactions are more efficient if they are written +** to disk all at once. When pages spill mid-transaction, that introduces +** additional overhead. This parameter can be used help identify +** inefficiencies that can be resolve by increasing the cache size. +** </dd> +** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been @@ -8337,7 +8298,8 @@ #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 -#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_CACHE_SPILL 12 +#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */ /* @@ -9841,6 +9803,128 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* +** CAPI3REF: Serialize a database +** +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory +** that is a serialization of the S database on [database connection] D. +** If P is not a NULL pointer, then the size of the database in bytes +** is written into *P. +** +** For an ordinary on-disk database file, the serialization is just a +** copy of the disk file. For an in-memory database or a "TEMP" database, +** the serialization is the same sequence of bytes which would be written +** to disk if that database where backed up to disk. +** +** The usual case is that sqlite3_serialize() copies the serialization of +** the database into memory obtained from [sqlite3_malloc64()] and returns +** a pointer to that memory. The caller is responsible for freeing the +** returned value to avoid a memory leak. However, if the F argument +** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations +** are made, and the sqlite3_serialize() function will return a pointer +** to the contiguous memory representation of the database that SQLite +** is currently using for that database, or NULL if the no such contiguous +** memory representation of the database exists. A contiguous memory +** representation of the database will usually only exist if there has +** been a prior call to [sqlite3_deserialize(D,S,...)] with the same +** values of D and S. +** The size of the database is written into *P even if the +** SQLITE_SERIALIZE_NOCOPY bit is set but no contigious copy +** of the database exists. +** +** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the +** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory +** allocation error occurs. +** +** This interface is only available if SQLite is compiled with the +** [SQLITE_ENABLE_DESERIALIZE] option. +*/ +SQLITE_API unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */ + sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */ + unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_serialize +** +** Zero or more of the following constants can be OR-ed together for +** the F argument to [sqlite3_serialize(D,S,P,F)]. +** +** SQLITE_SERIALIZE_NOCOPY means that [sqlite3_serialize()] will return +** a pointer to contiguous in-memory database that it is currently using, +** without making a copy of the database. If SQLite is not currently using +** a contiguous in-memory database, then this option causes +** [sqlite3_serialize()] to return a NULL pointer. SQLite will only be +** using a contiguous in-memory database if it has been initialized by a +** prior call to [sqlite3_deserialize()]. +*/ +#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ + +/* +** CAPI3REF: Deserialize a database +** +** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the +** [database connection] D to disconnect from database S and then +** reopen S as an in-memory database based on the serialization contained +** in P. The serialized database P is N bytes in size. M is the size of +** the buffer P, which might be larger than N. If M is larger than N, and +** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is +** permitted to add content to the in-memory database as long as the total +** size does not exceed M bytes. +** +** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will +** invoke sqlite3_free() on the serialization buffer when the database +** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then +** SQLite will try to increase the buffer size using sqlite3_realloc64() +** if writes on the database cause it to grow larger than M bytes. +** +** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the +** database is currently in a read transaction or is involved in a backup +** operation. +** +** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the +** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then +** [sqlite3_free()] is invoked on argument P prior to returning. +** +** This interface is only available if SQLite is compiled with the +** [SQLITE_ENABLE_DESERIALIZE] option. +*/ +SQLITE_API int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_deserialize() +** +** The following are allowed values for 6th argument (the F argument) to +** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. +** +** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization +** in the P argument is held in memory obtained from [sqlite3_malloc64()] +** and that SQLite should take ownership of this memory and automatically +** free it when it has finished using it. Without this flag, the caller +** is resposible for freeing any dynamically allocated memory. +** +** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to +** grow the size of the database using calls to [sqlite3_realloc64()]. This +** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used. +** Without this flag, the deserialized database cannot increase in size beyond +** the number of bytes specified by the M parameter. +** +** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database +** should be treated as read-only. +*/ +#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ +#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ +#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ @@ -9987,16 +10071,23 @@ /* ** CAPI3REF: Session Object Handle +** +** An instance of this object is a [session] that can be used to +** record changes to a database. */ typedef struct sqlite3_session sqlite3_session; /* ** CAPI3REF: Changeset Iterator Handle +** +** An instance of this object acts as a cursor for iterating +** over the elements of a [changeset] or [patchset]. */ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; /* ** CAPI3REF: Create A New Session Object +** CONSTRUCTOR: sqlite3_session ** ** Create a new session object attached to database handle db. If successful, ** a pointer to the new object is written to *ppSession and SQLITE_OK is @@ -10033,6 +10124,7 @@ /* ** CAPI3REF: Delete A Session Object +** DESTRUCTOR: sqlite3_session ** ** Delete a session object previously allocated using ** [sqlite3session_create()]. Once a session object has been deleted, the @@ -10048,6 +10140,7 @@ /* ** CAPI3REF: Enable Or Disable A Session Object +** METHOD: sqlite3_session ** ** Enable or disable the recording of changes by a session object. When ** enabled, a session object records changes made to the database. When @@ -10067,6 +10160,7 @@ /* ** CAPI3REF: Set Or Clear the Indirect Change Flag +** METHOD: sqlite3_session ** ** Each change recorded by a session object is marked as either direct or ** indirect. A change is marked as indirect if either: @@ -10096,6 +10190,7 @@ /* ** CAPI3REF: Attach A Table To A Session Object +** METHOD: sqlite3_session ** ** If argument zTab is not NULL, then it is the name of a table to attach ** to the session object passed as the first argument. All subsequent changes @@ -10158,6 +10253,7 @@ /* ** CAPI3REF: Set a table filter on a Session Object. +** METHOD: sqlite3_session ** ** The second argument (xFilter) is the "filter callback". For changes to rows ** in tables that are not attached to the Session object, the filter is called @@ -10176,6 +10272,7 @@ /* ** CAPI3REF: Generate A Changeset From A Session Object +** METHOD: sqlite3_session ** ** Obtain a changeset containing changes to the tables attached to the ** session object passed as the first argument. If successful, @@ -10286,6 +10383,7 @@ /* ** CAPI3REF: Load The Difference Between Tables Into A Session +** METHOD: sqlite3_session ** ** If it is not already attached to the session object passed as the first ** argument, this function attaches table zTbl in the same manner as the @@ -10350,6 +10448,7 @@ /* ** CAPI3REF: Generate A Patchset From A Session Object +** METHOD: sqlite3_session ** ** The differences between a patchset and a changeset are that: ** @@ -10401,6 +10500,7 @@ /* ** CAPI3REF: Create An Iterator To Traverse A Changeset +** CONSTRUCTOR: sqlite3_changeset_iter ** ** Create an iterator used to iterate through the contents of a changeset. ** If successful, *pp is set to point to the iterator handle and SQLITE_OK @@ -10441,6 +10541,7 @@ /* ** CAPI3REF: Advance A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function may only be used with iterators created by function ** [sqlite3changeset_start()]. If it is called on an iterator passed to @@ -10465,6 +10566,7 @@ /* ** CAPI3REF: Obtain The Current Operation From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -10499,6 +10601,7 @@ /* ** CAPI3REF: Obtain The Primary Key Definition Of A Table +** METHOD: sqlite3_changeset_iter ** ** For each modified table, a changeset includes the following: ** @@ -10530,6 +10633,7 @@ /* ** CAPI3REF: Obtain old.* Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -10560,6 +10664,7 @@ /* ** CAPI3REF: Obtain new.* Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -10593,6 +10698,7 @@ /* ** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function should only be used with iterator objects passed to a ** conflict-handler callback by [sqlite3changeset_apply()] with either @@ -10620,6 +10726,7 @@ /* ** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations +** METHOD: sqlite3_changeset_iter ** ** This function may only be called with an iterator passed to an ** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case @@ -10636,6 +10743,7 @@ /* ** CAPI3REF: Finalize A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function is used to finalize an iterator allocated with ** [sqlite3changeset_start()]. @@ -10652,6 +10760,7 @@ ** to that error is returned by this function. Otherwise, SQLITE_OK is ** returned. This is to allow the following pattern (pseudo-code): ** +** <pre> ** sqlite3changeset_start(); ** while( SQLITE_ROW==sqlite3changeset_next() ){ ** // Do something with change. @@ -10660,6 +10769,7 @@ ** if( rc!=SQLITE_OK ){ ** // An error has occurred ** } +** </pre> */ SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter); @@ -10707,6 +10817,7 @@ ** sqlite3_changegroup object. Calling it produces similar results as the ** following code fragment: ** +** <pre> ** sqlite3_changegroup *pGrp; ** rc = sqlite3_changegroup_new(&pGrp); ** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA); @@ -10717,6 +10828,7 @@ ** *ppOut = 0; ** *pnOut = 0; ** } +** </pre> ** ** Refer to the sqlite3_changegroup documentation below for details. */ @@ -10732,11 +10844,15 @@ /* ** CAPI3REF: Changegroup Handle +** +** A changegroup is an object used to combine two or more +** [changesets] or [patchsets] */ typedef struct sqlite3_changegroup sqlite3_changegroup; /* ** CAPI3REF: Create A New Changegroup Object +** CONSTRUCTOR: sqlite3_changegroup ** ** An sqlite3_changegroup object is used to combine two or more changesets ** (or patchsets) into a single changeset (or patchset). A single changegroup @@ -10774,6 +10890,7 @@ /* ** CAPI3REF: Add A Changeset To A Changegroup +** METHOD: sqlite3_changegroup ** ** Add all changes within the changeset (or patchset) in buffer pData (size ** nData bytes) to the changegroup. @@ -10851,6 +10968,7 @@ /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup +** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup @@ -10881,25 +10999,25 @@ /* ** CAPI3REF: Delete A Changegroup Object +** DESTRUCTOR: sqlite3_changegroup */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*); /* ** CAPI3REF: Apply A Changeset To A Database ** -** Apply a changeset to a database. This function attempts to update the -** "main" database attached to handle db with the changes found in the -** changeset passed via the second and third arguments. +** Apply a changeset or patchset to a database. These functions attempt to +** update the "main" database attached to handle db with the changes found in +** the changeset passed via the second and third arguments. ** -** The fourth argument (xFilter) passed to this function is the "filter +** The fourth argument (xFilter) passed to these functions is the "filter ** callback". If it is not NULL, then for each table affected by at least one ** change in the changeset, the filter callback is invoked with ** the table name as the second argument, and a copy of the context pointer -** passed as the sixth argument to this function as the first. If the "filter -** callback" returns zero, then no attempt is made to apply any changes to -** the table. Otherwise, if the return value is non-zero or the xFilter -** argument to this function is NULL, all changes related to the table are -** attempted. +** passed as the sixth argument as the first. If the "filter callback" +** returns zero, then no attempt is made to apply any changes to the table. +** Otherwise, if the return value is non-zero or the xFilter argument to +** is NULL, all changes related to the table are attempted. ** ** For each table that is not excluded by the filter callback, this function ** tests that the target database contains a compatible table. A table is @@ -10944,7 +11062,7 @@ ** ** <dl> ** <dt>DELETE Changes<dd> -** For each DELETE change, this function checks if the target database +** For each DELETE change, the function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all non-primary key columns also match the values stored in @@ -10989,7 +11107,7 @@ ** [SQLITE_CHANGESET_REPLACE]. ** ** <dt>UPDATE Changes<dd> -** For each UPDATE change, this function checks if the target database +** For each UPDATE change, the function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all modified non-primary key columns also match the values @@ -11020,11 +11138,28 @@ ** This can be used to further customize the applications conflict ** resolution strategy. ** -** All changes made by this function are enclosed in a savepoint transaction. +** All changes made by these functions are enclosed in a savepoint transaction. ** If any other error (aside from a constraint failure when attempting to ** write to the target database) occurs, then the savepoint transaction is ** rolled back, restoring the target database to its original state, and an ** SQLite error code returned. +** +** If the output parameters (ppRebase) and (pnRebase) are non-NULL and +** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() +** may set (*ppRebase) to point to a "rebase" that may be used with the +** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) +** is set to the size of the buffer in bytes. It is the responsibility of the +** caller to eventually free any such buffer using sqlite3_free(). The buffer +** is only allocated and populated if one or more conflicts were encountered +** while applying the patchset. See comments surrounding the sqlite3_rebaser +** APIs for further details. +** +** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent +** may be modified by passing a combination of +** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter. +** +** Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b> +** and therefore subject to change. */ SQLITE_API int sqlite3changeset_apply( sqlite3 *db, /* Apply change to "main" db of this handle */ @@ -11041,6 +11176,41 @@ ), void *pCtx /* First argument passed to xConflict */ ); +SQLITE_API int sqlite3changeset_apply_v2( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void *pChangeset, /* Changeset blob */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, /* OUT: Rebase data */ + int flags /* Combination of SESSION_APPLY_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3changeset_apply_v2 +** +** The following flags may passed via the 9th parameter to +** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]: +** +** <dl> +** <dt>SQLITE_CHANGESETAPPLY_NOSAVEPOINT <dd> +** Usually, the sessions module encloses all operations performed by +** a single call to apply_v2() or apply_v2_strm() in a [SAVEPOINT]. The +** SAVEPOINT is committed if the changeset or patchset is successfully +** applied, or rolled back if an error occurs. Specifying this flag +** causes the sessions module to omit this savepoint. In this case, if the +** caller has an open transaction or savepoint when apply_v2() is called, +** it may revert the partially applied changeset by rolling it back. +*/ +#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -11139,6 +11309,161 @@ #define SQLITE_CHANGESET_ABORT 2 /* +** CAPI3REF: Rebasing changesets +** EXPERIMENTAL +** +** Suppose there is a site hosting a database in state S0. And that +** modifications are made that move that database to state S1 and a +** changeset recorded (the "local" changeset). Then, a changeset based +** on S0 is received from another site (the "remote" changeset) and +** applied to the database. The database is then in state +** (S1+"remote"), where the exact state depends on any conflict +** resolution decisions (OMIT or REPLACE) made while applying "remote". +** Rebasing a changeset is to update it to take those conflict +** resolution decisions into account, so that the same conflicts +** do not have to be resolved elsewhere in the network. +** +** For example, if both the local and remote changesets contain an +** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)": +** +** local: INSERT INTO t1 VALUES(1, 'v1'); +** remote: INSERT INTO t1 VALUES(1, 'v2'); +** +** and the conflict resolution is REPLACE, then the INSERT change is +** removed from the local changeset (it was overridden). Or, if the +** conflict resolution was "OMIT", then the local changeset is modified +** to instead contain: +** +** UPDATE t1 SET b = 'v2' WHERE a=1; +** +** Changes within the local changeset are rebased as follows: +** +** <dl> +** <dt>Local INSERT<dd> +** This may only conflict with a remote INSERT. If the conflict +** resolution was OMIT, then add an UPDATE change to the rebased +** changeset. Or, if the conflict resolution was REPLACE, add +** nothing to the rebased changeset. +** +** <dt>Local DELETE<dd> +** This may conflict with a remote UPDATE or DELETE. In both cases the +** only possible resolution is OMIT. If the remote operation was a +** DELETE, then add no change to the rebased changeset. If the remote +** operation was an UPDATE, then the old.* fields of change are updated +** to reflect the new.* values in the UPDATE. +** +** <dt>Local UPDATE<dd> +** This may conflict with a remote UPDATE or DELETE. If it conflicts +** with a DELETE, and the conflict resolution was OMIT, then the update +** is changed into an INSERT. Any undefined values in the new.* record +** from the update change are filled in using the old.* values from +** the conflicting DELETE. Or, if the conflict resolution was REPLACE, +** the UPDATE change is simply omitted from the rebased changeset. +** +** If conflict is with a remote UPDATE and the resolution is OMIT, then +** the old.* values are rebased using the new.* values in the remote +** change. Or, if the resolution is REPLACE, then the change is copied +** into the rebased changeset with updates to columns also updated by +** the conflicting remote UPDATE removed. If this means no columns would +** be updated, the change is omitted. +** </dl> +** +** A local change may be rebased against multiple remote changes +** simultaneously. If a single key is modified by multiple remote +** changesets, they are combined as follows before the local changeset +** is rebased: +** +** <ul> +** <li> If there has been one or more REPLACE resolutions on a +** key, it is rebased according to a REPLACE. +** +** <li> If there have been no REPLACE resolutions on a key, then +** the local changeset is rebased according to the most recent +** of the OMIT resolutions. +** </ul> +** +** Note that conflict resolutions from multiple remote changesets are +** combined on a per-field basis, not per-row. This means that in the +** case of multiple remote UPDATE operations, some fields of a single +** local change may be rebased for REPLACE while others are rebased for +** OMIT. +** +** In order to rebase a local changeset, the remote changeset must first +** be applied to the local database using sqlite3changeset_apply_v2() and +** the buffer of rebase information captured. Then: +** +** <ol> +** <li> An sqlite3_rebaser object is created by calling +** sqlite3rebaser_create(). +** <li> The new object is configured with the rebase buffer obtained from +** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure(). +** If the local changeset is to be rebased against multiple remote +** changesets, then sqlite3rebaser_configure() should be called +** multiple times, in the same order that the multiple +** sqlite3changeset_apply_v2() calls were made. +** <li> Each local changeset is rebased by calling sqlite3rebaser_rebase(). +** <li> The sqlite3_rebaser object is deleted by calling +** sqlite3rebaser_delete(). +** </ol> +*/ +typedef struct sqlite3_rebaser sqlite3_rebaser; + +/* +** CAPI3REF: Create a changeset rebaser object. +** EXPERIMENTAL +** +** Allocate a new changeset rebaser object. If successful, set (*ppNew) to +** point to the new object and return SQLITE_OK. Otherwise, if an error +** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew) +** to NULL. +*/ +SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew); + +/* +** CAPI3REF: Configure a changeset rebaser object. +** EXPERIMENTAL +** +** Configure the changeset rebaser object to rebase changesets according +** to the conflict resolutions described by buffer pRebase (size nRebase +** bytes), which must have been obtained from a previous call to +** sqlite3changeset_apply_v2(). +*/ +SQLITE_API int sqlite3rebaser_configure( + sqlite3_rebaser*, + int nRebase, const void *pRebase +); + +/* +** CAPI3REF: Rebase a changeset +** EXPERIMENTAL +** +** Argument pIn must point to a buffer containing a changeset nIn bytes +** in size. This function allocates and populates a buffer with a copy +** of the changeset rebased rebased according to the configuration of the +** rebaser object passed as the first argument. If successful, (*ppOut) +** is set to point to the new buffer containing the rebased changset and +** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the +** responsibility of the caller to eventually free the new buffer using +** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) +** are set to zero and an SQLite error code returned. +*/ +SQLITE_API int sqlite3rebaser_rebase( + sqlite3_rebaser*, + int nIn, const void *pIn, + int *pnOut, void **ppOut +); + +/* +** CAPI3REF: Delete a changeset rebaser object. +** EXPERIMENTAL +** +** Delete the changeset rebaser object and all associated resources. There +** should be one call to this function for each successful invocation +** of sqlite3rebaser_create(). +*/ +SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p); + +/* ** CAPI3REF: Streaming Versions of API functions. ** ** The six streaming API xxx_strm() functions serve similar purposes to the @@ -11147,6 +11472,7 @@ ** <table border=1 style="margin-left:8ex;margin-right:8ex"> ** <tr><th>Streaming function<th>Non-streaming equivalent</th> ** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply] +** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2] ** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat] ** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert] ** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start] @@ -11242,6 +11568,23 @@ ), void *pCtx /* First argument passed to xConflict */ ); +SQLITE_API int sqlite3changeset_apply_v2_strm( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ + void *pIn, /* First arg for xInput */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +); SQLITE_API int sqlite3changeset_concat_strm( int (*xInputA)(void *pIn, void *pData, int *pnData), void *pInA, @@ -11279,6 +11622,13 @@ int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); +SQLITE_API int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *pRebaser, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +); /* @@ -12696,23 +13046,25 @@ #define TK_INDEX 140 #define TK_ALTER 141 #define TK_ADD 142 -#define TK_ISNOT 143 -#define TK_FUNCTION 144 -#define TK_COLUMN 145 -#define TK_AGG_FUNCTION 146 -#define TK_AGG_COLUMN 147 -#define TK_UMINUS 148 -#define TK_UPLUS 149 -#define TK_REGISTER 150 -#define TK_VECTOR 151 -#define TK_SELECT_COLUMN 152 -#define TK_IF_NULL_ROW 153 -#define TK_ASTERISK 154 -#define TK_SPAN 155 -#define TK_END_OF_FILE 156 -#define TK_UNCLOSED_STRING 157 -#define TK_SPACE 158 -#define TK_ILLEGAL 159 +#define TK_TRUEFALSE 143 +#define TK_ISNOT 144 +#define TK_FUNCTION 145 +#define TK_COLUMN 146 +#define TK_AGG_FUNCTION 147 +#define TK_AGG_COLUMN 148 +#define TK_UMINUS 149 +#define TK_UPLUS 150 +#define TK_TRUTH 151 +#define TK_REGISTER 152 +#define TK_VECTOR 153 +#define TK_SELECT_COLUMN 154 +#define TK_IF_NULL_ROW 155 +#define TK_ASTERISK 156 +#define TK_SPAN 157 +#define TK_END_OF_FILE 158 +#define TK_UNCLOSED_STRING 159 +#define TK_SPACE 160 +#define TK_ILLEGAL 161 /* The token codes above must all fit in 8 bits */ #define TKFLG_MASK 0xff @@ -13156,9 +13508,10 @@ */ typedef struct BusyHandler BusyHandler; struct BusyHandler { - int (*xFunc)(void *,int); /* The busy callback */ - void *pArg; /* First arg to busy callback */ - int nBusy; /* Incremented with each busy call */ + int (*xBusyHandler)(void *,int); /* The busy callback */ + void *pBusyArg; /* First arg to busy callback */ + int nBusy; /* Incremented with each busy call */ + u8 bExtraFileArg; /* Include sqlite3_file as callback arg */ }; /* @@ -13960,80 +14313,81 @@ #define OP_Concat 93 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ #define OP_Compare 94 /* synopsis: r[P1@P3] <-> r[P2@P3] */ #define OP_BitNot 95 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */ -#define OP_Offset 96 /* synopsis: r[P3] = sqlite_offset(P1) */ +#define OP_IsTrue 96 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ #define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */ -#define OP_Column 98 /* synopsis: r[P3]=PX */ -#define OP_Affinity 99 /* synopsis: affinity(r[P1@P2]) */ -#define OP_MakeRecord 100 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_Count 101 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 102 -#define OP_SetCookie 103 -#define OP_ReopenIdx 104 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenRead 105 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenWrite 106 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenDup 107 -#define OP_OpenAutoindex 108 /* synopsis: nColumn=P2 */ -#define OP_OpenEphemeral 109 /* synopsis: nColumn=P2 */ -#define OP_SorterOpen 110 -#define OP_SequenceTest 111 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ -#define OP_OpenPseudo 112 /* synopsis: P3 columns in r[P2] */ -#define OP_Close 113 -#define OP_ColumnsUsed 114 -#define OP_Sequence 115 /* synopsis: r[P2]=cursor[P1].ctr++ */ -#define OP_NewRowid 116 /* synopsis: r[P2]=rowid */ -#define OP_Insert 117 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_InsertInt 118 /* synopsis: intkey=P3 data=r[P2] */ -#define OP_Delete 119 -#define OP_ResetCount 120 -#define OP_SorterCompare 121 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -#define OP_SorterData 122 /* synopsis: r[P2]=data */ -#define OP_RowData 123 /* synopsis: r[P2]=data */ -#define OP_Rowid 124 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 125 -#define OP_SeekEnd 126 -#define OP_SorterInsert 127 /* synopsis: key=r[P2] */ -#define OP_IdxInsert 128 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 129 /* synopsis: key=r[P2@P3] */ -#define OP_DeferredSeek 130 /* synopsis: Move P3 to P1.rowid if needed */ -#define OP_IdxRowid 131 /* synopsis: r[P2]=rowid */ +#define OP_Offset 98 /* synopsis: r[P3] = sqlite_offset(P1) */ +#define OP_Column 99 /* synopsis: r[P3]=PX */ +#define OP_Affinity 100 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 101 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 102 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 103 +#define OP_SetCookie 104 +#define OP_ReopenIdx 105 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenRead 106 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 107 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenDup 108 +#define OP_OpenAutoindex 109 /* synopsis: nColumn=P2 */ +#define OP_OpenEphemeral 110 /* synopsis: nColumn=P2 */ +#define OP_SorterOpen 111 +#define OP_SequenceTest 112 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ +#define OP_OpenPseudo 113 /* synopsis: P3 columns in r[P2] */ +#define OP_Close 114 +#define OP_ColumnsUsed 115 +#define OP_Sequence 116 /* synopsis: r[P2]=cursor[P1].ctr++ */ +#define OP_NewRowid 117 /* synopsis: r[P2]=rowid */ +#define OP_Insert 118 /* synopsis: intkey=r[P3] data=r[P2] */ +#define OP_InsertInt 119 /* synopsis: intkey=P3 data=r[P2] */ +#define OP_Delete 120 +#define OP_ResetCount 121 +#define OP_SorterCompare 122 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 123 /* synopsis: r[P2]=data */ +#define OP_RowData 124 /* synopsis: r[P2]=data */ +#define OP_Rowid 125 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 126 +#define OP_SeekEnd 127 +#define OP_SorterInsert 128 /* synopsis: key=r[P2] */ +#define OP_IdxInsert 129 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 130 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 131 /* synopsis: Move P3 to P1.rowid if needed */ #define OP_Real 132 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_Destroy 133 -#define OP_Clear 134 -#define OP_ResetSorter 135 -#define OP_CreateBtree 136 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ -#define OP_SqlExec 137 -#define OP_ParseSchema 138 -#define OP_LoadAnalysis 139 -#define OP_DropTable 140 -#define OP_DropIndex 141 -#define OP_DropTrigger 142 -#define OP_IntegrityCk 143 -#define OP_RowSetAdd 144 /* synopsis: rowset(P1)=r[P2] */ -#define OP_Param 145 -#define OP_FkCounter 146 /* synopsis: fkctr[P1]+=P2 */ -#define OP_MemMax 147 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_OffsetLimit 148 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggStep0 149 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep 150 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggFinal 151 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 152 -#define OP_TableLock 153 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 154 -#define OP_VCreate 155 -#define OP_VDestroy 156 -#define OP_VOpen 157 -#define OP_VColumn 158 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 159 -#define OP_Pagecount 160 -#define OP_MaxPgcnt 161 -#define OP_PureFunc0 162 -#define OP_Function0 163 /* synopsis: r[P3]=func(r[P2@P5]) */ -#define OP_PureFunc 164 -#define OP_Function 165 /* synopsis: r[P3]=func(r[P2@P5]) */ -#define OP_Trace 166 -#define OP_CursorHint 167 -#define OP_Noop 168 -#define OP_Explain 169 +#define OP_IdxRowid 133 /* synopsis: r[P2]=rowid */ +#define OP_Destroy 134 +#define OP_Clear 135 +#define OP_ResetSorter 136 +#define OP_CreateBtree 137 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 138 +#define OP_ParseSchema 139 +#define OP_LoadAnalysis 140 +#define OP_DropTable 141 +#define OP_DropIndex 142 +#define OP_DropTrigger 143 +#define OP_IntegrityCk 144 +#define OP_RowSetAdd 145 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 146 +#define OP_FkCounter 147 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 148 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 149 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggStep0 150 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep 151 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggFinal 152 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 153 +#define OP_TableLock 154 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 155 +#define OP_VCreate 156 +#define OP_VDestroy 157 +#define OP_VOpen 158 +#define OP_VColumn 159 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 160 +#define OP_Pagecount 161 +#define OP_MaxPgcnt 162 +#define OP_PureFunc0 163 +#define OP_Function0 164 /* synopsis: r[P3]=func(r[P2@P5]) */ +#define OP_PureFunc 165 +#define OP_Function 166 /* synopsis: r[P3]=func(r[P2@P5]) */ +#define OP_Trace 167 +#define OP_CursorHint 168 +#define OP_Noop 169 +#define OP_Explain 170 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -14058,16 +14412,16 @@ /* 72 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ /* 80 */ 0x02, 0x02, 0x02, 0x00, 0x26, 0x26, 0x26, 0x26,\ /* 88 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\ -/* 96 */ 0x20, 0x10, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\ +/* 96 */ 0x12, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10,\ /* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 112 */ 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00,\ -/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x04,\ -/* 128 */ 0x04, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00,\ -/* 136 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 144 */ 0x06, 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00,\ +/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ +/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,\ +/* 128 */ 0x04, 0x04, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00,\ +/* 136 */ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 144 */ 0x00, 0x06, 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00,\ /* 152 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x00, 0x00,} +/* 160 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 168 */ 0x00, 0x00, 0x00,} /* The sqlite3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -14368,7 +14722,7 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ -SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); +SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); #ifdef SQLITE_HAS_CODEC SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager*,Pager*); @@ -14454,6 +14808,11 @@ SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager); +#else +# define sqlite3PagerResetLockTimeout(X) +#endif /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); @@ -15273,8 +15632,9 @@ int newTnum; /* Rootpage of table being initialized */ u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ - u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ - u8 imposterTable; /* Building an imposter table */ + unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ + unsigned imposterTable : 1; /* Building an imposter table */ + unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ @@ -15439,6 +15799,8 @@ #define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */ #define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */ /* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */ +#define SQLITE_PushDown 0x1000 /* The push-down optimization */ +#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */ #define SQLITE_AllOpts 0xffff /* All optimizations */ /* @@ -15662,6 +16024,7 @@ #define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ #define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ #define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */ +#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */ /* ** A "Collating Sequence" is defined by an instance of the following @@ -16899,7 +17262,6 @@ int nMaxArg; /* Max args passed to user function by sub-program */ #if SELECTTRACE_ENABLED int nSelect; /* Number of SELECT statements seen */ - int nSelectIndent; /* How far to indent SELECTTRACE() output */ #endif #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ @@ -17263,9 +17625,9 @@ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ int *aiCol; /* array of column indexes */ struct IdxCover *pIdxCover; /* Check for index coverage */ - struct IdxExprTrans *pIdxTrans; /* Convert indexed expr to column */ + struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */ ExprList *pGroupBy; /* GROUP BY clause */ - struct HavingToWhereCtx *pHavingCtx; /* HAVING to WHERE clause ctx */ + Select *pSelect; /* HAVING to WHERE clause ctx */ } u; }; @@ -17729,6 +18091,7 @@ SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int); SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); +SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); @@ -17746,6 +18109,8 @@ SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*); SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *); SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*); +SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); +SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); @@ -17928,6 +18293,10 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif +#ifdef SQLITE_ENABLE_DESERIALIZE +SQLITE_PRIVATE int sqlite3MemdbInit(void); +#endif + SQLITE_PRIVATE const char *sqlite3ErrStr(int); SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); @@ -17976,6 +18345,9 @@ SQLITE_PRIVATE int sqlite3PendingByte; #endif #endif +#ifdef VDBE_PROFILE +SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt; +#endif SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int); SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); SQLITE_PRIVATE void sqlite3AlterFunctions(void); @@ -17998,7 +18370,7 @@ SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); SQLITE_PRIVATE char sqlite3AffinityType(const char*, u8*); SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); -SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*); +SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*); SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *); SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB); @@ -18599,6 +18971,13 @@ { "1", 1 } }; +#ifdef VDBE_PROFILE +/* +** The following performance counter can be used in place of +** sqlite3Hwtime() for profiling. This is a no-op on standard builds. +*/ +SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0; +#endif /* ** The value of the "pending" byte must be 0x40000000 (1 byte past the @@ -18882,8 +19261,6 @@ ** If the MEM_Null flag is set, then the value is an SQL NULL value. ** For a pointer type created using sqlite3_bind_pointer() or ** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. -** If both MEM_Null and MEM_Zero are set, that means that the value is -** an unchanging column value from VColumn. ** ** If the MEM_Str flag is set then Mem.z points at a string representation. ** Usually this is encoded in the same unicode encoding as the main @@ -18977,7 +19354,6 @@ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ u8 skipFlag; /* Skip accumulator loading if true */ - u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */ u8 argc; /* Number of arguments */ sqlite3_value *argv[1]; /* Argument set */ }; @@ -19147,6 +19523,7 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*); SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*); +SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem*, int ifNull); SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*); @@ -19540,6 +19917,9 @@ ** pagers the database handle is connected to. *pHighwater is always set ** to zero. */ + case SQLITE_DBSTATUS_CACHE_SPILL: + op = SQLITE_DBSTATUS_CACHE_WRITE+1; + /* Fall through into the next case */ case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{ @@ -20960,8 +21340,11 @@ ** routine has no return value since the return value would be meaningless. */ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ + if( id->pMethods==0 ) return SQLITE_NOTFOUND; #ifdef SQLITE_TEST - if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO + && op!=SQLITE_FCNTL_LOCK_TIMEOUT + ){ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite ** is using a regular VFS, it is called after the corresponding ** transaction has been committed. Injecting a fault at this point @@ -20978,7 +21361,7 @@ return id->pMethods->xFileControl(id, op, pArg); } SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ - (void)id->pMethods->xFileControl(id, op, pArg); + if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg); } SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){ @@ -24810,7 +25193,7 @@ #ifdef SQLITE_DEBUG volatile int nRef; /* Number of enterances */ volatile DWORD owner; /* Thread holding this mutex */ - volatile int trace; /* True to trace changes */ + volatile LONG trace; /* True to trace changes */ #endif }; @@ -24822,10 +25205,10 @@ #define SQLITE_W32_MUTEX_INITIALIZER { 0 } #ifdef SQLITE_DEBUG -#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \ +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \ 0L, (DWORD)0, 0 } #else -#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 } +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id } #endif #ifdef SQLITE_DEBUG @@ -24868,18 +25251,18 @@ ** Initialize and deinitialize the mutex subsystem. */ static sqlite3_mutex winMutex_staticMutexes[] = { - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER + SQLITE3_MUTEX_INITIALIZER(2), + SQLITE3_MUTEX_INITIALIZER(3), + SQLITE3_MUTEX_INITIALIZER(4), + SQLITE3_MUTEX_INITIALIZER(5), + SQLITE3_MUTEX_INITIALIZER(6), + SQLITE3_MUTEX_INITIALIZER(7), + SQLITE3_MUTEX_INITIALIZER(8), + SQLITE3_MUTEX_INITIALIZER(9), + SQLITE3_MUTEX_INITIALIZER(10), + SQLITE3_MUTEX_INITIALIZER(11), + SQLITE3_MUTEX_INITIALIZER(12), + SQLITE3_MUTEX_INITIALIZER(13) }; static int winMutex_isInit = 0; @@ -25009,15 +25392,15 @@ } #endif p = &winMutex_staticMutexes[iType-2]; - p->id = iType; #ifdef SQLITE_DEBUG #ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC - p->trace = 1; + InterlockedCompareExchange(&p->trace, 1, 0); #endif #endif break; } } + assert( p==0 || p->id==iType ); return p; } @@ -26096,6 +26479,11 @@ PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ + /* pAccum never starts out with an empty buffer that was obtained from + ** malloc(). This precondition is required by the mprintf("%z...") + ** optimization. */ + assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); + bufpt = 0; if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){ pArgList = va_arg(ap, PrintfArguments*); @@ -26514,9 +26902,38 @@ case etCHARX: if( bArgList ){ bufpt = getTextArg(pArgList); - c = bufpt ? bufpt[0] : 0; + length = 1; + if( bufpt ){ + buf[0] = c = *(bufpt++); + if( (c&0xc0)==0xc0 ){ + while( length<4 && (bufpt[0]&0xc0)==0x80 ){ + buf[length++] = *(bufpt++); + } + } + }else{ + buf[0] = 0; + } }else{ - c = va_arg(ap,int); + unsigned int ch = va_arg(ap,unsigned int); + if( ch<0x00080 ){ + buf[0] = ch & 0xff; + length = 1; + }else if( ch<0x00800 ){ + buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); + buf[1] = 0x80 + (u8)(ch & 0x3f); + length = 2; + }else if( ch<0x10000 ){ + buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); + buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); + buf[2] = 0x80 + (u8)(ch & 0x3f); + length = 3; + }else{ + buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); + buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); + buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); + buf[3] = 0x80 + (u8)(ch & 0x3f); + length = 4; + } } if( precision>1 ){ width -= precision-1; @@ -26524,12 +26941,13 @@ sqlite3AppendChar(pAccum, width-1, ' '); width = 0; } - sqlite3AppendChar(pAccum, precision-1, c); + while( precision-- > 1 ){ + sqlite3StrAccumAppend(pAccum, buf, length); + } } - length = 1; - buf[0] = c; bufpt = buf; - break; + flag_altform2 = 1; + goto adjust_width_for_utf8; case etSTRING: case etDYNSTRING: if( bArgList ){ @@ -26541,17 +26959,45 @@ if( bufpt==0 ){ bufpt = ""; }else if( xtype==etDYNSTRING ){ + if( pAccum->nChar==0 && pAccum->mxAlloc && width==0 && precision<0 ){ + /* Special optimization for sqlite3_mprintf("%z..."): + ** Extend an existing memory allocation rather than creating + ** a new one. */ + assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); + pAccum->zText = bufpt; + pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt); + pAccum->nChar = 0x7fffffff & (int)strlen(bufpt); + pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED; + length = 0; + break; + } zExtra = bufpt; } if( precision>=0 ){ - for(length=0; length<precision && bufpt[length]; length++){} + if( flag_altform2 ){ + /* Set length to the number of bytes needed in order to display + ** precision characters */ + unsigned char *z = (unsigned char*)bufpt; + while( precision-- > 0 && z[0] ){ + SQLITE_SKIP_UTF8(z); + } + length = (int)(z - (unsigned char*)bufpt); + }else{ + for(length=0; length<precision && bufpt[length]; length++){} + } }else{ length = 0x7fffffff & (int)strlen(bufpt); } + adjust_width_for_utf8: + if( flag_altform2 && width>0 ){ + /* Adjust width to account for extra bytes in UTF-8 characters */ + int ii = length - 1; + while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; + } break; - case etSQLESCAPE: /* Escape ' characters */ - case etSQLESCAPE2: /* Escape ' and enclose in '...' */ - case etSQLESCAPE3: { /* Escape " characters */ + case etSQLESCAPE: /* %q: Escape ' characters */ + case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ + case etSQLESCAPE3: { /* %w: Escape " characters */ int i, j, k, n, isnull; int needQuote; char ch; @@ -26565,9 +27011,17 @@ } isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); + /* For %q, %Q, and %w, the precision is the number of byte (or + ** characters if the ! flags is present) to use from the input. + ** Because of the extra quoting characters inserted, the number + ** of output characters may be larger than the precision. + */ k = precision; for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){ if( ch==q ) n++; + if( flag_altform2 && (ch&0xc0)==0xc0 ){ + while( (escarg[i+1]&0xc0)==0x80 ){ i++; } + } } needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 3; @@ -26590,10 +27044,7 @@ if( needQuote ) bufpt[j++] = q; bufpt[j] = 0; length = j; - /* The precision in %q and %Q means how many input characters to - ** consume, not the length of the output... - ** if( precision>=0 && precision<length ) length = precision; */ - break; + goto adjust_width_for_utf8; } case etTOKEN: { Token *pToken; @@ -26632,7 +27083,10 @@ /* ** The text of the conversion is pointed to by "bufpt" and is ** "length" characters long. The field width is "width". Do - ** the output. + ** the output. Both length and width are in bytes, not characters, + ** at this point. If the "!" flag was present on string conversions + ** indicating that width and precision should be expressed in characters, + ** then the values have been translated prior to reaching this point. */ width -= length; if( width>0 ){ @@ -27147,11 +27601,21 @@ sqlite3TreeViewPush(pView, 1); } do{ +#if SELECTTRACE_ENABLED + sqlite3TreeViewLine(pView, + "SELECT%s%s (%s/%p) selFlags=0x%x nSelectRow=%d", + ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), + p->zSelName, p, p->selFlags, + (int)p->nSelectRow + ); +#else sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d", ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags, (int)p->nSelectRow ); +#endif if( cnt++ ) sqlite3TreeViewPop(pView); if( p->pPrior ){ n = 1000; @@ -27302,6 +27766,11 @@ sqlite3TreeViewLine(pView,"NULL"); break; } + case TK_TRUEFALSE: { + sqlite3TreeViewLine(pView, + sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE"); + break; + } #ifndef SQLITE_OMIT_BLOB_LITERAL case TK_BLOB: { sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); @@ -27358,6 +27827,19 @@ case TK_ISNULL: zUniOp = "ISNULL"; break; case TK_NOTNULL: zUniOp = "NOTNULL"; break; + case TK_TRUTH: { + int x; + const char *azOp[] = { + "IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE" + }; + assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); + assert( pExpr->pRight ); + assert( pExpr->pRight->op==TK_TRUEFALSE ); + x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight); + zUniOp = azOp[x]; + break; + } + case TK_SPAN: { sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); @@ -29095,7 +29577,7 @@ ** Returns: ** ** 0 Successful transformation. Fits in a 64-bit signed integer. -** 1 Excess text after the integer value +** 1 Excess non-space text after the integer value ** 2 Integer too large for a 64-bit signed integer or is malformed ** 3 Special case of 9223372036854775808 ** @@ -29138,47 +29620,57 @@ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ u = u*10 + c - '0'; } + testcase( i==18*incr ); + testcase( i==19*incr ); + testcase( i==20*incr ); if( u>LARGEST_INT64 ){ + /* This test and assignment is needed only to suppress UB warnings + ** from clang and -fsanitize=undefined. This test and assignment make + ** the code a little larger and slower, and no harm comes from omitting + ** them, but we must appaise the undefined-behavior pharisees. */ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; }else if( neg ){ *pNum = -(i64)u; }else{ *pNum = (i64)u; } - testcase( i==18 ); - testcase( i==19 ); - testcase( i==20 ); - if( &zNum[i]<zEnd /* Extra bytes at the end */ - || (i==0 && zStart==zNum) /* No digits */ + rc = 0; + if( (i==0 && zStart==zNum) /* No digits */ || nonNum /* UTF16 with high-order bytes non-zero */ ){ rc = 1; - }else{ - rc = 0; + }else if( &zNum[i]<zEnd ){ /* Extra bytes at the end */ + int jj = i; + do{ + if( !sqlite3Isspace(zNum[jj]) ){ + rc = 1; /* Extra non-space text after the integer */ + break; + } + jj += incr; + }while( &zNum[jj]<zEnd ); } - if( i>19*incr ){ /* Too many digits */ - /* zNum is empty or contains non-numeric text or is longer - ** than 19 digits (thus guaranteeing that it is too large) */ - return 2; - }else if( i<19*incr ){ + if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ assert( u<=LARGEST_INT64 ); return rc; }else{ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ - c = compare2pow63(zNum, incr); + c = i>19*incr ? 1 : compare2pow63(zNum, incr); if( c<0 ){ /* zNum is less than 9223372036854775808 so it fits */ assert( u<=LARGEST_INT64 ); return rc; - }else if( c>0 ){ - /* zNum is greater than 9223372036854775808 so it overflows */ - return 2; }else{ - /* zNum is exactly 9223372036854775808. Fits if negative. The - ** special case 2 overflow if positive */ - assert( u-1==LARGEST_INT64 ); - return neg ? rc : 3; + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; + if( c>0 ){ + /* zNum is greater than 9223372036854775808 so it overflows */ + return 2; + }else{ + /* zNum is exactly 9223372036854775808. Fits if negative. The + ** special case 2 overflow if positive */ + assert( u-1==LARGEST_INT64 ); + return neg ? rc : 3; + } } } } @@ -30495,80 +30987,81 @@ /* 93 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), /* 94 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), /* 95 */ "BitNot" OpHelp("r[P1]= ~r[P1]"), - /* 96 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), + /* 96 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), /* 97 */ "String8" OpHelp("r[P2]='P4'"), - /* 98 */ "Column" OpHelp("r[P3]=PX"), - /* 99 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 100 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 101 */ "Count" OpHelp("r[P2]=count()"), - /* 102 */ "ReadCookie" OpHelp(""), - /* 103 */ "SetCookie" OpHelp(""), - /* 104 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), - /* 105 */ "OpenRead" OpHelp("root=P2 iDb=P3"), - /* 106 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), - /* 107 */ "OpenDup" OpHelp(""), - /* 108 */ "OpenAutoindex" OpHelp("nColumn=P2"), - /* 109 */ "OpenEphemeral" OpHelp("nColumn=P2"), - /* 110 */ "SorterOpen" OpHelp(""), - /* 111 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), - /* 112 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), - /* 113 */ "Close" OpHelp(""), - /* 114 */ "ColumnsUsed" OpHelp(""), - /* 115 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), - /* 116 */ "NewRowid" OpHelp("r[P2]=rowid"), - /* 117 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 118 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), - /* 119 */ "Delete" OpHelp(""), - /* 120 */ "ResetCount" OpHelp(""), - /* 121 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), - /* 122 */ "SorterData" OpHelp("r[P2]=data"), - /* 123 */ "RowData" OpHelp("r[P2]=data"), - /* 124 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 125 */ "NullRow" OpHelp(""), - /* 126 */ "SeekEnd" OpHelp(""), - /* 127 */ "SorterInsert" OpHelp("key=r[P2]"), - /* 128 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 129 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 130 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), - /* 131 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 98 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), + /* 99 */ "Column" OpHelp("r[P3]=PX"), + /* 100 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 101 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 102 */ "Count" OpHelp("r[P2]=count()"), + /* 103 */ "ReadCookie" OpHelp(""), + /* 104 */ "SetCookie" OpHelp(""), + /* 105 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), + /* 106 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 107 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 108 */ "OpenDup" OpHelp(""), + /* 109 */ "OpenAutoindex" OpHelp("nColumn=P2"), + /* 110 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 111 */ "SorterOpen" OpHelp(""), + /* 112 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), + /* 113 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), + /* 114 */ "Close" OpHelp(""), + /* 115 */ "ColumnsUsed" OpHelp(""), + /* 116 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), + /* 117 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 118 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), + /* 119 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), + /* 120 */ "Delete" OpHelp(""), + /* 121 */ "ResetCount" OpHelp(""), + /* 122 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 123 */ "SorterData" OpHelp("r[P2]=data"), + /* 124 */ "RowData" OpHelp("r[P2]=data"), + /* 125 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 126 */ "NullRow" OpHelp(""), + /* 127 */ "SeekEnd" OpHelp(""), + /* 128 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 129 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 130 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 131 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), /* 132 */ "Real" OpHelp("r[P2]=P4"), - /* 133 */ "Destroy" OpHelp(""), - /* 134 */ "Clear" OpHelp(""), - /* 135 */ "ResetSorter" OpHelp(""), - /* 136 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), - /* 137 */ "SqlExec" OpHelp(""), - /* 138 */ "ParseSchema" OpHelp(""), - /* 139 */ "LoadAnalysis" OpHelp(""), - /* 140 */ "DropTable" OpHelp(""), - /* 141 */ "DropIndex" OpHelp(""), - /* 142 */ "DropTrigger" OpHelp(""), - /* 143 */ "IntegrityCk" OpHelp(""), - /* 144 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 145 */ "Param" OpHelp(""), - /* 146 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 147 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 148 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 149 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 150 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 151 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 152 */ "Expire" OpHelp(""), - /* 153 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 154 */ "VBegin" OpHelp(""), - /* 155 */ "VCreate" OpHelp(""), - /* 156 */ "VDestroy" OpHelp(""), - /* 157 */ "VOpen" OpHelp(""), - /* 158 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 159 */ "VRename" OpHelp(""), - /* 160 */ "Pagecount" OpHelp(""), - /* 161 */ "MaxPgcnt" OpHelp(""), - /* 162 */ "PureFunc0" OpHelp(""), - /* 163 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"), - /* 164 */ "PureFunc" OpHelp(""), - /* 165 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"), - /* 166 */ "Trace" OpHelp(""), - /* 167 */ "CursorHint" OpHelp(""), - /* 168 */ "Noop" OpHelp(""), - /* 169 */ "Explain" OpHelp(""), + /* 133 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 134 */ "Destroy" OpHelp(""), + /* 135 */ "Clear" OpHelp(""), + /* 136 */ "ResetSorter" OpHelp(""), + /* 137 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 138 */ "SqlExec" OpHelp(""), + /* 139 */ "ParseSchema" OpHelp(""), + /* 140 */ "LoadAnalysis" OpHelp(""), + /* 141 */ "DropTable" OpHelp(""), + /* 142 */ "DropIndex" OpHelp(""), + /* 143 */ "DropTrigger" OpHelp(""), + /* 144 */ "IntegrityCk" OpHelp(""), + /* 145 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 146 */ "Param" OpHelp(""), + /* 147 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 148 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 149 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 150 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 151 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 152 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 153 */ "Expire" OpHelp(""), + /* 154 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 155 */ "VBegin" OpHelp(""), + /* 156 */ "VCreate" OpHelp(""), + /* 157 */ "VDestroy" OpHelp(""), + /* 158 */ "VOpen" OpHelp(""), + /* 159 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 160 */ "VRename" OpHelp(""), + /* 161 */ "Pagecount" OpHelp(""), + /* 162 */ "MaxPgcnt" OpHelp(""), + /* 163 */ "PureFunc0" OpHelp(""), + /* 164 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"), + /* 165 */ "PureFunc" OpHelp(""), + /* 166 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"), + /* 167 */ "Trace" OpHelp(""), + /* 168 */ "CursorHint" OpHelp(""), + /* 169 */ "Noop" OpHelp(""), + /* 170 */ "Explain" OpHelp(""), }; return azName[i]; } @@ -30807,6 +31300,9 @@ #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + unsigned iBusyTimeout; /* Wait this many millisec on locks */ +#endif #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID */ #endif @@ -31244,7 +31740,11 @@ #endif #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) +#if defined(HAVE_FCHOWN) { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 }, +#else + { "geteuid", (sqlite3_syscall_ptr)0, 0 }, +#endif #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 @@ -31472,15 +31972,16 @@ ** assert( unixMutexHeld() ); ** unixEnterLeave() */ +static sqlite3_mutex *unixBigLock = 0; static void unixEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_enter(unixBigLock); } static void unixLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_leave(unixBigLock); } #ifdef SQLITE_DEBUG static int unixMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + return sqlite3_mutex_held(unixBigLock); } #endif @@ -32243,6 +32744,43 @@ } /* +** Set a posix-advisory-lock. +** +** There are two versions of this routine. If compiled with +** SQLITE_ENABLE_SETLK_TIMEOUT then the routine has an extra parameter +** which is a pointer to a unixFile. If the unixFile->iBusyTimeout +** value is set, then it is the number of milliseconds to wait before +** failing the lock. The iBusyTimeout value is always reset back to +** zero on each call. +** +** If SQLITE_ENABLE_SETLK_TIMEOUT is not defined, then do a non-blocking +** attempt to set the lock. +*/ +#ifndef SQLITE_ENABLE_SETLK_TIMEOUT +# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x) +#else +static int osSetPosixAdvisoryLock( + int h, /* The file descriptor on which to take the lock */ + struct flock *pLock, /* The description of the lock */ + unixFile *pFile /* Structure holding timeout value */ +){ + int rc = osFcntl(h,F_SETLK,pLock); + while( rc<0 && pFile->iBusyTimeout>0 ){ + /* On systems that support some kind of blocking file lock with a timeout, + ** make appropriate changes here to invoke that blocking file lock. On + ** generic posix, however, there is no such API. So we simply try the + ** lock once every millisecond until either the timeout expires, or until + ** the lock is obtained. */ + usleep(1000); + rc = osFcntl(h,F_SETLK,pLock); + pFile->iBusyTimeout--; + } + return rc; +} +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ + + +/* ** Attempt to set a system-lock on the file pFile. The lock is ** described by pLock. ** @@ -32274,7 +32812,7 @@ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; lock.l_type = F_WRLCK; - rc = osFcntl(pFile->h, F_SETLK, &lock); + rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile); if( rc<0 ) return rc; pInode->bProcessLock = 1; pInode->nLock++; @@ -32282,7 +32820,7 @@ rc = 0; } }else{ - rc = osFcntl(pFile->h, F_SETLK, pLock); + rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); } return rc; } @@ -34642,6 +35180,12 @@ *(int*)pArg = fileHasMoved(pFile); return SQLITE_OK; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + case SQLITE_FCNTL_LOCK_TIMEOUT: { + pFile->iBusyTimeout = *(int*)pArg; + return SQLITE_OK; + } +#endif #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; @@ -34957,13 +35501,11 @@ if( pShmNode->h>=0 ){ /* Initialize the locking parameters */ - memset(&f, 0, sizeof(f)); f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; - - rc = osFcntl(pShmNode->h, F_SETLK, &f); + rc = osSetPosixAdvisoryLock(pShmNode->h, &f, pFile); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; } @@ -38551,6 +39093,7 @@ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); } + unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); return SQLITE_OK; } @@ -38562,6 +39105,7 @@ ** This routine is a no-op for unix. */ SQLITE_API int sqlite3_os_end(void){ + unixBigLock = 0; return SQLITE_OK; } @@ -42400,15 +42944,16 @@ ** assert( winShmMutexHeld() ); ** winShmLeaveMutex() */ +static sqlite3_mutex *winBigLock = 0; static void winShmEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_enter(winBigLock); } static void winShmLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_leave(winBigLock); } #ifndef NDEBUG static int winShmMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + return sqlite3_mutex_held(winBigLock); } #endif @@ -44831,6 +45376,10 @@ sqlite3_vfs_register(&winLongPathNolockVfs, 0); #endif +#ifndef SQLITE_OMIT_WAL + winBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); +#endif + return SQLITE_OK; } @@ -44841,6 +45390,11 @@ sleepObj = NULL; } #endif + +#ifndef SQLITE_OMIT_WAL + winBigLock = 0; +#endif + return SQLITE_OK; } @@ -44855,6 +45409,598 @@ #endif /* SQLITE_OS_WIN */ /************** End of os_win.c **********************************************/ +/************** Begin file memdb.c *******************************************/ +/* +** 2016-09-07 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file implements an in-memory VFS. A database is held as a contiguous +** block of memory. +** +** This file also implements interface sqlite3_serialize() and +** sqlite3_deserialize(). +*/ +#ifdef SQLITE_ENABLE_DESERIALIZE +/* #include "sqliteInt.h" */ + +/* +** Forward declaration of objects used by this utility +*/ +typedef struct sqlite3_vfs MemVfs; +typedef struct MemFile MemFile; + +/* Access to a lower-level VFS that (might) implement dynamic loading, +** access to randomness, etc. +*/ +#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) + +/* An open file */ +struct MemFile { + sqlite3_file base; /* IO methods */ + sqlite3_int64 sz; /* Size of the file */ + sqlite3_int64 szMax; /* Space allocated to aData */ + unsigned char *aData; /* content of the file */ + int nMmap; /* Number of memory mapped pages */ + unsigned mFlags; /* Flags */ + int eLock; /* Most recent lock against this file */ +}; + +/* +** Methods for MemFile +*/ +static int memdbClose(sqlite3_file*); +static int memdbRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int memdbWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); +static int memdbTruncate(sqlite3_file*, sqlite3_int64 size); +static int memdbSync(sqlite3_file*, int flags); +static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int memdbLock(sqlite3_file*, int); +/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */ +static int memdbFileControl(sqlite3_file*, int op, void *pArg); +/* static int memdbSectorSize(sqlite3_file*); // not used */ +static int memdbDeviceCharacteristics(sqlite3_file*); +static int memdbFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); +static int memdbUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); + +/* +** Methods for MemVfs +*/ +static int memdbOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +/* static int memdbDelete(sqlite3_vfs*, const char *zName, int syncDir); */ +static int memdbAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int memdbFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); +static void *memdbDlOpen(sqlite3_vfs*, const char *zFilename); +static void memdbDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); +static void memdbDlClose(sqlite3_vfs*, void*); +static int memdbRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int memdbSleep(sqlite3_vfs*, int microseconds); +/* static int memdbCurrentTime(sqlite3_vfs*, double*); */ +static int memdbGetLastError(sqlite3_vfs*, int, char *); +static int memdbCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); + +static sqlite3_vfs memdb_vfs = { + 2, /* iVersion */ + 0, /* szOsFile (set when registered) */ + 1024, /* mxPathname */ + 0, /* pNext */ + "memdb", /* zName */ + 0, /* pAppData (set when registered) */ + memdbOpen, /* xOpen */ + 0, /* memdbDelete, */ /* xDelete */ + memdbAccess, /* xAccess */ + memdbFullPathname, /* xFullPathname */ + memdbDlOpen, /* xDlOpen */ + memdbDlError, /* xDlError */ + memdbDlSym, /* xDlSym */ + memdbDlClose, /* xDlClose */ + memdbRandomness, /* xRandomness */ + memdbSleep, /* xSleep */ + 0, /* memdbCurrentTime, */ /* xCurrentTime */ + memdbGetLastError, /* xGetLastError */ + memdbCurrentTimeInt64 /* xCurrentTimeInt64 */ +}; + +static const sqlite3_io_methods memdb_io_methods = { + 3, /* iVersion */ + memdbClose, /* xClose */ + memdbRead, /* xRead */ + memdbWrite, /* xWrite */ + memdbTruncate, /* xTruncate */ + memdbSync, /* xSync */ + memdbFileSize, /* xFileSize */ + memdbLock, /* xLock */ + memdbLock, /* xUnlock - same as xLock in this case */ + 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ + memdbFileControl, /* xFileControl */ + 0, /* memdbSectorSize,*/ /* xSectorSize */ + memdbDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + memdbFetch, /* xFetch */ + memdbUnfetch /* xUnfetch */ +}; + + + +/* +** Close an memdb-file. +** +** The pData pointer is owned by the application, so there is nothing +** to free. +*/ +static int memdbClose(sqlite3_file *pFile){ + MemFile *p = (MemFile *)pFile; + if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData); + return SQLITE_OK; +} + +/* +** Read data from an memdb-file. +*/ +static int memdbRead( + sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + MemFile *p = (MemFile *)pFile; + if( iOfst+iAmt>p->sz ){ + memset(zBuf, 0, iAmt); + if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); + return SQLITE_IOERR_SHORT_READ; + } + memcpy(zBuf, p->aData+iOfst, iAmt); + return SQLITE_OK; +} + +/* +** Try to enlarge the memory allocation to hold at least sz bytes +*/ +static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ + unsigned char *pNew; + if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ + return SQLITE_FULL; + } + pNew = sqlite3_realloc64(p->aData, newSz); + if( pNew==0 ) return SQLITE_NOMEM; + p->aData = pNew; + p->szMax = newSz; + return SQLITE_OK; +} + +/* +** Write data to an memdb-file. +*/ +static int memdbWrite( + sqlite3_file *pFile, + const void *z, + int iAmt, + sqlite_int64 iOfst +){ + MemFile *p = (MemFile *)pFile; + if( iOfst+iAmt>p->sz ){ + int rc; + if( iOfst+iAmt>p->szMax + && (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK + ){ + return rc; + } + if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); + p->sz = iOfst+iAmt; + } + memcpy(p->aData+iOfst, z, iAmt); + return SQLITE_OK; +} + +/* +** Truncate an memdb-file. +** +** In rollback mode (which is always the case for memdb, as it does not +** support WAL mode) the truncate() method is only used to reduce +** the size of a file, never to increase the size. +*/ +static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){ + MemFile *p = (MemFile *)pFile; + if( NEVER(size>p->sz) ) return SQLITE_FULL; + p->sz = size; + return SQLITE_OK; +} + +/* +** Sync an memdb-file. +*/ +static int memdbSync(sqlite3_file *pFile, int flags){ + return SQLITE_OK; +} + +/* +** Return the current file-size of an memdb-file. +*/ +static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + MemFile *p = (MemFile *)pFile; + *pSize = p->sz; + return SQLITE_OK; +} + +/* +** Lock an memdb-file. +*/ +static int memdbLock(sqlite3_file *pFile, int eLock){ + MemFile *p = (MemFile *)pFile; + p->eLock = eLock; + return SQLITE_OK; +} + +#if 0 /* Never used because memdbAccess() always returns false */ +/* +** Check if another file-handle holds a RESERVED lock on an memdb-file. +*/ +static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + *pResOut = 0; + return SQLITE_OK; +} +#endif + +/* +** File control method. For custom operations on an memdb-file. +*/ +static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ + MemFile *p = (MemFile *)pFile; + int rc = SQLITE_NOTFOUND; + if( op==SQLITE_FCNTL_VFSNAME ){ + *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); + rc = SQLITE_OK; + } + return rc; +} + +#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */ +/* +** Return the sector-size in bytes for an memdb-file. +*/ +static int memdbSectorSize(sqlite3_file *pFile){ + return 1024; +} +#endif + +/* +** Return the device characteristic flags supported by an memdb-file. +*/ +static int memdbDeviceCharacteristics(sqlite3_file *pFile){ + return SQLITE_IOCAP_ATOMIC | + SQLITE_IOCAP_POWERSAFE_OVERWRITE | + SQLITE_IOCAP_SAFE_APPEND | + SQLITE_IOCAP_SEQUENTIAL; +} + +/* Fetch a page of a memory-mapped file */ +static int memdbFetch( + sqlite3_file *pFile, + sqlite3_int64 iOfst, + int iAmt, + void **pp +){ + MemFile *p = (MemFile *)pFile; + p->nMmap++; + *pp = (void*)(p->aData + iOfst); + return SQLITE_OK; +} + +/* Release a memory-mapped page */ +static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ + MemFile *p = (MemFile *)pFile; + p->nMmap--; + return SQLITE_OK; +} + +/* +** Open an mem file handle. +*/ +static int memdbOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + MemFile *p = (MemFile*)pFile; + if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ + return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); + } + memset(p, 0, sizeof(*p)); + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; + assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ + *pOutFlags = flags | SQLITE_OPEN_MEMORY; + p->base.pMethods = &memdb_io_methods; + return SQLITE_OK; +} + +#if 0 /* Only used to delete rollback journals, master journals, and WAL + ** files, none of which exist in memdb. So this routine is never used */ +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int memdbDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + return SQLITE_IOERR_DELETE; +} +#endif + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +** +** With memdb, no files ever exist on disk. So always return false. +*/ +static int memdbAccess( + sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + *pResOut = 0; + return SQLITE_OK; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (INST_MAX_PATHNAME+1) bytes. +*/ +static int memdbFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + sqlite3_snprintf(nOut, zOut, "%s", zPath); + return SQLITE_OK; +} + +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *memdbDlOpen(sqlite3_vfs *pVfs, const char *zPath){ + return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); +} + +/* +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. +*/ +static void memdbDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); +} + +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ + return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); +} + +/* +** Close the dynamic library handle pHandle. +*/ +static void memdbDlClose(sqlite3_vfs *pVfs, void *pHandle){ + ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); +} + +/* +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. +*/ +static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); +} + +/* +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. +*/ +static int memdbSleep(sqlite3_vfs *pVfs, int nMicro){ + return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); +} + +#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */ +/* +** Return the current time as a Julian Day number in *pTimeOut. +*/ +static int memdbCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); +} +#endif + +static int memdbGetLastError(sqlite3_vfs *pVfs, int a, char *b){ + return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); +} +static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ + return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p); +} + +/* +** Translate a database connection pointer and schema name into a +** MemFile pointer. +*/ +static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){ + MemFile *p = 0; + int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); + if( rc ) return 0; + if( p->base.pMethods!=&memdb_io_methods ) return 0; + return p; +} + +/* +** Return the serialization of a database +*/ +SQLITE_API unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which database within the connection */ + sqlite3_int64 *piSize, /* Write size here, if not NULL */ + unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */ +){ + MemFile *p; + int iDb; + Btree *pBt; + sqlite3_int64 sz; + int szPage = 0; + sqlite3_stmt *pStmt = 0; + unsigned char *pOut; + char *zSql; + int rc; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + p = memdbFromDbSchema(db, zSchema); + iDb = sqlite3FindDbName(db, zSchema); + if( piSize ) *piSize = -1; + if( iDb<0 ) return 0; + if( p ){ + if( piSize ) *piSize = p->sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = p->aData; + }else{ + pOut = sqlite3_malloc64( p->sz ); + if( pOut ) memcpy(pOut, p->aData, p->sz); + } + return pOut; + } + pBt = db->aDb[iDb].pBt; + if( pBt==0 ) return 0; + szPage = sqlite3BtreeGetPageSize(pBt); + zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema); + rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM; + sqlite3_free(zSql); + if( rc ) return 0; + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + pOut = 0; + }else{ + sz = sqlite3_column_int64(pStmt, 0)*szPage; + if( piSize ) *piSize = sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = 0; + }else{ + pOut = sqlite3_malloc64( sz ); + if( pOut ){ + int nPage = sqlite3_column_int(pStmt, 0); + Pager *pPager = sqlite3BtreePager(pBt); + int pgno; + for(pgno=1; pgno<=nPage; pgno++){ + DbPage *pPage = 0; + unsigned char *pTo = pOut + szPage*(sqlite3_int64)(pgno-1); + rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0); + if( rc==SQLITE_OK ){ + memcpy(pTo, sqlite3PagerGetData(pPage), szPage); + }else{ + memset(pTo, 0, szPage); + } + sqlite3PagerUnref(pPage); + } + } + } + } + sqlite3_finalize(pStmt); + return pOut; +} + +/* Convert zSchema to a MemDB and initialize its content. +*/ +SQLITE_API int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ +){ + MemFile *p; + char *zSql; + sqlite3_stmt *pStmt = 0; + int rc; + int iDb; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } + if( szDb<0 ) return SQLITE_MISUSE_BKPT; + if( szBuf<0 ) return SQLITE_MISUSE_BKPT; +#endif + + sqlite3_mutex_enter(db->mutex); + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + iDb = sqlite3FindDbName(db, zSchema); + if( iDb<0 ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc ) goto end_deserialize; + db->init.iDb = (u8)iDb; + db->init.reopenMemdb = 1; + rc = sqlite3_step(pStmt); + db->init.reopenMemdb = 0; + if( rc!=SQLITE_DONE ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + p = memdbFromDbSchema(db, zSchema); + if( p==0 ){ + rc = SQLITE_ERROR; + }else{ + p->aData = pData; + p->sz = szDb; + p->szMax = szBuf; + p->mFlags = mFlags; + rc = SQLITE_OK; + } + +end_deserialize: + sqlite3_finalize(pStmt); + sqlite3_mutex_leave(db->mutex); + return rc; +} + +/* +** This routine is called when the extension is loaded. +** Register the new VFS. +*/ +SQLITE_PRIVATE int sqlite3MemdbInit(void){ + sqlite3_vfs *pLower = sqlite3_vfs_find(0); + int sz = pLower->szOsFile; + memdb_vfs.pAppData = pLower; + /* In all known configurations of SQLite, the size of a default + ** sqlite3_file is greater than the size of a memdb sqlite3_file. + ** Should that ever change, remove the following NEVER() */ + if( NEVER(sz<sizeof(MemFile)) ) sz = sizeof(MemFile); + memdb_vfs.szOsFile = sz; + return sqlite3_vfs_register(&memdb_vfs, 0); +} +#endif /* SQLITE_ENABLE_DESERIALIZE */ + +/************** End of memdb.c ***********************************************/ /************** Begin file bitvec.c ******************************************/ /* ** 2008 February 16 @@ -45703,7 +46849,7 @@ sqlite3_log(SQLITE_FULL, "spill page %d making room for %d - cache used: %d/%d", pPg->pgno, pgno, - sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), + sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache), numberOfCachePages(pCache)); #endif pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno)); @@ -48763,7 +49909,7 @@ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ - int aStat[3]; /* Total cache hits, misses and writes */ + int aStat[4]; /* Total cache hits, misses, writes, spills */ #ifdef SQLITE_TEST int nRead; /* Database pages read */ #endif @@ -48791,6 +49937,7 @@ #define PAGER_STAT_HIT 0 #define PAGER_STAT_MISS 1 #define PAGER_STAT_WRITE 2 +#define PAGER_STAT_SPILL 3 /* ** The following global variables hold counters used for @@ -49277,7 +50424,7 @@ #endif #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( dc&SQLITE_IOCAP_BATCH_ATOMIC ){ + if( pPager->dbSize>0 && (dc&SQLITE_IOCAP_BATCH_ATOMIC) ){ return -1; } #endif @@ -50193,7 +51340,7 @@ rc = pager_truncate(pPager, pPager->dbSize); } - if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){ + if( rc==SQLITE_OK && bCommit ){ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; } @@ -51012,9 +52159,7 @@ ** assertion that the transaction counter was modified. */ #ifdef SQLITE_DEBUG - if( pPager->fd->pMethods ){ - sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); - } + sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); #endif /* If this playback is happening automatically as a result of an IO or @@ -51767,20 +52912,18 @@ ** retried. If it returns zero, then the SQLITE_BUSY error is ** returned to the caller of the pager API function. */ -SQLITE_PRIVATE void sqlite3PagerSetBusyhandler( +SQLITE_PRIVATE void sqlite3PagerSetBusyHandler( Pager *pPager, /* Pager object */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ ){ + void **ap; pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; - - if( isOpen(pPager->fd) ){ - void **ap = (void **)&pPager->xBusyHandler; - assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); - assert( ap[1]==pBusyHandlerArg ); - sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); - } + ap = (void **)&pPager->xBusyHandler; + assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); + assert( ap[1]==pBusyHandlerArg ); + sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); } /* @@ -52166,6 +53309,30 @@ } } +/* Verify that the database file has not be deleted or renamed out from +** under the pager. Return SQLITE_OK if the database is still where it ought +** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error +** code from sqlite3OsAccess()) if the database has gone missing. +*/ +static int databaseIsUnmoved(Pager *pPager){ + int bHasMoved = 0; + int rc; + + if( pPager->tempFile ) return SQLITE_OK; + if( pPager->dbSize==0 ) return SQLITE_OK; + assert( pPager->zFilename && pPager->zFilename[0] ); + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); + if( rc==SQLITE_NOTFOUND ){ + /* If the HAS_MOVED file-control is unimplemented, assume that the file + ** has not been moved. That is the historical behavior of SQLite: prior to + ** version 3.8.3, it never checked */ + rc = SQLITE_OK; + }else if( rc==SQLITE_OK && bHasMoved ){ + rc = SQLITE_READONLY_DBMOVED; + } + return rc; +} + /* ** Shutdown the page cache. Free all memory and close all files. @@ -52182,8 +53349,7 @@ ** to the caller. */ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ - u8 *pTmp = (u8 *)pPager->pTmpSpace; - + u8 *pTmp = (u8*)pPager->pTmpSpace; assert( db || pagerUseWal(pPager)==0 ); assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); @@ -52192,11 +53358,17 @@ /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL - assert( db || pPager->pWal==0 ); - sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize, - (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp) - ); - pPager->pWal = 0; + { + u8 *a = 0; + assert( db || pPager->pWal==0 ); + if( db && 0==(db->flags & SQLITE_NoCkptOnClose) + && SQLITE_OK==databaseIsUnmoved(pPager) + ){ + a = pTmp; + } + sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); + pPager->pWal = 0; + } #endif pager_reset(pPager); if( MEMDB ){ @@ -52653,6 +53825,7 @@ return SQLITE_OK; } + pPager->aStat[PAGER_STAT_SPILL]++; pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* Write a single frame for this page to the log. */ @@ -52758,6 +53931,11 @@ int rc = SQLITE_OK; /* Return code */ int tempFile = 0; /* True for temp files (incl. in-memory files) */ int memDb = 0; /* True if this is an in-memory file */ +#ifdef SQLITE_ENABLE_DESERIALIZE + int memJM = 0; /* Memory journal mode */ +#else +# define memJM 0 +#endif int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ @@ -52885,7 +54063,10 @@ int fout = 0; /* VFS flags returned by xOpen() */ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); assert( !memDb ); - readOnly = (fout&SQLITE_OPEN_READONLY); +#ifdef SQLITE_ENABLE_DESERIALIZE + memJM = (fout&SQLITE_OPEN_MEMORY)!=0; +#endif + readOnly = (fout&SQLITE_OPEN_READONLY)!=0; /* If the file was successfully opened for read/write access, ** choose a default page size in case we have to create the @@ -53016,7 +54197,7 @@ setSectorSize(pPager); if( !useJournal ){ pPager->journalMode = PAGER_JOURNALMODE_OFF; - }else if( memDb ){ + }else if( memDb || memJM ){ pPager->journalMode = PAGER_JOURNALMODE_MEMORY; } /* pPager->xBusyHandler = 0; */ @@ -53031,30 +54212,6 @@ } -/* Verify that the database file has not be deleted or renamed out from -** under the pager. Return SQLITE_OK if the database is still were it ought -** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error -** code from sqlite3OsAccess()) if the database has gone missing. -*/ -static int databaseIsUnmoved(Pager *pPager){ - int bHasMoved = 0; - int rc; - - if( pPager->tempFile ) return SQLITE_OK; - if( pPager->dbSize==0 ) return SQLITE_OK; - assert( pPager->zFilename && pPager->zFilename[0] ); - rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); - if( rc==SQLITE_NOTFOUND ){ - /* If the HAS_MOVED file-control is unimplemented, assume that the file - ** has not been moved. That is the historical behavior of SQLite: prior to - ** version 3.8.3, it never checked */ - rc = SQLITE_OK; - }else if( rc==SQLITE_OK && bHasMoved ){ - rc = SQLITE_READONLY_DBMOVED; - } - return rc; -} - /* ** This function is called after transitioning from PAGER_UNLOCK to @@ -53742,6 +54899,7 @@ assert( pPg->pgno==1 ); assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ pPager = pPg->pPager; + sqlite3PagerResetLockTimeout(pPager); sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } @@ -54337,12 +55495,9 @@ */ SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){ int rc = SQLITE_OK; - - if( isOpen(pPager->fd) ){ - void *pArg = (void*)zMaster; - rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - } + void *pArg = (void*)zMaster; + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; if( rc==SQLITE_OK && !pPager->noSync ){ assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); @@ -54563,8 +55718,9 @@ if( bBatch ){ if( rc==SQLITE_OK ){ rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); - }else{ - sqlite3OsFileControl(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); + } + if( rc!=SQLITE_OK ){ + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); } } @@ -54788,8 +55944,12 @@ #endif /* -** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or -** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the +** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE, +** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation +** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because +** it was added later. +** +** Before returning, *pnVal is incremented by the ** current cache hit or miss count, according to the value of eStat. If the ** reset parameter is non-zero, the cache hit or miss count is zeroed before ** returning. @@ -54799,15 +55959,18 @@ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT || eStat==SQLITE_DBSTATUS_CACHE_MISS || eStat==SQLITE_DBSTATUS_CACHE_WRITE + || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1 ); assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS ); assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE ); - assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 ); + assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 + && PAGER_STAT_WRITE==2 && PAGER_STAT_SPILL==3 ); - *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT]; + eStat -= SQLITE_DBSTATUS_CACHE_HIT; + *pnVal += pPager->aStat[eStat]; if( reset ){ - pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0; + pPager->aStat[eStat] = 0; } } @@ -55011,6 +56174,16 @@ return pPager->fd; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +/* +** Reset the lock timeout for pager. +*/ +SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){ + int x = 0; + sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x); +} +#endif + /* ** Return the file handle for the journal file (if it exists). ** This will be either the rollback journal or the WAL file. @@ -55471,6 +56644,7 @@ pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, pnLog, pnCkpt ); + sqlite3PagerResetLockTimeout(pPager); } return rc; } @@ -56253,7 +57427,11 @@ ** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, ** then an SQLite error code is returned and *ppPage is set to 0. */ -static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ +static SQLITE_NOINLINE int walIndexPageRealloc( + Wal *pWal, /* The WAL context */ + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ +){ int rc = SQLITE_OK; /* Enlarge the pWal->apWiData[] array if required */ @@ -56272,21 +57450,20 @@ } /* Request a pointer to the required page from the VFS */ - if( pWal->apWiData[iPage]==0 ){ - if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ - pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); - if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; - }else{ - rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, - pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] - ); - assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); - testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); - if( (rc&0xff)==SQLITE_READONLY ){ - pWal->readOnly |= WAL_SHM_RDONLY; - if( rc==SQLITE_READONLY ){ - rc = SQLITE_OK; - } + assert( pWal->apWiData[iPage]==0 ); + if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ + pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); + if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; + }else{ + rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, + pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] + ); + assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); + testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); + if( (rc&0xff)==SQLITE_READONLY ){ + pWal->readOnly |= WAL_SHM_RDONLY; + if( rc==SQLITE_READONLY ){ + rc = SQLITE_OK; } } } @@ -56295,6 +57472,16 @@ assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); return rc; } +static int walIndexPage( + Wal *pWal, /* The WAL context */ + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ +){ + if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ + return walIndexPageRealloc(pWal, iPage, ppPage); + } + return SQLITE_OK; +} /* ** Return a pointer to the WalCkptInfo structure in the wal-index. @@ -57271,8 +58458,9 @@ /* ** Construct a WalInterator object that can be used to loop over all -** pages in the WAL in ascending order. The caller must hold the checkpoint -** lock. +** pages in the WAL following frame nBackfill in ascending order. Frames +** nBackfill or earlier may be included - excluding them is an optimization +** only. The caller must hold the checkpoint lock. ** ** On success, make *pp point to the newly allocated WalInterator object ** return SQLITE_OK. Otherwise, return an error code. If this routine @@ -57281,7 +58469,7 @@ ** The calling routine should invoke walIteratorFree() to destroy the ** WalIterator object when it has finished with it. */ -static int walIteratorInit(Wal *pWal, WalIterator **pp){ +static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ WalIterator *p; /* Return value */ int nSegment; /* Number of segments to merge */ u32 iLast; /* Last frame in log */ @@ -57318,7 +58506,7 @@ rc = SQLITE_NOMEM_BKPT; } - for(i=0; rc==SQLITE_OK && i<nSegment; i++){ + for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){ volatile ht_slot *aHash; u32 iZero; volatile u32 *aPgno; @@ -57352,6 +58540,7 @@ if( rc!=SQLITE_OK ){ walIteratorFree(p); + p = 0; } *pp = p; return rc; @@ -57474,13 +58663,6 @@ pInfo = walCkptInfo(pWal); if( pInfo->nBackfill<pWal->hdr.mxFrame ){ - /* Allocate the iterator */ - rc = walIteratorInit(pWal, &pIter); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pIter ); - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); @@ -57517,7 +58699,13 @@ } } - if( pInfo->nBackfill<mxSafeFrame + /* Allocate the iterator */ + if( pInfo->nBackfill<mxSafeFrame ){ + rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter); + assert( rc==SQLITE_OK || pIter==0 ); + } + + if( pIter && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK ){ i64 nSize; /* Current size of database file */ @@ -58567,7 +59755,7 @@ ** table after the current read-transaction had started. */ iMinHash = walFramePage(pWal->minFrame); - for(iHash=walFramePage(iLast); iHash>=iMinHash && iRead==0; iHash--){ + for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ volatile ht_slot *aHash; /* Pointer to hash table */ volatile u32 *aPgno; /* Pointer to array of page numbers */ u32 iZero; /* Frame number corresponding to aPgno[0] */ @@ -58590,6 +59778,7 @@ return SQLITE_CORRUPT_BKPT; } } + if( iRead ) break; } #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT @@ -60005,20 +61194,20 @@ u8 curFlags; /* zero or more BTCF_* flags defined below */ u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */ u8 hints; /* As configured by CursorSetHints() */ - int nOvflAlloc; /* Allocated size of aOverflow[] array */ - Btree *pBtree; /* The Btree to which this cursor belongs */ - BtShared *pBt; /* The BtShared this cursor points to */ - BtCursor *pNext; /* Forms a linked list of all cursors */ - Pgno *aOverflow; /* Cache of overflow page locations */ - CellInfo info; /* A parse of the cell we are pointing at */ - i64 nKey; /* Size of pKey, or last integer key */ - void *pKey; /* Saved key that was cursor last known position */ - Pgno pgnoRoot; /* The root page of this tree */ int skipNext; /* Prev() is noop if negative. Next() is noop if positive. ** Error code if eState==CURSOR_FAULT */ + Btree *pBtree; /* The Btree to which this cursor belongs */ + Pgno *aOverflow; /* Cache of overflow page locations */ + void *pKey; /* Saved key that was cursor last known position */ /* All fields above are zeroed when the cursor is allocated. See ** sqlite3BtreeCursorZero(). Fields that follow must be manually ** initialized. */ +#define BTCURSOR_FIRST_UNINIT pBt /* Name of first uninitialized field */ + BtShared *pBt; /* The BtShared this cursor points to */ + BtCursor *pNext; /* Forms a linked list of all cursors */ + CellInfo info; /* A parse of the cell we are pointing at */ + i64 nKey; /* Size of pKey, or last integer key */ + Pgno pgnoRoot; /* The root page of this tree */ i8 iPage; /* Index of current page in apPage */ u8 curIntKey; /* Value of apPage[0]->intKey */ u16 ix; /* Current index for apPage[iPage] */ @@ -60068,8 +61257,8 @@ ** Do nothing else with this cursor. Any attempt to use the cursor ** should return the error code stored in BtCursor.skipNext */ -#define CURSOR_INVALID 0 -#define CURSOR_VALID 1 +#define CURSOR_VALID 0 +#define CURSOR_INVALID 1 #define CURSOR_SKIPNEXT 2 #define CURSOR_REQUIRESEEK 3 #define CURSOR_FAULT 4 @@ -62733,7 +63922,8 @@ BtShared *pBt = (BtShared*)pArg; assert( pBt->db ); assert( sqlite3_mutex_held(pBt->db->mutex) ); - return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); + return sqlite3InvokeBusyHandler(&pBt->db->busyHandler, + sqlite3PagerFile(pBt->pPager)); } /* @@ -62911,7 +64101,7 @@ } pBt->openFlags = (u8)flags; pBt->db = db; - sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); + sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; @@ -63914,6 +65104,7 @@ } }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && btreeInvokeBusyHandler(pBt) ); + sqlite3PagerResetLockTimeout(pBt->pPager); if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ @@ -64901,7 +66092,7 @@ ** of run-time by skipping the initialization of those elements. */ SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){ - memset(p, 0, offsetof(BtCursor, iPage)); + memset(p, 0, offsetof(BtCursor, BTCURSOR_FIRST_UNINIT)); } /* @@ -64944,11 +66135,19 @@ ** Using this cache reduces the number of calls to btreeParseCell(). */ #ifndef NDEBUG + static int cellInfoEqual(CellInfo *a, CellInfo *b){ + if( a->nKey!=b->nKey ) return 0; + if( a->pPayload!=b->pPayload ) return 0; + if( a->nPayload!=b->nPayload ) return 0; + if( a->nLocal!=b->nLocal ) return 0; + if( a->nSize!=b->nSize ) return 0; + return 1; + } static void assertCellInfo(BtCursor *pCur){ CellInfo info; memset(&info, 0, sizeof(info)); btreeParseCell(pCur->pPage, pCur->ix, &info); - assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 ); + assert( CORRUPT_DB || cellInfoEqual(&info, &pCur->info) ); } #else #define assertCellInfo(x) @@ -65224,14 +66423,15 @@ */ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; - if( nOvfl>pCur->nOvflAlloc ){ + if( pCur->aOverflow==0 + || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) + ){ Pgno *aNew = (Pgno*)sqlite3Realloc( pCur->aOverflow, nOvfl*2*sizeof(Pgno) ); if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ - pCur->nOvflAlloc = nOvfl*2; pCur->aOverflow = aNew; } } @@ -66745,9 +67945,8 @@ } /* -** Free any overflow pages associated with the given Cell. Write the -** local Cell size (the number of bytes on the original page, omitting -** overflow) into *pnSize. +** Free any overflow pages associated with the given Cell. Store +** size information about the cell in pInfo. */ static int clearCell( MemPage *pPage, /* The page that contains the Cell */ @@ -67951,7 +69150,7 @@ } /* Load b.apCell[] with pointers to all cells in pOld. If pOld - ** constains overflow cells, include them in the b.apCell[] array + ** contains overflow cells, include them in the b.apCell[] array ** in the correct spot. ** ** Note that when there are multiple overflow cells, it is always the @@ -71436,6 +72635,51 @@ } #endif +#ifdef SQLITE_DEBUG +/* +** Check that string value of pMem agrees with its integer or real value. +** +** A single int or real value always converts to the same strings. But +** many different strings can be converted into the same int or real. +** If a table contains a numeric value and an index is based on the +** corresponding string value, then it is important that the string be +** derived from the numeric value, not the other way around, to ensure +** that the index and table are consistent. See ticket +** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for +** an example. +** +** This routine looks at pMem to verify that if it has both a numeric +** representation and a string representation then the string rep has +** been derived from the numeric and not the other way around. It returns +** true if everything is ok and false if there is a problem. +** +** This routine is for use inside of assert() statements only. +*/ +SQLITE_PRIVATE int sqlite3VdbeMemConsistentDualRep(Mem *p){ + char zBuf[100]; + char *z; + int i, j, incr; + if( (p->flags & MEM_Str)==0 ) return 1; + if( (p->flags & (MEM_Int|MEM_Real))==0 ) return 1; + if( p->flags & MEM_Int ){ + sqlite3_snprintf(sizeof(zBuf),zBuf,"%lld",p->u.i); + }else{ + sqlite3_snprintf(sizeof(zBuf),zBuf,"%!.15g",p->u.r); + } + z = p->z; + i = j = 0; + incr = 1; + if( p->enc!=SQLITE_UTF8 ){ + incr = 2; + if( p->enc==SQLITE_UTF16BE ) z++; + } + while( zBuf[j] ){ + if( zBuf[j++]!=z[i] ) return 0; + i += incr; + } + return 1; +} +#endif /* SQLITE_DEBUG */ /* ** If pMem is an object with a valid string representation, this routine @@ -71871,6 +73115,16 @@ } /* +** Return 1 if pMem represents true, and return 0 if pMem represents false. +** Return the value ifNull if pMem is NULL. +*/ +SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ + if( pMem->flags & MEM_Int ) return pMem->u.i!=0; + if( pMem->flags & MEM_Null ) return ifNull; + return sqlite3VdbeRealValue(pMem)!=0.0; +} + +/* ** The MEM structure is already a MEM_Real. Try to also make it a ** MEM_Int if we can. */ @@ -71925,6 +73179,18 @@ return SQLITE_OK; } +/* Compare a floating point value to an integer. Return true if the two +** values are the same within the precision of the floating point value. +** +** For some versions of GCC on 32-bit machines, if you do the more obvious +** comparison of "r1==(double)i" you sometimes get an answer of false even +** though the r1 and (double)i values are bit-for-bit the same. +*/ +static int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ + double r2 = (double)i; + return memcmp(&r1, &r2, sizeof(r1))==0; +} + /* ** Convert pMem so that it has types MEM_Real or MEM_Int or both. ** Invalidate any prior representations. @@ -71944,7 +73210,7 @@ }else{ i64 i = pMem->u.i; sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( rc==1 && pMem->u.r==(double)i ){ + if( rc==1 && sqlite3RealSameAsInt(pMem->u.r, i) ){ pMem->u.i = i; MemSetTypeFlag(pMem, MEM_Int); }else{ @@ -72427,6 +73693,7 @@ assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 || pVal->db->mallocFailed ); if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){ + assert( sqlite3VdbeMemConsistentDualRep(pVal) ); return pVal->z; }else{ return 0; @@ -72449,6 +73716,7 @@ assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); assert( (pVal->flags & MEM_RowSet)==0 ); if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){ + assert( sqlite3VdbeMemConsistentDualRep(pVal) ); return pVal->z; } if( pVal->flags&MEM_Null ){ @@ -72758,6 +74026,11 @@ rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); } #endif + else if( op==TK_TRUEFALSE ){ + pVal = valueNew(db, pCtx); + pVal->flags = MEM_Int; + pVal->u.i = pExpr->u.zToken[4]==0; + } *ppVal = pVal; return rc; @@ -78230,14 +79503,12 @@ SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; - pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; - pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif @@ -78343,8 +79614,7 @@ return SQLITE_OK; } SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ - pCtx->isError = errCode; - pCtx->fErrorOrAux = 1; + pCtx->isError = errCode ? errCode : -1; #ifdef SQLITE_DEBUG if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; #endif @@ -78358,7 +79628,6 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_TOOBIG; - pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, SQLITE_UTF8, SQLITE_STATIC); } @@ -78368,7 +79637,6 @@ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM_BKPT; - pCtx->fErrorOrAux = 1; sqlite3OomFault(pCtx->pOut->db); } @@ -78775,10 +80043,7 @@ pAuxData->iAuxArg = iArg; pAuxData->pNextAux = pVdbe->pAuxData; pVdbe->pAuxData = pAuxData; - if( pCtx->fErrorOrAux==0 ){ - pCtx->isError = 0; - pCtx->fErrorOrAux = 1; - } + if( pCtx->isError==0 ) pCtx->isError = -1; }else if( pAuxData->xDeleteAux ){ pAuxData->xDeleteAux(pAuxData->pAux); } @@ -79534,7 +80799,9 @@ Vdbe *pVdbe = (Vdbe*)pStmt; u32 v; #ifdef SQLITE_ENABLE_API_ARMOR - if( !pStmt ){ + if( !pStmt + || (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter))) + ){ (void)SQLITE_MISUSE_BKPT; return 0; } @@ -80308,6 +81575,11 @@ pRec->flags |= MEM_Real; if( bTryForInt ) sqlite3VdbeIntegerAffinity(pRec); } + /* TEXT->NUMERIC is many->one. Hence, it is important to invalidate the + ** string representation after computing a numeric equivalent, because the + ** string representation might not be the canonical representation for the + ** numeric value. Ticket [343634942dd54ab57b7024] 2018-01-31. */ + pRec->flags &= ~MEM_Str; } /* @@ -80776,7 +82048,7 @@ assert( pOp>=aOp && pOp<&aOp[p->nOp]); #ifdef VDBE_PROFILE - start = sqlite3Hwtime(); + start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); #endif nVmStep++; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS @@ -82300,18 +83572,8 @@ int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ){ - v1 = 2; - }else{ - v1 = sqlite3VdbeIntValue(pIn1)!=0; - } - pIn2 = &aMem[pOp->p2]; - if( pIn2->flags & MEM_Null ){ - v2 = 2; - }else{ - v2 = sqlite3VdbeIntValue(pIn2)!=0; - } + v1 = sqlite3VdbeBooleanValue(&aMem[pOp->p1], 2); + v2 = sqlite3VdbeBooleanValue(&aMem[pOp->p2], 2); if( pOp->opcode==OP_And ){ static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; v1 = and_logic[v1*3+v2]; @@ -82329,6 +83591,35 @@ break; } +/* Opcode: IsTrue P1 P2 P3 P4 * +** Synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 +** +** This opcode implements the IS TRUE, IS FALSE, IS NOT TRUE, and +** IS NOT FALSE operators. +** +** Interpret the value in register P1 as a boolean value. Store that +** boolean (a 0 or 1) in register P2. Or if the value in register P1 is +** NULL, then the P3 is stored in register P2. Invert the answer if P4 +** is 1. +** +** The logic is summarized like this: +** +** <ul> +** <li> If P3==0 and P4==0 then r[P2] := r[P1] IS TRUE +** <li> If P3==1 and P4==1 then r[P2] := r[P1] IS FALSE +** <li> If P3==0 and P4==1 then r[P2] := r[P1] IS NOT TRUE +** <li> If P3==1 and P4==0 then r[P2] := r[P1] IS NOT FALSE +** </ul> +*/ +case OP_IsTrue: { /* in1, out2 */ + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p4.i==0 || pOp->p4.i==1 ); + assert( pOp->p3==0 || pOp->p3==1 ); + sqlite3VdbeMemSetInt64(&aMem[pOp->p2], + sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3) ^ pOp->p4.i); + break; +} + /* Opcode: Not P1 P2 * * * ** Synopsis: r[P2]= !r[P1] ** @@ -82339,10 +83630,10 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; - sqlite3VdbeMemSetNull(pOut); if( (pIn1->flags & MEM_Null)==0 ){ - pOut->flags = MEM_Int; - pOut->u.i = !sqlite3VdbeIntValue(pIn1); + sqlite3VdbeMemSetInt64(pOut, !sqlite3VdbeBooleanValue(pIn1,0)); + }else{ + sqlite3VdbeMemSetNull(pOut); } break; } @@ -82409,30 +83700,25 @@ ** is considered true if it is numeric and non-zero. If the value ** in P1 is NULL then take the jump if and only if P3 is non-zero. */ +case OP_If: { /* jump, in1 */ + int c; + c = sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3); + VdbeBranchTaken(c!=0, 2); + if( c ) goto jump_to_p2; + break; +} + /* Opcode: IfNot P1 P2 P3 * * ** ** Jump to P2 if the value in register P1 is False. The value ** is considered false if it has a numeric value of zero. If the value ** in P1 is NULL then take the jump if and only if P3 is non-zero. */ -case OP_If: /* jump, in1 */ case OP_IfNot: { /* jump, in1 */ int c; - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ){ - c = pOp->p3; - }else{ -#ifdef SQLITE_OMIT_FLOATING_POINT - c = sqlite3VdbeIntValue(pIn1)!=0; -#else - c = sqlite3VdbeRealValue(pIn1)!=0.0; -#endif - if( pOp->opcode==OP_IfNot ) c = !c; - } + c = !sqlite3VdbeBooleanValue(&aMem[pOp->p1], !pOp->p3); VdbeBranchTaken(c!=0, 2); - if( c ){ - goto jump_to_p2; - } + if( c ) goto jump_to_p2; break; } @@ -82493,7 +83779,7 @@ ** P2 is the column number for the argument to the sqlite_offset() function. ** This opcode does not use P2 itself, but the P2 value is used by the ** code generator. The P1, P2, and P3 operands to this opcode are the -** as as for OP_Column. +** same as for OP_Column. ** ** This opcode is only available if SQLite is compiled with the ** -DSQLITE_ENABLE_OFFSET_SQL_FUNC option. @@ -84401,6 +85687,10 @@ pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; + if( !pC->isTable ){ + rc = SQLITE_CORRUPT_BKPT; + goto abort_due_to_error; + } assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); @@ -86337,12 +87627,17 @@ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); - pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*)); + pCtx = sqlite3DbMallocRawNN(db, n*sizeof(sqlite3_value*) + + (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlite3_value*))); if( pCtx==0 ) goto no_mem; pCtx->pMem = 0; + pCtx->pOut = (Mem*)&(pCtx->argv[n]); + sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); pCtx->pFunc = pOp->p4.pFunc; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; + pCtx->skipFlag = 0; + pCtx->isError = 0; pCtx->argc = n; pOp->p4type = P4_FUNCCTX; pOp->p4.pCtx = pCtx; @@ -86353,7 +87648,6 @@ int i; sqlite3_context *pCtx; Mem *pMem; - Mem t; assert( pOp->p4type==P4_FUNCCTX ); pCtx = pOp->p4.pCtx; @@ -86376,26 +87670,28 @@ #endif pMem->n++; - sqlite3VdbeMemInit(&t, db, MEM_Null); - pCtx->pOut = &t; - pCtx->fErrorOrAux = 0; - pCtx->skipFlag = 0; + assert( pCtx->pOut->flags==MEM_Null ); + assert( pCtx->isError==0 ); + assert( pCtx->skipFlag==0 ); (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */ - if( pCtx->fErrorOrAux ){ - if( pCtx->isError ){ - sqlite3VdbeError(p, "%s", sqlite3_value_text(&t)); + if( pCtx->isError ){ + if( pCtx->isError>0 ){ + sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut)); rc = pCtx->isError; } - sqlite3VdbeMemRelease(&t); + if( pCtx->skipFlag ){ + assert( pOp[-1].opcode==OP_CollSeq ); + i = pOp[-1].p1; + if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1); + pCtx->skipFlag = 0; + } + sqlite3VdbeMemRelease(pCtx->pOut); + pCtx->pOut->flags = MEM_Null; + pCtx->isError = 0; if( rc ) goto abort_due_to_error; - }else{ - assert( t.flags==MEM_Null ); } - if( pCtx->skipFlag ){ - assert( pOp[-1].opcode==OP_CollSeq ); - i = pOp[-1].p1; - if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1); - } + assert( pCtx->pOut->flags==MEM_Null ); + assert( pCtx->skipFlag==0 ); break; } @@ -86882,7 +88178,8 @@ } rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); sqlite3VtabImportErrmsg(p, pVtab); - if( sContext.isError ){ + if( sContext.isError>0 ){ + sqlite3VdbeError(p, "%s", sqlite3_value_text(pDest)); rc = sContext.isError; } sqlite3VdbeChangeEncoding(pDest, encoding); @@ -87147,6 +88444,7 @@ pCtx->pFunc = pOp->p4.pFunc; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; + pCtx->isError = 0; pCtx->argc = n; pOp->p4type = P4_FUNCCTX; pOp->p4.pCtx = pCtx; @@ -87181,16 +88479,17 @@ } #endif MemSetTypeFlag(pOut, MEM_Null); - pCtx->fErrorOrAux = 0; + assert( pCtx->isError==0 ); (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */ /* If the function returned an error, throw an exception */ - if( pCtx->fErrorOrAux ){ - if( pCtx->isError ){ + if( pCtx->isError ){ + if( pCtx->isError>0 ){ sqlite3VdbeError(p, "%s", sqlite3_value_text(pOut)); rc = pCtx->isError; } sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1); + pCtx->isError = 0; if( rc ) goto abort_due_to_error; } @@ -87232,8 +88531,10 @@ */ case OP_Trace: case OP_Init: { /* jump */ - char *zTrace; int i; +#ifndef SQLITE_OMIT_TRACE + char *zTrace; +#endif /* If the P4 argument is not NULL, then it must be an SQL comment string. ** The "--" string is broken up to prevent false-positives with srcck1.c. @@ -87350,7 +88651,7 @@ #ifdef VDBE_PROFILE { - u64 endTime = sqlite3Hwtime(); + u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); if( endTime>start ) pOrigOp->cycles += endTime - start; pOrigOp->cnt++; } @@ -91737,10 +93038,16 @@ ** Because no reference was made to outer contexts, the pNC->nRef ** fields are not changed in any context. */ - if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){ - pExpr->op = TK_STRING; - pExpr->pTab = 0; - return WRC_Prune; + if( cnt==0 && zTab==0 ){ + assert( pExpr->op==TK_ID ); + if( ExprHasProperty(pExpr,EP_DblQuoted) ){ + pExpr->op = TK_STRING; + pExpr->pTab = 0; + return WRC_Prune; + } + if( sqlite3ExprIdToTrueFalse(pExpr) ){ + return WRC_Prune; + } } /* @@ -92089,15 +93396,30 @@ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } + case TK_IS: + case TK_ISNOT: { + Expr *pRight; + assert( !ExprHasProperty(pExpr, EP_Reduced) ); + /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", + ** and "x IS NOT FALSE". */ + if( (pRight = pExpr->pRight)->op==TK_ID ){ + int rc = resolveExprStep(pWalker, pRight); + if( rc==WRC_Abort ) return WRC_Abort; + if( pRight->op==TK_TRUEFALSE ){ + pExpr->op2 = pExpr->op; + pExpr->op = TK_TRUTH; + return WRC_Continue; + } + } + /* Fall thru */ + } case TK_BETWEEN: case TK_EQ: case TK_NE: case TK_LT: case TK_LE: case TK_GT: - case TK_GE: - case TK_IS: - case TK_ISNOT: { + case TK_GE: { int nLeft, nRight; if( pParse->db->mallocFailed ) break; assert( pExpr->pLeft!=0 ); @@ -94573,6 +95895,34 @@ } /* +** If the input expression is an ID with the name "true" or "false" +** then convert it into an TK_TRUEFALSE term. Return non-zero if +** the conversion happened, and zero if the expression is unaltered. +*/ +SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ + assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); + if( sqlite3StrICmp(pExpr->u.zToken, "true")==0 + || sqlite3StrICmp(pExpr->u.zToken, "false")==0 + ){ + pExpr->op = TK_TRUEFALSE; + return 1; + } + return 0; +} + +/* +** The argument must be a TK_TRUEFALSE Expr node. Return 1 if it is TRUE +** and 0 if it is FALSE. +*/ +SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){ + assert( pExpr->op==TK_TRUEFALSE ); + assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0 + || sqlite3StrICmp(pExpr->u.zToken,"false")==0 ); + return pExpr->u.zToken[4]==0; +} + + +/* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The ** Walker.eCode value determines the type of "constant" we are looking @@ -94619,6 +95969,12 @@ return WRC_Abort; } case TK_ID: + /* Convert "true" or "false" in a DEFAULT clause into the + ** appropriate TK_TRUEFALSE operator */ + if( sqlite3ExprIdToTrueFalse(pExpr) ){ + return WRC_Prune; + } + /* Fall thru */ case TK_COLUMN: case TK_AGG_FUNCTION: case TK_AGG_COLUMN: @@ -96383,6 +97739,10 @@ codeInteger(pParse, pExpr, 0, target); return target; } + case TK_TRUEFALSE: { + sqlite3VdbeAddOp2(v, OP_Integer, sqlite3ExprTruthValue(pExpr), target); + return target; + } #ifndef SQLITE_OMIT_FLOATING_POINT case TK_FLOAT: { assert( !ExprHasProperty(pExpr, EP_IntValue) ); @@ -96538,6 +97898,18 @@ sqlite3VdbeAddOp2(v, op, r1, inReg); break; } + case TK_TRUTH: { + int isTrue; /* IS TRUE or IS NOT TRUE */ + int bNormal; /* IS TRUE or IS FALSE */ + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + testcase( regFree1==0 ); + isTrue = sqlite3ExprTruthValue(pExpr->pRight); + bNormal = pExpr->op2==TK_IS; + testcase( isTrue && bNormal); + testcase( !isTrue && bNormal); + sqlite3VdbeAddOp4Int(v, OP_IsTrue, r1, inReg, !isTrue, isTrue ^ bNormal); + break; + } case TK_ISNULL: case TK_NOTNULL: { int addr; @@ -97313,6 +98685,23 @@ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); break; } + case TK_TRUTH: { + int isNot; /* IS NOT TRUE or IS NOT FALSE */ + int isTrue; /* IS TRUE or IS NOT TRUE */ + testcase( jumpIfNull==0 ); + isNot = pExpr->op2==TK_ISNOT; + isTrue = sqlite3ExprTruthValue(pExpr->pRight); + testcase( isTrue && isNot ); + testcase( !isTrue && isNot ); + if( isTrue ^ isNot ){ + sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, + isNot ? SQLITE_JUMPIFNULL : 0); + }else{ + sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, + isNot ? SQLITE_JUMPIFNULL : 0); + } + break; + } case TK_IS: case TK_ISNOT: testcase( op==TK_IS ); @@ -97467,6 +98856,26 @@ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); break; } + case TK_TRUTH: { + int isNot; /* IS NOT TRUE or IS NOT FALSE */ + int isTrue; /* IS TRUE or IS NOT TRUE */ + testcase( jumpIfNull==0 ); + isNot = pExpr->op2==TK_ISNOT; + isTrue = sqlite3ExprTruthValue(pExpr->pRight); + testcase( isTrue && isNot ); + testcase( !isTrue && isNot ); + if( isTrue ^ isNot ){ + /* IS TRUE and IS NOT FALSE */ + sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, + isNot ? 0 : SQLITE_JUMPIFNULL); + + }else{ + /* IS FALSE and IS NOT TRUE */ + sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, + isNot ? 0 : SQLITE_JUMPIFNULL); + } + break; + } case TK_IS: case TK_ISNOT: testcase( pExpr->op==TK_IS ); @@ -97755,6 +99164,105 @@ } /* +** This is the Expr node callback for sqlite3ExprImpliesNotNullRow(). +** If the expression node requires that the table at pWalker->iCur +** have a non-NULL column, then set pWalker->eCode to 1 and abort. +*/ +static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + /* This routine is only called for WHERE clause expressions and so it + ** cannot have any TK_AGG_COLUMN entries because those are only found + ** in HAVING clauses. We can get a TK_AGG_FUNCTION in a WHERE clause, + ** but that is an illegal construct and the query will be rejected at + ** a later stage of processing, so the TK_AGG_FUNCTION case does not + ** need to be considered here. */ + assert( pExpr->op!=TK_AGG_COLUMN ); + testcase( pExpr->op==TK_AGG_FUNCTION ); + + if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; + switch( pExpr->op ){ + case TK_ISNOT: + case TK_NOT: + case TK_ISNULL: + case TK_IS: + case TK_OR: + case TK_CASE: + case TK_IN: + case TK_FUNCTION: + testcase( pExpr->op==TK_ISNOT ); + testcase( pExpr->op==TK_NOT ); + testcase( pExpr->op==TK_ISNULL ); + testcase( pExpr->op==TK_IS ); + testcase( pExpr->op==TK_OR ); + testcase( pExpr->op==TK_CASE ); + testcase( pExpr->op==TK_IN ); + testcase( pExpr->op==TK_FUNCTION ); + return WRC_Prune; + case TK_COLUMN: + if( pWalker->u.iCur==pExpr->iTable ){ + pWalker->eCode = 1; + return WRC_Abort; + } + return WRC_Prune; + + /* Virtual tables are allowed to use constraints like x=NULL. So + ** a term of the form x=y does not prove that y is not null if x + ** is the column of a virtual table */ + case TK_EQ: + case TK_NE: + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + testcase( pExpr->op==TK_EQ ); + testcase( pExpr->op==TK_NE ); + testcase( pExpr->op==TK_LT ); + testcase( pExpr->op==TK_LE ); + testcase( pExpr->op==TK_GT ); + testcase( pExpr->op==TK_GE ); + if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->pTab)) + || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->pTab)) + ){ + return WRC_Prune; + } + default: + return WRC_Continue; + } +} + +/* +** Return true (non-zero) if expression p can only be true if at least +** one column of table iTab is non-null. In other words, return true +** if expression p will always be NULL or false if every column of iTab +** is NULL. +** +** False negatives are acceptable. In other words, it is ok to return +** zero even if expression p will never be true of every column of iTab +** is NULL. A false negative is merely a missed optimization opportunity. +** +** False positives are not allowed, however. A false positive may result +** in an incorrect answer. +** +** Terms of p that are marked with EP_FromJoin (and hence that come from +** the ON or USING clauses of LEFT JOINS) are excluded from the analysis. +** +** This routine is used to check if a LEFT JOIN can be converted into +** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE +** clause requires that some column of the right table of the LEFT JOIN +** be non-NULL, then the LEFT JOIN can be safely converted into an +** ordinary join. +*/ +SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ + Walker w; + w.xExprCallback = impliesNotNullRow; + w.xSelectCallback = 0; + w.xSelectCallback2 = 0; + w.eCode = 0; + w.u.iCur = iTab; + sqlite3WalkExpr(&w, p); + return w.eCode; +} + +/* ** An instance of the following structure is used by the tree walker ** to determine if an expression can be evaluated by reference to the ** index only, without having to do a search for the corresponding @@ -99998,7 +101506,7 @@ /* Do not gather statistics on views or virtual tables */ return; } - if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){ + if( sqlite3_strlike("sqlite\\_%", pTab->zName, '\\')==0 ){ /* Do not gather statistics on system tables */ return; } @@ -100977,6 +102485,10 @@ ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the ** third argument. +** +** If the db->init.reopenMemdb flags is set, then instead of attaching a +** new database, close the database on db->init.iDb and reopen it as an +** empty MemDB. */ static void attachFunc( sqlite3_context *context, @@ -100997,65 +102509,85 @@ sqlite3_vfs *pVfs; UNUSED_PARAMETER(NotUsed); - zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; - /* Check for the following errors: - ** - ** * Too many attached databases, - ** * Transaction currently open - ** * Specified database name already being used. - */ - if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ - zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", - db->aLimit[SQLITE_LIMIT_ATTACHED] - ); - goto attach_error; - } - for(i=0; i<db->nDb; i++){ - char *z = db->aDb[i].zDbSName; - assert( z && zName ); - if( sqlite3StrICmp(z, zName)==0 ){ - zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); +#ifdef SQLITE_ENABLE_DESERIALIZE +# define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb) +#else +# define REOPEN_AS_MEMDB(db) (0) +#endif + + if( REOPEN_AS_MEMDB(db) ){ + /* This is not a real ATTACH. Instead, this routine is being called + ** from sqlite3_deserialize() to close database db->init.iDb and + ** reopen it as a MemDB */ + pVfs = sqlite3_vfs_find("memdb"); + if( pVfs==0 ) return; + pNew = &db->aDb[db->init.iDb]; + if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt); + pNew->pBt = 0; + pNew->pSchema = 0; + rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); + }else{ + /* This is a real ATTACH + ** + ** Check for the following errors: + ** + ** * Too many attached databases, + ** * Transaction currently open + ** * Specified database name already being used. + */ + if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ + zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", + db->aLimit[SQLITE_LIMIT_ATTACHED] + ); goto attach_error; } - } + for(i=0; i<db->nDb; i++){ + char *z = db->aDb[i].zDbSName; + assert( z && zName ); + if( sqlite3StrICmp(z, zName)==0 ){ + zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); + goto attach_error; + } + } - /* Allocate the new entry in the db->aDb[] array and initialize the schema - ** hash tables. - */ - if( db->aDb==db->aDbStatic ){ - aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); - if( aNew==0 ) return; - memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); - }else{ - aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); - if( aNew==0 ) return; - } - db->aDb = aNew; - pNew = &db->aDb[db->nDb]; - memset(pNew, 0, sizeof(*pNew)); + /* Allocate the new entry in the db->aDb[] array and initialize the schema + ** hash tables. + */ + if( db->aDb==db->aDbStatic ){ + aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); + if( aNew==0 ) return; + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ + aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); + if( aNew==0 ) return; + } + db->aDb = aNew; + pNew = &db->aDb[db->nDb]; + memset(pNew, 0, sizeof(*pNew)); - /* Open the database file. If the btree is successfully opened, use - ** it to obtain the database schema. At this point the schema may - ** or may not be initialized. - */ - flags = db->openFlags; - rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; + /* Open the database file. If the btree is successfully opened, use + ** it to obtain the database schema. At this point the schema may + ** or may not be initialized. + */ + flags = db->openFlags; + rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); + sqlite3_result_error(context, zErr, -1); + sqlite3_free(zErr); + return; + } + assert( pVfs ); + flags |= SQLITE_OPEN_MAIN_DB; + rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); + sqlite3_free( zPath ); + db->nDb++; } - assert( pVfs ); - flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); - sqlite3_free( zPath ); - db->nDb++; db->skipBtreeMutex = 0; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; @@ -101082,7 +102614,7 @@ sqlite3BtreeLeave(pNew->pBt); } pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; - pNew->zDbSName = sqlite3DbStrDup(db, zName); + if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } @@ -101122,13 +102654,15 @@ /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and - ** remove the entry from the db->aDb[] array. i.e. put everything back the way - ** we found it. + ** remove the entry from the db->aDb[] array. i.e. put everything back the + ** way we found it. */ if( rc==SQLITE_OK ){ sqlite3BtreeEnterAll(db); + db->init.iDb = 0; rc = sqlite3Init(db, &zErrDyn); sqlite3BtreeLeaveAll(db); + assert( zErrDyn==0 || rc!=SQLITE_OK ); } #ifdef SQLITE_USER_AUTHENTICATION if( rc==SQLITE_OK ){ @@ -101140,21 +102674,23 @@ } #endif if( rc ){ - int iDb = db->nDb - 1; - assert( iDb>=2 ); - if( db->aDb[iDb].pBt ){ - sqlite3BtreeClose(db->aDb[iDb].pBt); - db->aDb[iDb].pBt = 0; - db->aDb[iDb].pSchema = 0; - } - sqlite3ResetAllSchemasOfConnection(db); - db->nDb = iDb; - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ - sqlite3OomFault(db); - sqlite3DbFree(db, zErrDyn); - zErrDyn = sqlite3MPrintf(db, "out of memory"); - }else if( zErrDyn==0 ){ - zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); + if( !REOPEN_AS_MEMDB(db) ){ + int iDb = db->nDb - 1; + assert( iDb>=2 ); + if( db->aDb[iDb].pBt ){ + sqlite3BtreeClose(db->aDb[iDb].pBt); + db->aDb[iDb].pBt = 0; + db->aDb[iDb].pSchema = 0; + } + sqlite3ResetAllSchemasOfConnection(db); + db->nDb = iDb; + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ + sqlite3OomFault(db); + sqlite3DbFree(db, zErrDyn); + zErrDyn = sqlite3MPrintf(db, "out of memory"); + }else if( zErrDyn==0 ){ + zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); + } } goto attach_error; } @@ -101426,6 +102962,14 @@ if( sqlite3FixExpr(pFix, pSelect->pLimit) ){ return 1; } + if( pSelect->pWith ){ + int i; + for(i=0; i<pSelect->pWith->nCte; i++){ + if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){ + return 1; + } + } + } pSelect = pSelect->pPrior; } return 0; @@ -102889,10 +104433,24 @@ */ SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){ Table *p; + Column *pCol; p = pParse->pNewTable; if( p==0 || NEVER(p->nCol<1) ) return; - p->aCol[p->nCol-1].notNull = (u8)onError; + pCol = &p->aCol[p->nCol-1]; + pCol->notNull = (u8)onError; p->tabFlags |= TF_HasNotNull; + + /* Set the uniqNotNull flag on any UNIQUE or PK indexes already created + ** on this column. */ + if( pCol->colFlags & COLFLAG_UNIQUE ){ + Index *pIdx; + for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->nKeyCol==1 && pIdx->onError!=OE_None ); + if( pIdx->aiColumn[0]==p->nCol-1 ){ + pIdx->uniqNotNull = 1; + } + } + } } /* @@ -103253,7 +104811,7 @@ Vdbe *v = pParse->pVdbe; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, - db->aDb[iDb].pSchema->schema_cookie+1); + (int)(1+(unsigned)db->aDb[iDb].pSchema->schema_cookie)); } /* @@ -103627,8 +105185,6 @@ p = pParse->pNewTable; if( p==0 ) return; - assert( !db->init.busy || !pSelect ); - /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. ** So do not write to the disk again. Extract the root page number @@ -103639,6 +105195,10 @@ ** table itself. So mark it read-only. */ if( db->init.busy ){ + if( pSelect ){ + sqlite3ErrorMsg(pParse, ""); + return; + } p->tnum = db->init.newTnum; if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } @@ -104856,7 +106416,9 @@ */ if( pList==0 ){ Token prevCol; - sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol-1].zName); + Column *pCol = &pTab->aCol[pTab->nCol-1]; + pCol->colFlags |= COLFLAG_UNIQUE; + sqlite3TokenInit(&prevCol, pCol->zName); pList = sqlite3ExprListAppend(pParse, 0, sqlite3ExprAlloc(db, TK_ID, &prevCol, 0)); if( pList==0 ) goto exit_create_index; @@ -107701,6 +109263,8 @@ ** iteration of the aggregate loop. */ static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){ + assert( context->isError<=0 ); + context->isError = -1; context->skipFlag = 1; } @@ -107767,8 +109331,6 @@ int argc, sqlite3_value **argv ){ - int len; - assert( argc==1 ); UNUSED_PARAMETER(argc); switch( sqlite3_value_type(argv[0]) ){ @@ -107780,13 +109342,17 @@ } case SQLITE_TEXT: { const unsigned char *z = sqlite3_value_text(argv[0]); + const unsigned char *z0; + unsigned char c; if( z==0 ) return; - len = 0; - while( *z ){ - len++; - SQLITE_SKIP_UTF8(z); + z0 = z; + while( (c = *z)!=0 ){ + z++; + if( c>=0xc0 ){ + while( (*z & 0xc0)==0x80 ){ z++; z0++; } + } } - sqlite3_result_int(context, len); + sqlite3_result_int(context, (int)(z-z0)); break; } default: { @@ -108861,6 +110427,8 @@ i64 nOut; /* Maximum size of zOut */ int loopLimit; /* Last zStr[] that might match zPattern[] */ int i, j; /* Loop counters */ + unsigned cntExpand; /* Number zOut expansions */ + sqlite3 *db = sqlite3_context_db_handle(context); assert( argc==3 ); UNUSED_PARAMETER(argc); @@ -108892,33 +110460,40 @@ return; } loopLimit = nStr - nPattern; + cntExpand = 0; for(i=j=0; i<=loopLimit; i++){ if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){ zOut[j++] = zStr[i]; }else{ - u8 *zOld; - sqlite3 *db = sqlite3_context_db_handle(context); - nOut += nRep - nPattern; - testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); - testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - sqlite3_free(zOut); - return; - } - zOld = zOut; - zOut = sqlite3_realloc64(zOut, (int)nOut); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - sqlite3_free(zOld); - return; + if( nRep>nPattern ){ + nOut += nRep - nPattern; + testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); + testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); + if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + sqlite3_result_error_toobig(context); + sqlite3_free(zOut); + return; + } + cntExpand++; + if( (cntExpand&(cntExpand-1))==0 ){ + /* Grow the size of the output buffer only on substitutions + ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ + u8 *zOld; + zOld = zOut; + zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1)); + if( zOut==0 ){ + sqlite3_result_error_nomem(context); + sqlite3_free(zOld); + return; + } + } } memcpy(&zOut[j], zRep, nRep); j += nRep; i += nPattern-1; } } - assert( j+nStr-i+1==nOut ); + assert( j+nStr-i+1<=nOut ); memcpy(&zOut[j], &zStr[i], nStr-i); j += nStr - i; assert( j<=nOut ); @@ -111200,11 +112775,12 @@ ** first use of table pTab. On 2nd and subsequent uses, the original ** AutoincInfo structure is used. ** -** Three memory locations are allocated: +** Four consecutive registers are allocated: ** -** (1) Register to hold the name of the pTab table. -** (2) Register to hold the maximum ROWID of pTab. -** (3) Register to hold the rowid in sqlite_sequence of pTab +** (1) The name of the pTab table. +** (2) The maximum ROWID of pTab. +** (3) The rowid in sqlite_sequence of pTab +** (4) The original value of the max ROWID in pTab, or NULL if none ** ** The 2nd register is the one that is returned. That is all the ** insert routine needs to know about. @@ -111232,7 +112808,7 @@ pInfo->iDb = iDb; pToplevel->nMem++; /* Register to hold name of table */ pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ - pToplevel->nMem++; /* Rowid in sqlite_sequence */ + pToplevel->nMem +=2; /* Rowid in sqlite_sequence + orig max val */ } memId = pInfo->regCtr; } @@ -111260,15 +112836,17 @@ static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList autoInc[] = { /* 0 */ {OP_Null, 0, 0, 0}, - /* 1 */ {OP_Rewind, 0, 9, 0}, + /* 1 */ {OP_Rewind, 0, 10, 0}, /* 2 */ {OP_Column, 0, 0, 0}, - /* 3 */ {OP_Ne, 0, 7, 0}, + /* 3 */ {OP_Ne, 0, 9, 0}, /* 4 */ {OP_Rowid, 0, 0, 0}, /* 5 */ {OP_Column, 0, 1, 0}, - /* 6 */ {OP_Goto, 0, 9, 0}, - /* 7 */ {OP_Next, 0, 2, 0}, - /* 8 */ {OP_Integer, 0, 0, 0}, - /* 9 */ {OP_Close, 0, 0, 0} + /* 6 */ {OP_AddImm, 0, 0, 0}, + /* 7 */ {OP_Copy, 0, 0, 0}, + /* 8 */ {OP_Goto, 0, 11, 0}, + /* 9 */ {OP_Next, 0, 2, 0}, + /* 10 */ {OP_Integer, 0, 0, 0}, + /* 11 */ {OP_Close, 0, 0, 0} }; VdbeOp *aOp; pDb = &db->aDb[p->iDb]; @@ -111279,14 +112857,17 @@ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn); if( aOp==0 ) break; aOp[0].p2 = memId; - aOp[0].p3 = memId+1; + aOp[0].p3 = memId+2; aOp[2].p3 = memId; aOp[3].p1 = memId-1; aOp[3].p3 = memId; aOp[3].p5 = SQLITE_JUMPIFNULL; aOp[4].p2 = memId+1; aOp[5].p3 = memId; - aOp[8].p2 = memId; + aOp[6].p1 = memId; + aOp[7].p2 = memId+2; + aOp[7].p1 = memId; + aOp[10].p2 = memId; } } @@ -111333,6 +112914,8 @@ iRec = sqlite3GetTempReg(pParse); assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); + sqlite3VdbeAddOp3(v, OP_Le, memId+2, sqlite3VdbeCurrentAddr(v)+7, memId); + VdbeCoverage(v); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn); if( aOp==0 ) break; @@ -113981,8 +115564,8 @@ #define sqlite3_value_pointer sqlite3_api->value_pointer /* Version 3.22.0 and later */ #define sqlite3_vtab_nochange sqlite3_api->vtab_nochange -#define sqlite3_value_nochange sqltie3_api->value_nochange -#define sqlite3_vtab_collation sqltie3_api->vtab_collation +#define sqlite3_value_nochange sqlite3_api->value_nochange +#define sqlite3_vtab_collation sqlite3_api->vtab_collation #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -117993,7 +119576,7 @@ char *z; if( zObj==0 ) zObj = "?"; z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj); - if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); + if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); sqlite3DbFree(db, *pData->pzErrMsg); *pData->pzErrMsg = z; } @@ -118887,8 +120470,7 @@ /***/ int sqlite3SelectTrace = 0; # define SELECTTRACE(K,P,S,X) \ if(sqlite3SelectTrace&(K)) \ - sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\ - (S)->zSelName,(S)),\ + sqlite3DebugPrintf("%s/%p: ",(S)->zSelName,(S)),\ sqlite3DebugPrintf X #else # define SELECTTRACE(K,P,S,X) @@ -119249,6 +120831,29 @@ } } +/* Undo the work of setJoinExpr(). In the expression tree p, convert every +** term that is marked with EP_FromJoin and iRightJoinTable==iTable into +** an ordinary term that omits the EP_FromJoin mark. +** +** This happens when a LEFT JOIN is simplified into an ordinary JOIN. +*/ +static void unsetJoinExpr(Expr *p, int iTable){ + while( p ){ + if( ExprHasProperty(p, EP_FromJoin) + && (iTable<0 || p->iRightJoinTable==iTable) ){ + ExprClearProperty(p, EP_FromJoin); + } + if( p->op==TK_FUNCTION && p->x.pList ){ + int i; + for(i=0; i<p->x.pList->nExpr; i++){ + unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); + } + } + unsetJoinExpr(p->pLeft, iTable); + p = p->pRight; + } +} + /* ** This routine processes the join information for a SELECT statement. ** ON and USING clauses are converted into extra terms of the WHERE clause. @@ -120132,12 +121737,15 @@ iSortTab = iTab; bSeq = 1; } - for(i=0, iCol=nKey+bSeq; i<nSortData; i++){ + for(i=0, iCol=nKey+bSeq-1; i<nSortData; i++){ + if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++; + } + for(i=nSortData-1; i>=0; i--){ int iRead; if( aOutEx[i].u.x.iOrderByCol ){ iRead = aOutEx[i].u.x.iOrderByCol-1; }else{ - iRead = iCol++; + iRead = iCol--; } sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i); VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan)); @@ -122614,7 +124222,6 @@ pOrderBy->a[i].u.x.iOrderByCol = 0; } assert( pParent->pOrderBy==0 ); - assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } @@ -122698,12 +124305,22 @@ ** (3) The inner query has a LIMIT clause (since the changes to the WHERE ** close would change the meaning of the LIMIT). ** -** (4) The inner query is the right operand of a LEFT JOIN. (The caller -** enforces this restriction since this routine does not have enough -** information to know.) +** (4) The inner query is the right operand of a LEFT JOIN and the +** expression to be pushed down does not come from the ON clause +** on that LEFT JOIN. ** ** (5) The WHERE clause expression originates in the ON or USING clause -** of a LEFT JOIN. +** of a LEFT JOIN where iCursor is not the right-hand table of that +** left join. An example: +** +** SELECT * +** FROM (SELECT 1 AS a1 UNION ALL SELECT 2) AS aa +** JOIN (SELECT 1 AS b2 UNION ALL SELECT 2) AS bb ON (a1=b2) +** LEFT JOIN (SELECT 8 AS c3 UNION ALL SELECT 9) AS cc ON (b2=2); +** +** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9). +** But if the (b2=2) term were to be pushed down into the bb subquery, +** then the (1,1,NULL) row would be suppressed. ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. @@ -122712,7 +124329,8 @@ Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ Expr *pWhere, /* The WHERE clause of the outer query */ - int iCursor /* Cursor number of the subquery */ + int iCursor, /* Cursor number of the subquery */ + int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ ){ Expr *pNew; int nChng = 0; @@ -122736,15 +124354,25 @@ return 0; /* restriction (3) */ } while( pWhere->op==TK_AND ){ - nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, iCursor); + nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, + iCursor, isLeftJoin); pWhere = pWhere->pLeft; } - if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction (5) */ + if( isLeftJoin + && (ExprHasProperty(pWhere,EP_FromJoin)==0 + || pWhere->iRightJoinTable!=iCursor) + ){ + return 0; /* restriction (4) */ + } + if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ + return 0; /* restriction (5) */ + } if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ nChng++; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); + unsetJoinExpr(pNew, -1); x.pParse = pParse; x.iTable = iCursor; x.iNewTable = iCursor; @@ -123199,9 +124827,7 @@ } pTabList = p->pSrc; pEList = p->pEList; - if( OK_IF_ALWAYS_TRUE(p->pWith) ){ - sqlite3WithPush(pParse, p->pWith, 0); - } + sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -123777,14 +125403,6 @@ #endif /* -** Context object for havingToWhereExprCb(). -*/ -struct HavingToWhereCtx { - Expr **ppWhere; - ExprList *pGroupBy; -}; - -/* ** sqlite3WalkExpr() callback used by havingToWhere(). ** ** If the node passed to the callback is a TK_AND node, return @@ -123797,15 +125415,16 @@ */ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op!=TK_AND ){ - struct HavingToWhereCtx *p = pWalker->u.pHavingCtx; - if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, p->pGroupBy) ){ + Select *pS = pWalker->u.pSelect; + if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){ sqlite3 *db = pWalker->pParse->db; Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0); if( pNew ){ - Expr *pWhere = *(p->ppWhere); + Expr *pWhere = pS->pWhere; SWAP(Expr, *pNew, *pExpr); pNew = sqlite3ExprAnd(db, pWhere, pNew); - *(p->ppWhere) = pNew; + pS->pWhere = pNew; + pWalker->eCode = 1; } } return WRC_Prune; @@ -123828,23 +125447,19 @@ ** entirely of constants and expressions that are also GROUP BY terms that ** use the "BINARY" collation sequence. */ -static void havingToWhere( - Parse *pParse, - ExprList *pGroupBy, - Expr *pHaving, - Expr **ppWhere -){ - struct HavingToWhereCtx sCtx; +static void havingToWhere(Parse *pParse, Select *p){ Walker sWalker; - - sCtx.ppWhere = ppWhere; - sCtx.pGroupBy = pGroupBy; - memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.xExprCallback = havingToWhereExprCb; - sWalker.u.pHavingCtx = &sCtx; - sqlite3WalkExpr(&sWalker, pHaving); + sWalker.u.pSelect = p; + sqlite3WalkExpr(&sWalker, p->pHaving); +#if SELECTTRACE_ENABLED + if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){ + SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); + sqlite3TreeViewSelect(0, p, 0); + } +#endif } /* @@ -124005,7 +125620,6 @@ if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; memset(&sAggInfo, 0, sizeof(sAggInfo)); #if SELECTTRACE_ENABLED - pParse->nSelectIndent++; SELECTTRACE(1,pParse,p, ("begin processing:\n")); if( sqlite3SelectTrace & 0x100 ){ sqlite3TreeViewSelect(0, p, 0); @@ -124051,13 +125665,29 @@ generateColumnNames(pParse, p); } - /* Try to flatten subqueries in the FROM clause up into the main query + /* Try to various optimizations (flattening subqueries, and strength + ** reduction of join operators) in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; Table *pTab = pItem->pTab; + + /* Convert LEFT JOIN into JOIN if there are terms of the right table + ** of the LEFT JOIN used in the WHERE clause. + */ + if( (pItem->fg.jointype & JT_LEFT)!=0 + && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) + && OptimizationEnabled(db, SQLITE_SimplifyJoin) + ){ + SELECTTRACE(0x100,pParse,p, + ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); + pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); + unsetJoinExpr(p->pWhere, pItem->iCursor); + } + + /* No futher action if this term of the FROM clause is no a subquery */ if( pSub==0 ) continue; /* Catch mismatch in the declared columns of a view and the number of @@ -124126,7 +125756,6 @@ explainSetInteger(pParse->iSelectId, iRestoreSelectId); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p,("end compound-select processing\n")); - pParse->nSelectIndent--; #endif return rc; } @@ -124199,8 +125828,9 @@ /* Make copies of constant WHERE-clause terms in the outer query down ** inside the subquery. This can help the subquery to run more efficiently. */ - if( (pItem->fg.jointype & JT_OUTER)==0 - && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor) + if( OptimizationEnabled(db, SQLITE_PushDown) + && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, + (pItem->fg.jointype & JT_OUTER)!=0) ){ #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x100 ){ @@ -124208,6 +125838,8 @@ sqlite3TreeViewSelect(0, p, 0); } #endif + }else{ + SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); } zSavedAuthContext = pParse->zAuthContext; @@ -124410,6 +126042,7 @@ wctrlFlags |= p->selFlags & SF_FixedLimit; /* Begin the database scan. */ + SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, p->pEList, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; @@ -124511,7 +126144,9 @@ if( pHaving ){ if( pGroupBy ){ assert( pWhere==p->pWhere ); - havingToWhere(pParse, pGroupBy, pHaving, &p->pWhere); + assert( pHaving==p->pHaving ); + assert( pGroupBy==p->pGroupBy ); + havingToWhere(pParse, p); pWhere = p->pWhere; } sqlite3ExprAnalyzeAggregates(&sNC, pHaving); @@ -124598,6 +126233,7 @@ ** in the right order to begin with. */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); + SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 ); @@ -124853,6 +126489,7 @@ assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 ); assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 ); + SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, 0, minMaxFlag, 0); if( pWInfo==0 ){ @@ -124908,7 +126545,6 @@ sqlite3DbFree(db, sAggInfo.aFunc); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p,("end processing\n")); - pParse->nSelectIndent--; #endif return rc; } @@ -126654,7 +128290,7 @@ regKey = ++pParse->nMem; iEph = pParse->nTab++; - sqlite3VdbeAddOp2(v, OP_Null, 0, iPk); + sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1); addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); sqlite3VdbeSetP4KeyInfo(pParse, pPk); } @@ -127204,8 +128840,8 @@ while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0); assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 ); - if( zSubSql ){ - assert( zSubSql[0]!='S' ); + assert( sqlite3_strnicmp(zSubSql,"SELECT",6)!=0 || CORRUPT_DB ); + if( zSubSql && zSubSql[0]!='S' ){ rc = execSql(db, pzErrMsg, zSubSql); if( rc!=SQLITE_OK ) break; } @@ -128826,7 +130462,7 @@ ** Trace output macros */ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/***/ int sqlite3WhereTrace; +/***/ extern int sqlite3WhereTrace; #endif #if defined(SQLITE_DEBUG) \ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) @@ -130743,7 +132379,15 @@ if( sqlite3ExprIsVector(pX->pRight) ){ r1 = rTemp = sqlite3GetTempReg(pParse); codeExprOrVector(pParse, pX->pRight, r1, 1); - op = aMoveOp[(pX->op - TK_GT) | 0x0001]; + testcase( pX->op==TK_GT ); + testcase( pX->op==TK_GE ); + testcase( pX->op==TK_LT ); + testcase( pX->op==TK_LE ); + op = aMoveOp[((pX->op - TK_GT - 1) & 0x3) | 0x1]; + assert( pX->op!=TK_GT || op==OP_SeekGE ); + assert( pX->op!=TK_GE || op==OP_SeekGE ); + assert( pX->op!=TK_LT || op==OP_SeekLE ); + assert( pX->op!=TK_LE || op==OP_SeekLE ); }else{ r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp); disableTerm(pLevel, pStart); @@ -131467,7 +133111,7 @@ continue; } - if( pTerm->wtFlags & TERM_LIKECOND ){ + if( (pTerm->wtFlags & TERM_LIKECOND)!=0 ){ /* If the TERM_LIKECOND flag is set, that means that the range search ** is sufficient to guarantee that the LIKE operator is true, so we ** can skip the call to the like(A,B) function. But this only works @@ -131477,8 +133121,9 @@ continue; #else u32 x = pLevel->iLikeRepCntr; - assert( x>0 ); - skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If, (int)(x>>1)); + if( x>0 ){ + skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1)); + } VdbeCoverage(v); #endif } @@ -131518,6 +133163,12 @@ WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; + if( (pAlt->eOperator & WO_IN) + && (pAlt->pExpr->flags & EP_xIsSelect) + && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) + ){ + continue; + } testcase( pAlt->eOperator & WO_EQ ); testcase( pAlt->eOperator & WO_IS ); testcase( pAlt->eOperator & WO_IN ); @@ -132432,6 +134083,9 @@ for(i=0; i<pSrc->nSrc; i++){ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); + if( pSrc->a[i].fg.isTabFunc ){ + mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); + } } } pS = pS->pPrior; @@ -132844,7 +134498,7 @@ exprAnalyze(pSrc, pWC, idxNew); } pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */ + pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */ pTerm->eOperator = 0; } @@ -135461,8 +137115,8 @@ pNew = pBuilder->pNew; if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; - WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n", - pProbe->zName, pNew->u.btree.nEq)); + WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n", + pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq)); assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); @@ -135569,10 +137223,12 @@ if( iCol==XN_ROWID || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) ){ - if( iCol>=0 && pProbe->uniqNotNull==0 ){ - pNew->wsFlags |= WHERE_UNQ_WANTED; - }else{ + if( iCol==XN_ROWID || pProbe->uniqNotNull + || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) + ){ pNew->wsFlags |= WHERE_ONEROW; + }else{ + pNew->wsFlags |= WHERE_UNQ_WANTED; } } }else if( eOp & WO_ISNULL ){ @@ -135746,8 +137402,8 @@ pNew->wsFlags = saved_wsFlags; } - WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n", - pProbe->zName, saved_nEq, rc)); + WHERETRACE(0x800, ("END %s.addBtreeIdx(%s), nEq=%d, rc=%d\n", + pProbe->pTable->zName, pProbe->zName, saved_nEq, rc)); return rc; } @@ -136185,9 +137841,9 @@ || pNew->aLTerm[iTerm]!=0 || pIdxCons->usable==0 ){ - rc = SQLITE_ERROR; sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); - return rc; + testcase( pIdxInfo->needToFreeIdxStr ); + return SQLITE_ERROR; } testcase( iTerm==nConstraint-1 ); testcase( j==0 ); @@ -136215,6 +137871,15 @@ pNew->u.vtab.omitMask &= ~mNoOmit; pNew->nLTerm = mxTerm+1; + for(i=0; i<=mxTerm; i++){ + if( pNew->aLTerm[i]==0 ){ + /* The non-zero argvIdx values must be contiguous. Raise an + ** error if they are not */ + sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); + testcase( pIdxInfo->needToFreeIdxStr ); + return SQLITE_ERROR; + } + } assert( pNew->nLTerm<=pNew->nLSlot ); pNew->u.vtab.idxNum = pIdxInfo->idxNum; pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; @@ -136330,6 +137995,7 @@ } /* First call xBestIndex() with all constraints usable. */ + WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); @@ -136405,6 +138071,7 @@ if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); sqlite3DbFreeNN(pParse->db, p); + WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -137719,6 +139386,7 @@ */ for(ii=0; ii<sWLB.pWC->nTerm; ii++){ WhereTerm *pT = &sWLB.pWC->a[ii]; + if( pT->wtFlags & TERM_VIRTUAL ) continue; if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); pT->wtFlags |= TERM_CODED; @@ -138544,17 +140212,17 @@ #define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse #define sqlite3ParserARG_STORE yypParser->pParse = pParse #define YYFALLBACK 1 -#define YYNSTATE 466 -#define YYNRULE 330 +#define YYNSTATE 472 +#define YYNRULE 333 #define YYNTOKEN 143 -#define YY_MAX_SHIFT 465 -#define YY_MIN_SHIFTREDUCE 675 -#define YY_MAX_SHIFTREDUCE 1004 -#define YY_ERROR_ACTION 1005 -#define YY_ACCEPT_ACTION 1006 -#define YY_NO_ACTION 1007 -#define YY_MIN_REDUCE 1008 -#define YY_MAX_REDUCE 1337 +#define YY_MAX_SHIFT 471 +#define YY_MIN_SHIFTREDUCE 681 +#define YY_MAX_SHIFTREDUCE 1013 +#define YY_ERROR_ACTION 1014 +#define YY_ACCEPT_ACTION 1015 +#define YY_NO_ACTION 1016 +#define YY_MIN_REDUCE 1017 +#define YY_MAX_REDUCE 1349 /************* End control #defines *******************************************/ /* Define the yytestcase() macro to be a no-op if is not already defined @@ -138620,322 +140288,324 @@ ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (1541) +#define YY_ACTTAB_COUNT (1566) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 1006, 156, 156, 2, 1302, 90, 87, 179, 90, 87, - /* 10 */ 179, 460, 1048, 460, 465, 1010, 460, 333, 1130, 335, - /* 20 */ 246, 330, 112, 303, 439, 1258, 304, 419, 1129, 1087, - /* 30 */ 72, 798, 50, 50, 50, 50, 331, 30, 30, 799, - /* 40 */ 951, 364, 371, 97, 98, 88, 983, 983, 859, 862, - /* 50 */ 851, 851, 95, 95, 96, 96, 96, 96, 120, 371, - /* 60 */ 370, 120, 348, 22, 90, 87, 179, 438, 423, 438, - /* 70 */ 440, 335, 420, 385, 90, 87, 179, 116, 73, 163, - /* 80 */ 848, 848, 860, 863, 94, 94, 94, 94, 93, 93, - /* 90 */ 92, 92, 92, 91, 361, 97, 98, 88, 983, 983, - /* 100 */ 859, 862, 851, 851, 95, 95, 96, 96, 96, 96, - /* 110 */ 718, 365, 339, 93, 93, 92, 92, 92, 91, 361, - /* 120 */ 99, 371, 453, 335, 94, 94, 94, 94, 93, 93, - /* 130 */ 92, 92, 92, 91, 361, 852, 94, 94, 94, 94, - /* 140 */ 93, 93, 92, 92, 92, 91, 361, 97, 98, 88, - /* 150 */ 983, 983, 859, 862, 851, 851, 95, 95, 96, 96, - /* 160 */ 96, 96, 92, 92, 92, 91, 361, 838, 132, 195, - /* 170 */ 58, 244, 412, 409, 408, 335, 457, 457, 457, 304, - /* 180 */ 59, 332, 831, 407, 394, 962, 830, 391, 94, 94, - /* 190 */ 94, 94, 93, 93, 92, 92, 92, 91, 361, 97, - /* 200 */ 98, 88, 983, 983, 859, 862, 851, 851, 95, 95, - /* 210 */ 96, 96, 96, 96, 426, 357, 460, 830, 830, 832, - /* 220 */ 91, 361, 962, 963, 964, 195, 459, 335, 412, 409, - /* 230 */ 408, 280, 361, 820, 132, 11, 11, 50, 50, 407, - /* 240 */ 94, 94, 94, 94, 93, 93, 92, 92, 92, 91, - /* 250 */ 361, 97, 98, 88, 983, 983, 859, 862, 851, 851, - /* 260 */ 95, 95, 96, 96, 96, 96, 460, 221, 460, 264, - /* 270 */ 375, 254, 438, 428, 1276, 1276, 383, 1074, 1053, 335, - /* 280 */ 245, 422, 299, 713, 271, 271, 1074, 50, 50, 50, - /* 290 */ 50, 962, 94, 94, 94, 94, 93, 93, 92, 92, - /* 300 */ 92, 91, 361, 97, 98, 88, 983, 983, 859, 862, - /* 310 */ 851, 851, 95, 95, 96, 96, 96, 96, 90, 87, - /* 320 */ 179, 1306, 438, 437, 438, 418, 368, 253, 962, 963, - /* 330 */ 964, 335, 360, 360, 360, 706, 359, 358, 324, 962, - /* 340 */ 1281, 951, 364, 230, 94, 94, 94, 94, 93, 93, - /* 350 */ 92, 92, 92, 91, 361, 97, 98, 88, 983, 983, - /* 360 */ 859, 862, 851, 851, 95, 95, 96, 96, 96, 96, - /* 370 */ 769, 460, 120, 226, 226, 366, 962, 963, 964, 1089, - /* 380 */ 990, 900, 990, 335, 1057, 425, 421, 839, 759, 759, - /* 390 */ 425, 427, 50, 50, 432, 381, 94, 94, 94, 94, - /* 400 */ 93, 93, 92, 92, 92, 91, 361, 97, 98, 88, - /* 410 */ 983, 983, 859, 862, 851, 851, 95, 95, 96, 96, - /* 420 */ 96, 96, 460, 259, 460, 120, 117, 354, 942, 1332, - /* 430 */ 942, 1333, 1332, 278, 1333, 335, 680, 681, 682, 825, - /* 440 */ 201, 176, 303, 50, 50, 49, 49, 404, 94, 94, - /* 450 */ 94, 94, 93, 93, 92, 92, 92, 91, 361, 97, - /* 460 */ 98, 88, 983, 983, 859, 862, 851, 851, 95, 95, - /* 470 */ 96, 96, 96, 96, 199, 460, 380, 265, 433, 380, - /* 480 */ 265, 383, 256, 158, 258, 319, 1003, 335, 155, 940, - /* 490 */ 177, 940, 273, 379, 276, 322, 34, 34, 302, 962, - /* 500 */ 94, 94, 94, 94, 93, 93, 92, 92, 92, 91, - /* 510 */ 361, 97, 98, 88, 983, 983, 859, 862, 851, 851, - /* 520 */ 95, 95, 96, 96, 96, 96, 905, 905, 397, 460, - /* 530 */ 301, 158, 101, 319, 941, 340, 962, 963, 964, 313, - /* 540 */ 283, 449, 335, 327, 146, 1266, 1004, 257, 234, 248, - /* 550 */ 35, 35, 94, 94, 94, 94, 93, 93, 92, 92, - /* 560 */ 92, 91, 361, 709, 785, 1227, 97, 98, 88, 983, - /* 570 */ 983, 859, 862, 851, 851, 95, 95, 96, 96, 96, - /* 580 */ 96, 962, 1227, 1229, 245, 422, 838, 198, 197, 196, - /* 590 */ 1079, 1079, 1077, 1077, 1004, 1334, 320, 335, 172, 171, - /* 600 */ 709, 831, 159, 271, 271, 830, 76, 94, 94, 94, - /* 610 */ 94, 93, 93, 92, 92, 92, 91, 361, 962, 963, - /* 620 */ 964, 97, 98, 88, 983, 983, 859, 862, 851, 851, - /* 630 */ 95, 95, 96, 96, 96, 96, 830, 830, 832, 1157, - /* 640 */ 1157, 199, 1157, 173, 1227, 231, 232, 1282, 2, 335, - /* 650 */ 271, 764, 271, 820, 271, 271, 763, 389, 389, 389, - /* 660 */ 132, 79, 94, 94, 94, 94, 93, 93, 92, 92, - /* 670 */ 92, 91, 361, 97, 98, 88, 983, 983, 859, 862, - /* 680 */ 851, 851, 95, 95, 96, 96, 96, 96, 460, 264, - /* 690 */ 223, 460, 1257, 783, 1223, 1157, 1086, 1082, 80, 271, - /* 700 */ 78, 335, 340, 1031, 341, 344, 345, 902, 346, 10, - /* 710 */ 10, 902, 25, 25, 94, 94, 94, 94, 93, 93, - /* 720 */ 92, 92, 92, 91, 361, 97, 86, 88, 983, 983, - /* 730 */ 859, 862, 851, 851, 95, 95, 96, 96, 96, 96, - /* 740 */ 1157, 270, 395, 117, 233, 263, 235, 70, 456, 341, - /* 750 */ 225, 176, 335, 1305, 342, 133, 736, 966, 980, 249, - /* 760 */ 1150, 396, 325, 1085, 1028, 178, 94, 94, 94, 94, - /* 770 */ 93, 93, 92, 92, 92, 91, 361, 98, 88, 983, - /* 780 */ 983, 859, 862, 851, 851, 95, 95, 96, 96, 96, - /* 790 */ 96, 783, 783, 132, 120, 966, 120, 120, 120, 798, - /* 800 */ 252, 937, 335, 353, 321, 429, 355, 799, 822, 692, - /* 810 */ 390, 203, 446, 450, 372, 716, 454, 94, 94, 94, - /* 820 */ 94, 93, 93, 92, 92, 92, 91, 361, 88, 983, - /* 830 */ 983, 859, 862, 851, 851, 95, 95, 96, 96, 96, - /* 840 */ 96, 84, 455, 1225, 3, 1209, 120, 120, 382, 387, - /* 850 */ 120, 203, 1271, 716, 384, 168, 266, 203, 458, 72, - /* 860 */ 260, 1246, 84, 455, 178, 3, 378, 94, 94, 94, - /* 870 */ 94, 93, 93, 92, 92, 92, 91, 361, 350, 458, - /* 880 */ 1245, 362, 430, 213, 228, 290, 415, 285, 414, 200, - /* 890 */ 783, 882, 444, 726, 725, 405, 283, 921, 209, 921, - /* 900 */ 281, 132, 362, 72, 838, 289, 147, 733, 734, 392, - /* 910 */ 81, 82, 922, 444, 922, 267, 288, 83, 362, 462, - /* 920 */ 461, 272, 132, 830, 23, 838, 388, 923, 1216, 923, - /* 930 */ 1056, 81, 82, 84, 455, 899, 3, 899, 83, 362, - /* 940 */ 462, 461, 761, 962, 830, 75, 1, 443, 275, 747, - /* 950 */ 458, 5, 962, 204, 830, 830, 832, 833, 18, 748, - /* 960 */ 229, 962, 277, 19, 153, 317, 317, 316, 216, 314, - /* 970 */ 279, 460, 689, 362, 1055, 830, 830, 832, 833, 18, - /* 980 */ 962, 963, 964, 962, 444, 181, 460, 251, 981, 962, - /* 990 */ 963, 964, 8, 8, 20, 250, 838, 1070, 962, 963, - /* 1000 */ 964, 417, 81, 82, 768, 204, 347, 36, 36, 83, - /* 1010 */ 362, 462, 461, 1054, 284, 830, 84, 455, 1123, 3, - /* 1020 */ 962, 963, 964, 460, 183, 962, 981, 764, 889, 1107, - /* 1030 */ 460, 184, 763, 458, 132, 182, 74, 455, 460, 3, - /* 1040 */ 981, 898, 834, 898, 8, 8, 830, 830, 832, 833, - /* 1050 */ 18, 8, 8, 458, 219, 1156, 362, 1103, 349, 8, - /* 1060 */ 8, 240, 962, 963, 964, 236, 889, 444, 792, 336, - /* 1070 */ 158, 203, 885, 435, 700, 209, 362, 114, 981, 838, - /* 1080 */ 834, 227, 334, 1114, 441, 81, 82, 444, 442, 305, - /* 1090 */ 784, 306, 83, 362, 462, 461, 369, 1162, 830, 838, - /* 1100 */ 460, 1037, 237, 1030, 237, 81, 82, 7, 96, 96, - /* 1110 */ 96, 96, 83, 362, 462, 461, 1019, 1018, 830, 1020, - /* 1120 */ 1289, 37, 37, 400, 96, 96, 96, 96, 89, 830, - /* 1130 */ 830, 832, 833, 18, 1100, 318, 962, 292, 94, 94, - /* 1140 */ 94, 94, 93, 93, 92, 92, 92, 91, 361, 830, - /* 1150 */ 830, 832, 833, 18, 94, 94, 94, 94, 93, 93, - /* 1160 */ 92, 92, 92, 91, 361, 359, 358, 226, 226, 727, - /* 1170 */ 294, 296, 460, 962, 963, 964, 460, 989, 160, 425, - /* 1180 */ 170, 1295, 262, 460, 987, 374, 988, 386, 1145, 255, - /* 1190 */ 326, 460, 373, 38, 38, 410, 174, 39, 39, 413, - /* 1200 */ 460, 287, 460, 1053, 40, 40, 298, 728, 1220, 990, - /* 1210 */ 445, 990, 26, 26, 1219, 460, 311, 460, 169, 1292, - /* 1220 */ 460, 27, 27, 29, 29, 998, 460, 206, 135, 995, - /* 1230 */ 1265, 1263, 460, 57, 60, 460, 41, 41, 42, 42, - /* 1240 */ 460, 43, 43, 460, 343, 351, 460, 9, 9, 460, - /* 1250 */ 144, 460, 130, 44, 44, 460, 103, 103, 460, 137, - /* 1260 */ 70, 45, 45, 460, 46, 46, 460, 31, 31, 1142, - /* 1270 */ 47, 47, 48, 48, 460, 376, 32, 32, 460, 122, - /* 1280 */ 122, 460, 157, 460, 123, 123, 139, 124, 124, 460, - /* 1290 */ 186, 460, 377, 460, 115, 54, 54, 460, 403, 33, - /* 1300 */ 33, 460, 104, 104, 51, 51, 460, 161, 460, 140, - /* 1310 */ 105, 105, 106, 106, 102, 102, 460, 141, 121, 121, - /* 1320 */ 460, 142, 119, 119, 190, 460, 1152, 110, 110, 109, - /* 1330 */ 109, 702, 460, 148, 393, 65, 460, 107, 107, 460, - /* 1340 */ 323, 108, 108, 399, 460, 1234, 53, 53, 1214, 269, - /* 1350 */ 154, 416, 1115, 55, 55, 220, 401, 52, 52, 191, - /* 1360 */ 24, 24, 274, 192, 193, 28, 28, 1021, 328, 702, - /* 1370 */ 1073, 352, 1072, 718, 1071, 431, 1111, 1064, 329, 1045, - /* 1380 */ 69, 205, 6, 291, 1044, 286, 1112, 1043, 1304, 1110, - /* 1390 */ 293, 300, 295, 297, 1063, 1200, 1109, 77, 241, 448, - /* 1400 */ 356, 452, 436, 100, 214, 71, 434, 1027, 1093, 21, - /* 1410 */ 463, 242, 243, 957, 215, 217, 218, 464, 309, 307, - /* 1420 */ 308, 310, 1016, 125, 1250, 1251, 1011, 1249, 126, 127, - /* 1430 */ 1248, 113, 676, 337, 238, 338, 134, 363, 167, 1041, - /* 1440 */ 1040, 56, 247, 367, 180, 897, 111, 895, 136, 1038, - /* 1450 */ 818, 128, 138, 750, 261, 911, 185, 143, 145, 61, - /* 1460 */ 62, 63, 64, 129, 914, 187, 188, 910, 118, 12, - /* 1470 */ 189, 903, 268, 992, 203, 162, 398, 150, 149, 691, - /* 1480 */ 402, 288, 194, 406, 151, 411, 66, 13, 729, 239, - /* 1490 */ 282, 14, 67, 131, 837, 836, 865, 758, 15, 4, - /* 1500 */ 68, 762, 175, 222, 224, 424, 152, 869, 791, 202, - /* 1510 */ 786, 75, 72, 880, 866, 864, 16, 17, 920, 207, - /* 1520 */ 919, 208, 447, 946, 164, 211, 947, 210, 165, 451, - /* 1530 */ 868, 166, 315, 835, 701, 85, 212, 1297, 312, 952, - /* 1540 */ 1296, + /* 0 */ 1169, 1015, 167, 167, 1, 168, 466, 1313, 466, 1083, + /* 10 */ 1062, 466, 97, 94, 183, 1057, 466, 329, 1083, 342, + /* 20 */ 97, 94, 183, 459, 459, 459, 436, 57, 57, 57, + /* 30 */ 57, 807, 57, 57, 367, 367, 367, 57, 57, 808, + /* 40 */ 1270, 1088, 1088, 104, 105, 95, 991, 991, 868, 871, + /* 50 */ 860, 860, 102, 102, 103, 103, 103, 103, 233, 233, + /* 60 */ 326, 1011, 449, 437, 449, 446, 351, 449, 461, 1142, + /* 70 */ 463, 342, 449, 426, 1316, 209, 180, 742, 80, 299, + /* 80 */ 857, 857, 869, 872, 101, 101, 101, 101, 100, 100, + /* 90 */ 99, 99, 99, 98, 368, 104, 105, 95, 991, 991, + /* 100 */ 868, 871, 860, 860, 102, 102, 103, 103, 103, 103, + /* 110 */ 99, 99, 99, 98, 368, 355, 97, 94, 183, 228, + /* 120 */ 106, 1012, 407, 342, 101, 101, 101, 101, 100, 100, + /* 130 */ 99, 99, 99, 98, 368, 861, 101, 101, 101, 101, + /* 140 */ 100, 100, 99, 99, 99, 98, 368, 104, 105, 95, + /* 150 */ 991, 991, 868, 871, 860, 860, 102, 102, 103, 103, + /* 160 */ 103, 103, 201, 368, 375, 420, 417, 416, 387, 273, + /* 170 */ 65, 97, 94, 183, 168, 342, 415, 951, 1343, 396, + /* 180 */ 66, 1343, 320, 959, 371, 970, 334, 340, 101, 101, + /* 190 */ 101, 101, 100, 100, 99, 99, 99, 98, 368, 104, + /* 200 */ 105, 95, 991, 991, 868, 871, 860, 860, 102, 102, + /* 210 */ 103, 103, 103, 103, 373, 100, 100, 99, 99, 99, + /* 220 */ 98, 368, 970, 971, 972, 201, 1100, 342, 420, 417, + /* 230 */ 416, 287, 366, 365, 337, 970, 1162, 463, 949, 415, + /* 240 */ 101, 101, 101, 101, 100, 100, 99, 99, 99, 98, + /* 250 */ 368, 104, 105, 95, 991, 991, 868, 871, 860, 860, + /* 260 */ 102, 102, 103, 103, 103, 103, 777, 241, 233, 233, + /* 270 */ 9, 847, 970, 971, 972, 390, 998, 1141, 998, 342, + /* 280 */ 463, 252, 829, 719, 98, 368, 840, 298, 338, 142, + /* 290 */ 839, 339, 101, 101, 101, 101, 100, 100, 99, 99, + /* 300 */ 99, 98, 368, 104, 105, 95, 991, 991, 868, 871, + /* 310 */ 860, 860, 102, 102, 103, 103, 103, 103, 272, 466, + /* 320 */ 392, 839, 839, 841, 97, 94, 183, 390, 1317, 253, + /* 330 */ 456, 342, 125, 166, 807, 712, 208, 407, 386, 970, + /* 340 */ 57, 57, 808, 238, 101, 101, 101, 101, 100, 100, + /* 350 */ 99, 99, 99, 98, 368, 104, 105, 95, 991, 991, + /* 360 */ 868, 871, 860, 860, 102, 102, 103, 103, 103, 103, + /* 370 */ 466, 108, 466, 267, 465, 442, 970, 971, 972, 261, + /* 380 */ 951, 1344, 909, 342, 1344, 142, 829, 848, 1292, 959, + /* 390 */ 371, 55, 55, 57, 57, 242, 101, 101, 101, 101, + /* 400 */ 100, 100, 99, 99, 99, 98, 368, 104, 105, 95, + /* 410 */ 991, 991, 868, 871, 860, 860, 102, 102, 103, 103, + /* 420 */ 103, 103, 272, 382, 262, 253, 456, 310, 364, 253, + /* 430 */ 456, 86, 264, 84, 266, 342, 441, 176, 175, 834, + /* 440 */ 464, 949, 767, 767, 332, 313, 1094, 396, 101, 101, + /* 450 */ 101, 101, 100, 100, 99, 99, 99, 98, 368, 104, + /* 460 */ 105, 95, 991, 991, 868, 871, 860, 860, 102, 102, + /* 470 */ 103, 103, 103, 103, 227, 227, 233, 233, 233, 233, + /* 480 */ 387, 273, 234, 234, 326, 950, 463, 342, 463, 298, + /* 490 */ 463, 914, 914, 404, 463, 1037, 123, 265, 27, 970, + /* 500 */ 101, 101, 101, 101, 100, 100, 99, 99, 99, 98, + /* 510 */ 368, 104, 105, 95, 991, 991, 868, 871, 860, 860, + /* 520 */ 102, 102, 103, 103, 103, 103, 435, 233, 233, 466, + /* 530 */ 285, 686, 687, 688, 127, 271, 970, 971, 972, 463, + /* 540 */ 1345, 327, 342, 407, 157, 1012, 988, 13, 13, 181, + /* 550 */ 41, 41, 101, 101, 101, 101, 100, 100, 99, 99, + /* 560 */ 99, 98, 368, 715, 794, 378, 104, 105, 95, 991, + /* 570 */ 991, 868, 871, 860, 860, 102, 102, 103, 103, 103, + /* 580 */ 103, 970, 378, 377, 346, 239, 847, 1086, 1086, 280, + /* 590 */ 1169, 283, 204, 203, 202, 177, 298, 342, 407, 298, + /* 600 */ 715, 840, 169, 299, 407, 839, 82, 101, 101, 101, + /* 610 */ 101, 100, 100, 99, 99, 99, 98, 368, 970, 971, + /* 620 */ 972, 104, 105, 95, 991, 991, 868, 871, 860, 860, + /* 630 */ 102, 102, 103, 103, 103, 103, 839, 839, 841, 362, + /* 640 */ 240, 124, 1169, 172, 126, 378, 1269, 1169, 1066, 342, + /* 650 */ 253, 456, 407, 407, 407, 396, 352, 401, 407, 429, + /* 660 */ 398, 85, 101, 101, 101, 101, 100, 100, 99, 99, + /* 670 */ 99, 98, 368, 104, 105, 95, 991, 991, 868, 871, + /* 680 */ 860, 860, 102, 102, 103, 103, 103, 103, 1169, 466, + /* 690 */ 230, 233, 233, 792, 1235, 1095, 1091, 1293, 1, 77, + /* 700 */ 278, 342, 205, 463, 974, 911, 1040, 348, 353, 911, + /* 710 */ 42, 42, 79, 403, 101, 101, 101, 101, 100, 100, + /* 720 */ 99, 99, 99, 98, 368, 104, 93, 95, 991, 991, + /* 730 */ 868, 871, 860, 860, 102, 102, 103, 103, 103, 103, + /* 740 */ 402, 9, 974, 243, 772, 458, 348, 232, 180, 771, + /* 750 */ 946, 312, 342, 328, 363, 349, 143, 831, 389, 1278, + /* 760 */ 211, 211, 21, 347, 432, 182, 101, 101, 101, 101, + /* 770 */ 100, 100, 99, 99, 99, 98, 368, 105, 95, 991, + /* 780 */ 991, 868, 871, 860, 860, 102, 102, 103, 103, 103, + /* 790 */ 103, 792, 724, 22, 732, 731, 233, 233, 1239, 256, + /* 800 */ 391, 274, 342, 211, 79, 360, 257, 413, 463, 397, + /* 810 */ 207, 288, 260, 450, 79, 1239, 1241, 101, 101, 101, + /* 820 */ 101, 100, 100, 99, 99, 99, 98, 368, 95, 991, + /* 830 */ 991, 868, 871, 860, 860, 102, 102, 103, 103, 103, + /* 840 */ 103, 91, 457, 296, 3, 233, 233, 5, 438, 212, + /* 850 */ 331, 394, 739, 740, 295, 898, 894, 463, 460, 207, + /* 860 */ 801, 1237, 722, 211, 698, 843, 1283, 101, 101, 101, + /* 870 */ 101, 100, 100, 99, 99, 99, 98, 368, 1239, 380, + /* 880 */ 357, 369, 233, 233, 989, 219, 236, 297, 423, 292, + /* 890 */ 422, 206, 454, 898, 463, 970, 91, 457, 290, 3, + /* 900 */ 722, 142, 268, 843, 847, 466, 1258, 149, 388, 425, + /* 910 */ 88, 89, 769, 460, 930, 87, 447, 90, 369, 468, + /* 920 */ 467, 385, 989, 839, 1257, 439, 57, 57, 395, 931, + /* 930 */ 1065, 158, 970, 971, 972, 772, 369, 471, 1019, 399, + /* 940 */ 771, 253, 456, 254, 932, 119, 891, 454, 233, 233, + /* 950 */ 4, 970, 1096, 275, 839, 839, 841, 842, 19, 847, + /* 960 */ 463, 449, 448, 163, 453, 88, 89, 776, 970, 1127, + /* 970 */ 279, 930, 90, 369, 468, 467, 91, 457, 839, 3, + /* 980 */ 235, 1064, 466, 1228, 233, 233, 931, 970, 970, 971, + /* 990 */ 972, 970, 908, 460, 908, 2, 463, 81, 457, 212, + /* 1000 */ 3, 932, 282, 10, 10, 970, 971, 972, 189, 839, + /* 1010 */ 839, 841, 842, 19, 460, 284, 369, 354, 907, 286, + /* 1020 */ 907, 753, 466, 1079, 970, 971, 972, 454, 970, 971, + /* 1030 */ 972, 754, 970, 1063, 989, 372, 792, 369, 1118, 847, + /* 1040 */ 291, 452, 466, 10, 10, 88, 89, 142, 454, 168, + /* 1050 */ 300, 412, 90, 369, 468, 467, 793, 356, 839, 706, + /* 1060 */ 847, 341, 121, 10, 10, 301, 88, 89, 379, 970, + /* 1070 */ 971, 972, 989, 90, 369, 468, 467, 244, 205, 839, + /* 1080 */ 1306, 245, 1135, 245, 250, 1168, 1114, 253, 456, 839, + /* 1090 */ 839, 841, 842, 19, 1125, 237, 122, 451, 1174, 733, + /* 1100 */ 324, 324, 323, 222, 321, 466, 1046, 695, 182, 225, + /* 1110 */ 839, 839, 841, 842, 19, 103, 103, 103, 103, 96, + /* 1120 */ 185, 466, 259, 1039, 1028, 170, 10, 10, 1027, 421, + /* 1130 */ 258, 1029, 1300, 708, 792, 466, 408, 734, 8, 347, + /* 1140 */ 444, 174, 12, 12, 290, 101, 101, 101, 101, 100, + /* 1150 */ 100, 99, 99, 99, 98, 368, 32, 32, 466, 187, + /* 1160 */ 466, 1111, 103, 103, 103, 103, 188, 466, 325, 138, + /* 1170 */ 186, 708, 303, 305, 307, 358, 970, 270, 393, 43, + /* 1180 */ 43, 44, 44, 1157, 333, 178, 418, 294, 45, 45, + /* 1190 */ 1232, 318, 101, 101, 101, 101, 100, 100, 99, 99, + /* 1200 */ 99, 98, 368, 381, 343, 366, 365, 466, 263, 253, + /* 1210 */ 456, 466, 1062, 970, 971, 972, 1231, 997, 309, 466, + /* 1220 */ 455, 466, 427, 466, 995, 173, 996, 1303, 46, 46, + /* 1230 */ 145, 376, 37, 37, 1006, 1277, 466, 214, 1275, 64, + /* 1240 */ 47, 47, 33, 33, 34, 34, 1003, 67, 466, 998, + /* 1250 */ 350, 998, 466, 155, 233, 233, 466, 36, 36, 24, + /* 1260 */ 140, 77, 1154, 466, 383, 466, 463, 428, 466, 48, + /* 1270 */ 48, 466, 147, 49, 49, 466, 150, 50, 50, 466, + /* 1280 */ 151, 152, 466, 384, 11, 11, 51, 51, 466, 110, + /* 1290 */ 110, 153, 52, 52, 411, 466, 38, 38, 466, 191, + /* 1300 */ 53, 53, 466, 54, 54, 466, 400, 466, 330, 39, + /* 1310 */ 39, 466, 1164, 466, 25, 466, 56, 56, 466, 131, + /* 1320 */ 131, 72, 466, 132, 132, 159, 133, 133, 61, 61, + /* 1330 */ 1226, 195, 40, 40, 111, 111, 58, 58, 406, 112, + /* 1340 */ 112, 466, 277, 113, 113, 466, 226, 466, 1246, 466, + /* 1350 */ 197, 466, 164, 466, 409, 466, 198, 466, 199, 466, + /* 1360 */ 335, 281, 109, 109, 466, 1030, 130, 130, 129, 129, + /* 1370 */ 117, 117, 116, 116, 114, 114, 115, 115, 60, 60, + /* 1380 */ 62, 62, 466, 359, 466, 59, 59, 424, 1082, 1081, + /* 1390 */ 1080, 724, 1073, 1054, 336, 293, 1053, 1052, 1315, 431, + /* 1400 */ 361, 76, 248, 31, 31, 35, 35, 1072, 249, 440, + /* 1410 */ 302, 434, 213, 1122, 6, 311, 1212, 107, 83, 251, + /* 1420 */ 78, 1123, 445, 220, 443, 1036, 304, 23, 1121, 469, + /* 1430 */ 965, 221, 223, 1104, 314, 224, 344, 317, 315, 316, + /* 1440 */ 470, 306, 1025, 1120, 308, 1262, 1020, 134, 120, 246, + /* 1450 */ 682, 370, 171, 255, 1263, 135, 184, 1261, 1260, 374, + /* 1460 */ 118, 906, 904, 827, 1050, 146, 136, 137, 148, 1049, + /* 1470 */ 63, 1047, 756, 190, 269, 920, 154, 156, 68, 69, + /* 1480 */ 70, 71, 139, 923, 192, 193, 144, 919, 345, 128, + /* 1490 */ 14, 194, 276, 211, 1000, 405, 196, 161, 912, 160, + /* 1500 */ 26, 697, 410, 295, 200, 289, 414, 162, 419, 73, + /* 1510 */ 15, 16, 141, 74, 28, 247, 846, 845, 735, 874, + /* 1520 */ 954, 75, 430, 955, 29, 433, 179, 229, 231, 800, + /* 1530 */ 165, 795, 87, 210, 889, 79, 875, 17, 873, 877, + /* 1540 */ 929, 18, 928, 216, 215, 878, 20, 30, 462, 844, + /* 1550 */ 707, 92, 766, 770, 7, 322, 217, 218, 319, 1308, + /* 1560 */ 960, 1016, 1016, 1016, 1016, 1307, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 144, 145, 146, 147, 172, 222, 223, 224, 222, 223, - /* 10 */ 224, 152, 180, 152, 148, 149, 152, 173, 176, 19, - /* 20 */ 154, 173, 156, 152, 163, 242, 152, 163, 176, 163, - /* 30 */ 26, 31, 173, 174, 173, 174, 173, 173, 174, 39, - /* 40 */ 1, 2, 152, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 197, 169, - /* 60 */ 170, 197, 188, 197, 222, 223, 224, 208, 209, 208, - /* 70 */ 209, 19, 208, 152, 222, 223, 224, 22, 26, 24, + /* 0 */ 152, 144, 145, 146, 147, 152, 152, 172, 152, 180, + /* 10 */ 181, 152, 223, 224, 225, 180, 152, 164, 189, 19, + /* 20 */ 223, 224, 225, 168, 169, 170, 163, 173, 174, 173, + /* 30 */ 174, 31, 173, 174, 168, 169, 170, 173, 174, 39, + /* 40 */ 243, 191, 192, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 195, 196, + /* 60 */ 22, 23, 208, 209, 208, 209, 218, 208, 209, 176, + /* 70 */ 207, 19, 208, 209, 23, 212, 213, 26, 26, 152, /* 80 */ 46, 47, 48, 49, 84, 85, 86, 87, 88, 89, /* 90 */ 90, 91, 92, 93, 94, 43, 44, 45, 46, 47, /* 100 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 110 */ 106, 245, 157, 88, 89, 90, 91, 92, 93, 94, - /* 120 */ 68, 231, 251, 19, 84, 85, 86, 87, 88, 89, + /* 110 */ 90, 91, 92, 93, 94, 188, 223, 224, 225, 171, + /* 120 */ 68, 83, 152, 19, 84, 85, 86, 87, 88, 89, /* 130 */ 90, 91, 92, 93, 94, 101, 84, 85, 86, 87, /* 140 */ 88, 89, 90, 91, 92, 93, 94, 43, 44, 45, /* 150 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 160 */ 56, 57, 90, 91, 92, 93, 94, 82, 79, 99, - /* 170 */ 66, 200, 102, 103, 104, 19, 168, 169, 170, 152, - /* 180 */ 24, 210, 97, 113, 229, 59, 101, 232, 84, 85, + /* 160 */ 56, 57, 99, 94, 194, 102, 103, 104, 109, 110, + /* 170 */ 66, 223, 224, 225, 152, 19, 113, 22, 23, 152, + /* 180 */ 24, 26, 160, 1, 2, 59, 164, 173, 84, 85, /* 190 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 43, /* 200 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 210 */ 54, 55, 56, 57, 152, 188, 152, 132, 133, 134, - /* 220 */ 93, 94, 96, 97, 98, 99, 152, 19, 102, 103, - /* 230 */ 104, 23, 94, 72, 79, 173, 174, 173, 174, 113, + /* 210 */ 54, 55, 56, 57, 244, 88, 89, 90, 91, 92, + /* 220 */ 93, 94, 96, 97, 98, 99, 196, 19, 102, 103, + /* 230 */ 104, 23, 88, 89, 173, 59, 163, 207, 83, 113, /* 240 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, /* 250 */ 94, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 260 */ 52, 53, 54, 55, 56, 57, 152, 171, 152, 108, - /* 270 */ 109, 110, 208, 209, 119, 120, 152, 180, 181, 19, - /* 280 */ 119, 120, 152, 23, 152, 152, 189, 173, 174, 173, - /* 290 */ 174, 59, 84, 85, 86, 87, 88, 89, 90, 91, + /* 260 */ 52, 53, 54, 55, 56, 57, 90, 240, 195, 196, + /* 270 */ 171, 82, 96, 97, 98, 152, 132, 176, 134, 19, + /* 280 */ 207, 200, 72, 23, 93, 94, 97, 152, 173, 79, + /* 290 */ 101, 210, 84, 85, 86, 87, 88, 89, 90, 91, /* 300 */ 92, 93, 94, 43, 44, 45, 46, 47, 48, 49, - /* 310 */ 50, 51, 52, 53, 54, 55, 56, 57, 222, 223, - /* 320 */ 224, 186, 208, 209, 208, 209, 194, 194, 96, 97, - /* 330 */ 98, 19, 168, 169, 170, 23, 88, 89, 163, 59, - /* 340 */ 0, 1, 2, 219, 84, 85, 86, 87, 88, 89, + /* 310 */ 50, 51, 52, 53, 54, 55, 56, 57, 108, 152, + /* 320 */ 152, 132, 133, 134, 223, 224, 225, 152, 186, 119, + /* 330 */ 120, 19, 197, 234, 31, 23, 26, 152, 239, 59, + /* 340 */ 173, 174, 39, 220, 84, 85, 86, 87, 88, 89, /* 350 */ 90, 91, 92, 93, 94, 43, 44, 45, 46, 47, /* 360 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 370 */ 90, 152, 197, 195, 196, 243, 96, 97, 98, 196, - /* 380 */ 132, 11, 134, 19, 182, 207, 115, 23, 117, 118, - /* 390 */ 207, 163, 173, 174, 152, 220, 84, 85, 86, 87, + /* 370 */ 152, 22, 152, 16, 152, 208, 96, 97, 98, 194, + /* 380 */ 22, 23, 11, 19, 26, 79, 72, 23, 0, 1, + /* 390 */ 2, 173, 174, 173, 174, 220, 84, 85, 86, 87, /* 400 */ 88, 89, 90, 91, 92, 93, 94, 43, 44, 45, /* 410 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 420 */ 56, 57, 152, 16, 152, 197, 171, 208, 22, 23, - /* 430 */ 22, 23, 26, 16, 26, 19, 7, 8, 9, 23, - /* 440 */ 212, 213, 152, 173, 174, 173, 174, 19, 84, 85, + /* 420 */ 56, 57, 108, 109, 110, 119, 120, 152, 208, 119, + /* 430 */ 120, 137, 75, 139, 77, 19, 152, 88, 89, 23, + /* 440 */ 115, 83, 117, 118, 163, 227, 163, 152, 84, 85, /* 450 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 43, /* 460 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 470 */ 54, 55, 56, 57, 46, 152, 109, 110, 208, 109, - /* 480 */ 110, 152, 75, 152, 77, 22, 23, 19, 233, 83, - /* 490 */ 152, 83, 75, 238, 77, 164, 173, 174, 226, 59, + /* 470 */ 54, 55, 56, 57, 195, 196, 195, 196, 195, 196, + /* 480 */ 109, 110, 195, 196, 22, 23, 207, 19, 207, 152, + /* 490 */ 207, 108, 109, 110, 207, 163, 22, 140, 24, 59, /* 500 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, /* 510 */ 94, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 520 */ 52, 53, 54, 55, 56, 57, 108, 109, 110, 152, - /* 530 */ 152, 152, 22, 22, 23, 107, 96, 97, 98, 160, - /* 540 */ 112, 251, 19, 164, 22, 152, 83, 140, 219, 152, + /* 520 */ 52, 53, 54, 55, 56, 57, 152, 195, 196, 152, + /* 530 */ 16, 7, 8, 9, 197, 240, 96, 97, 98, 207, + /* 540 */ 249, 250, 19, 152, 22, 83, 26, 173, 174, 152, /* 550 */ 173, 174, 84, 85, 86, 87, 88, 89, 90, 91, /* 560 */ 92, 93, 94, 59, 124, 152, 43, 44, 45, 46, /* 570 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 580 */ 57, 59, 169, 170, 119, 120, 82, 108, 109, 110, - /* 590 */ 191, 192, 191, 192, 83, 248, 249, 19, 88, 89, + /* 580 */ 57, 59, 169, 170, 157, 194, 82, 191, 192, 75, + /* 590 */ 152, 77, 108, 109, 110, 26, 152, 19, 152, 152, /* 600 */ 96, 97, 24, 152, 152, 101, 138, 84, 85, 86, /* 610 */ 87, 88, 89, 90, 91, 92, 93, 94, 96, 97, /* 620 */ 98, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 630 */ 52, 53, 54, 55, 56, 57, 132, 133, 134, 152, - /* 640 */ 152, 46, 152, 26, 231, 194, 194, 146, 147, 19, - /* 650 */ 152, 116, 152, 72, 152, 152, 121, 152, 152, 152, - /* 660 */ 79, 138, 84, 85, 86, 87, 88, 89, 90, 91, + /* 630 */ 52, 53, 54, 55, 56, 57, 132, 133, 134, 188, + /* 640 */ 194, 197, 152, 123, 197, 232, 194, 152, 182, 19, + /* 650 */ 119, 120, 152, 152, 152, 152, 218, 230, 152, 163, + /* 660 */ 233, 138, 84, 85, 86, 87, 88, 89, 90, 91, /* 670 */ 92, 93, 94, 43, 44, 45, 46, 47, 48, 49, - /* 680 */ 50, 51, 52, 53, 54, 55, 56, 57, 152, 108, - /* 690 */ 23, 152, 194, 26, 194, 152, 194, 194, 137, 152, - /* 700 */ 139, 19, 107, 166, 167, 218, 218, 29, 218, 173, - /* 710 */ 174, 33, 173, 174, 84, 85, 86, 87, 88, 89, + /* 680 */ 50, 51, 52, 53, 54, 55, 56, 57, 152, 152, + /* 690 */ 23, 195, 196, 26, 194, 194, 194, 146, 147, 130, + /* 700 */ 194, 19, 46, 207, 59, 29, 166, 167, 218, 33, + /* 710 */ 173, 174, 26, 218, 84, 85, 86, 87, 88, 89, /* 720 */ 90, 91, 92, 93, 94, 43, 44, 45, 46, 47, /* 730 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 740 */ 152, 194, 64, 171, 239, 239, 239, 130, 166, 167, - /* 750 */ 212, 213, 19, 23, 246, 247, 26, 59, 26, 152, - /* 760 */ 163, 218, 163, 163, 163, 98, 84, 85, 86, 87, + /* 740 */ 64, 171, 97, 240, 116, 166, 167, 212, 213, 121, + /* 750 */ 23, 152, 19, 26, 218, 247, 248, 23, 23, 152, + /* 760 */ 26, 26, 22, 107, 163, 98, 84, 85, 86, 87, /* 770 */ 88, 89, 90, 91, 92, 93, 94, 44, 45, 46, /* 780 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 790 */ 57, 124, 26, 79, 197, 97, 197, 197, 197, 31, - /* 800 */ 152, 23, 19, 19, 26, 19, 218, 39, 23, 21, - /* 810 */ 238, 26, 163, 163, 100, 59, 163, 84, 85, 86, + /* 790 */ 57, 124, 106, 53, 100, 101, 195, 196, 152, 152, + /* 800 */ 23, 23, 19, 26, 26, 19, 152, 23, 207, 239, + /* 810 */ 26, 23, 152, 163, 26, 169, 170, 84, 85, 86, /* 820 */ 87, 88, 89, 90, 91, 92, 93, 94, 45, 46, /* 830 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 840 */ 57, 19, 20, 152, 22, 23, 197, 197, 23, 19, - /* 850 */ 197, 26, 152, 97, 23, 123, 23, 26, 36, 26, - /* 860 */ 152, 152, 19, 20, 98, 22, 78, 84, 85, 86, - /* 870 */ 87, 88, 89, 90, 91, 92, 93, 94, 94, 36, - /* 880 */ 152, 59, 96, 99, 100, 101, 102, 103, 104, 105, - /* 890 */ 124, 103, 70, 100, 101, 23, 112, 12, 26, 12, - /* 900 */ 23, 79, 59, 26, 82, 101, 22, 7, 8, 152, - /* 910 */ 88, 89, 27, 70, 27, 152, 112, 95, 96, 97, - /* 920 */ 98, 152, 79, 101, 22, 82, 96, 42, 140, 42, - /* 930 */ 182, 88, 89, 19, 20, 132, 22, 134, 95, 96, - /* 940 */ 97, 98, 23, 59, 101, 26, 22, 62, 152, 62, - /* 950 */ 36, 22, 59, 24, 132, 133, 134, 135, 136, 72, - /* 960 */ 5, 59, 152, 22, 71, 10, 11, 12, 13, 14, - /* 970 */ 152, 152, 17, 59, 182, 132, 133, 134, 135, 136, - /* 980 */ 96, 97, 98, 59, 70, 30, 152, 32, 59, 96, - /* 990 */ 97, 98, 173, 174, 53, 40, 82, 152, 96, 97, - /* 1000 */ 98, 90, 88, 89, 90, 24, 187, 173, 174, 95, - /* 1010 */ 96, 97, 98, 152, 152, 101, 19, 20, 152, 22, - /* 1020 */ 96, 97, 98, 152, 69, 59, 97, 116, 59, 214, - /* 1030 */ 152, 76, 121, 36, 79, 80, 19, 20, 152, 22, - /* 1040 */ 59, 132, 59, 134, 173, 174, 132, 133, 134, 135, - /* 1050 */ 136, 173, 174, 36, 234, 152, 59, 152, 187, 173, - /* 1060 */ 174, 211, 96, 97, 98, 187, 97, 70, 23, 114, - /* 1070 */ 152, 26, 23, 187, 23, 26, 59, 26, 97, 82, - /* 1080 */ 97, 22, 164, 152, 152, 88, 89, 70, 192, 152, - /* 1090 */ 124, 152, 95, 96, 97, 98, 141, 152, 101, 82, - /* 1100 */ 152, 152, 184, 152, 186, 88, 89, 199, 54, 55, - /* 1110 */ 56, 57, 95, 96, 97, 98, 152, 152, 101, 152, - /* 1120 */ 152, 173, 174, 235, 54, 55, 56, 57, 58, 132, - /* 1130 */ 133, 134, 135, 136, 211, 150, 59, 211, 84, 85, - /* 1140 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 132, - /* 1150 */ 133, 134, 135, 136, 84, 85, 86, 87, 88, 89, - /* 1160 */ 90, 91, 92, 93, 94, 88, 89, 195, 196, 35, - /* 1170 */ 211, 211, 152, 96, 97, 98, 152, 100, 198, 207, - /* 1180 */ 171, 122, 240, 152, 107, 215, 109, 240, 202, 215, - /* 1190 */ 202, 152, 220, 173, 174, 177, 185, 173, 174, 65, - /* 1200 */ 152, 176, 152, 181, 173, 174, 215, 73, 176, 132, - /* 1210 */ 228, 134, 173, 174, 176, 152, 201, 152, 199, 155, - /* 1220 */ 152, 173, 174, 173, 174, 60, 152, 122, 244, 38, - /* 1230 */ 159, 159, 152, 241, 241, 152, 173, 174, 173, 174, - /* 1240 */ 152, 173, 174, 152, 159, 111, 152, 173, 174, 152, - /* 1250 */ 22, 152, 43, 173, 174, 152, 173, 174, 152, 190, - /* 1260 */ 130, 173, 174, 152, 173, 174, 152, 173, 174, 202, - /* 1270 */ 173, 174, 173, 174, 152, 18, 173, 174, 152, 173, - /* 1280 */ 174, 152, 221, 152, 173, 174, 193, 173, 174, 152, - /* 1290 */ 158, 152, 159, 152, 22, 173, 174, 152, 18, 173, - /* 1300 */ 174, 152, 173, 174, 173, 174, 152, 221, 152, 193, - /* 1310 */ 173, 174, 173, 174, 173, 174, 152, 193, 173, 174, - /* 1320 */ 152, 193, 173, 174, 158, 152, 190, 173, 174, 173, - /* 1330 */ 174, 59, 152, 190, 159, 137, 152, 173, 174, 152, - /* 1340 */ 202, 173, 174, 61, 152, 237, 173, 174, 202, 236, - /* 1350 */ 22, 107, 159, 173, 174, 159, 178, 173, 174, 158, - /* 1360 */ 173, 174, 159, 158, 158, 173, 174, 159, 178, 97, - /* 1370 */ 175, 63, 175, 106, 175, 125, 217, 183, 178, 175, - /* 1380 */ 107, 159, 22, 216, 177, 175, 217, 175, 175, 217, - /* 1390 */ 216, 159, 216, 216, 183, 225, 217, 137, 227, 178, - /* 1400 */ 94, 178, 126, 129, 25, 128, 127, 162, 206, 26, - /* 1410 */ 161, 230, 230, 13, 153, 153, 6, 151, 203, 205, - /* 1420 */ 204, 202, 151, 165, 171, 171, 151, 171, 165, 165, - /* 1430 */ 171, 179, 4, 250, 179, 250, 247, 3, 22, 171, - /* 1440 */ 171, 171, 142, 81, 15, 23, 16, 23, 131, 171, - /* 1450 */ 120, 111, 123, 20, 16, 1, 125, 123, 131, 53, - /* 1460 */ 53, 53, 53, 111, 96, 34, 122, 1, 5, 22, - /* 1470 */ 107, 67, 140, 74, 26, 24, 41, 107, 67, 20, - /* 1480 */ 19, 112, 105, 66, 22, 66, 22, 22, 28, 66, - /* 1490 */ 23, 22, 22, 37, 23, 23, 23, 116, 22, 22, - /* 1500 */ 26, 23, 122, 23, 23, 26, 22, 11, 96, 34, - /* 1510 */ 124, 26, 26, 23, 23, 23, 34, 34, 23, 26, - /* 1520 */ 23, 22, 24, 23, 22, 122, 23, 26, 22, 24, - /* 1530 */ 23, 22, 15, 23, 23, 22, 122, 122, 23, 1, - /* 1540 */ 122, 252, 252, 252, 252, 252, 252, 252, 252, 252, - /* 1550 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - /* 1560 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + /* 840 */ 57, 19, 20, 101, 22, 195, 196, 22, 19, 24, + /* 850 */ 163, 19, 7, 8, 112, 59, 23, 207, 36, 26, + /* 860 */ 23, 152, 59, 26, 21, 59, 152, 84, 85, 86, + /* 870 */ 87, 88, 89, 90, 91, 92, 93, 94, 232, 221, + /* 880 */ 94, 59, 195, 196, 59, 99, 100, 101, 102, 103, + /* 890 */ 104, 105, 70, 97, 207, 59, 19, 20, 112, 22, + /* 900 */ 97, 79, 152, 97, 82, 152, 152, 71, 221, 90, + /* 910 */ 88, 89, 23, 36, 12, 26, 163, 95, 96, 97, + /* 920 */ 98, 78, 97, 101, 152, 96, 173, 174, 96, 27, + /* 930 */ 182, 22, 96, 97, 98, 116, 59, 148, 149, 152, + /* 940 */ 121, 119, 120, 154, 42, 156, 103, 70, 195, 196, + /* 950 */ 22, 59, 163, 152, 132, 133, 134, 135, 136, 82, + /* 960 */ 207, 208, 209, 71, 62, 88, 89, 90, 59, 152, + /* 970 */ 152, 12, 95, 96, 97, 98, 19, 20, 101, 22, + /* 980 */ 22, 182, 152, 140, 195, 196, 27, 59, 96, 97, + /* 990 */ 98, 59, 132, 36, 134, 22, 207, 19, 20, 24, + /* 1000 */ 22, 42, 152, 173, 174, 96, 97, 98, 219, 132, + /* 1010 */ 133, 134, 135, 136, 36, 152, 59, 187, 132, 152, + /* 1020 */ 134, 62, 152, 152, 96, 97, 98, 70, 96, 97, + /* 1030 */ 98, 72, 59, 152, 59, 246, 26, 59, 214, 82, + /* 1040 */ 152, 192, 152, 173, 174, 88, 89, 79, 70, 152, + /* 1050 */ 152, 19, 95, 96, 97, 98, 124, 187, 101, 23, + /* 1060 */ 82, 164, 26, 173, 174, 152, 88, 89, 100, 96, + /* 1070 */ 97, 98, 97, 95, 96, 97, 98, 187, 46, 101, + /* 1080 */ 122, 184, 152, 186, 211, 152, 152, 119, 120, 132, + /* 1090 */ 133, 134, 135, 136, 152, 5, 22, 152, 152, 35, + /* 1100 */ 10, 11, 12, 13, 14, 152, 152, 17, 98, 235, + /* 1110 */ 132, 133, 134, 135, 136, 54, 55, 56, 57, 58, + /* 1120 */ 30, 152, 32, 152, 152, 198, 173, 174, 152, 65, + /* 1130 */ 40, 152, 152, 59, 124, 152, 236, 73, 199, 107, + /* 1140 */ 187, 171, 173, 174, 112, 84, 85, 86, 87, 88, + /* 1150 */ 89, 90, 91, 92, 93, 94, 173, 174, 152, 69, + /* 1160 */ 152, 211, 54, 55, 56, 57, 76, 152, 150, 79, + /* 1170 */ 80, 97, 211, 211, 211, 111, 59, 241, 241, 173, + /* 1180 */ 174, 173, 174, 202, 202, 185, 177, 176, 173, 174, + /* 1190 */ 176, 201, 84, 85, 86, 87, 88, 89, 90, 91, + /* 1200 */ 92, 93, 94, 215, 114, 88, 89, 152, 215, 119, + /* 1210 */ 120, 152, 181, 96, 97, 98, 176, 100, 215, 152, + /* 1220 */ 229, 152, 163, 152, 107, 199, 109, 155, 173, 174, + /* 1230 */ 245, 141, 173, 174, 60, 159, 152, 122, 159, 242, + /* 1240 */ 173, 174, 173, 174, 173, 174, 38, 242, 152, 132, + /* 1250 */ 159, 134, 152, 22, 195, 196, 152, 173, 174, 222, + /* 1260 */ 43, 130, 202, 152, 18, 152, 207, 208, 152, 173, + /* 1270 */ 174, 152, 190, 173, 174, 152, 193, 173, 174, 152, + /* 1280 */ 193, 193, 152, 159, 173, 174, 173, 174, 152, 173, + /* 1290 */ 174, 193, 173, 174, 18, 152, 173, 174, 152, 158, + /* 1300 */ 173, 174, 152, 173, 174, 152, 159, 152, 202, 173, + /* 1310 */ 174, 152, 190, 152, 222, 152, 173, 174, 152, 173, + /* 1320 */ 174, 137, 152, 173, 174, 190, 173, 174, 173, 174, + /* 1330 */ 202, 158, 173, 174, 173, 174, 173, 174, 61, 173, + /* 1340 */ 174, 152, 237, 173, 174, 152, 159, 152, 238, 152, + /* 1350 */ 158, 152, 22, 152, 178, 152, 158, 152, 158, 152, + /* 1360 */ 178, 159, 173, 174, 152, 159, 173, 174, 173, 174, + /* 1370 */ 173, 174, 173, 174, 173, 174, 173, 174, 173, 174, + /* 1380 */ 173, 174, 152, 63, 152, 173, 174, 107, 175, 175, + /* 1390 */ 175, 106, 183, 175, 178, 175, 177, 175, 175, 178, + /* 1400 */ 94, 107, 231, 173, 174, 173, 174, 183, 231, 125, + /* 1410 */ 216, 178, 159, 217, 22, 159, 226, 129, 137, 228, + /* 1420 */ 128, 217, 126, 25, 127, 162, 216, 26, 217, 161, + /* 1430 */ 13, 153, 153, 206, 205, 6, 251, 202, 204, 203, + /* 1440 */ 151, 216, 151, 217, 216, 171, 151, 165, 179, 179, + /* 1450 */ 4, 3, 22, 142, 171, 165, 15, 171, 171, 81, + /* 1460 */ 16, 23, 23, 120, 171, 131, 165, 111, 123, 171, + /* 1470 */ 171, 171, 20, 125, 16, 1, 123, 131, 53, 53, + /* 1480 */ 53, 53, 111, 96, 34, 122, 248, 1, 251, 5, + /* 1490 */ 22, 107, 140, 26, 74, 41, 122, 107, 67, 67, + /* 1500 */ 24, 20, 19, 112, 105, 23, 66, 22, 66, 22, + /* 1510 */ 22, 22, 37, 22, 22, 66, 23, 23, 28, 23, + /* 1520 */ 23, 26, 24, 23, 22, 24, 122, 23, 23, 96, + /* 1530 */ 22, 124, 26, 34, 23, 26, 23, 34, 23, 23, + /* 1540 */ 23, 34, 23, 22, 26, 11, 22, 22, 26, 23, + /* 1550 */ 23, 22, 116, 23, 22, 15, 122, 122, 23, 122, + /* 1560 */ 1, 252, 252, 252, 252, 122, 252, 252, 252, 252, /* 1570 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, /* 1580 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, /* 1590 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, @@ -138947,147 +140617,152 @@ /* 1650 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, /* 1660 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, /* 1670 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - /* 1680 */ 252, 252, 252, 252, + /* 1680 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + /* 1690 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + /* 1700 */ 252, 252, 252, 252, 252, 252, 252, 252, 252, }; -#define YY_SHIFT_COUNT (465) +#define YY_SHIFT_COUNT (471) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (1538) +#define YY_SHIFT_MAX (1559) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 39, 822, 955, 843, 997, 997, 997, 997, 0, 0, - /* 10 */ 104, 630, 997, 997, 997, 997, 997, 997, 997, 1077, - /* 20 */ 1077, 126, 161, 155, 52, 156, 208, 260, 312, 364, - /* 30 */ 416, 468, 523, 578, 630, 630, 630, 630, 630, 630, - /* 40 */ 630, 630, 630, 630, 630, 630, 630, 630, 630, 630, - /* 50 */ 630, 682, 630, 733, 783, 783, 914, 997, 997, 997, - /* 60 */ 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, - /* 70 */ 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, - /* 80 */ 997, 997, 997, 997, 997, 997, 997, 997, 1017, 997, - /* 90 */ 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, - /* 100 */ 997, 997, 1070, 1054, 1054, 1054, 1054, 1054, 40, 25, - /* 110 */ 72, 232, 788, 428, 248, 248, 232, 581, 367, 127, - /* 120 */ 465, 138, 1541, 1541, 1541, 784, 784, 784, 522, 522, - /* 130 */ 887, 887, 893, 406, 408, 232, 232, 232, 232, 232, - /* 140 */ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, - /* 150 */ 232, 232, 232, 232, 232, 370, 340, 714, 698, 698, - /* 160 */ 465, 89, 89, 89, 89, 89, 89, 1541, 1541, 1541, - /* 170 */ 504, 85, 85, 884, 70, 280, 902, 440, 966, 924, - /* 180 */ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, - /* 190 */ 232, 232, 232, 232, 232, 232, 1134, 1134, 1134, 232, - /* 200 */ 232, 667, 232, 232, 232, 929, 232, 232, 885, 232, - /* 210 */ 232, 232, 232, 232, 232, 232, 232, 232, 232, 418, - /* 220 */ 678, 981, 981, 981, 981, 766, 271, 911, 510, 429, - /* 230 */ 617, 786, 786, 830, 617, 830, 4, 730, 595, 768, - /* 240 */ 786, 561, 768, 768, 732, 535, 55, 1165, 1105, 1105, - /* 250 */ 1191, 1191, 1105, 1228, 1209, 1130, 1257, 1257, 1257, 1257, - /* 260 */ 1105, 1280, 1130, 1228, 1209, 1209, 1130, 1105, 1280, 1198, - /* 270 */ 1282, 1105, 1105, 1280, 1328, 1105, 1280, 1105, 1280, 1328, - /* 280 */ 1244, 1244, 1244, 1308, 1328, 1244, 1267, 1244, 1308, 1244, - /* 290 */ 1244, 1250, 1273, 1250, 1273, 1250, 1273, 1250, 1273, 1105, - /* 300 */ 1360, 1105, 1260, 1328, 1306, 1306, 1328, 1274, 1276, 1277, - /* 310 */ 1279, 1130, 1379, 1383, 1400, 1400, 1410, 1410, 1410, 1541, - /* 320 */ 1541, 1541, 1541, 1541, 1541, 1541, 1541, 1541, 1541, 1541, - /* 330 */ 1541, 1541, 1541, 1541, 1541, 34, 407, 463, 511, 417, - /* 340 */ 479, 1272, 778, 941, 785, 825, 831, 833, 872, 877, - /* 350 */ 756, 793, 900, 804, 919, 1045, 969, 1049, 803, 909, - /* 360 */ 1051, 983, 1059, 1428, 1434, 1416, 1300, 1429, 1362, 1430, - /* 370 */ 1422, 1424, 1330, 1317, 1340, 1329, 1433, 1331, 1438, 1454, - /* 380 */ 1334, 1327, 1406, 1407, 1408, 1409, 1352, 1368, 1431, 1344, - /* 390 */ 1466, 1463, 1447, 1363, 1332, 1404, 1448, 1411, 1399, 1435, - /* 400 */ 1370, 1451, 1459, 1461, 1369, 1377, 1462, 1417, 1464, 1465, - /* 410 */ 1467, 1469, 1419, 1460, 1470, 1423, 1456, 1471, 1472, 1473, - /* 420 */ 1474, 1381, 1476, 1478, 1477, 1479, 1380, 1480, 1481, 1412, - /* 430 */ 1475, 1484, 1386, 1485, 1482, 1486, 1483, 1490, 1485, 1491, - /* 440 */ 1492, 1495, 1493, 1497, 1499, 1496, 1500, 1502, 1498, 1501, - /* 450 */ 1503, 1506, 1505, 1501, 1507, 1509, 1510, 1511, 1513, 1403, - /* 460 */ 1414, 1415, 1418, 1515, 1517, 1538, + /* 0 */ 182, 1090, 822, 822, 306, 957, 957, 957, 957, 210, + /* 10 */ 0, 0, 104, 630, 957, 957, 957, 957, 957, 957, + /* 20 */ 957, 1117, 1117, 126, 968, 306, 306, 306, 306, 306, + /* 30 */ 306, 52, 156, 208, 260, 312, 364, 416, 468, 523, + /* 40 */ 578, 630, 630, 630, 630, 630, 630, 630, 630, 630, + /* 50 */ 630, 630, 630, 630, 630, 630, 630, 630, 682, 630, + /* 60 */ 733, 783, 783, 877, 957, 957, 957, 957, 957, 957, + /* 70 */ 957, 957, 957, 957, 957, 957, 957, 957, 957, 957, + /* 80 */ 957, 957, 957, 957, 957, 957, 957, 957, 957, 957, + /* 90 */ 957, 957, 957, 957, 957, 978, 957, 957, 957, 957, + /* 100 */ 957, 957, 957, 957, 957, 957, 957, 957, 957, 1061, + /* 110 */ 1108, 1108, 1108, 1108, 1108, 40, 127, 20, 280, 843, + /* 120 */ 1032, 144, 144, 280, 310, 310, 310, 310, 59, 191, + /* 130 */ 69, 1566, 1566, 1566, 786, 786, 786, 522, 836, 522, + /* 140 */ 959, 959, 892, 155, 358, 280, 280, 280, 280, 280, + /* 150 */ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + /* 160 */ 280, 280, 280, 280, 280, 280, 371, 388, 645, 645, + /* 170 */ 531, 1566, 1566, 1566, 504, 189, 189, 909, 63, 176, + /* 180 */ 928, 440, 932, 973, 280, 280, 280, 280, 280, 314, + /* 190 */ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + /* 200 */ 280, 280, 1064, 1064, 1064, 280, 280, 280, 280, 667, + /* 210 */ 280, 280, 280, 825, 280, 280, 902, 280, 280, 280, + /* 220 */ 280, 280, 280, 280, 280, 383, 676, 325, 975, 975, + /* 230 */ 975, 975, 1010, 325, 325, 819, 349, 524, 569, 829, + /* 240 */ 829, 832, 569, 832, 686, 51, 656, 303, 303, 303, + /* 250 */ 829, 294, 520, 628, 474, 1174, 1115, 1115, 1208, 1208, + /* 260 */ 1115, 1231, 1217, 1131, 1246, 1246, 1246, 1246, 1115, 1276, + /* 270 */ 1131, 1231, 1217, 1217, 1131, 1115, 1276, 1184, 1277, 1115, + /* 280 */ 1276, 1330, 1115, 1276, 1115, 1276, 1330, 1280, 1280, 1280, + /* 290 */ 1320, 1330, 1280, 1285, 1280, 1320, 1280, 1280, 1330, 1306, + /* 300 */ 1306, 1330, 1284, 1294, 1284, 1294, 1284, 1294, 1284, 1294, + /* 310 */ 1115, 1392, 1115, 1281, 1288, 1296, 1292, 1297, 1131, 1398, + /* 320 */ 1401, 1417, 1417, 1429, 1429, 1429, 1566, 1566, 1566, 1566, + /* 330 */ 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, + /* 340 */ 1566, 1566, 34, 357, 38, 462, 514, 484, 1074, 727, + /* 350 */ 740, 734, 735, 777, 778, 784, 788, 803, 694, 845, + /* 360 */ 742, 796, 833, 837, 889, 860, 886, 1036, 806, 958, + /* 370 */ 1446, 1448, 1430, 1311, 1441, 1378, 1444, 1438, 1439, 1343, + /* 380 */ 1334, 1356, 1345, 1452, 1348, 1458, 1474, 1353, 1346, 1425, + /* 390 */ 1426, 1427, 1428, 1371, 1387, 1450, 1363, 1486, 1484, 1468, + /* 400 */ 1384, 1352, 1431, 1467, 1432, 1420, 1454, 1374, 1390, 1476, + /* 410 */ 1481, 1483, 1391, 1399, 1485, 1440, 1487, 1488, 1482, 1489, + /* 420 */ 1442, 1490, 1491, 1449, 1475, 1493, 1494, 1496, 1495, 1497, + /* 430 */ 1492, 1498, 1500, 1502, 1501, 1404, 1504, 1505, 1433, 1499, + /* 440 */ 1508, 1407, 1506, 1503, 1509, 1507, 1511, 1513, 1515, 1506, + /* 450 */ 1516, 1517, 1518, 1519, 1521, 1534, 1524, 1525, 1526, 1527, + /* 460 */ 1529, 1530, 1532, 1522, 1436, 1434, 1435, 1437, 1443, 1535, + /* 470 */ 1540, 1559, }; -#define YY_REDUCE_COUNT (334) -#define YY_REDUCE_MIN (-217) -#define YY_REDUCE_MAX (1278) +#define YY_REDUCE_COUNT (341) +#define YY_REDUCE_MIN (-211) +#define YY_REDUCE_MAX (1301) static const short yy_reduce_ofst[] = { - /* 0 */ -144, -139, -134, -136, -141, 64, 114, 116, -158, -148, - /* 10 */ -217, 96, 819, 871, 878, 219, 270, 886, 272, -110, - /* 20 */ 413, 918, 972, 228, -214, -214, -214, -214, -214, -214, - /* 30 */ -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - /* 40 */ -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - /* 50 */ -214, -214, -214, -214, -214, -214, 62, 323, 377, 536, - /* 60 */ 539, 834, 948, 1020, 1024, 1031, 1039, 1048, 1050, 1063, - /* 70 */ 1065, 1068, 1074, 1080, 1083, 1088, 1091, 1094, 1097, 1099, - /* 80 */ 1103, 1106, 1111, 1114, 1122, 1126, 1129, 1131, 1137, 1139, - /* 90 */ 1141, 1145, 1149, 1154, 1156, 1164, 1168, 1173, 1180, 1184, - /* 100 */ 1187, 1192, -214, -214, -214, -214, -214, -214, -214, -214, - /* 110 */ -214, 132, -45, 97, 8, 164, 379, 175, 255, -214, - /* 120 */ 178, -214, -214, -214, -214, -168, -168, -168, 124, 329, - /* 130 */ 399, 401, -129, 347, 347, 331, 133, 451, 452, 498, - /* 140 */ 500, 502, 503, 505, 487, 506, 488, 490, 507, 543, - /* 150 */ 547, -126, 588, 290, 27, 572, 501, 597, 537, 582, - /* 160 */ 183, 599, 600, 601, 649, 650, 653, 508, 538, -29, - /* 170 */ -156, -152, -137, -79, 135, 74, 130, 242, 338, 378, - /* 180 */ 393, 397, 607, 648, 691, 700, 708, 709, 728, 757, - /* 190 */ 763, 769, 796, 810, 818, 845, 202, 748, 792, 861, - /* 200 */ 862, 815, 866, 903, 905, 850, 931, 932, 896, 937, - /* 210 */ 939, 945, 74, 949, 951, 964, 965, 967, 968, 888, - /* 220 */ 820, 923, 926, 959, 960, 815, 980, 908, 1009, 985, - /* 230 */ 986, 970, 974, 942, 988, 947, 1018, 1011, 1022, 1025, - /* 240 */ 991, 982, 1032, 1038, 1015, 1019, 1064, 984, 1071, 1072, - /* 250 */ 992, 993, 1085, 1061, 1069, 1067, 1093, 1116, 1124, 1128, - /* 260 */ 1133, 1132, 1138, 1086, 1136, 1143, 1146, 1175, 1166, 1108, - /* 270 */ 1113, 1193, 1196, 1201, 1178, 1203, 1205, 1208, 1206, 1190, - /* 280 */ 1195, 1197, 1199, 1194, 1200, 1204, 1207, 1210, 1211, 1212, - /* 290 */ 1213, 1159, 1167, 1169, 1174, 1172, 1176, 1179, 1177, 1222, - /* 300 */ 1170, 1232, 1171, 1221, 1181, 1182, 1223, 1202, 1214, 1216, - /* 310 */ 1215, 1219, 1245, 1249, 1261, 1262, 1266, 1271, 1275, 1183, - /* 320 */ 1185, 1189, 1258, 1253, 1254, 1256, 1259, 1263, 1252, 1255, - /* 330 */ 1268, 1269, 1270, 1278, 1264, + /* 0 */ -143, 789, 753, 1059, -137, -146, -144, -141, -136, 687, + /* 10 */ -107, 101, -203, -52, 830, 870, 890, 167, 953, 218, + /* 20 */ 220, 413, 646, 897, 73, 281, 283, 332, 496, 601, + /* 30 */ 650, -211, -211, -211, -211, -211, -211, -211, -211, -211, + /* 40 */ -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + /* 50 */ -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + /* 60 */ -211, -211, -211, 374, 377, 537, 969, 983, 1006, 1008, + /* 70 */ 1015, 1055, 1067, 1069, 1071, 1084, 1096, 1100, 1104, 1111, + /* 80 */ 1113, 1116, 1119, 1123, 1127, 1130, 1136, 1143, 1146, 1150, + /* 90 */ 1153, 1155, 1159, 1161, 1163, 1166, 1170, 1189, 1193, 1195, + /* 100 */ 1197, 1199, 1201, 1203, 1205, 1207, 1212, 1230, 1232, -211, + /* 110 */ -211, -211, -211, -211, -211, -211, -211, -211, -30, 427, + /* 120 */ -171, -145, -134, 22, 279, 287, 279, 287, 99, -211, + /* 130 */ -211, -211, -211, -211, -165, -165, -165, 123, 135, 175, + /* 140 */ -150, 396, 337, 291, 291, -147, 185, 391, 446, 444, + /* 150 */ 452, 500, 501, 502, 27, -152, 295, 438, 490, 503, + /* 160 */ 495, 506, -73, 447, 451, 536, 570, 551, 540, 579, + /* 170 */ 30, 508, 535, 81, 14, 61, 115, 168, 142, 222, + /* 180 */ 275, 284, 397, 599, 607, 647, 654, 660, 709, 658, + /* 190 */ 714, 750, 754, 772, 787, 801, 817, 818, 850, 863, + /* 200 */ 867, 871, 466, 748, 799, 881, 888, 898, 913, 824, + /* 210 */ 930, 933, 934, 873, 942, 945, 849, 946, 222, 954, + /* 220 */ 971, 972, 976, 979, 980, 900, 874, 927, 950, 961, + /* 230 */ 962, 963, 824, 927, 927, 939, 970, 1018, 981, 988, + /* 240 */ 993, 936, 982, 937, 1009, 1000, 1031, 1011, 1014, 1040, + /* 250 */ 1003, 991, 990, 1026, 1072, 985, 1076, 1079, 997, 1005, + /* 260 */ 1091, 1037, 1082, 1060, 1083, 1087, 1088, 1098, 1124, 1141, + /* 270 */ 1106, 1092, 1122, 1135, 1128, 1147, 1173, 1110, 1105, 1187, + /* 280 */ 1192, 1176, 1202, 1198, 1206, 1200, 1182, 1213, 1214, 1215, + /* 290 */ 1209, 1216, 1218, 1219, 1220, 1224, 1222, 1223, 1221, 1171, + /* 300 */ 1177, 1233, 1196, 1194, 1204, 1210, 1211, 1225, 1226, 1228, + /* 310 */ 1253, 1190, 1256, 1191, 1227, 1229, 1234, 1236, 1235, 1263, + /* 320 */ 1268, 1278, 1279, 1289, 1291, 1295, 1185, 1237, 1238, 1282, + /* 330 */ 1274, 1283, 1286, 1287, 1290, 1269, 1270, 1293, 1298, 1299, + /* 340 */ 1300, 1301, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1286, 1276, 1276, 1276, 1209, 1209, 1209, 1209, 1133, 1133, - /* 10 */ 1260, 1036, 1005, 1005, 1005, 1005, 1005, 1005, 1208, 1005, - /* 20 */ 1005, 1005, 1005, 1108, 1139, 1005, 1005, 1005, 1005, 1210, - /* 30 */ 1211, 1005, 1005, 1005, 1259, 1261, 1149, 1148, 1147, 1146, - /* 40 */ 1242, 1120, 1144, 1137, 1141, 1210, 1204, 1205, 1203, 1207, - /* 50 */ 1211, 1005, 1140, 1174, 1188, 1173, 1005, 1005, 1005, 1005, - /* 60 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 70 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 80 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 90 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 100 */ 1005, 1005, 1182, 1187, 1194, 1186, 1183, 1176, 1175, 1177, - /* 110 */ 1178, 1005, 1026, 1075, 1005, 1005, 1005, 1276, 1036, 1179, - /* 120 */ 1005, 1180, 1191, 1190, 1189, 1267, 1294, 1293, 1005, 1005, - /* 130 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 140 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 150 */ 1005, 1005, 1005, 1005, 1005, 1036, 1286, 1276, 1032, 1032, - /* 160 */ 1005, 1276, 1276, 1276, 1276, 1276, 1276, 1272, 1108, 1099, - /* 170 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 180 */ 1005, 1264, 1262, 1005, 1224, 1005, 1005, 1005, 1005, 1005, - /* 190 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 200 */ 1005, 1005, 1005, 1005, 1005, 1104, 1005, 1005, 1005, 1005, - /* 210 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1288, 1005, - /* 220 */ 1237, 1104, 1104, 1104, 1104, 1106, 1088, 1098, 1036, 1012, - /* 230 */ 1143, 1122, 1122, 1327, 1143, 1327, 1050, 1308, 1047, 1133, - /* 240 */ 1122, 1206, 1133, 1133, 1105, 1098, 1005, 1330, 1113, 1113, - /* 250 */ 1329, 1329, 1113, 1154, 1078, 1143, 1084, 1084, 1084, 1084, - /* 260 */ 1113, 1023, 1143, 1154, 1078, 1078, 1143, 1113, 1023, 1241, - /* 270 */ 1324, 1113, 1113, 1023, 1217, 1113, 1023, 1113, 1023, 1217, - /* 280 */ 1076, 1076, 1076, 1065, 1217, 1076, 1050, 1076, 1065, 1076, - /* 290 */ 1076, 1126, 1121, 1126, 1121, 1126, 1121, 1126, 1121, 1113, - /* 300 */ 1212, 1113, 1005, 1217, 1221, 1221, 1217, 1138, 1127, 1136, - /* 310 */ 1134, 1143, 1029, 1068, 1291, 1291, 1287, 1287, 1287, 1335, - /* 320 */ 1335, 1272, 1303, 1036, 1036, 1036, 1036, 1303, 1052, 1052, - /* 330 */ 1036, 1036, 1036, 1036, 1303, 1005, 1005, 1005, 1005, 1005, - /* 340 */ 1005, 1298, 1005, 1226, 1005, 1005, 1005, 1005, 1005, 1005, - /* 350 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 360 */ 1005, 1005, 1159, 1005, 1008, 1269, 1005, 1005, 1268, 1005, - /* 370 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 380 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1326, - /* 390 */ 1005, 1005, 1005, 1005, 1005, 1005, 1240, 1239, 1005, 1005, - /* 400 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 410 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - /* 420 */ 1005, 1090, 1005, 1005, 1005, 1312, 1005, 1005, 1005, 1005, - /* 430 */ 1005, 1005, 1005, 1135, 1005, 1128, 1005, 1005, 1317, 1005, - /* 440 */ 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1278, - /* 450 */ 1005, 1005, 1005, 1277, 1005, 1005, 1005, 1005, 1005, 1161, - /* 460 */ 1005, 1160, 1164, 1005, 1017, 1005, + /* 0 */ 1297, 1349, 1221, 1014, 1119, 1221, 1221, 1221, 1221, 1014, + /* 10 */ 1145, 1145, 1272, 1045, 1014, 1014, 1014, 1014, 1014, 1220, + /* 20 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 30 */ 1014, 1151, 1014, 1014, 1014, 1014, 1222, 1223, 1014, 1014, + /* 40 */ 1014, 1271, 1273, 1161, 1160, 1159, 1158, 1254, 1132, 1156, + /* 50 */ 1149, 1153, 1216, 1217, 1215, 1219, 1222, 1223, 1014, 1152, + /* 60 */ 1186, 1200, 1185, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 70 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 80 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 90 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 100 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1194, + /* 110 */ 1199, 1206, 1198, 1195, 1188, 1187, 1189, 1190, 1014, 1035, + /* 120 */ 1084, 1014, 1014, 1014, 1289, 1288, 1014, 1014, 1045, 1191, + /* 130 */ 1192, 1203, 1202, 1201, 1279, 1305, 1304, 1014, 1014, 1014, + /* 140 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 150 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 160 */ 1014, 1014, 1014, 1014, 1014, 1014, 1045, 1297, 1041, 1041, + /* 170 */ 1014, 1284, 1119, 1110, 1014, 1014, 1014, 1014, 1014, 1014, + /* 180 */ 1014, 1014, 1014, 1014, 1014, 1276, 1274, 1014, 1236, 1014, + /* 190 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 200 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 210 */ 1014, 1014, 1014, 1115, 1014, 1014, 1014, 1014, 1014, 1014, + /* 220 */ 1014, 1014, 1014, 1014, 1299, 1014, 1249, 1098, 1115, 1115, + /* 230 */ 1115, 1115, 1117, 1099, 1097, 1109, 1045, 1021, 1155, 1134, + /* 240 */ 1134, 1338, 1155, 1338, 1059, 1319, 1056, 1145, 1145, 1145, + /* 250 */ 1134, 1218, 1116, 1109, 1014, 1341, 1124, 1124, 1340, 1340, + /* 260 */ 1124, 1166, 1087, 1155, 1093, 1093, 1093, 1093, 1124, 1032, + /* 270 */ 1155, 1166, 1087, 1087, 1155, 1124, 1032, 1253, 1335, 1124, + /* 280 */ 1032, 1229, 1124, 1032, 1124, 1032, 1229, 1085, 1085, 1085, + /* 290 */ 1074, 1229, 1085, 1059, 1085, 1074, 1085, 1085, 1229, 1233, + /* 300 */ 1233, 1229, 1138, 1133, 1138, 1133, 1138, 1133, 1138, 1133, + /* 310 */ 1124, 1224, 1124, 1014, 1150, 1139, 1148, 1146, 1155, 1038, + /* 320 */ 1077, 1302, 1302, 1298, 1298, 1298, 1346, 1346, 1284, 1314, + /* 330 */ 1045, 1045, 1045, 1045, 1314, 1061, 1061, 1045, 1045, 1045, + /* 340 */ 1045, 1314, 1014, 1014, 1014, 1014, 1014, 1014, 1309, 1014, + /* 350 */ 1238, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 360 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1171, + /* 370 */ 1014, 1017, 1281, 1014, 1014, 1280, 1014, 1014, 1014, 1014, + /* 380 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 390 */ 1014, 1014, 1014, 1014, 1014, 1014, 1337, 1014, 1014, 1014, + /* 400 */ 1014, 1014, 1014, 1252, 1251, 1014, 1014, 1126, 1014, 1014, + /* 410 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 420 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 430 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 440 */ 1014, 1014, 1147, 1014, 1140, 1014, 1014, 1014, 1014, 1328, + /* 450 */ 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, + /* 460 */ 1014, 1014, 1014, 1323, 1101, 1173, 1014, 1172, 1176, 1014, + /* 470 */ 1026, 1014, }; /********** End of lemon-generated parsing tables *****************************/ @@ -139473,7 +141148,7 @@ /* 194 */ "fullname", /* 195 */ "selectnowith", /* 196 */ "oneselect", - /* 197 */ "with", + /* 197 */ "wqlist", /* 198 */ "multiselect_op", /* 199 */ "distinct", /* 200 */ "selcollist", @@ -139495,39 +141170,39 @@ /* 216 */ "on_opt", /* 217 */ "using_opt", /* 218 */ "idlist", - /* 219 */ "setlist", - /* 220 */ "insert_cmd", - /* 221 */ "idlist_opt", - /* 222 */ "likeop", - /* 223 */ "between_op", - /* 224 */ "in_op", - /* 225 */ "paren_exprlist", - /* 226 */ "case_operand", - /* 227 */ "case_exprlist", - /* 228 */ "case_else", - /* 229 */ "uniqueflag", - /* 230 */ "collate", - /* 231 */ "nmnum", - /* 232 */ "trigger_decl", - /* 233 */ "trigger_cmd_list", - /* 234 */ "trigger_time", - /* 235 */ "trigger_event", - /* 236 */ "foreach_clause", - /* 237 */ "when_clause", - /* 238 */ "trigger_cmd", - /* 239 */ "trnm", - /* 240 */ "tridxby", - /* 241 */ "database_kw_opt", - /* 242 */ "key_opt", - /* 243 */ "add_column_fullname", - /* 244 */ "kwcolumn_opt", - /* 245 */ "create_vtab", - /* 246 */ "vtabarglist", - /* 247 */ "vtabarg", - /* 248 */ "vtabargtoken", - /* 249 */ "lp", - /* 250 */ "anylist", - /* 251 */ "wqlist", + /* 219 */ "with", + /* 220 */ "setlist", + /* 221 */ "insert_cmd", + /* 222 */ "idlist_opt", + /* 223 */ "likeop", + /* 224 */ "between_op", + /* 225 */ "in_op", + /* 226 */ "paren_exprlist", + /* 227 */ "case_operand", + /* 228 */ "case_exprlist", + /* 229 */ "case_else", + /* 230 */ "uniqueflag", + /* 231 */ "collate", + /* 232 */ "nmnum", + /* 233 */ "trigger_decl", + /* 234 */ "trigger_cmd_list", + /* 235 */ "trigger_time", + /* 236 */ "trigger_event", + /* 237 */ "foreach_clause", + /* 238 */ "when_clause", + /* 239 */ "trigger_cmd", + /* 240 */ "trnm", + /* 241 */ "tridxby", + /* 242 */ "database_kw_opt", + /* 243 */ "key_opt", + /* 244 */ "add_column_fullname", + /* 245 */ "kwcolumn_opt", + /* 246 */ "create_vtab", + /* 247 */ "vtabarglist", + /* 248 */ "vtabarg", + /* 249 */ "vtabargtoken", + /* 250 */ "lp", + /* 251 */ "anylist", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -139615,256 +141290,259 @@ /* 77 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select", /* 78 */ "cmd ::= DROP VIEW ifexists fullname", /* 79 */ "cmd ::= select", - /* 80 */ "select ::= with selectnowith", - /* 81 */ "selectnowith ::= selectnowith multiselect_op oneselect", - /* 82 */ "multiselect_op ::= UNION", - /* 83 */ "multiselect_op ::= UNION ALL", - /* 84 */ "multiselect_op ::= EXCEPT|INTERSECT", - /* 85 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 86 */ "values ::= VALUES LP nexprlist RP", - /* 87 */ "values ::= values COMMA LP exprlist RP", - /* 88 */ "distinct ::= DISTINCT", - /* 89 */ "distinct ::= ALL", - /* 90 */ "distinct ::=", - /* 91 */ "sclp ::=", - /* 92 */ "selcollist ::= sclp scanpt expr scanpt as", - /* 93 */ "selcollist ::= sclp scanpt STAR", - /* 94 */ "selcollist ::= sclp scanpt nm DOT STAR", - /* 95 */ "as ::= AS nm", - /* 96 */ "as ::=", - /* 97 */ "from ::=", - /* 98 */ "from ::= FROM seltablist", - /* 99 */ "stl_prefix ::= seltablist joinop", - /* 100 */ "stl_prefix ::=", - /* 101 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", - /* 102 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", - /* 103 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", - /* 104 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", - /* 105 */ "dbnm ::=", - /* 106 */ "dbnm ::= DOT nm", - /* 107 */ "fullname ::= nm dbnm", - /* 108 */ "joinop ::= COMMA|JOIN", - /* 109 */ "joinop ::= JOIN_KW JOIN", - /* 110 */ "joinop ::= JOIN_KW nm JOIN", - /* 111 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 112 */ "on_opt ::= ON expr", - /* 113 */ "on_opt ::=", - /* 114 */ "indexed_opt ::=", - /* 115 */ "indexed_opt ::= INDEXED BY nm", - /* 116 */ "indexed_opt ::= NOT INDEXED", - /* 117 */ "using_opt ::= USING LP idlist RP", - /* 118 */ "using_opt ::=", - /* 119 */ "orderby_opt ::=", - /* 120 */ "orderby_opt ::= ORDER BY sortlist", - /* 121 */ "sortlist ::= sortlist COMMA expr sortorder", - /* 122 */ "sortlist ::= expr sortorder", - /* 123 */ "sortorder ::= ASC", - /* 124 */ "sortorder ::= DESC", - /* 125 */ "sortorder ::=", - /* 126 */ "groupby_opt ::=", - /* 127 */ "groupby_opt ::= GROUP BY nexprlist", - /* 128 */ "having_opt ::=", - /* 129 */ "having_opt ::= HAVING expr", - /* 130 */ "limit_opt ::=", - /* 131 */ "limit_opt ::= LIMIT expr", - /* 132 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 133 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 134 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt", - /* 135 */ "where_opt ::=", - /* 136 */ "where_opt ::= WHERE expr", - /* 137 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt", - /* 138 */ "setlist ::= setlist COMMA nm EQ expr", - /* 139 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", - /* 140 */ "setlist ::= nm EQ expr", - /* 141 */ "setlist ::= LP idlist RP EQ expr", - /* 142 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select", - /* 143 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES", - /* 144 */ "insert_cmd ::= INSERT orconf", - /* 145 */ "insert_cmd ::= REPLACE", - /* 146 */ "idlist_opt ::=", - /* 147 */ "idlist_opt ::= LP idlist RP", - /* 148 */ "idlist ::= idlist COMMA nm", - /* 149 */ "idlist ::= nm", - /* 150 */ "expr ::= LP expr RP", - /* 151 */ "expr ::= ID|INDEXED", - /* 152 */ "expr ::= JOIN_KW", - /* 153 */ "expr ::= nm DOT nm", - /* 154 */ "expr ::= nm DOT nm DOT nm", - /* 155 */ "term ::= NULL|FLOAT|BLOB", - /* 156 */ "term ::= STRING", - /* 157 */ "term ::= INTEGER", - /* 158 */ "expr ::= VARIABLE", - /* 159 */ "expr ::= expr COLLATE ID|STRING", - /* 160 */ "expr ::= CAST LP expr AS typetoken RP", - /* 161 */ "expr ::= ID|INDEXED LP distinct exprlist RP", - /* 162 */ "expr ::= ID|INDEXED LP STAR RP", - /* 163 */ "term ::= CTIME_KW", - /* 164 */ "expr ::= LP nexprlist COMMA expr RP", - /* 165 */ "expr ::= expr AND expr", - /* 166 */ "expr ::= expr OR expr", - /* 167 */ "expr ::= expr LT|GT|GE|LE expr", - /* 168 */ "expr ::= expr EQ|NE expr", - /* 169 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 170 */ "expr ::= expr PLUS|MINUS expr", - /* 171 */ "expr ::= expr STAR|SLASH|REM expr", - /* 172 */ "expr ::= expr CONCAT expr", - /* 173 */ "likeop ::= NOT LIKE_KW|MATCH", - /* 174 */ "expr ::= expr likeop expr", - /* 175 */ "expr ::= expr likeop expr ESCAPE expr", - /* 176 */ "expr ::= expr ISNULL|NOTNULL", - /* 177 */ "expr ::= expr NOT NULL", - /* 178 */ "expr ::= expr IS expr", - /* 179 */ "expr ::= expr IS NOT expr", - /* 180 */ "expr ::= NOT expr", - /* 181 */ "expr ::= BITNOT expr", - /* 182 */ "expr ::= MINUS expr", - /* 183 */ "expr ::= PLUS expr", - /* 184 */ "between_op ::= BETWEEN", - /* 185 */ "between_op ::= NOT BETWEEN", - /* 186 */ "expr ::= expr between_op expr AND expr", - /* 187 */ "in_op ::= IN", - /* 188 */ "in_op ::= NOT IN", - /* 189 */ "expr ::= expr in_op LP exprlist RP", - /* 190 */ "expr ::= LP select RP", - /* 191 */ "expr ::= expr in_op LP select RP", - /* 192 */ "expr ::= expr in_op nm dbnm paren_exprlist", - /* 193 */ "expr ::= EXISTS LP select RP", - /* 194 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 195 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 196 */ "case_exprlist ::= WHEN expr THEN expr", - /* 197 */ "case_else ::= ELSE expr", - /* 198 */ "case_else ::=", - /* 199 */ "case_operand ::= expr", - /* 200 */ "case_operand ::=", - /* 201 */ "exprlist ::=", - /* 202 */ "nexprlist ::= nexprlist COMMA expr", - /* 203 */ "nexprlist ::= expr", - /* 204 */ "paren_exprlist ::=", - /* 205 */ "paren_exprlist ::= LP exprlist RP", - /* 206 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", - /* 207 */ "uniqueflag ::= UNIQUE", - /* 208 */ "uniqueflag ::=", - /* 209 */ "eidlist_opt ::=", - /* 210 */ "eidlist_opt ::= LP eidlist RP", - /* 211 */ "eidlist ::= eidlist COMMA nm collate sortorder", - /* 212 */ "eidlist ::= nm collate sortorder", - /* 213 */ "collate ::=", - /* 214 */ "collate ::= COLLATE ID|STRING", - /* 215 */ "cmd ::= DROP INDEX ifexists fullname", - /* 216 */ "cmd ::= VACUUM", - /* 217 */ "cmd ::= VACUUM nm", - /* 218 */ "cmd ::= PRAGMA nm dbnm", - /* 219 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 220 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 221 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 222 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 223 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 224 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 225 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 226 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 227 */ "trigger_time ::= BEFORE|AFTER", - /* 228 */ "trigger_time ::= INSTEAD OF", - /* 229 */ "trigger_time ::=", - /* 230 */ "trigger_event ::= DELETE|INSERT", - /* 231 */ "trigger_event ::= UPDATE", - /* 232 */ "trigger_event ::= UPDATE OF idlist", - /* 233 */ "when_clause ::=", - /* 234 */ "when_clause ::= WHEN expr", - /* 235 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 236 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 237 */ "trnm ::= nm DOT nm", - /* 238 */ "tridxby ::= INDEXED BY nm", - /* 239 */ "tridxby ::= NOT INDEXED", - /* 240 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt", - /* 241 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select scanpt", - /* 242 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 243 */ "trigger_cmd ::= scanpt select scanpt", - /* 244 */ "expr ::= RAISE LP IGNORE RP", - /* 245 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 246 */ "raisetype ::= ROLLBACK", - /* 247 */ "raisetype ::= ABORT", - /* 248 */ "raisetype ::= FAIL", - /* 249 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 250 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 251 */ "cmd ::= DETACH database_kw_opt expr", - /* 252 */ "key_opt ::=", - /* 253 */ "key_opt ::= KEY expr", - /* 254 */ "cmd ::= REINDEX", - /* 255 */ "cmd ::= REINDEX nm dbnm", - /* 256 */ "cmd ::= ANALYZE", - /* 257 */ "cmd ::= ANALYZE nm dbnm", - /* 258 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 259 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 260 */ "add_column_fullname ::= fullname", - /* 261 */ "cmd ::= create_vtab", - /* 262 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 263 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 264 */ "vtabarg ::=", - /* 265 */ "vtabargtoken ::= ANY", - /* 266 */ "vtabargtoken ::= lp anylist RP", - /* 267 */ "lp ::= LP", - /* 268 */ "with ::=", - /* 269 */ "with ::= WITH wqlist", - /* 270 */ "with ::= WITH RECURSIVE wqlist", - /* 271 */ "wqlist ::= nm eidlist_opt AS LP select RP", - /* 272 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", - /* 273 */ "input ::= cmdlist", - /* 274 */ "cmdlist ::= cmdlist ecmd", - /* 275 */ "cmdlist ::= ecmd", - /* 276 */ "ecmd ::= SEMI", - /* 277 */ "ecmd ::= explain cmdx SEMI", - /* 278 */ "explain ::=", - /* 279 */ "trans_opt ::=", - /* 280 */ "trans_opt ::= TRANSACTION", - /* 281 */ "trans_opt ::= TRANSACTION nm", - /* 282 */ "savepoint_opt ::= SAVEPOINT", - /* 283 */ "savepoint_opt ::=", - /* 284 */ "cmd ::= create_table create_table_args", - /* 285 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 286 */ "columnlist ::= columnname carglist", - /* 287 */ "nm ::= ID|INDEXED", - /* 288 */ "nm ::= STRING", - /* 289 */ "nm ::= JOIN_KW", - /* 290 */ "typetoken ::= typename", - /* 291 */ "typename ::= ID|STRING", - /* 292 */ "signed ::= plus_num", - /* 293 */ "signed ::= minus_num", - /* 294 */ "carglist ::= carglist ccons", - /* 295 */ "carglist ::=", - /* 296 */ "ccons ::= NULL onconf", - /* 297 */ "conslist_opt ::= COMMA conslist", - /* 298 */ "conslist ::= conslist tconscomma tcons", - /* 299 */ "conslist ::= tcons", - /* 300 */ "tconscomma ::=", - /* 301 */ "defer_subclause_opt ::= defer_subclause", - /* 302 */ "resolvetype ::= raisetype", - /* 303 */ "selectnowith ::= oneselect", - /* 304 */ "oneselect ::= values", - /* 305 */ "sclp ::= selcollist COMMA", - /* 306 */ "as ::= ID|STRING", - /* 307 */ "expr ::= term", - /* 308 */ "likeop ::= LIKE_KW|MATCH", - /* 309 */ "exprlist ::= nexprlist", - /* 310 */ "nmnum ::= plus_num", - /* 311 */ "nmnum ::= nm", - /* 312 */ "nmnum ::= ON", - /* 313 */ "nmnum ::= DELETE", - /* 314 */ "nmnum ::= DEFAULT", - /* 315 */ "plus_num ::= INTEGER|FLOAT", - /* 316 */ "foreach_clause ::=", - /* 317 */ "foreach_clause ::= FOR EACH ROW", - /* 318 */ "trnm ::= nm", - /* 319 */ "tridxby ::=", - /* 320 */ "database_kw_opt ::= DATABASE", - /* 321 */ "database_kw_opt ::=", - /* 322 */ "kwcolumn_opt ::=", - /* 323 */ "kwcolumn_opt ::= COLUMNKW", - /* 324 */ "vtabarglist ::= vtabarg", - /* 325 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 326 */ "vtabarg ::= vtabarg vtabargtoken", - /* 327 */ "anylist ::=", - /* 328 */ "anylist ::= anylist LP anylist RP", - /* 329 */ "anylist ::= anylist ANY", + /* 80 */ "select ::= WITH wqlist selectnowith", + /* 81 */ "select ::= WITH RECURSIVE wqlist selectnowith", + /* 82 */ "select ::= selectnowith", + /* 83 */ "selectnowith ::= selectnowith multiselect_op oneselect", + /* 84 */ "multiselect_op ::= UNION", + /* 85 */ "multiselect_op ::= UNION ALL", + /* 86 */ "multiselect_op ::= EXCEPT|INTERSECT", + /* 87 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", + /* 88 */ "values ::= VALUES LP nexprlist RP", + /* 89 */ "values ::= values COMMA LP exprlist RP", + /* 90 */ "distinct ::= DISTINCT", + /* 91 */ "distinct ::= ALL", + /* 92 */ "distinct ::=", + /* 93 */ "sclp ::=", + /* 94 */ "selcollist ::= sclp scanpt expr scanpt as", + /* 95 */ "selcollist ::= sclp scanpt STAR", + /* 96 */ "selcollist ::= sclp scanpt nm DOT STAR", + /* 97 */ "as ::= AS nm", + /* 98 */ "as ::=", + /* 99 */ "from ::=", + /* 100 */ "from ::= FROM seltablist", + /* 101 */ "stl_prefix ::= seltablist joinop", + /* 102 */ "stl_prefix ::=", + /* 103 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", + /* 104 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt", + /* 105 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", + /* 106 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", + /* 107 */ "dbnm ::=", + /* 108 */ "dbnm ::= DOT nm", + /* 109 */ "fullname ::= nm", + /* 110 */ "fullname ::= nm DOT nm", + /* 111 */ "joinop ::= COMMA|JOIN", + /* 112 */ "joinop ::= JOIN_KW JOIN", + /* 113 */ "joinop ::= JOIN_KW nm JOIN", + /* 114 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 115 */ "on_opt ::= ON expr", + /* 116 */ "on_opt ::=", + /* 117 */ "indexed_opt ::=", + /* 118 */ "indexed_opt ::= INDEXED BY nm", + /* 119 */ "indexed_opt ::= NOT INDEXED", + /* 120 */ "using_opt ::= USING LP idlist RP", + /* 121 */ "using_opt ::=", + /* 122 */ "orderby_opt ::=", + /* 123 */ "orderby_opt ::= ORDER BY sortlist", + /* 124 */ "sortlist ::= sortlist COMMA expr sortorder", + /* 125 */ "sortlist ::= expr sortorder", + /* 126 */ "sortorder ::= ASC", + /* 127 */ "sortorder ::= DESC", + /* 128 */ "sortorder ::=", + /* 129 */ "groupby_opt ::=", + /* 130 */ "groupby_opt ::= GROUP BY nexprlist", + /* 131 */ "having_opt ::=", + /* 132 */ "having_opt ::= HAVING expr", + /* 133 */ "limit_opt ::=", + /* 134 */ "limit_opt ::= LIMIT expr", + /* 135 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 136 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 137 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt", + /* 138 */ "where_opt ::=", + /* 139 */ "where_opt ::= WHERE expr", + /* 140 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt", + /* 141 */ "setlist ::= setlist COMMA nm EQ expr", + /* 142 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 143 */ "setlist ::= nm EQ expr", + /* 144 */ "setlist ::= LP idlist RP EQ expr", + /* 145 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select", + /* 146 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES", + /* 147 */ "insert_cmd ::= INSERT orconf", + /* 148 */ "insert_cmd ::= REPLACE", + /* 149 */ "idlist_opt ::=", + /* 150 */ "idlist_opt ::= LP idlist RP", + /* 151 */ "idlist ::= idlist COMMA nm", + /* 152 */ "idlist ::= nm", + /* 153 */ "expr ::= LP expr RP", + /* 154 */ "expr ::= ID|INDEXED", + /* 155 */ "expr ::= JOIN_KW", + /* 156 */ "expr ::= nm DOT nm", + /* 157 */ "expr ::= nm DOT nm DOT nm", + /* 158 */ "term ::= NULL|FLOAT|BLOB", + /* 159 */ "term ::= STRING", + /* 160 */ "term ::= INTEGER", + /* 161 */ "expr ::= VARIABLE", + /* 162 */ "expr ::= expr COLLATE ID|STRING", + /* 163 */ "expr ::= CAST LP expr AS typetoken RP", + /* 164 */ "expr ::= ID|INDEXED LP distinct exprlist RP", + /* 165 */ "expr ::= ID|INDEXED LP STAR RP", + /* 166 */ "term ::= CTIME_KW", + /* 167 */ "expr ::= LP nexprlist COMMA expr RP", + /* 168 */ "expr ::= expr AND expr", + /* 169 */ "expr ::= expr OR expr", + /* 170 */ "expr ::= expr LT|GT|GE|LE expr", + /* 171 */ "expr ::= expr EQ|NE expr", + /* 172 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 173 */ "expr ::= expr PLUS|MINUS expr", + /* 174 */ "expr ::= expr STAR|SLASH|REM expr", + /* 175 */ "expr ::= expr CONCAT expr", + /* 176 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 177 */ "expr ::= expr likeop expr", + /* 178 */ "expr ::= expr likeop expr ESCAPE expr", + /* 179 */ "expr ::= expr ISNULL|NOTNULL", + /* 180 */ "expr ::= expr NOT NULL", + /* 181 */ "expr ::= expr IS expr", + /* 182 */ "expr ::= expr IS NOT expr", + /* 183 */ "expr ::= NOT expr", + /* 184 */ "expr ::= BITNOT expr", + /* 185 */ "expr ::= MINUS expr", + /* 186 */ "expr ::= PLUS expr", + /* 187 */ "between_op ::= BETWEEN", + /* 188 */ "between_op ::= NOT BETWEEN", + /* 189 */ "expr ::= expr between_op expr AND expr", + /* 190 */ "in_op ::= IN", + /* 191 */ "in_op ::= NOT IN", + /* 192 */ "expr ::= expr in_op LP exprlist RP", + /* 193 */ "expr ::= LP select RP", + /* 194 */ "expr ::= expr in_op LP select RP", + /* 195 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 196 */ "expr ::= EXISTS LP select RP", + /* 197 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 198 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 199 */ "case_exprlist ::= WHEN expr THEN expr", + /* 200 */ "case_else ::= ELSE expr", + /* 201 */ "case_else ::=", + /* 202 */ "case_operand ::= expr", + /* 203 */ "case_operand ::=", + /* 204 */ "exprlist ::=", + /* 205 */ "nexprlist ::= nexprlist COMMA expr", + /* 206 */ "nexprlist ::= expr", + /* 207 */ "paren_exprlist ::=", + /* 208 */ "paren_exprlist ::= LP exprlist RP", + /* 209 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 210 */ "uniqueflag ::= UNIQUE", + /* 211 */ "uniqueflag ::=", + /* 212 */ "eidlist_opt ::=", + /* 213 */ "eidlist_opt ::= LP eidlist RP", + /* 214 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 215 */ "eidlist ::= nm collate sortorder", + /* 216 */ "collate ::=", + /* 217 */ "collate ::= COLLATE ID|STRING", + /* 218 */ "cmd ::= DROP INDEX ifexists fullname", + /* 219 */ "cmd ::= VACUUM", + /* 220 */ "cmd ::= VACUUM nm", + /* 221 */ "cmd ::= PRAGMA nm dbnm", + /* 222 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 223 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 224 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 225 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 226 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 227 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 228 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 229 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 230 */ "trigger_time ::= BEFORE|AFTER", + /* 231 */ "trigger_time ::= INSTEAD OF", + /* 232 */ "trigger_time ::=", + /* 233 */ "trigger_event ::= DELETE|INSERT", + /* 234 */ "trigger_event ::= UPDATE", + /* 235 */ "trigger_event ::= UPDATE OF idlist", + /* 236 */ "when_clause ::=", + /* 237 */ "when_clause ::= WHEN expr", + /* 238 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 239 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 240 */ "trnm ::= nm DOT nm", + /* 241 */ "tridxby ::= INDEXED BY nm", + /* 242 */ "tridxby ::= NOT INDEXED", + /* 243 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt", + /* 244 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select scanpt", + /* 245 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 246 */ "trigger_cmd ::= scanpt select scanpt", + /* 247 */ "expr ::= RAISE LP IGNORE RP", + /* 248 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 249 */ "raisetype ::= ROLLBACK", + /* 250 */ "raisetype ::= ABORT", + /* 251 */ "raisetype ::= FAIL", + /* 252 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 253 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 254 */ "cmd ::= DETACH database_kw_opt expr", + /* 255 */ "key_opt ::=", + /* 256 */ "key_opt ::= KEY expr", + /* 257 */ "cmd ::= REINDEX", + /* 258 */ "cmd ::= REINDEX nm dbnm", + /* 259 */ "cmd ::= ANALYZE", + /* 260 */ "cmd ::= ANALYZE nm dbnm", + /* 261 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 262 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 263 */ "add_column_fullname ::= fullname", + /* 264 */ "cmd ::= create_vtab", + /* 265 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 266 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 267 */ "vtabarg ::=", + /* 268 */ "vtabargtoken ::= ANY", + /* 269 */ "vtabargtoken ::= lp anylist RP", + /* 270 */ "lp ::= LP", + /* 271 */ "with ::= WITH wqlist", + /* 272 */ "with ::= WITH RECURSIVE wqlist", + /* 273 */ "wqlist ::= nm eidlist_opt AS LP select RP", + /* 274 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", + /* 275 */ "input ::= cmdlist", + /* 276 */ "cmdlist ::= cmdlist ecmd", + /* 277 */ "cmdlist ::= ecmd", + /* 278 */ "ecmd ::= SEMI", + /* 279 */ "ecmd ::= explain cmdx SEMI", + /* 280 */ "explain ::=", + /* 281 */ "trans_opt ::=", + /* 282 */ "trans_opt ::= TRANSACTION", + /* 283 */ "trans_opt ::= TRANSACTION nm", + /* 284 */ "savepoint_opt ::= SAVEPOINT", + /* 285 */ "savepoint_opt ::=", + /* 286 */ "cmd ::= create_table create_table_args", + /* 287 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 288 */ "columnlist ::= columnname carglist", + /* 289 */ "nm ::= ID|INDEXED", + /* 290 */ "nm ::= STRING", + /* 291 */ "nm ::= JOIN_KW", + /* 292 */ "typetoken ::= typename", + /* 293 */ "typename ::= ID|STRING", + /* 294 */ "signed ::= plus_num", + /* 295 */ "signed ::= minus_num", + /* 296 */ "carglist ::= carglist ccons", + /* 297 */ "carglist ::=", + /* 298 */ "ccons ::= NULL onconf", + /* 299 */ "conslist_opt ::= COMMA conslist", + /* 300 */ "conslist ::= conslist tconscomma tcons", + /* 301 */ "conslist ::= tcons", + /* 302 */ "tconscomma ::=", + /* 303 */ "defer_subclause_opt ::= defer_subclause", + /* 304 */ "resolvetype ::= raisetype", + /* 305 */ "selectnowith ::= oneselect", + /* 306 */ "oneselect ::= values", + /* 307 */ "sclp ::= selcollist COMMA", + /* 308 */ "as ::= ID|STRING", + /* 309 */ "expr ::= term", + /* 310 */ "likeop ::= LIKE_KW|MATCH", + /* 311 */ "exprlist ::= nexprlist", + /* 312 */ "nmnum ::= plus_num", + /* 313 */ "nmnum ::= nm", + /* 314 */ "nmnum ::= ON", + /* 315 */ "nmnum ::= DELETE", + /* 316 */ "nmnum ::= DEFAULT", + /* 317 */ "plus_num ::= INTEGER|FLOAT", + /* 318 */ "foreach_clause ::=", + /* 319 */ "foreach_clause ::= FOR EACH ROW", + /* 320 */ "trnm ::= nm", + /* 321 */ "tridxby ::=", + /* 322 */ "database_kw_opt ::= DATABASE", + /* 323 */ "database_kw_opt ::=", + /* 324 */ "kwcolumn_opt ::=", + /* 325 */ "kwcolumn_opt ::= COLUMNKW", + /* 326 */ "vtabarglist ::= vtabarg", + /* 327 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 328 */ "vtabarg ::= vtabarg vtabargtoken", + /* 329 */ "anylist ::=", + /* 330 */ "anylist ::= anylist LP anylist RP", + /* 331 */ "anylist ::= anylist ANY", + /* 332 */ "with ::=", }; #endif /* NDEBUG */ @@ -139998,10 +141676,10 @@ case 202: /* where_opt */ case 204: /* having_opt */ case 216: /* on_opt */ - case 226: /* case_operand */ - case 228: /* case_else */ - case 237: /* when_clause */ - case 242: /* key_opt */ + case 227: /* case_operand */ + case 229: /* case_else */ + case 238: /* when_clause */ + case 243: /* key_opt */ { sqlite3ExprDelete(pParse->db, (yypminor->yy314)); } @@ -140015,9 +141693,9 @@ case 208: /* nexprlist */ case 209: /* exprlist */ case 210: /* sclp */ - case 219: /* setlist */ - case 225: /* paren_exprlist */ - case 227: /* case_exprlist */ + case 220: /* setlist */ + case 226: /* paren_exprlist */ + case 228: /* case_exprlist */ { sqlite3ExprListDelete(pParse->db, (yypminor->yy322)); } @@ -140030,26 +141708,25 @@ sqlite3SrcListDelete(pParse->db, (yypminor->yy259)); } break; - case 197: /* with */ - case 251: /* wqlist */ + case 197: /* wqlist */ { sqlite3WithDelete(pParse->db, (yypminor->yy451)); } break; case 217: /* using_opt */ case 218: /* idlist */ - case 221: /* idlist_opt */ + case 222: /* idlist_opt */ { sqlite3IdListDelete(pParse->db, (yypminor->yy384)); } break; - case 233: /* trigger_cmd_list */ - case 238: /* trigger_cmd */ + case 234: /* trigger_cmd_list */ + case 239: /* trigger_cmd */ { sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy203)); } break; - case 235: /* trigger_event */ + case 236: /* trigger_event */ { sqlite3IdListDelete(pParse->db, (yypminor->yy90).b); } @@ -140177,7 +141854,8 @@ #endif do{ i = yy_shift_ofst[stateno]; - assert( i>=0 && i+YYNTOKEN<=sizeof(yy_lookahead)/sizeof(yy_lookahead[0]) ); + assert( i>=0 ); + assert( i+YYNTOKEN<=(int)sizeof(yy_lookahead)/sizeof(yy_lookahead[0]) ); assert( iLookAhead!=YYNOCODE ); assert( iLookAhead < YYNTOKEN ); i += iLookAhead; @@ -140427,256 +142105,259 @@ { 149, -9 }, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { 149, -4 }, /* (78) cmd ::= DROP VIEW ifexists fullname */ { 149, -1 }, /* (79) cmd ::= select */ - { 163, -2 }, /* (80) select ::= with selectnowith */ - { 195, -3 }, /* (81) selectnowith ::= selectnowith multiselect_op oneselect */ - { 198, -1 }, /* (82) multiselect_op ::= UNION */ - { 198, -2 }, /* (83) multiselect_op ::= UNION ALL */ - { 198, -1 }, /* (84) multiselect_op ::= EXCEPT|INTERSECT */ - { 196, -9 }, /* (85) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - { 207, -4 }, /* (86) values ::= VALUES LP nexprlist RP */ - { 207, -5 }, /* (87) values ::= values COMMA LP exprlist RP */ - { 199, -1 }, /* (88) distinct ::= DISTINCT */ - { 199, -1 }, /* (89) distinct ::= ALL */ - { 199, 0 }, /* (90) distinct ::= */ - { 210, 0 }, /* (91) sclp ::= */ - { 200, -5 }, /* (92) selcollist ::= sclp scanpt expr scanpt as */ - { 200, -3 }, /* (93) selcollist ::= sclp scanpt STAR */ - { 200, -5 }, /* (94) selcollist ::= sclp scanpt nm DOT STAR */ - { 211, -2 }, /* (95) as ::= AS nm */ - { 211, 0 }, /* (96) as ::= */ - { 201, 0 }, /* (97) from ::= */ - { 201, -2 }, /* (98) from ::= FROM seltablist */ - { 213, -2 }, /* (99) stl_prefix ::= seltablist joinop */ - { 213, 0 }, /* (100) stl_prefix ::= */ - { 212, -7 }, /* (101) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - { 212, -9 }, /* (102) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - { 212, -7 }, /* (103) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - { 212, -7 }, /* (104) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - { 159, 0 }, /* (105) dbnm ::= */ - { 159, -2 }, /* (106) dbnm ::= DOT nm */ - { 194, -2 }, /* (107) fullname ::= nm dbnm */ - { 214, -1 }, /* (108) joinop ::= COMMA|JOIN */ - { 214, -2 }, /* (109) joinop ::= JOIN_KW JOIN */ - { 214, -3 }, /* (110) joinop ::= JOIN_KW nm JOIN */ - { 214, -4 }, /* (111) joinop ::= JOIN_KW nm nm JOIN */ - { 216, -2 }, /* (112) on_opt ::= ON expr */ - { 216, 0 }, /* (113) on_opt ::= */ - { 215, 0 }, /* (114) indexed_opt ::= */ - { 215, -3 }, /* (115) indexed_opt ::= INDEXED BY nm */ - { 215, -2 }, /* (116) indexed_opt ::= NOT INDEXED */ - { 217, -4 }, /* (117) using_opt ::= USING LP idlist RP */ - { 217, 0 }, /* (118) using_opt ::= */ - { 205, 0 }, /* (119) orderby_opt ::= */ - { 205, -3 }, /* (120) orderby_opt ::= ORDER BY sortlist */ - { 187, -4 }, /* (121) sortlist ::= sortlist COMMA expr sortorder */ - { 187, -2 }, /* (122) sortlist ::= expr sortorder */ - { 176, -1 }, /* (123) sortorder ::= ASC */ - { 176, -1 }, /* (124) sortorder ::= DESC */ - { 176, 0 }, /* (125) sortorder ::= */ - { 203, 0 }, /* (126) groupby_opt ::= */ - { 203, -3 }, /* (127) groupby_opt ::= GROUP BY nexprlist */ - { 204, 0 }, /* (128) having_opt ::= */ - { 204, -2 }, /* (129) having_opt ::= HAVING expr */ - { 206, 0 }, /* (130) limit_opt ::= */ - { 206, -2 }, /* (131) limit_opt ::= LIMIT expr */ - { 206, -4 }, /* (132) limit_opt ::= LIMIT expr OFFSET expr */ - { 206, -4 }, /* (133) limit_opt ::= LIMIT expr COMMA expr */ - { 149, -6 }, /* (134) cmd ::= with DELETE FROM fullname indexed_opt where_opt */ - { 202, 0 }, /* (135) where_opt ::= */ - { 202, -2 }, /* (136) where_opt ::= WHERE expr */ - { 149, -8 }, /* (137) cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */ - { 219, -5 }, /* (138) setlist ::= setlist COMMA nm EQ expr */ - { 219, -7 }, /* (139) setlist ::= setlist COMMA LP idlist RP EQ expr */ - { 219, -3 }, /* (140) setlist ::= nm EQ expr */ - { 219, -5 }, /* (141) setlist ::= LP idlist RP EQ expr */ - { 149, -6 }, /* (142) cmd ::= with insert_cmd INTO fullname idlist_opt select */ - { 149, -7 }, /* (143) cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */ - { 220, -2 }, /* (144) insert_cmd ::= INSERT orconf */ - { 220, -1 }, /* (145) insert_cmd ::= REPLACE */ - { 221, 0 }, /* (146) idlist_opt ::= */ - { 221, -3 }, /* (147) idlist_opt ::= LP idlist RP */ - { 218, -3 }, /* (148) idlist ::= idlist COMMA nm */ - { 218, -1 }, /* (149) idlist ::= nm */ - { 174, -3 }, /* (150) expr ::= LP expr RP */ - { 174, -1 }, /* (151) expr ::= ID|INDEXED */ - { 174, -1 }, /* (152) expr ::= JOIN_KW */ - { 174, -3 }, /* (153) expr ::= nm DOT nm */ - { 174, -5 }, /* (154) expr ::= nm DOT nm DOT nm */ - { 173, -1 }, /* (155) term ::= NULL|FLOAT|BLOB */ - { 173, -1 }, /* (156) term ::= STRING */ - { 173, -1 }, /* (157) term ::= INTEGER */ - { 174, -1 }, /* (158) expr ::= VARIABLE */ - { 174, -3 }, /* (159) expr ::= expr COLLATE ID|STRING */ - { 174, -6 }, /* (160) expr ::= CAST LP expr AS typetoken RP */ - { 174, -5 }, /* (161) expr ::= ID|INDEXED LP distinct exprlist RP */ - { 174, -4 }, /* (162) expr ::= ID|INDEXED LP STAR RP */ - { 173, -1 }, /* (163) term ::= CTIME_KW */ - { 174, -5 }, /* (164) expr ::= LP nexprlist COMMA expr RP */ - { 174, -3 }, /* (165) expr ::= expr AND expr */ - { 174, -3 }, /* (166) expr ::= expr OR expr */ - { 174, -3 }, /* (167) expr ::= expr LT|GT|GE|LE expr */ - { 174, -3 }, /* (168) expr ::= expr EQ|NE expr */ - { 174, -3 }, /* (169) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - { 174, -3 }, /* (170) expr ::= expr PLUS|MINUS expr */ - { 174, -3 }, /* (171) expr ::= expr STAR|SLASH|REM expr */ - { 174, -3 }, /* (172) expr ::= expr CONCAT expr */ - { 222, -2 }, /* (173) likeop ::= NOT LIKE_KW|MATCH */ - { 174, -3 }, /* (174) expr ::= expr likeop expr */ - { 174, -5 }, /* (175) expr ::= expr likeop expr ESCAPE expr */ - { 174, -2 }, /* (176) expr ::= expr ISNULL|NOTNULL */ - { 174, -3 }, /* (177) expr ::= expr NOT NULL */ - { 174, -3 }, /* (178) expr ::= expr IS expr */ - { 174, -4 }, /* (179) expr ::= expr IS NOT expr */ - { 174, -2 }, /* (180) expr ::= NOT expr */ - { 174, -2 }, /* (181) expr ::= BITNOT expr */ - { 174, -2 }, /* (182) expr ::= MINUS expr */ - { 174, -2 }, /* (183) expr ::= PLUS expr */ - { 223, -1 }, /* (184) between_op ::= BETWEEN */ - { 223, -2 }, /* (185) between_op ::= NOT BETWEEN */ - { 174, -5 }, /* (186) expr ::= expr between_op expr AND expr */ - { 224, -1 }, /* (187) in_op ::= IN */ - { 224, -2 }, /* (188) in_op ::= NOT IN */ - { 174, -5 }, /* (189) expr ::= expr in_op LP exprlist RP */ - { 174, -3 }, /* (190) expr ::= LP select RP */ - { 174, -5 }, /* (191) expr ::= expr in_op LP select RP */ - { 174, -5 }, /* (192) expr ::= expr in_op nm dbnm paren_exprlist */ - { 174, -4 }, /* (193) expr ::= EXISTS LP select RP */ - { 174, -5 }, /* (194) expr ::= CASE case_operand case_exprlist case_else END */ - { 227, -5 }, /* (195) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - { 227, -4 }, /* (196) case_exprlist ::= WHEN expr THEN expr */ - { 228, -2 }, /* (197) case_else ::= ELSE expr */ - { 228, 0 }, /* (198) case_else ::= */ - { 226, -1 }, /* (199) case_operand ::= expr */ - { 226, 0 }, /* (200) case_operand ::= */ - { 209, 0 }, /* (201) exprlist ::= */ - { 208, -3 }, /* (202) nexprlist ::= nexprlist COMMA expr */ - { 208, -1 }, /* (203) nexprlist ::= expr */ - { 225, 0 }, /* (204) paren_exprlist ::= */ - { 225, -3 }, /* (205) paren_exprlist ::= LP exprlist RP */ - { 149, -12 }, /* (206) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - { 229, -1 }, /* (207) uniqueflag ::= UNIQUE */ - { 229, 0 }, /* (208) uniqueflag ::= */ - { 178, 0 }, /* (209) eidlist_opt ::= */ - { 178, -3 }, /* (210) eidlist_opt ::= LP eidlist RP */ - { 188, -5 }, /* (211) eidlist ::= eidlist COMMA nm collate sortorder */ - { 188, -3 }, /* (212) eidlist ::= nm collate sortorder */ - { 230, 0 }, /* (213) collate ::= */ - { 230, -2 }, /* (214) collate ::= COLLATE ID|STRING */ - { 149, -4 }, /* (215) cmd ::= DROP INDEX ifexists fullname */ - { 149, -1 }, /* (216) cmd ::= VACUUM */ - { 149, -2 }, /* (217) cmd ::= VACUUM nm */ - { 149, -3 }, /* (218) cmd ::= PRAGMA nm dbnm */ - { 149, -5 }, /* (219) cmd ::= PRAGMA nm dbnm EQ nmnum */ - { 149, -6 }, /* (220) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - { 149, -5 }, /* (221) cmd ::= PRAGMA nm dbnm EQ minus_num */ - { 149, -6 }, /* (222) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - { 169, -2 }, /* (223) plus_num ::= PLUS INTEGER|FLOAT */ - { 170, -2 }, /* (224) minus_num ::= MINUS INTEGER|FLOAT */ - { 149, -5 }, /* (225) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - { 232, -11 }, /* (226) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - { 234, -1 }, /* (227) trigger_time ::= BEFORE|AFTER */ - { 234, -2 }, /* (228) trigger_time ::= INSTEAD OF */ - { 234, 0 }, /* (229) trigger_time ::= */ - { 235, -1 }, /* (230) trigger_event ::= DELETE|INSERT */ - { 235, -1 }, /* (231) trigger_event ::= UPDATE */ - { 235, -3 }, /* (232) trigger_event ::= UPDATE OF idlist */ - { 237, 0 }, /* (233) when_clause ::= */ - { 237, -2 }, /* (234) when_clause ::= WHEN expr */ - { 233, -3 }, /* (235) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - { 233, -2 }, /* (236) trigger_cmd_list ::= trigger_cmd SEMI */ - { 239, -3 }, /* (237) trnm ::= nm DOT nm */ - { 240, -3 }, /* (238) tridxby ::= INDEXED BY nm */ - { 240, -2 }, /* (239) tridxby ::= NOT INDEXED */ - { 238, -8 }, /* (240) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ - { 238, -7 }, /* (241) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select scanpt */ - { 238, -6 }, /* (242) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - { 238, -3 }, /* (243) trigger_cmd ::= scanpt select scanpt */ - { 174, -4 }, /* (244) expr ::= RAISE LP IGNORE RP */ - { 174, -6 }, /* (245) expr ::= RAISE LP raisetype COMMA nm RP */ - { 192, -1 }, /* (246) raisetype ::= ROLLBACK */ - { 192, -1 }, /* (247) raisetype ::= ABORT */ - { 192, -1 }, /* (248) raisetype ::= FAIL */ - { 149, -4 }, /* (249) cmd ::= DROP TRIGGER ifexists fullname */ - { 149, -6 }, /* (250) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - { 149, -3 }, /* (251) cmd ::= DETACH database_kw_opt expr */ - { 242, 0 }, /* (252) key_opt ::= */ - { 242, -2 }, /* (253) key_opt ::= KEY expr */ - { 149, -1 }, /* (254) cmd ::= REINDEX */ - { 149, -3 }, /* (255) cmd ::= REINDEX nm dbnm */ - { 149, -1 }, /* (256) cmd ::= ANALYZE */ - { 149, -3 }, /* (257) cmd ::= ANALYZE nm dbnm */ - { 149, -6 }, /* (258) cmd ::= ALTER TABLE fullname RENAME TO nm */ - { 149, -7 }, /* (259) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - { 243, -1 }, /* (260) add_column_fullname ::= fullname */ - { 149, -1 }, /* (261) cmd ::= create_vtab */ - { 149, -4 }, /* (262) cmd ::= create_vtab LP vtabarglist RP */ - { 245, -8 }, /* (263) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - { 247, 0 }, /* (264) vtabarg ::= */ - { 248, -1 }, /* (265) vtabargtoken ::= ANY */ - { 248, -3 }, /* (266) vtabargtoken ::= lp anylist RP */ - { 249, -1 }, /* (267) lp ::= LP */ - { 197, 0 }, /* (268) with ::= */ - { 197, -2 }, /* (269) with ::= WITH wqlist */ - { 197, -3 }, /* (270) with ::= WITH RECURSIVE wqlist */ - { 251, -6 }, /* (271) wqlist ::= nm eidlist_opt AS LP select RP */ - { 251, -8 }, /* (272) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ - { 144, -1 }, /* (273) input ::= cmdlist */ - { 145, -2 }, /* (274) cmdlist ::= cmdlist ecmd */ - { 145, -1 }, /* (275) cmdlist ::= ecmd */ - { 146, -1 }, /* (276) ecmd ::= SEMI */ - { 146, -3 }, /* (277) ecmd ::= explain cmdx SEMI */ - { 147, 0 }, /* (278) explain ::= */ - { 151, 0 }, /* (279) trans_opt ::= */ - { 151, -1 }, /* (280) trans_opt ::= TRANSACTION */ - { 151, -2 }, /* (281) trans_opt ::= TRANSACTION nm */ - { 153, -1 }, /* (282) savepoint_opt ::= SAVEPOINT */ - { 153, 0 }, /* (283) savepoint_opt ::= */ - { 149, -2 }, /* (284) cmd ::= create_table create_table_args */ - { 160, -4 }, /* (285) columnlist ::= columnlist COMMA columnname carglist */ - { 160, -2 }, /* (286) columnlist ::= columnname carglist */ - { 152, -1 }, /* (287) nm ::= ID|INDEXED */ - { 152, -1 }, /* (288) nm ::= STRING */ - { 152, -1 }, /* (289) nm ::= JOIN_KW */ - { 166, -1 }, /* (290) typetoken ::= typename */ - { 167, -1 }, /* (291) typename ::= ID|STRING */ - { 168, -1 }, /* (292) signed ::= plus_num */ - { 168, -1 }, /* (293) signed ::= minus_num */ - { 165, -2 }, /* (294) carglist ::= carglist ccons */ - { 165, 0 }, /* (295) carglist ::= */ - { 172, -2 }, /* (296) ccons ::= NULL onconf */ - { 161, -2 }, /* (297) conslist_opt ::= COMMA conslist */ - { 184, -3 }, /* (298) conslist ::= conslist tconscomma tcons */ - { 184, -1 }, /* (299) conslist ::= tcons */ - { 185, 0 }, /* (300) tconscomma ::= */ - { 189, -1 }, /* (301) defer_subclause_opt ::= defer_subclause */ - { 191, -1 }, /* (302) resolvetype ::= raisetype */ - { 195, -1 }, /* (303) selectnowith ::= oneselect */ - { 196, -1 }, /* (304) oneselect ::= values */ - { 210, -2 }, /* (305) sclp ::= selcollist COMMA */ - { 211, -1 }, /* (306) as ::= ID|STRING */ - { 174, -1 }, /* (307) expr ::= term */ - { 222, -1 }, /* (308) likeop ::= LIKE_KW|MATCH */ - { 209, -1 }, /* (309) exprlist ::= nexprlist */ - { 231, -1 }, /* (310) nmnum ::= plus_num */ - { 231, -1 }, /* (311) nmnum ::= nm */ - { 231, -1 }, /* (312) nmnum ::= ON */ - { 231, -1 }, /* (313) nmnum ::= DELETE */ - { 231, -1 }, /* (314) nmnum ::= DEFAULT */ - { 169, -1 }, /* (315) plus_num ::= INTEGER|FLOAT */ - { 236, 0 }, /* (316) foreach_clause ::= */ - { 236, -3 }, /* (317) foreach_clause ::= FOR EACH ROW */ - { 239, -1 }, /* (318) trnm ::= nm */ - { 240, 0 }, /* (319) tridxby ::= */ - { 241, -1 }, /* (320) database_kw_opt ::= DATABASE */ - { 241, 0 }, /* (321) database_kw_opt ::= */ - { 244, 0 }, /* (322) kwcolumn_opt ::= */ - { 244, -1 }, /* (323) kwcolumn_opt ::= COLUMNKW */ - { 246, -1 }, /* (324) vtabarglist ::= vtabarg */ - { 246, -3 }, /* (325) vtabarglist ::= vtabarglist COMMA vtabarg */ - { 247, -2 }, /* (326) vtabarg ::= vtabarg vtabargtoken */ - { 250, 0 }, /* (327) anylist ::= */ - { 250, -4 }, /* (328) anylist ::= anylist LP anylist RP */ - { 250, -2 }, /* (329) anylist ::= anylist ANY */ + { 163, -3 }, /* (80) select ::= WITH wqlist selectnowith */ + { 163, -4 }, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ + { 163, -1 }, /* (82) select ::= selectnowith */ + { 195, -3 }, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ + { 198, -1 }, /* (84) multiselect_op ::= UNION */ + { 198, -2 }, /* (85) multiselect_op ::= UNION ALL */ + { 198, -1 }, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ + { 196, -9 }, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + { 207, -4 }, /* (88) values ::= VALUES LP nexprlist RP */ + { 207, -5 }, /* (89) values ::= values COMMA LP exprlist RP */ + { 199, -1 }, /* (90) distinct ::= DISTINCT */ + { 199, -1 }, /* (91) distinct ::= ALL */ + { 199, 0 }, /* (92) distinct ::= */ + { 210, 0 }, /* (93) sclp ::= */ + { 200, -5 }, /* (94) selcollist ::= sclp scanpt expr scanpt as */ + { 200, -3 }, /* (95) selcollist ::= sclp scanpt STAR */ + { 200, -5 }, /* (96) selcollist ::= sclp scanpt nm DOT STAR */ + { 211, -2 }, /* (97) as ::= AS nm */ + { 211, 0 }, /* (98) as ::= */ + { 201, 0 }, /* (99) from ::= */ + { 201, -2 }, /* (100) from ::= FROM seltablist */ + { 213, -2 }, /* (101) stl_prefix ::= seltablist joinop */ + { 213, 0 }, /* (102) stl_prefix ::= */ + { 212, -7 }, /* (103) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + { 212, -9 }, /* (104) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + { 212, -7 }, /* (105) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + { 212, -7 }, /* (106) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + { 159, 0 }, /* (107) dbnm ::= */ + { 159, -2 }, /* (108) dbnm ::= DOT nm */ + { 194, -1 }, /* (109) fullname ::= nm */ + { 194, -3 }, /* (110) fullname ::= nm DOT nm */ + { 214, -1 }, /* (111) joinop ::= COMMA|JOIN */ + { 214, -2 }, /* (112) joinop ::= JOIN_KW JOIN */ + { 214, -3 }, /* (113) joinop ::= JOIN_KW nm JOIN */ + { 214, -4 }, /* (114) joinop ::= JOIN_KW nm nm JOIN */ + { 216, -2 }, /* (115) on_opt ::= ON expr */ + { 216, 0 }, /* (116) on_opt ::= */ + { 215, 0 }, /* (117) indexed_opt ::= */ + { 215, -3 }, /* (118) indexed_opt ::= INDEXED BY nm */ + { 215, -2 }, /* (119) indexed_opt ::= NOT INDEXED */ + { 217, -4 }, /* (120) using_opt ::= USING LP idlist RP */ + { 217, 0 }, /* (121) using_opt ::= */ + { 205, 0 }, /* (122) orderby_opt ::= */ + { 205, -3 }, /* (123) orderby_opt ::= ORDER BY sortlist */ + { 187, -4 }, /* (124) sortlist ::= sortlist COMMA expr sortorder */ + { 187, -2 }, /* (125) sortlist ::= expr sortorder */ + { 176, -1 }, /* (126) sortorder ::= ASC */ + { 176, -1 }, /* (127) sortorder ::= DESC */ + { 176, 0 }, /* (128) sortorder ::= */ + { 203, 0 }, /* (129) groupby_opt ::= */ + { 203, -3 }, /* (130) groupby_opt ::= GROUP BY nexprlist */ + { 204, 0 }, /* (131) having_opt ::= */ + { 204, -2 }, /* (132) having_opt ::= HAVING expr */ + { 206, 0 }, /* (133) limit_opt ::= */ + { 206, -2 }, /* (134) limit_opt ::= LIMIT expr */ + { 206, -4 }, /* (135) limit_opt ::= LIMIT expr OFFSET expr */ + { 206, -4 }, /* (136) limit_opt ::= LIMIT expr COMMA expr */ + { 149, -6 }, /* (137) cmd ::= with DELETE FROM fullname indexed_opt where_opt */ + { 202, 0 }, /* (138) where_opt ::= */ + { 202, -2 }, /* (139) where_opt ::= WHERE expr */ + { 149, -8 }, /* (140) cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */ + { 220, -5 }, /* (141) setlist ::= setlist COMMA nm EQ expr */ + { 220, -7 }, /* (142) setlist ::= setlist COMMA LP idlist RP EQ expr */ + { 220, -3 }, /* (143) setlist ::= nm EQ expr */ + { 220, -5 }, /* (144) setlist ::= LP idlist RP EQ expr */ + { 149, -6 }, /* (145) cmd ::= with insert_cmd INTO fullname idlist_opt select */ + { 149, -7 }, /* (146) cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */ + { 221, -2 }, /* (147) insert_cmd ::= INSERT orconf */ + { 221, -1 }, /* (148) insert_cmd ::= REPLACE */ + { 222, 0 }, /* (149) idlist_opt ::= */ + { 222, -3 }, /* (150) idlist_opt ::= LP idlist RP */ + { 218, -3 }, /* (151) idlist ::= idlist COMMA nm */ + { 218, -1 }, /* (152) idlist ::= nm */ + { 174, -3 }, /* (153) expr ::= LP expr RP */ + { 174, -1 }, /* (154) expr ::= ID|INDEXED */ + { 174, -1 }, /* (155) expr ::= JOIN_KW */ + { 174, -3 }, /* (156) expr ::= nm DOT nm */ + { 174, -5 }, /* (157) expr ::= nm DOT nm DOT nm */ + { 173, -1 }, /* (158) term ::= NULL|FLOAT|BLOB */ + { 173, -1 }, /* (159) term ::= STRING */ + { 173, -1 }, /* (160) term ::= INTEGER */ + { 174, -1 }, /* (161) expr ::= VARIABLE */ + { 174, -3 }, /* (162) expr ::= expr COLLATE ID|STRING */ + { 174, -6 }, /* (163) expr ::= CAST LP expr AS typetoken RP */ + { 174, -5 }, /* (164) expr ::= ID|INDEXED LP distinct exprlist RP */ + { 174, -4 }, /* (165) expr ::= ID|INDEXED LP STAR RP */ + { 173, -1 }, /* (166) term ::= CTIME_KW */ + { 174, -5 }, /* (167) expr ::= LP nexprlist COMMA expr RP */ + { 174, -3 }, /* (168) expr ::= expr AND expr */ + { 174, -3 }, /* (169) expr ::= expr OR expr */ + { 174, -3 }, /* (170) expr ::= expr LT|GT|GE|LE expr */ + { 174, -3 }, /* (171) expr ::= expr EQ|NE expr */ + { 174, -3 }, /* (172) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + { 174, -3 }, /* (173) expr ::= expr PLUS|MINUS expr */ + { 174, -3 }, /* (174) expr ::= expr STAR|SLASH|REM expr */ + { 174, -3 }, /* (175) expr ::= expr CONCAT expr */ + { 223, -2 }, /* (176) likeop ::= NOT LIKE_KW|MATCH */ + { 174, -3 }, /* (177) expr ::= expr likeop expr */ + { 174, -5 }, /* (178) expr ::= expr likeop expr ESCAPE expr */ + { 174, -2 }, /* (179) expr ::= expr ISNULL|NOTNULL */ + { 174, -3 }, /* (180) expr ::= expr NOT NULL */ + { 174, -3 }, /* (181) expr ::= expr IS expr */ + { 174, -4 }, /* (182) expr ::= expr IS NOT expr */ + { 174, -2 }, /* (183) expr ::= NOT expr */ + { 174, -2 }, /* (184) expr ::= BITNOT expr */ + { 174, -2 }, /* (185) expr ::= MINUS expr */ + { 174, -2 }, /* (186) expr ::= PLUS expr */ + { 224, -1 }, /* (187) between_op ::= BETWEEN */ + { 224, -2 }, /* (188) between_op ::= NOT BETWEEN */ + { 174, -5 }, /* (189) expr ::= expr between_op expr AND expr */ + { 225, -1 }, /* (190) in_op ::= IN */ + { 225, -2 }, /* (191) in_op ::= NOT IN */ + { 174, -5 }, /* (192) expr ::= expr in_op LP exprlist RP */ + { 174, -3 }, /* (193) expr ::= LP select RP */ + { 174, -5 }, /* (194) expr ::= expr in_op LP select RP */ + { 174, -5 }, /* (195) expr ::= expr in_op nm dbnm paren_exprlist */ + { 174, -4 }, /* (196) expr ::= EXISTS LP select RP */ + { 174, -5 }, /* (197) expr ::= CASE case_operand case_exprlist case_else END */ + { 228, -5 }, /* (198) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + { 228, -4 }, /* (199) case_exprlist ::= WHEN expr THEN expr */ + { 229, -2 }, /* (200) case_else ::= ELSE expr */ + { 229, 0 }, /* (201) case_else ::= */ + { 227, -1 }, /* (202) case_operand ::= expr */ + { 227, 0 }, /* (203) case_operand ::= */ + { 209, 0 }, /* (204) exprlist ::= */ + { 208, -3 }, /* (205) nexprlist ::= nexprlist COMMA expr */ + { 208, -1 }, /* (206) nexprlist ::= expr */ + { 226, 0 }, /* (207) paren_exprlist ::= */ + { 226, -3 }, /* (208) paren_exprlist ::= LP exprlist RP */ + { 149, -12 }, /* (209) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + { 230, -1 }, /* (210) uniqueflag ::= UNIQUE */ + { 230, 0 }, /* (211) uniqueflag ::= */ + { 178, 0 }, /* (212) eidlist_opt ::= */ + { 178, -3 }, /* (213) eidlist_opt ::= LP eidlist RP */ + { 188, -5 }, /* (214) eidlist ::= eidlist COMMA nm collate sortorder */ + { 188, -3 }, /* (215) eidlist ::= nm collate sortorder */ + { 231, 0 }, /* (216) collate ::= */ + { 231, -2 }, /* (217) collate ::= COLLATE ID|STRING */ + { 149, -4 }, /* (218) cmd ::= DROP INDEX ifexists fullname */ + { 149, -1 }, /* (219) cmd ::= VACUUM */ + { 149, -2 }, /* (220) cmd ::= VACUUM nm */ + { 149, -3 }, /* (221) cmd ::= PRAGMA nm dbnm */ + { 149, -5 }, /* (222) cmd ::= PRAGMA nm dbnm EQ nmnum */ + { 149, -6 }, /* (223) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + { 149, -5 }, /* (224) cmd ::= PRAGMA nm dbnm EQ minus_num */ + { 149, -6 }, /* (225) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + { 169, -2 }, /* (226) plus_num ::= PLUS INTEGER|FLOAT */ + { 170, -2 }, /* (227) minus_num ::= MINUS INTEGER|FLOAT */ + { 149, -5 }, /* (228) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + { 233, -11 }, /* (229) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + { 235, -1 }, /* (230) trigger_time ::= BEFORE|AFTER */ + { 235, -2 }, /* (231) trigger_time ::= INSTEAD OF */ + { 235, 0 }, /* (232) trigger_time ::= */ + { 236, -1 }, /* (233) trigger_event ::= DELETE|INSERT */ + { 236, -1 }, /* (234) trigger_event ::= UPDATE */ + { 236, -3 }, /* (235) trigger_event ::= UPDATE OF idlist */ + { 238, 0 }, /* (236) when_clause ::= */ + { 238, -2 }, /* (237) when_clause ::= WHEN expr */ + { 234, -3 }, /* (238) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + { 234, -2 }, /* (239) trigger_cmd_list ::= trigger_cmd SEMI */ + { 240, -3 }, /* (240) trnm ::= nm DOT nm */ + { 241, -3 }, /* (241) tridxby ::= INDEXED BY nm */ + { 241, -2 }, /* (242) tridxby ::= NOT INDEXED */ + { 239, -8 }, /* (243) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + { 239, -7 }, /* (244) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select scanpt */ + { 239, -6 }, /* (245) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + { 239, -3 }, /* (246) trigger_cmd ::= scanpt select scanpt */ + { 174, -4 }, /* (247) expr ::= RAISE LP IGNORE RP */ + { 174, -6 }, /* (248) expr ::= RAISE LP raisetype COMMA nm RP */ + { 192, -1 }, /* (249) raisetype ::= ROLLBACK */ + { 192, -1 }, /* (250) raisetype ::= ABORT */ + { 192, -1 }, /* (251) raisetype ::= FAIL */ + { 149, -4 }, /* (252) cmd ::= DROP TRIGGER ifexists fullname */ + { 149, -6 }, /* (253) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + { 149, -3 }, /* (254) cmd ::= DETACH database_kw_opt expr */ + { 243, 0 }, /* (255) key_opt ::= */ + { 243, -2 }, /* (256) key_opt ::= KEY expr */ + { 149, -1 }, /* (257) cmd ::= REINDEX */ + { 149, -3 }, /* (258) cmd ::= REINDEX nm dbnm */ + { 149, -1 }, /* (259) cmd ::= ANALYZE */ + { 149, -3 }, /* (260) cmd ::= ANALYZE nm dbnm */ + { 149, -6 }, /* (261) cmd ::= ALTER TABLE fullname RENAME TO nm */ + { 149, -7 }, /* (262) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + { 244, -1 }, /* (263) add_column_fullname ::= fullname */ + { 149, -1 }, /* (264) cmd ::= create_vtab */ + { 149, -4 }, /* (265) cmd ::= create_vtab LP vtabarglist RP */ + { 246, -8 }, /* (266) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + { 248, 0 }, /* (267) vtabarg ::= */ + { 249, -1 }, /* (268) vtabargtoken ::= ANY */ + { 249, -3 }, /* (269) vtabargtoken ::= lp anylist RP */ + { 250, -1 }, /* (270) lp ::= LP */ + { 219, -2 }, /* (271) with ::= WITH wqlist */ + { 219, -3 }, /* (272) with ::= WITH RECURSIVE wqlist */ + { 197, -6 }, /* (273) wqlist ::= nm eidlist_opt AS LP select RP */ + { 197, -8 }, /* (274) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + { 144, -1 }, /* (275) input ::= cmdlist */ + { 145, -2 }, /* (276) cmdlist ::= cmdlist ecmd */ + { 145, -1 }, /* (277) cmdlist ::= ecmd */ + { 146, -1 }, /* (278) ecmd ::= SEMI */ + { 146, -3 }, /* (279) ecmd ::= explain cmdx SEMI */ + { 147, 0 }, /* (280) explain ::= */ + { 151, 0 }, /* (281) trans_opt ::= */ + { 151, -1 }, /* (282) trans_opt ::= TRANSACTION */ + { 151, -2 }, /* (283) trans_opt ::= TRANSACTION nm */ + { 153, -1 }, /* (284) savepoint_opt ::= SAVEPOINT */ + { 153, 0 }, /* (285) savepoint_opt ::= */ + { 149, -2 }, /* (286) cmd ::= create_table create_table_args */ + { 160, -4 }, /* (287) columnlist ::= columnlist COMMA columnname carglist */ + { 160, -2 }, /* (288) columnlist ::= columnname carglist */ + { 152, -1 }, /* (289) nm ::= ID|INDEXED */ + { 152, -1 }, /* (290) nm ::= STRING */ + { 152, -1 }, /* (291) nm ::= JOIN_KW */ + { 166, -1 }, /* (292) typetoken ::= typename */ + { 167, -1 }, /* (293) typename ::= ID|STRING */ + { 168, -1 }, /* (294) signed ::= plus_num */ + { 168, -1 }, /* (295) signed ::= minus_num */ + { 165, -2 }, /* (296) carglist ::= carglist ccons */ + { 165, 0 }, /* (297) carglist ::= */ + { 172, -2 }, /* (298) ccons ::= NULL onconf */ + { 161, -2 }, /* (299) conslist_opt ::= COMMA conslist */ + { 184, -3 }, /* (300) conslist ::= conslist tconscomma tcons */ + { 184, -1 }, /* (301) conslist ::= tcons */ + { 185, 0 }, /* (302) tconscomma ::= */ + { 189, -1 }, /* (303) defer_subclause_opt ::= defer_subclause */ + { 191, -1 }, /* (304) resolvetype ::= raisetype */ + { 195, -1 }, /* (305) selectnowith ::= oneselect */ + { 196, -1 }, /* (306) oneselect ::= values */ + { 210, -2 }, /* (307) sclp ::= selcollist COMMA */ + { 211, -1 }, /* (308) as ::= ID|STRING */ + { 174, -1 }, /* (309) expr ::= term */ + { 223, -1 }, /* (310) likeop ::= LIKE_KW|MATCH */ + { 209, -1 }, /* (311) exprlist ::= nexprlist */ + { 232, -1 }, /* (312) nmnum ::= plus_num */ + { 232, -1 }, /* (313) nmnum ::= nm */ + { 232, -1 }, /* (314) nmnum ::= ON */ + { 232, -1 }, /* (315) nmnum ::= DELETE */ + { 232, -1 }, /* (316) nmnum ::= DEFAULT */ + { 169, -1 }, /* (317) plus_num ::= INTEGER|FLOAT */ + { 237, 0 }, /* (318) foreach_clause ::= */ + { 237, -3 }, /* (319) foreach_clause ::= FOR EACH ROW */ + { 240, -1 }, /* (320) trnm ::= nm */ + { 241, 0 }, /* (321) tridxby ::= */ + { 242, -1 }, /* (322) database_kw_opt ::= DATABASE */ + { 242, 0 }, /* (323) database_kw_opt ::= */ + { 245, 0 }, /* (324) kwcolumn_opt ::= */ + { 245, -1 }, /* (325) kwcolumn_opt ::= COLUMNKW */ + { 247, -1 }, /* (326) vtabarglist ::= vtabarg */ + { 247, -3 }, /* (327) vtabarglist ::= vtabarglist COMMA vtabarg */ + { 248, -2 }, /* (328) vtabarg ::= vtabarg vtabargtoken */ + { 251, 0 }, /* (329) anylist ::= */ + { 251, -4 }, /* (330) anylist ::= anylist LP anylist RP */ + { 251, -2 }, /* (331) anylist ::= anylist ANY */ + { 219, 0 }, /* (332) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -140810,8 +142491,8 @@ case 57: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==57); case 67: /* defer_subclause_opt ::= */ yytestcase(yyruleno==67); case 76: /* ifexists ::= */ yytestcase(yyruleno==76); - case 90: /* distinct ::= */ yytestcase(yyruleno==90); - case 213: /* collate ::= */ yytestcase(yyruleno==213); + case 92: /* distinct ::= */ yytestcase(yyruleno==92); + case 216: /* collate ::= */ yytestcase(yyruleno==216); {yymsp[1].minor.yy4 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ @@ -140847,7 +142528,7 @@ break; case 24: /* typetoken ::= */ case 60: /* conslist_opt ::= */ yytestcase(yyruleno==60); - case 96: /* as ::= */ yytestcase(yyruleno==96); + case 98: /* as ::= */ yytestcase(yyruleno==98); {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} break; case 25: /* typetoken ::= typename LP signed RP */ @@ -140891,6 +142572,10 @@ case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */ { Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0); + if( p ){ + sqlite3ExprIdToTrueFalse(p); + testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) ); + } sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n); } break; @@ -140954,14 +142639,14 @@ break; case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71); - case 144: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==144); + case 147: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==147); {yymsp[-1].minor.yy4 = yymsp[0].minor.yy4;} break; case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75); - case 185: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==185); - case 188: /* in_op ::= NOT IN */ yytestcase(yyruleno==188); - case 214: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==214); + case 188: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==188); + case 191: /* in_op ::= NOT IN */ yytestcase(yyruleno==191); + case 217: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==217); {yymsp[-1].minor.yy4 = 1;} break; case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ @@ -140997,7 +142682,7 @@ {yymsp[0].minor.yy4 = OE_Ignore;} break; case 73: /* resolvetype ::= REPLACE */ - case 145: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==145); + case 148: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==148); {yymsp[0].minor.yy4 = OE_Replace;} break; case 74: /* cmd ::= DROP TABLE ifexists fullname */ @@ -141022,7 +142707,7 @@ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy387); } break; - case 80: /* select ::= with selectnowith */ + case 80: /* select ::= WITH wqlist selectnowith */ { Select *p = yymsp[0].minor.yy387; if( p ){ @@ -141031,10 +142716,31 @@ }else{ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy451); } - yymsp[-1].minor.yy387 = p; /*A-overwrites-W*/ + yymsp[-2].minor.yy387 = p; } break; - case 81: /* selectnowith ::= selectnowith multiselect_op oneselect */ + case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */ +{ + Select *p = yymsp[0].minor.yy387; + if( p ){ + p->pWith = yymsp[-1].minor.yy451; + parserDoubleLinkSelect(pParse, p); + }else{ + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy451); + } + yymsp[-3].minor.yy387 = p; +} + break; + case 82: /* select ::= selectnowith */ +{ + Select *p = yymsp[0].minor.yy387; + if( p ){ + parserDoubleLinkSelect(pParse, p); + } + yymsp[0].minor.yy387 = p; /*A-overwrites-X*/ +} + break; + case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */ { Select *pRhs = yymsp[0].minor.yy387; Select *pLhs = yymsp[-2].minor.yy387; @@ -141058,14 +142764,14 @@ yymsp[-2].minor.yy387 = pRhs; } break; - case 82: /* multiselect_op ::= UNION */ - case 84: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==84); + case 84: /* multiselect_op ::= UNION */ + case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86); {yymsp[0].minor.yy4 = yymsp[0].major; /*A-overwrites-OP*/} break; - case 83: /* multiselect_op ::= UNION ALL */ + case 85: /* multiselect_op ::= UNION ALL */ {yymsp[-1].minor.yy4 = TK_ALL;} break; - case 85: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { #if SELECTTRACE_ENABLED Token s = yymsp[-8].minor.yy0; /*A-overwrites-S*/ @@ -141084,8 +142790,7 @@ if( yymsp[-8].minor.yy387!=0 ){ const char *z = s.z+6; int i; - sqlite3_snprintf(sizeof(yymsp[-8].minor.yy387->zSelName), yymsp[-8].minor.yy387->zSelName, "#%d", - ++pParse->nSelect); + sqlite3_snprintf(sizeof(yymsp[-8].minor.yy387->zSelName), yymsp[-8].minor.yy387->zSelName,"#%d",++pParse->nSelect); while( z[0]==' ' ) z++; if( z[0]=='/' && z[1]=='*' ){ z += 2; @@ -141097,12 +142802,12 @@ #endif /* SELECTRACE_ENABLED */ } break; - case 86: /* values ::= VALUES LP nexprlist RP */ + case 88: /* values ::= VALUES LP nexprlist RP */ { yymsp[-3].minor.yy387 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0); } break; - case 87: /* values ::= values COMMA LP exprlist RP */ + case 89: /* values ::= values COMMA LP exprlist RP */ { Select *pRight, *pLeft = yymsp[-4].minor.yy387; pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0); @@ -141116,34 +142821,34 @@ } } break; - case 88: /* distinct ::= DISTINCT */ + case 90: /* distinct ::= DISTINCT */ {yymsp[0].minor.yy4 = SF_Distinct;} break; - case 89: /* distinct ::= ALL */ + case 91: /* distinct ::= ALL */ {yymsp[0].minor.yy4 = SF_All;} break; - case 91: /* sclp ::= */ - case 119: /* orderby_opt ::= */ yytestcase(yyruleno==119); - case 126: /* groupby_opt ::= */ yytestcase(yyruleno==126); - case 201: /* exprlist ::= */ yytestcase(yyruleno==201); - case 204: /* paren_exprlist ::= */ yytestcase(yyruleno==204); - case 209: /* eidlist_opt ::= */ yytestcase(yyruleno==209); + case 93: /* sclp ::= */ + case 122: /* orderby_opt ::= */ yytestcase(yyruleno==122); + case 129: /* groupby_opt ::= */ yytestcase(yyruleno==129); + case 204: /* exprlist ::= */ yytestcase(yyruleno==204); + case 207: /* paren_exprlist ::= */ yytestcase(yyruleno==207); + case 212: /* eidlist_opt ::= */ yytestcase(yyruleno==212); {yymsp[1].minor.yy322 = 0;} break; - case 92: /* selcollist ::= sclp scanpt expr scanpt as */ + case 94: /* selcollist ::= sclp scanpt expr scanpt as */ { yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy314); if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1); sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy336,yymsp[-1].minor.yy336); } break; - case 93: /* selcollist ::= sclp scanpt STAR */ + case 95: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); } break; - case 94: /* selcollist ::= sclp scanpt nm DOT STAR */ + case 96: /* selcollist ::= sclp scanpt nm DOT STAR */ { Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); @@ -141151,47 +142856,47 @@ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); } break; - case 95: /* as ::= AS nm */ - case 106: /* dbnm ::= DOT nm */ yytestcase(yyruleno==106); - case 223: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==223); - case 224: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==224); + case 97: /* as ::= AS nm */ + case 108: /* dbnm ::= DOT nm */ yytestcase(yyruleno==108); + case 226: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==226); + case 227: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==227); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; - case 97: /* from ::= */ + case 99: /* from ::= */ {yymsp[1].minor.yy259 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy259));} break; - case 98: /* from ::= FROM seltablist */ + case 100: /* from ::= FROM seltablist */ { yymsp[-1].minor.yy259 = yymsp[0].minor.yy259; sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy259); } break; - case 99: /* stl_prefix ::= seltablist joinop */ + case 101: /* stl_prefix ::= seltablist joinop */ { if( ALWAYS(yymsp[-1].minor.yy259 && yymsp[-1].minor.yy259->nSrc>0) ) yymsp[-1].minor.yy259->a[yymsp[-1].minor.yy259->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy4; } break; - case 100: /* stl_prefix ::= */ + case 102: /* stl_prefix ::= */ {yymsp[1].minor.yy259 = 0;} break; - case 101: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + case 103: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { yymsp[-6].minor.yy259 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy259,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy314,yymsp[0].minor.yy384); sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy259, &yymsp[-2].minor.yy0); } break; - case 102: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + case 104: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ { yymsp[-8].minor.yy259 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy259,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy314,yymsp[0].minor.yy384); sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy259, yymsp[-4].minor.yy322); } break; - case 103: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + case 105: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { yymsp[-6].minor.yy259 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy259,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy387,yymsp[-1].minor.yy314,yymsp[0].minor.yy384); } break; - case 104: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + case 106: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { if( yymsp[-6].minor.yy259==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy314==0 && yymsp[0].minor.yy384==0 ){ yymsp[-6].minor.yy259 = yymsp[-4].minor.yy259; @@ -141215,153 +142920,152 @@ } } break; - case 105: /* dbnm ::= */ - case 114: /* indexed_opt ::= */ yytestcase(yyruleno==114); + case 107: /* dbnm ::= */ + case 117: /* indexed_opt ::= */ yytestcase(yyruleno==117); {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; - case 107: /* fullname ::= nm dbnm */ -{yymsp[-1].minor.yy259 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 109: /* fullname ::= nm */ +{yymsp[0].minor.yy259 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; - case 108: /* joinop ::= COMMA|JOIN */ + case 110: /* fullname ::= nm DOT nm */ +{yymsp[-2].minor.yy259 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + break; + case 111: /* joinop ::= COMMA|JOIN */ { yymsp[0].minor.yy4 = JT_INNER; } break; - case 109: /* joinop ::= JOIN_KW JOIN */ + case 112: /* joinop ::= JOIN_KW JOIN */ {yymsp[-1].minor.yy4 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; - case 110: /* joinop ::= JOIN_KW nm JOIN */ + case 113: /* joinop ::= JOIN_KW nm JOIN */ {yymsp[-2].minor.yy4 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; - case 111: /* joinop ::= JOIN_KW nm nm JOIN */ + case 114: /* joinop ::= JOIN_KW nm nm JOIN */ {yymsp[-3].minor.yy4 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; - case 112: /* on_opt ::= ON expr */ - case 129: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==129); - case 136: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==136); - case 197: /* case_else ::= ELSE expr */ yytestcase(yyruleno==197); + case 115: /* on_opt ::= ON expr */ + case 132: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==132); + case 139: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==139); + case 200: /* case_else ::= ELSE expr */ yytestcase(yyruleno==200); {yymsp[-1].minor.yy314 = yymsp[0].minor.yy314;} break; - case 113: /* on_opt ::= */ - case 128: /* having_opt ::= */ yytestcase(yyruleno==128); - case 130: /* limit_opt ::= */ yytestcase(yyruleno==130); - case 135: /* where_opt ::= */ yytestcase(yyruleno==135); - case 198: /* case_else ::= */ yytestcase(yyruleno==198); - case 200: /* case_operand ::= */ yytestcase(yyruleno==200); + case 116: /* on_opt ::= */ + case 131: /* having_opt ::= */ yytestcase(yyruleno==131); + case 133: /* limit_opt ::= */ yytestcase(yyruleno==133); + case 138: /* where_opt ::= */ yytestcase(yyruleno==138); + case 201: /* case_else ::= */ yytestcase(yyruleno==201); + case 203: /* case_operand ::= */ yytestcase(yyruleno==203); {yymsp[1].minor.yy314 = 0;} break; - case 115: /* indexed_opt ::= INDEXED BY nm */ + case 118: /* indexed_opt ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} break; - case 116: /* indexed_opt ::= NOT INDEXED */ + case 119: /* indexed_opt ::= NOT INDEXED */ {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; - case 117: /* using_opt ::= USING LP idlist RP */ + case 120: /* using_opt ::= USING LP idlist RP */ {yymsp[-3].minor.yy384 = yymsp[-1].minor.yy384;} break; - case 118: /* using_opt ::= */ - case 146: /* idlist_opt ::= */ yytestcase(yyruleno==146); + case 121: /* using_opt ::= */ + case 149: /* idlist_opt ::= */ yytestcase(yyruleno==149); {yymsp[1].minor.yy384 = 0;} break; - case 120: /* orderby_opt ::= ORDER BY sortlist */ - case 127: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==127); + case 123: /* orderby_opt ::= ORDER BY sortlist */ + case 130: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==130); {yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;} break; - case 121: /* sortlist ::= sortlist COMMA expr sortorder */ + case 124: /* sortlist ::= sortlist COMMA expr sortorder */ { yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322,yymsp[-1].minor.yy314); sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy322,yymsp[0].minor.yy4); } break; - case 122: /* sortlist ::= expr sortorder */ + case 125: /* sortlist ::= expr sortorder */ { yymsp[-1].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy314); /*A-overwrites-Y*/ sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy322,yymsp[0].minor.yy4); } break; - case 123: /* sortorder ::= ASC */ + case 126: /* sortorder ::= ASC */ {yymsp[0].minor.yy4 = SQLITE_SO_ASC;} break; - case 124: /* sortorder ::= DESC */ + case 127: /* sortorder ::= DESC */ {yymsp[0].minor.yy4 = SQLITE_SO_DESC;} break; - case 125: /* sortorder ::= */ + case 128: /* sortorder ::= */ {yymsp[1].minor.yy4 = SQLITE_SO_UNDEFINED;} break; - case 131: /* limit_opt ::= LIMIT expr */ + case 134: /* limit_opt ::= LIMIT expr */ {yymsp[-1].minor.yy314 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy314,0);} break; - case 132: /* limit_opt ::= LIMIT expr OFFSET expr */ + case 135: /* limit_opt ::= LIMIT expr OFFSET expr */ {yymsp[-3].minor.yy314 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy314,yymsp[0].minor.yy314);} break; - case 133: /* limit_opt ::= LIMIT expr COMMA expr */ + case 136: /* limit_opt ::= LIMIT expr COMMA expr */ {yymsp[-3].minor.yy314 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy314,yymsp[-2].minor.yy314);} break; - case 134: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */ + case 137: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */ { - sqlite3WithPush(pParse, yymsp[-5].minor.yy451, 1); sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy259, &yymsp[-1].minor.yy0); sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy259,yymsp[0].minor.yy314,0,0); } break; - case 137: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */ + case 140: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */ { - sqlite3WithPush(pParse, yymsp[-7].minor.yy451, 1); sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy259, &yymsp[-3].minor.yy0); sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy322,"set list"); sqlite3Update(pParse,yymsp[-4].minor.yy259,yymsp[-1].minor.yy322,yymsp[0].minor.yy314,yymsp[-5].minor.yy4,0,0); } break; - case 138: /* setlist ::= setlist COMMA nm EQ expr */ + case 141: /* setlist ::= setlist COMMA nm EQ expr */ { yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy314); sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1); } break; - case 139: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + case 142: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { yymsp[-6].minor.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy384, yymsp[0].minor.yy314); } break; - case 140: /* setlist ::= nm EQ expr */ + case 143: /* setlist ::= nm EQ expr */ { yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy314); sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1); } yymsp[-2].minor.yy322 = yylhsminor.yy322; break; - case 141: /* setlist ::= LP idlist RP EQ expr */ + case 144: /* setlist ::= LP idlist RP EQ expr */ { yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy384, yymsp[0].minor.yy314); } break; - case 142: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */ + case 145: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */ { - sqlite3WithPush(pParse, yymsp[-5].minor.yy451, 1); sqlite3Insert(pParse, yymsp[-2].minor.yy259, yymsp[0].minor.yy387, yymsp[-1].minor.yy384, yymsp[-4].minor.yy4); } break; - case 143: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */ + case 146: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */ { - sqlite3WithPush(pParse, yymsp[-6].minor.yy451, 1); sqlite3Insert(pParse, yymsp[-3].minor.yy259, 0, yymsp[-2].minor.yy384, yymsp[-5].minor.yy4); } break; - case 147: /* idlist_opt ::= LP idlist RP */ + case 150: /* idlist_opt ::= LP idlist RP */ {yymsp[-2].minor.yy384 = yymsp[-1].minor.yy384;} break; - case 148: /* idlist ::= idlist COMMA nm */ + case 151: /* idlist ::= idlist COMMA nm */ {yymsp[-2].minor.yy384 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy384,&yymsp[0].minor.yy0);} break; - case 149: /* idlist ::= nm */ + case 152: /* idlist ::= nm */ {yymsp[0].minor.yy384 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; - case 150: /* expr ::= LP expr RP */ + case 153: /* expr ::= LP expr RP */ {yymsp[-2].minor.yy314 = yymsp[-1].minor.yy314;} break; - case 151: /* expr ::= ID|INDEXED */ - case 152: /* expr ::= JOIN_KW */ yytestcase(yyruleno==152); + case 154: /* expr ::= ID|INDEXED */ + case 155: /* expr ::= JOIN_KW */ yytestcase(yyruleno==155); {yymsp[0].minor.yy314=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 153: /* expr ::= nm DOT nm */ + case 156: /* expr ::= nm DOT nm */ { Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1); @@ -141369,7 +143073,7 @@ } yymsp[-2].minor.yy314 = yylhsminor.yy314; break; - case 154: /* expr ::= nm DOT nm DOT nm */ + case 157: /* expr ::= nm DOT nm DOT nm */ { Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1); Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); @@ -141379,17 +143083,17 @@ } yymsp[-4].minor.yy314 = yylhsminor.yy314; break; - case 155: /* term ::= NULL|FLOAT|BLOB */ - case 156: /* term ::= STRING */ yytestcase(yyruleno==156); + case 158: /* term ::= NULL|FLOAT|BLOB */ + case 159: /* term ::= STRING */ yytestcase(yyruleno==159); {yymsp[0].minor.yy314=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 157: /* term ::= INTEGER */ + case 160: /* term ::= INTEGER */ { yylhsminor.yy314 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); } yymsp[0].minor.yy314 = yylhsminor.yy314; break; - case 158: /* expr ::= VARIABLE */ + case 161: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; @@ -141411,18 +143115,18 @@ } } break; - case 159: /* expr ::= expr COLLATE ID|STRING */ + case 162: /* expr ::= expr COLLATE ID|STRING */ { yymsp[-2].minor.yy314 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy314, &yymsp[0].minor.yy0, 1); } break; - case 160: /* expr ::= CAST LP expr AS typetoken RP */ + case 163: /* expr ::= CAST LP expr AS typetoken RP */ { yymsp[-5].minor.yy314 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy314, yymsp[-3].minor.yy314, 0); } break; - case 161: /* expr ::= ID|INDEXED LP distinct exprlist RP */ + case 164: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { if( yymsp[-1].minor.yy322 && yymsp[-1].minor.yy322->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0); @@ -141434,19 +143138,19 @@ } yymsp[-4].minor.yy314 = yylhsminor.yy314; break; - case 162: /* expr ::= ID|INDEXED LP STAR RP */ + case 165: /* expr ::= ID|INDEXED LP STAR RP */ { yylhsminor.yy314 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0); } yymsp[-3].minor.yy314 = yylhsminor.yy314; break; - case 163: /* term ::= CTIME_KW */ + case 166: /* term ::= CTIME_KW */ { yylhsminor.yy314 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0); } yymsp[0].minor.yy314 = yylhsminor.yy314; break; - case 164: /* expr ::= LP nexprlist COMMA expr RP */ + case 167: /* expr ::= LP nexprlist COMMA expr RP */ { ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy314); yymsp[-4].minor.yy314 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); @@ -141457,20 +143161,20 @@ } } break; - case 165: /* expr ::= expr AND expr */ - case 166: /* expr ::= expr OR expr */ yytestcase(yyruleno==166); - case 167: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==167); - case 168: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==168); - case 169: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==169); - case 170: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==170); - case 171: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==171); - case 172: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==172); + case 168: /* expr ::= expr AND expr */ + case 169: /* expr ::= expr OR expr */ yytestcase(yyruleno==169); + case 170: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==170); + case 171: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==171); + case 172: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==172); + case 173: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==173); + case 174: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==174); + case 175: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==175); {yymsp[-2].minor.yy314=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy314,yymsp[0].minor.yy314);} break; - case 173: /* likeop ::= NOT LIKE_KW|MATCH */ + case 176: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} break; - case 174: /* expr ::= expr likeop expr */ + case 177: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; @@ -141482,7 +143186,7 @@ if( yymsp[-2].minor.yy314 ) yymsp[-2].minor.yy314->flags |= EP_InfixFunc; } break; - case 175: /* expr ::= expr likeop expr ESCAPE expr */ + case 178: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; @@ -141495,39 +143199,39 @@ if( yymsp[-4].minor.yy314 ) yymsp[-4].minor.yy314->flags |= EP_InfixFunc; } break; - case 176: /* expr ::= expr ISNULL|NOTNULL */ + case 179: /* expr ::= expr ISNULL|NOTNULL */ {yymsp[-1].minor.yy314 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy314,0);} break; - case 177: /* expr ::= expr NOT NULL */ + case 180: /* expr ::= expr NOT NULL */ {yymsp[-2].minor.yy314 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy314,0);} break; - case 178: /* expr ::= expr IS expr */ + case 181: /* expr ::= expr IS expr */ { yymsp[-2].minor.yy314 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy314,yymsp[0].minor.yy314); binaryToUnaryIfNull(pParse, yymsp[0].minor.yy314, yymsp[-2].minor.yy314, TK_ISNULL); } break; - case 179: /* expr ::= expr IS NOT expr */ + case 182: /* expr ::= expr IS NOT expr */ { yymsp[-3].minor.yy314 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy314,yymsp[0].minor.yy314); binaryToUnaryIfNull(pParse, yymsp[0].minor.yy314, yymsp[-3].minor.yy314, TK_NOTNULL); } break; - case 180: /* expr ::= NOT expr */ - case 181: /* expr ::= BITNOT expr */ yytestcase(yyruleno==181); + case 183: /* expr ::= NOT expr */ + case 184: /* expr ::= BITNOT expr */ yytestcase(yyruleno==184); {yymsp[-1].minor.yy314 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy314, 0);/*A-overwrites-B*/} break; - case 182: /* expr ::= MINUS expr */ + case 185: /* expr ::= MINUS expr */ {yymsp[-1].minor.yy314 = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy314, 0);} break; - case 183: /* expr ::= PLUS expr */ + case 186: /* expr ::= PLUS expr */ {yymsp[-1].minor.yy314 = sqlite3PExpr(pParse, TK_UPLUS, yymsp[0].minor.yy314, 0);} break; - case 184: /* between_op ::= BETWEEN */ - case 187: /* in_op ::= IN */ yytestcase(yyruleno==187); + case 187: /* between_op ::= BETWEEN */ + case 190: /* in_op ::= IN */ yytestcase(yyruleno==190); {yymsp[0].minor.yy4 = 0;} break; - case 186: /* expr ::= expr between_op expr AND expr */ + case 189: /* expr ::= expr between_op expr AND expr */ { ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy314); pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy314); @@ -141540,7 +143244,7 @@ if( yymsp[-3].minor.yy4 ) yymsp[-4].minor.yy314 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy314, 0); } break; - case 189: /* expr ::= expr in_op LP exprlist RP */ + case 192: /* expr ::= expr in_op LP exprlist RP */ { if( yymsp[-1].minor.yy322==0 ){ /* Expressions of the form @@ -141592,20 +143296,20 @@ } } break; - case 190: /* expr ::= LP select RP */ + case 193: /* expr ::= LP select RP */ { yymsp[-2].minor.yy314 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy314, yymsp[-1].minor.yy387); } break; - case 191: /* expr ::= expr in_op LP select RP */ + case 194: /* expr ::= expr in_op LP select RP */ { yymsp[-4].minor.yy314 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy314, 0); sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy314, yymsp[-1].minor.yy387); if( yymsp[-3].minor.yy4 ) yymsp[-4].minor.yy314 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy314, 0); } break; - case 192: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 195: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); @@ -141615,14 +143319,14 @@ if( yymsp[-3].minor.yy4 ) yymsp[-4].minor.yy314 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy314, 0); } break; - case 193: /* expr ::= EXISTS LP select RP */ + case 196: /* expr ::= EXISTS LP select RP */ { Expr *p; p = yymsp[-3].minor.yy314 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy387); } break; - case 194: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 197: /* expr ::= CASE case_operand case_exprlist case_else END */ { yymsp[-4].minor.yy314 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy314, 0); if( yymsp[-4].minor.yy314 ){ @@ -141634,80 +143338,80 @@ } } break; - case 195: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 198: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy314); yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy314); } break; - case 196: /* case_exprlist ::= WHEN expr THEN expr */ + case 199: /* case_exprlist ::= WHEN expr THEN expr */ { yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy314); yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy314); } break; - case 199: /* case_operand ::= expr */ + case 202: /* case_operand ::= expr */ {yymsp[0].minor.yy314 = yymsp[0].minor.yy314; /*A-overwrites-X*/} break; - case 202: /* nexprlist ::= nexprlist COMMA expr */ + case 205: /* nexprlist ::= nexprlist COMMA expr */ {yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy314);} break; - case 203: /* nexprlist ::= expr */ + case 206: /* nexprlist ::= expr */ {yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy314); /*A-overwrites-Y*/} break; - case 205: /* paren_exprlist ::= LP exprlist RP */ - case 210: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==210); + case 208: /* paren_exprlist ::= LP exprlist RP */ + case 213: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==213); {yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} break; - case 206: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 209: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy4, &yymsp[-11].minor.yy0, yymsp[0].minor.yy314, SQLITE_SO_ASC, yymsp[-8].minor.yy4, SQLITE_IDXTYPE_APPDEF); } break; - case 207: /* uniqueflag ::= UNIQUE */ - case 247: /* raisetype ::= ABORT */ yytestcase(yyruleno==247); + case 210: /* uniqueflag ::= UNIQUE */ + case 250: /* raisetype ::= ABORT */ yytestcase(yyruleno==250); {yymsp[0].minor.yy4 = OE_Abort;} break; - case 208: /* uniqueflag ::= */ + case 211: /* uniqueflag ::= */ {yymsp[1].minor.yy4 = OE_None;} break; - case 211: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 214: /* eidlist ::= eidlist COMMA nm collate sortorder */ { yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy4, yymsp[0].minor.yy4); } break; - case 212: /* eidlist ::= nm collate sortorder */ + case 215: /* eidlist ::= nm collate sortorder */ { yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy4, yymsp[0].minor.yy4); /*A-overwrites-Y*/ } break; - case 215: /* cmd ::= DROP INDEX ifexists fullname */ + case 218: /* cmd ::= DROP INDEX ifexists fullname */ {sqlite3DropIndex(pParse, yymsp[0].minor.yy259, yymsp[-1].minor.yy4);} break; - case 216: /* cmd ::= VACUUM */ + case 219: /* cmd ::= VACUUM */ {sqlite3Vacuum(pParse,0);} break; - case 217: /* cmd ::= VACUUM nm */ + case 220: /* cmd ::= VACUUM nm */ {sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);} break; - case 218: /* cmd ::= PRAGMA nm dbnm */ + case 221: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 219: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 222: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 220: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 223: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 221: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 224: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 222: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 225: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 225: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 228: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; @@ -141715,50 +143419,50 @@ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy203, &all); } break; - case 226: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 229: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy4, yymsp[-4].minor.yy90.a, yymsp[-4].minor.yy90.b, yymsp[-2].minor.yy259, yymsp[0].minor.yy314, yymsp[-10].minor.yy4, yymsp[-8].minor.yy4); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 227: /* trigger_time ::= BEFORE|AFTER */ + case 230: /* trigger_time ::= BEFORE|AFTER */ { yymsp[0].minor.yy4 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 228: /* trigger_time ::= INSTEAD OF */ + case 231: /* trigger_time ::= INSTEAD OF */ { yymsp[-1].minor.yy4 = TK_INSTEAD;} break; - case 229: /* trigger_time ::= */ + case 232: /* trigger_time ::= */ { yymsp[1].minor.yy4 = TK_BEFORE; } break; - case 230: /* trigger_event ::= DELETE|INSERT */ - case 231: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==231); + case 233: /* trigger_event ::= DELETE|INSERT */ + case 234: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==234); {yymsp[0].minor.yy90.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy90.b = 0;} break; - case 232: /* trigger_event ::= UPDATE OF idlist */ + case 235: /* trigger_event ::= UPDATE OF idlist */ {yymsp[-2].minor.yy90.a = TK_UPDATE; yymsp[-2].minor.yy90.b = yymsp[0].minor.yy384;} break; - case 233: /* when_clause ::= */ - case 252: /* key_opt ::= */ yytestcase(yyruleno==252); + case 236: /* when_clause ::= */ + case 255: /* key_opt ::= */ yytestcase(yyruleno==255); { yymsp[1].minor.yy314 = 0; } break; - case 234: /* when_clause ::= WHEN expr */ - case 253: /* key_opt ::= KEY expr */ yytestcase(yyruleno==253); + case 237: /* when_clause ::= WHEN expr */ + case 256: /* key_opt ::= KEY expr */ yytestcase(yyruleno==256); { yymsp[-1].minor.yy314 = yymsp[0].minor.yy314; } break; - case 235: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 238: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { assert( yymsp[-2].minor.yy203!=0 ); yymsp[-2].minor.yy203->pLast->pNext = yymsp[-1].minor.yy203; yymsp[-2].minor.yy203->pLast = yymsp[-1].minor.yy203; } break; - case 236: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 239: /* trigger_cmd_list ::= trigger_cmd SEMI */ { assert( yymsp[-1].minor.yy203!=0 ); yymsp[-1].minor.yy203->pLast = yymsp[-1].minor.yy203; } break; - case 237: /* trnm ::= nm DOT nm */ + case 240: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -141766,37 +143470,37 @@ "statements within triggers"); } break; - case 238: /* tridxby ::= INDEXED BY nm */ + case 241: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 239: /* tridxby ::= NOT INDEXED */ + case 242: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 240: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + case 243: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ {yylhsminor.yy203 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy314, yymsp[-6].minor.yy4, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy336);} yymsp[-7].minor.yy203 = yylhsminor.yy203; break; - case 241: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select scanpt */ + case 244: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select scanpt */ {yylhsminor.yy203 = sqlite3TriggerInsertStep(pParse->db,&yymsp[-3].minor.yy0,yymsp[-2].minor.yy384,yymsp[-1].minor.yy387,yymsp[-5].minor.yy4,yymsp[-6].minor.yy336,yymsp[0].minor.yy336);/*yylhsminor.yy203-overwrites-yymsp[-5].minor.yy4*/} yymsp[-6].minor.yy203 = yylhsminor.yy203; break; - case 242: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + case 245: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ {yylhsminor.yy203 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy314, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy336);} yymsp[-5].minor.yy203 = yylhsminor.yy203; break; - case 243: /* trigger_cmd ::= scanpt select scanpt */ + case 246: /* trigger_cmd ::= scanpt select scanpt */ {yylhsminor.yy203 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy387, yymsp[-2].minor.yy336, yymsp[0].minor.yy336); /*yylhsminor.yy203-overwrites-yymsp[-1].minor.yy387*/} yymsp[-2].minor.yy203 = yylhsminor.yy203; break; - case 244: /* expr ::= RAISE LP IGNORE RP */ + case 247: /* expr ::= RAISE LP IGNORE RP */ { yymsp[-3].minor.yy314 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); if( yymsp[-3].minor.yy314 ){ @@ -141804,7 +143508,7 @@ } } break; - case 245: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 248: /* expr ::= RAISE LP raisetype COMMA nm RP */ { yymsp[-5].minor.yy314 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); if( yymsp[-5].minor.yy314 ) { @@ -141812,152 +143516,148 @@ } } break; - case 246: /* raisetype ::= ROLLBACK */ + case 249: /* raisetype ::= ROLLBACK */ {yymsp[0].minor.yy4 = OE_Rollback;} break; - case 248: /* raisetype ::= FAIL */ + case 251: /* raisetype ::= FAIL */ {yymsp[0].minor.yy4 = OE_Fail;} break; - case 249: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 252: /* cmd ::= DROP TRIGGER ifexists fullname */ { sqlite3DropTrigger(pParse,yymsp[0].minor.yy259,yymsp[-1].minor.yy4); } break; - case 250: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 253: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { sqlite3Attach(pParse, yymsp[-3].minor.yy314, yymsp[-1].minor.yy314, yymsp[0].minor.yy314); } break; - case 251: /* cmd ::= DETACH database_kw_opt expr */ + case 254: /* cmd ::= DETACH database_kw_opt expr */ { sqlite3Detach(pParse, yymsp[0].minor.yy314); } break; - case 254: /* cmd ::= REINDEX */ + case 257: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 255: /* cmd ::= REINDEX nm dbnm */ + case 258: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 256: /* cmd ::= ANALYZE */ + case 259: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 257: /* cmd ::= ANALYZE nm dbnm */ + case 260: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 258: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 261: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy259,&yymsp[0].minor.yy0); } break; - case 259: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 262: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 260: /* add_column_fullname ::= fullname */ + case 263: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy259); } break; - case 261: /* cmd ::= create_vtab */ + case 264: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 262: /* cmd ::= create_vtab LP vtabarglist RP */ + case 265: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 263: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 266: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy4); } break; - case 264: /* vtabarg ::= */ + case 267: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 265: /* vtabargtoken ::= ANY */ - case 266: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==266); - case 267: /* lp ::= LP */ yytestcase(yyruleno==267); + case 268: /* vtabargtoken ::= ANY */ + case 269: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==269); + case 270: /* lp ::= LP */ yytestcase(yyruleno==270); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 268: /* with ::= */ -{yymsp[1].minor.yy451 = 0;} + case 271: /* with ::= WITH wqlist */ + case 272: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==272); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy451, 1); } break; - case 269: /* with ::= WITH wqlist */ -{ yymsp[-1].minor.yy451 = yymsp[0].minor.yy451; } - break; - case 270: /* with ::= WITH RECURSIVE wqlist */ -{ yymsp[-2].minor.yy451 = yymsp[0].minor.yy451; } - break; - case 271: /* wqlist ::= nm eidlist_opt AS LP select RP */ + case 273: /* wqlist ::= nm eidlist_opt AS LP select RP */ { yymsp[-5].minor.yy451 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy387); /*A-overwrites-X*/ } break; - case 272: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + case 274: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ { yymsp[-7].minor.yy451 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy451, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy387); } break; default: - /* (273) input ::= cmdlist */ yytestcase(yyruleno==273); - /* (274) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==274); - /* (275) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=275); - /* (276) ecmd ::= SEMI */ yytestcase(yyruleno==276); - /* (277) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==277); - /* (278) explain ::= */ yytestcase(yyruleno==278); - /* (279) trans_opt ::= */ yytestcase(yyruleno==279); - /* (280) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==280); - /* (281) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==281); - /* (282) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==282); - /* (283) savepoint_opt ::= */ yytestcase(yyruleno==283); - /* (284) cmd ::= create_table create_table_args */ yytestcase(yyruleno==284); - /* (285) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==285); - /* (286) columnlist ::= columnname carglist */ yytestcase(yyruleno==286); - /* (287) nm ::= ID|INDEXED */ yytestcase(yyruleno==287); - /* (288) nm ::= STRING */ yytestcase(yyruleno==288); - /* (289) nm ::= JOIN_KW */ yytestcase(yyruleno==289); - /* (290) typetoken ::= typename */ yytestcase(yyruleno==290); - /* (291) typename ::= ID|STRING */ yytestcase(yyruleno==291); - /* (292) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=292); - /* (293) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=293); - /* (294) carglist ::= carglist ccons */ yytestcase(yyruleno==294); - /* (295) carglist ::= */ yytestcase(yyruleno==295); - /* (296) ccons ::= NULL onconf */ yytestcase(yyruleno==296); - /* (297) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==297); - /* (298) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==298); - /* (299) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=299); - /* (300) tconscomma ::= */ yytestcase(yyruleno==300); - /* (301) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=301); - /* (302) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=302); - /* (303) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=303); - /* (304) oneselect ::= values */ yytestcase(yyruleno==304); - /* (305) sclp ::= selcollist COMMA */ yytestcase(yyruleno==305); - /* (306) as ::= ID|STRING */ yytestcase(yyruleno==306); - /* (307) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=307); - /* (308) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==308); - /* (309) exprlist ::= nexprlist */ yytestcase(yyruleno==309); - /* (310) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=310); - /* (311) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=311); - /* (312) nmnum ::= ON */ yytestcase(yyruleno==312); - /* (313) nmnum ::= DELETE */ yytestcase(yyruleno==313); - /* (314) nmnum ::= DEFAULT */ yytestcase(yyruleno==314); - /* (315) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==315); - /* (316) foreach_clause ::= */ yytestcase(yyruleno==316); - /* (317) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==317); - /* (318) trnm ::= nm */ yytestcase(yyruleno==318); - /* (319) tridxby ::= */ yytestcase(yyruleno==319); - /* (320) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==320); - /* (321) database_kw_opt ::= */ yytestcase(yyruleno==321); - /* (322) kwcolumn_opt ::= */ yytestcase(yyruleno==322); - /* (323) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==323); - /* (324) vtabarglist ::= vtabarg */ yytestcase(yyruleno==324); - /* (325) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==325); - /* (326) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==326); - /* (327) anylist ::= */ yytestcase(yyruleno==327); - /* (328) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==328); - /* (329) anylist ::= anylist ANY */ yytestcase(yyruleno==329); + /* (275) input ::= cmdlist */ yytestcase(yyruleno==275); + /* (276) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==276); + /* (277) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=277); + /* (278) ecmd ::= SEMI */ yytestcase(yyruleno==278); + /* (279) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==279); + /* (280) explain ::= */ yytestcase(yyruleno==280); + /* (281) trans_opt ::= */ yytestcase(yyruleno==281); + /* (282) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==282); + /* (283) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==283); + /* (284) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==284); + /* (285) savepoint_opt ::= */ yytestcase(yyruleno==285); + /* (286) cmd ::= create_table create_table_args */ yytestcase(yyruleno==286); + /* (287) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==287); + /* (288) columnlist ::= columnname carglist */ yytestcase(yyruleno==288); + /* (289) nm ::= ID|INDEXED */ yytestcase(yyruleno==289); + /* (290) nm ::= STRING */ yytestcase(yyruleno==290); + /* (291) nm ::= JOIN_KW */ yytestcase(yyruleno==291); + /* (292) typetoken ::= typename */ yytestcase(yyruleno==292); + /* (293) typename ::= ID|STRING */ yytestcase(yyruleno==293); + /* (294) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=294); + /* (295) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=295); + /* (296) carglist ::= carglist ccons */ yytestcase(yyruleno==296); + /* (297) carglist ::= */ yytestcase(yyruleno==297); + /* (298) ccons ::= NULL onconf */ yytestcase(yyruleno==298); + /* (299) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==299); + /* (300) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==300); + /* (301) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=301); + /* (302) tconscomma ::= */ yytestcase(yyruleno==302); + /* (303) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=303); + /* (304) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=304); + /* (305) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=305); + /* (306) oneselect ::= values */ yytestcase(yyruleno==306); + /* (307) sclp ::= selcollist COMMA */ yytestcase(yyruleno==307); + /* (308) as ::= ID|STRING */ yytestcase(yyruleno==308); + /* (309) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=309); + /* (310) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==310); + /* (311) exprlist ::= nexprlist */ yytestcase(yyruleno==311); + /* (312) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=312); + /* (313) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=313); + /* (314) nmnum ::= ON */ yytestcase(yyruleno==314); + /* (315) nmnum ::= DELETE */ yytestcase(yyruleno==315); + /* (316) nmnum ::= DEFAULT */ yytestcase(yyruleno==316); + /* (317) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==317); + /* (318) foreach_clause ::= */ yytestcase(yyruleno==318); + /* (319) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==319); + /* (320) trnm ::= nm */ yytestcase(yyruleno==320); + /* (321) tridxby ::= */ yytestcase(yyruleno==321); + /* (322) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==322); + /* (323) database_kw_opt ::= */ yytestcase(yyruleno==323); + /* (324) kwcolumn_opt ::= */ yytestcase(yyruleno==324); + /* (325) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==325); + /* (326) vtabarglist ::= vtabarg */ yytestcase(yyruleno==326); + /* (327) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==327); + /* (328) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==328); + /* (329) anylist ::= */ yytestcase(yyruleno==329); + /* (330) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==330); + /* (331) anylist ::= anylist ANY */ yytestcase(yyruleno==331); + /* (332) with ::= */ yytestcase(yyruleno==332); break; /********** End reduce actions ************************************************/ }; @@ -143784,6 +145484,11 @@ sqlite3GlobalConfig.isPCacheInit = 1; rc = sqlite3OsInit(); } +#ifdef SQLITE_ENABLE_DESERIALIZE + if( rc==SQLITE_OK ){ + rc = sqlite3MemdbInit(); + } +#endif if( rc==SQLITE_OK ){ sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); @@ -143816,7 +145521,7 @@ #ifndef NDEBUG #ifndef SQLITE_OMIT_FLOATING_POINT /* This section of code's only "output" is via assert() statements. */ - if ( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ u64 x = (((u64)1)<<63)-1; double y; assert(sizeof(x)==8); @@ -144983,6 +146688,8 @@ /* SQLITE_FORMAT */ 0, /* SQLITE_RANGE */ "column index out of range", /* SQLITE_NOTADB */ "file is not a database", + /* SQLITE_NOTICE */ "notification message", + /* SQLITE_WARNING */ "warning message", }; const char *zErr = "unknown error"; switch( rc ){ @@ -144990,6 +146697,14 @@ zErr = "abort due to ROLLBACK"; break; } + case SQLITE_ROW: { + zErr = "another row available"; + break; + } + case SQLITE_DONE: { + zErr = "no more rows available"; + break; + } default: { rc &= 0xff; if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){ @@ -145006,21 +146721,40 @@ ** again until a timeout value is reached. The timeout value is ** an integer number of milliseconds passed in as the first ** argument. +** +** Return non-zero to retry the lock. Return zero to stop trying +** and cause SQLite to return SQLITE_BUSY. */ static int sqliteDefaultBusyCallback( - void *ptr, /* Database connection */ - int count /* Number of times table has been busy */ + void *ptr, /* Database connection */ + int count, /* Number of times table has been busy */ + sqlite3_file *pFile /* The file on which the lock occurred */ ){ #if SQLITE_OS_WIN || HAVE_USLEEP + /* This case is for systems that have support for sleeping for fractions of + ** a second. Examples: All windows systems, unix systems with usleep() */ static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; # define NDELAY ArraySize(delays) sqlite3 *db = (sqlite3 *)ptr; - int timeout = db->busyTimeout; + int tmout = db->busyTimeout; int delay, prior; +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){ + if( count ){ + tmout = 0; + sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout); + return 0; + }else{ + return 1; + } + } +#else + UNUSED_PARAMETER(pFile); +#endif assert( count>=0 ); if( count < NDELAY ){ delay = delays[count]; @@ -145029,16 +146763,19 @@ delay = delays[NDELAY-1]; prior = totals[NDELAY-1] + delay*(count-(NDELAY-1)); } - if( prior + delay > timeout ){ - delay = timeout - prior; + if( prior + delay > tmout ){ + delay = tmout - prior; if( delay<=0 ) return 0; } sqlite3OsSleep(db->pVfs, delay*1000); return 1; #else + /* This case for unix systems that lack usleep() support. Sleeping + ** must be done in increments of whole seconds */ sqlite3 *db = (sqlite3 *)ptr; - int timeout = ((sqlite3 *)ptr)->busyTimeout; - if( (count+1)*1000 > timeout ){ + int tmout = ((sqlite3 *)ptr)->busyTimeout; + UNUSED_PARAMETER(pFile); + if( (count+1)*1000 > tmout ){ return 0; } sqlite3OsSleep(db->pVfs, 1000000); @@ -145049,14 +146786,25 @@ /* ** Invoke the given busy handler. ** -** This routine is called when an operation failed with a lock. +** This routine is called when an operation failed to acquire a +** lock on VFS file pFile. +** ** If this routine returns non-zero, the lock is retried. If it ** returns 0, the operation aborts with an SQLITE_BUSY error. */ -SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){ +SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){ int rc; - if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0; - rc = p->xFunc(p->pArg, p->nBusy); + if( p->xBusyHandler==0 || p->nBusy<0 ) return 0; + if( p->bExtraFileArg ){ + /* Add an extra parameter with the pFile pointer to the end of the + ** callback argument list */ + int (*xTra)(void*,int,sqlite3_file*); + xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler; + rc = xTra(p->pBusyArg, p->nBusy, pFile); + }else{ + /* Legacy style busy handler callback */ + rc = p->xBusyHandler(p->pBusyArg, p->nBusy); + } if( rc==0 ){ p->nBusy = -1; }else{ @@ -145078,9 +146826,10 @@ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif sqlite3_mutex_enter(db->mutex); - db->busyHandler.xFunc = xBusy; - db->busyHandler.pArg = pArg; + db->busyHandler.xBusyHandler = xBusy; + db->busyHandler.pBusyArg = pArg; db->busyHandler.nBusy = 0; + db->busyHandler.bExtraFileArg = 0; db->busyTimeout = 0; sqlite3_mutex_leave(db->mutex); return SQLITE_OK; @@ -145128,8 +146877,10 @@ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif if( ms>0 ){ - sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db); + sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, + (void*)db); db->busyTimeout = ms; + db->busyHandler.bExtraFileArg = 1; }else{ sqlite3_busy_handler(db, 0, 0); } @@ -147130,10 +148881,8 @@ }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); rc = SQLITE_OK; - }else if( fd->pMethods ){ - rc = sqlite3OsFileControl(fd, op, pArg); }else{ - rc = SQLITE_NOTFOUND; + rc = sqlite3OsFileControl(fd, op, pArg); } sqlite3BtreeLeave(pBtree); } @@ -160998,6 +162747,7 @@ sqlite3_bind_blob(pStmt, 2, z, n, SQLITE_STATIC); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); + sqlite3_bind_null(pStmt, 2); } return rc; } @@ -161054,6 +162804,7 @@ sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); + sqlite3_bind_null(pStmt, 6); } return rc; } @@ -162533,6 +164284,7 @@ sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, SQLITE_STATIC); sqlite3_step(pStmt); *pRC = sqlite3_reset(pStmt); + sqlite3_bind_null(pStmt, 2); sqlite3_free(a); } @@ -163721,6 +165473,7 @@ sqlite3_bind_int(pChomp, 4, iIdx); sqlite3_step(pChomp); rc = sqlite3_reset(pChomp); + sqlite3_bind_null(pChomp, 2); } } @@ -163800,6 +165553,7 @@ sqlite3_bind_blob(pReplace, 2, pHint->a, pHint->n, SQLITE_STATIC); sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 2); } return rc; @@ -164614,7 +166368,6 @@ ){ Fts3Table *p = (Fts3Table *)pVtab; int rc = SQLITE_OK; /* Return Code */ - int isRemove = 0; /* True for an UPDATE or DELETE */ u32 *aSzIns = 0; /* Sizes of inserted documents */ u32 *aSzDel = 0; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ @@ -164712,7 +166465,6 @@ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ); rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel); - isRemove = 1; } /* If this is an INSERT or UPDATE operation, insert the new record. */ @@ -164724,7 +166476,7 @@ rc = FTS_CORRUPT_VTAB; } } - if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){ + if( rc==SQLITE_OK ){ rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid); } if( rc==SQLITE_OK ){ @@ -168032,6 +169784,7 @@ sqlite3_step(p); pNode->isDirty = 0; rc = sqlite3_reset(p); + sqlite3_bind_null(p, 2); if( pNode->iNode==0 && rc==SQLITE_OK ){ pNode->iNode = sqlite3_last_insert_rowid(pRtree->db); nodeHashInsert(pRtree, pNode); @@ -174719,7 +176472,7 @@ int iCid = sqlite3_column_int(pXInfo, 1); int bDesc = sqlite3_column_int(pXInfo, 3); const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4); - zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma, + zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma, iCid, pIter->azTblType[iCid], zCollate ); zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":""); @@ -174780,7 +176533,7 @@ ** "PRIMARY KEY" to the imposter table column declaration. */ zPk = "PRIMARY KEY "; } - zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %s%s", + zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s", zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl, (pIter->abNotNull[iCol] ? " NOT NULL" : "") ); @@ -178214,7 +179967,7 @@ */ fd = sqlite3PagerFile(pPager); x[0] = pCsr->iPageno; - if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ + if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ pCsr->iOffset = x[0]; pCsr->szPage = (int)x[1]; } @@ -178980,7 +180733,7 @@ ** sqlite3changeset_start_strm()). */ struct SessionInput { - int bNoDiscard; /* If true, discard no data */ + int bNoDiscard; /* If true, do not discard in InputBuffer() */ int iCurrent; /* Offset in aData[] of current change */ int iNext; /* Offset in aData[] of next change */ u8 *aData; /* Pointer to buffer containing changeset */ @@ -179144,8 +180897,8 @@ ** statement. ** ** For a DELETE change, all fields within the record except those associated -** with PRIMARY KEY columns are set to "undefined". The PRIMARY KEY fields -** contain the values identifying the row to delete. +** with PRIMARY KEY columns are omitted. The PRIMARY KEY fields contain the +** values identifying the row to delete. ** ** For an UPDATE change, all fields except those associated with PRIMARY KEY ** columns and columns that are modified by the UPDATE are set to "undefined". @@ -179428,7 +181181,7 @@ static int sessionSerialLen(u8 *a){ int e = *a; int n; - if( e==0 ) return 1; + if( e==0 || e==0xFF ) return 1; if( e==SQLITE_NULL ) return 1; if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; return sessionVarintGet(&a[1], &n) + 1 + n; @@ -179508,7 +181261,7 @@ int n1 = sessionSerialLen(a1); int n2 = sessionSerialLen(a2); - if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){ + if( n1!=n2 || memcmp(a1, a2, n1) ){ return 0; } a1 += n1; @@ -179751,7 +181504,7 @@ }else{ z = sqlite3_value_blob(pVal); } - if( memcmp(a, z, n) ) return 0; + if( n>0 && memcmp(a, z, n) ) return 0; a += n; } } @@ -180027,7 +181780,7 @@ int iHash; int bNull = 0; int rc = SQLITE_OK; - SessionStat1Ctx stat1; + SessionStat1Ctx stat1 = {0}; if( pSession->rc ) return; @@ -181095,6 +182848,7 @@ "SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND " "idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb ); + if( zSql==0 ) rc = SQLITE_NOMEM; }else{ int i; const char *zSep = ""; @@ -181504,7 +183258,7 @@ ** object and the buffer is full, discard some data to free up space. */ static void sessionDiscardData(SessionInput *pIn){ - if( pIn->bEof && pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){ + if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){ int nMove = pIn->buf.nBuf - pIn->iNext; assert( nMove>=0 ); if( nMove>0 ){ @@ -181632,13 +183386,16 @@ if( abPK && abPK[i]==0 ) continue; rc = sessionInputBuffer(pIn, 9); if( rc==SQLITE_OK ){ - eType = pIn->aData[pIn->iNext++]; - } - - assert( apOut[i]==0 ); - if( eType ){ - apOut[i] = sqlite3ValueNew(0); - if( !apOut[i] ) rc = SQLITE_NOMEM; + if( pIn->iNext>=pIn->nData ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + eType = pIn->aData[pIn->iNext++]; + assert( apOut[i]==0 ); + if( eType ){ + apOut[i] = sqlite3ValueNew(0); + if( !apOut[i] ) rc = SQLITE_NOMEM; + } + } } if( rc==SQLITE_OK ){ @@ -181648,10 +183405,14 @@ pIn->iNext += sessionVarintGet(aVal, &nByte); rc = sessionInputBuffer(pIn, nByte); if( rc==SQLITE_OK ){ - u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0); - rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc); + if( nByte<0 || nByte>pIn->nData-pIn->iNext ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0); + rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc); + pIn->iNext += nByte; + } } - pIn->iNext += nByte; } if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ sqlite3_int64 v = sessionGetI64(aVal); @@ -181691,8 +183452,19 @@ rc = sessionInputBuffer(pIn, 9); if( rc==SQLITE_OK ){ nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol); - rc = sessionInputBuffer(pIn, nRead+nCol+100); - nRead += nCol; + /* The hard upper limit for the number of columns in an SQLite + ** database table is, according to sqliteLimit.h, 32676. So + ** consider any table-header that purports to have more than 65536 + ** columns to be corrupt. This is convenient because otherwise, + ** if the (nCol>65536) condition below were omitted, a sufficiently + ** large value for nCol may cause nRead to wrap around and become + ** negative. Leading to a crash. */ + if( nCol<0 || nCol>65536 ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = sessionInputBuffer(pIn, nRead+nCol+100); + nRead += nCol; + } } while( rc==SQLITE_OK ){ @@ -181769,11 +183541,15 @@ int nByte; int nVarint; nVarint = sessionVarintGet(&p->in.aData[p->in.iNext], &p->nCol); - nCopy -= nVarint; - p->in.iNext += nVarint; - nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy; - p->tblhdr.nBuf = 0; - sessionBufferGrow(&p->tblhdr, nByte, &rc); + if( p->nCol>0 ){ + nCopy -= nVarint; + p->in.iNext += nVarint; + nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy; + p->tblhdr.nBuf = 0; + sessionBufferGrow(&p->tblhdr, nByte, &rc); + }else{ + rc = SQLITE_CORRUPT_BKPT; + } } if( rc==SQLITE_OK ){ @@ -181808,7 +183584,8 @@ static int sessionChangesetNext( sqlite3_changeset_iter *p, /* Changeset iterator */ u8 **paRec, /* If non-NULL, store record pointer here */ - int *pnRec /* If non-NULL, store size of record here */ + int *pnRec, /* If non-NULL, store size of record here */ + int *pbNew /* If non-NULL, true if new table */ ){ int i; u8 op; @@ -181843,6 +183620,7 @@ op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ + if( pbNew ) *pbNew = 1; p->bPatchset = (op=='P'); if( sessionChangesetReadTblhdr(p) ) return p->rc; if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; @@ -181851,6 +183629,13 @@ op = p->in.aData[p->in.iNext++]; } + if( p->zTab==0 ){ + /* The first record in the changeset is not a table header. Must be a + ** corrupt changeset. */ + assert( p->in.iNext==1 ); + return (p->rc = SQLITE_CORRUPT_BKPT); + } + p->op = op; p->bIndirect = p->in.aData[p->in.iNext++]; if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ @@ -181893,9 +183678,9 @@ ** new.* to old.*, to accommodate the code that reads these arrays. */ for(i=0; i<p->nCol; i++){ assert( p->apValue[i]==0 ); - assert( p->abPK[i]==0 || p->apValue[i+p->nCol] ); if( p->abPK[i] ){ p->apValue[i] = p->apValue[i+p->nCol]; + if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); p->apValue[i+p->nCol] = 0; } } @@ -181914,7 +183699,7 @@ ** callback by changeset_apply(). */ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *p){ - return sessionChangesetNext(p, 0, 0); + return sessionChangesetNext(p, 0, 0, 0); } /* @@ -182293,6 +184078,8 @@ int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ SessionBuffer constraints; /* Deferred constraints are stored here */ + SessionBuffer rebase; /* Rebase information (if any) here */ + int bRebaseStarted; /* If table header is already in rebase */ }; /* @@ -182559,7 +184346,6 @@ "AND (?4 OR stat IS ?3)" ); } - assert( rc==SQLITE_OK ); return rc; } @@ -182620,7 +184406,13 @@ if( !abPK || abPK[i] ){ sqlite3_value *pVal; (void)xValue(pIter, i, &pVal); - rc = sessionBindValue(pStmt, i+1, pVal); + if( pVal==0 ){ + /* The value in the changeset was "undefined". This indicates a + ** corrupt changeset blob. */ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = sessionBindValue(pStmt, i+1, pVal); + } } } return rc; @@ -182669,6 +184461,54 @@ } /* +** This function is called from within sqlite3changset_apply_v2() when +** a conflict is encountered and resolved using conflict resolution +** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE).. +** It adds a conflict resolution record to the buffer in +** SessionApplyCtx.rebase, which will eventually be returned to the caller +** of apply_v2() as the "rebase" buffer. +** +** Return SQLITE_OK if successful, or an SQLite error code otherwise. +*/ +static int sessionRebaseAdd( + SessionApplyCtx *p, /* Apply context */ + int eType, /* Conflict resolution (OMIT or REPLACE) */ + sqlite3_changeset_iter *pIter /* Iterator pointing at current change */ +){ + int rc = SQLITE_OK; + int i; + int eOp = pIter->op; + if( p->bRebaseStarted==0 ){ + /* Append a table-header to the rebase buffer */ + const char *zTab = pIter->zTab; + sessionAppendByte(&p->rebase, 'T', &rc); + sessionAppendVarint(&p->rebase, p->nCol, &rc); + sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc); + sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc); + p->bRebaseStarted = 1; + } + + assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT ); + assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE ); + + sessionAppendByte(&p->rebase, + (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc + ); + sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc); + for(i=0; i<p->nCol; i++){ + sqlite3_value *pVal = 0; + if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){ + sqlite3changeset_old(pIter, i, &pVal); + }else{ + sqlite3changeset_new(pIter, i, &pVal); + } + sessionAppendValue(&p->rebase, pVal, &rc); + } + + return rc; +} + +/* ** Invoke the conflict handler for the change that the changeset iterator ** currently points to. ** @@ -182743,7 +184583,7 @@ u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent]; int nBlob = pIter->in.iNext - pIter->in.iCurrent; sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc); - res = SQLITE_CHANGESET_OMIT; + return SQLITE_OK; }else{ /* No other row with the new.* primary key. */ res = xConflict(pCtx, eType+1, pIter); @@ -182769,6 +184609,9 @@ rc = SQLITE_MISUSE; break; } + if( rc==SQLITE_OK ){ + rc = sessionRebaseAdd(p, res, pIter); + } } return rc; @@ -182944,42 +184787,42 @@ int rc; rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry); - assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) ); - - /* If the bRetry flag is set, the change has not been applied due to an - ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and - ** a row with the correct PK is present in the db, but one or more other - ** fields do not contain the expected values) and the conflict handler - ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation, - ** but pass NULL as the final argument so that sessionApplyOneOp() ignores - ** the SQLITE_CHANGESET_DATA problem. */ - if( bRetry ){ - assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE ); - rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0); - } - - /* If the bReplace flag is set, the change is an INSERT that has not - ** been performed because the database already contains a row with the - ** specified primary key and the conflict handler returned - ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row - ** before reattempting the INSERT. */ - else if( bReplace ){ - assert( pIter->op==SQLITE_INSERT ); - rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = sessionBindRow(pIter, - sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete); - sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1); - } - if( rc==SQLITE_OK ){ - sqlite3_step(pApply->pDelete); - rc = sqlite3_reset(pApply->pDelete); - } - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ + /* If the bRetry flag is set, the change has not been applied due to an + ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and + ** a row with the correct PK is present in the db, but one or more other + ** fields do not contain the expected values) and the conflict handler + ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation, + ** but pass NULL as the final argument so that sessionApplyOneOp() ignores + ** the SQLITE_CHANGESET_DATA problem. */ + if( bRetry ){ + assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE ); rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0); } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0); + + /* If the bReplace flag is set, the change is an INSERT that has not + ** been performed because the database already contains a row with the + ** specified primary key and the conflict handler returned + ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row + ** before reattempting the INSERT. */ + else if( bReplace ){ + assert( pIter->op==SQLITE_INSERT ); + rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0); + if( rc==SQLITE_OK ){ + rc = sessionBindRow(pIter, + sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete); + sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1); + } + if( rc==SQLITE_OK ){ + sqlite3_step(pApply->pDelete); + rc = sqlite3_reset(pApply->pDelete); + } + if( rc==SQLITE_OK ){ + rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0); + } } } @@ -183055,10 +184898,12 @@ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ sqlite3_changeset_iter *p /* Handle describing change and conflict */ ), - void *pCtx /* First argument passed to xConflict */ + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, /* OUT: Rebase information */ + int flags /* SESSION_APPLY_XXX flags */ ){ int schemaMismatch = 0; - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ const char *zTab = 0; /* Name of current table */ int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ SessionApplyCtx sApply; /* changeset_apply() context object */ @@ -183069,7 +184914,9 @@ pIter->in.bNoDiscard = 1; memset(&sApply, 0, sizeof(sApply)); sqlite3_mutex_enter(sqlite3_db_mutex(db)); - rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); + if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ + rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); + } if( rc==SQLITE_OK ){ rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0); } @@ -183093,9 +184940,18 @@ sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pSelect); - memset(&sApply, 0, sizeof(sApply)); sApply.db = db; + sApply.pDelete = 0; + sApply.pUpdate = 0; + sApply.pInsert = 0; + sApply.pSelect = 0; + sApply.nCol = 0; + sApply.azCol = 0; + sApply.abPK = 0; + sApply.bStat1 = 0; sApply.bDeferConstraints = 1; + sApply.bRebaseStarted = 0; + memset(&sApply.constraints, 0, sizeof(SessionBuffer)); /* If an xFilter() callback was specified, invoke it now. If the ** xFilter callback returns zero, skip this table. If it returns @@ -183198,24 +185054,63 @@ } sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); - }else{ - sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0); - sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); + if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); + }else{ + sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0); + sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); + } } + if( rc==SQLITE_OK && bPatchset==0 && ppRebase && pnRebase ){ + *ppRebase = (void*)sApply.rebase.aBuf; + *pnRebase = sApply.rebase.nBuf; + sApply.rebase.aBuf = 0; + } sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pDelete); sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pSelect); sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_free((char*)sApply.constraints.aBuf); + sqlite3_free((char*)sApply.rebase.aBuf); sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } /* +** Apply the changeset passed via pChangeset/nChangeset to the main +** database attached to handle "db". +*/ +SQLITE_API int sqlite3changeset_apply_v2( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void *pChangeset, /* Changeset blob */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +){ + sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ + int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset); + if( rc==SQLITE_OK ){ + rc = sessionChangesetApply( + db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags + ); + } + return rc; +} + +/* ** Apply the changeset passed via pChangeset/nChangeset to the main database ** attached to handle "db". Invoke the supplied conflict handler callback ** to resolve any conflicts encountered while applying the change. @@ -183235,12 +185130,9 @@ ), void *pCtx /* First argument passed to xConflict */ ){ - sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset); - if( rc==SQLITE_OK ){ - rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx); - } - return rc; + return sqlite3changeset_apply_v2( + db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0 + ); } /* @@ -183248,6 +185140,32 @@ ** attached to handle "db". Invoke the supplied conflict handler callback ** to resolve any conflicts encountered while applying the change. */ +SQLITE_API int sqlite3changeset_apply_v2_strm( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ + void *pIn, /* First arg for xInput */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +){ + sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ + int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); + if( rc==SQLITE_OK ){ + rc = sessionChangesetApply( + db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags + ); + } + return rc; +} SQLITE_API int sqlite3changeset_apply_strm( sqlite3 *db, /* Apply change to "main" db of this handle */ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ @@ -183263,12 +185181,9 @@ ), void *pCtx /* First argument passed to xConflict */ ){ - sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); - if( rc==SQLITE_OK ){ - rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx); - } - return rc; + return sqlite3changeset_apply_v2_strm( + db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0 + ); } /* @@ -183287,6 +185202,7 @@ */ static int sessionChangeMerge( SessionTable *pTab, /* Table structure */ + int bRebase, /* True for a rebase hash-table */ int bPatchset, /* True for patchsets */ SessionChange *pExist, /* Existing change */ int op2, /* Second change operation */ @@ -183296,6 +185212,7 @@ SessionChange **ppNew /* OUT: Merged change */ ){ SessionChange *pNew = 0; + int rc = SQLITE_OK; if( !pExist ){ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec); @@ -183305,9 +185222,66 @@ memset(pNew, 0, sizeof(SessionChange)); pNew->op = op2; pNew->bIndirect = bIndirect; - pNew->nRecord = nRec; pNew->aRecord = (u8*)&pNew[1]; - memcpy(pNew->aRecord, aRec, nRec); + if( bIndirect==0 || bRebase==0 ){ + pNew->nRecord = nRec; + memcpy(pNew->aRecord, aRec, nRec); + }else{ + int i; + u8 *pIn = aRec; + u8 *pOut = pNew->aRecord; + for(i=0; i<pTab->nCol; i++){ + int nIn = sessionSerialLen(pIn); + if( *pIn==0 ){ + *pOut++ = 0; + }else if( pTab->abPK[i]==0 ){ + *pOut++ = 0xFF; + }else{ + memcpy(pOut, pIn, nIn); + pOut += nIn; + } + pIn += nIn; + } + pNew->nRecord = pOut - pNew->aRecord; + } + }else if( bRebase ){ + if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){ + *ppNew = pExist; + }else{ + int nByte = nRec + pExist->nRecord + sizeof(SessionChange); + pNew = (SessionChange*)sqlite3_malloc(nByte); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + int i; + u8 *a1 = pExist->aRecord; + u8 *a2 = aRec; + u8 *pOut; + + memset(pNew, 0, nByte); + pNew->bIndirect = bIndirect || pExist->bIndirect; + pNew->op = op2; + pOut = pNew->aRecord = (u8*)&pNew[1]; + + for(i=0; i<pTab->nCol; i++){ + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( *a1==0xFF || (pTab->abPK[i]==0 && bIndirect) ){ + *pOut++ = 0xFF; + }else if( *a2==0 ){ + memcpy(pOut, a1, n1); + pOut += n1; + }else{ + memcpy(pOut, a2, n2); + pOut += n2; + } + a1 += n1; + a2 += n2; + } + pNew->nRecord = pOut - pNew->aRecord; + } + sqlite3_free(pExist); + } }else{ int op1 = pExist->op; @@ -183401,7 +185375,7 @@ } *ppNew = pNew; - return SQLITE_OK; + return rc; } /* @@ -183410,15 +185384,15 @@ */ static int sessionChangesetToHash( sqlite3_changeset_iter *pIter, /* Iterator to read from */ - sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */ + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ + int bRebase /* True if hash table is for rebasing */ ){ u8 *aRec; int nRec; int rc = SQLITE_OK; SessionTable *pTab = 0; - - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){ + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ const char *zNew; int nCol; int op; @@ -183498,7 +185472,7 @@ } } - rc = sessionChangeMerge(pTab, + rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); if( rc ) break; @@ -183606,7 +185580,7 @@ rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ - rc = sessionChangesetToHash(pIter, pGrp); + rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; @@ -183637,7 +185611,7 @@ rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); if( rc==SQLITE_OK ){ - rc = sessionChangesetToHash(pIter, pGrp); + rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; @@ -183722,6 +185696,349 @@ return rc; } +/* +** Changeset rebaser handle. +*/ +struct sqlite3_rebaser { + sqlite3_changegroup grp; /* Hash table */ +}; + +/* +** Buffers a1 and a2 must both contain a sessions module record nCol +** fields in size. This function appends an nCol sessions module +** record to buffer pBuf that is a copy of a1, except that for +** each field that is undefined in a1[], swap in the field from a2[]. +*/ +static void sessionAppendRecordMerge( + SessionBuffer *pBuf, /* Buffer to append to */ + int nCol, /* Number of columns in each record */ + u8 *a1, int n1, /* Record 1 */ + u8 *a2, int n2, /* Record 2 */ + int *pRc /* IN/OUT: error code */ +){ + sessionBufferGrow(pBuf, n1+n2, pRc); + if( *pRc==SQLITE_OK ){ + int i; + u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; + for(i=0; i<nCol; i++){ + int nn1 = sessionSerialLen(a1); + int nn2 = sessionSerialLen(a2); + if( *a1==0 || *a1==0xFF ){ + memcpy(pOut, a2, nn2); + pOut += nn2; + }else{ + memcpy(pOut, a1, nn1); + pOut += nn1; + } + a1 += nn1; + a2 += nn2; + } + + pBuf->nBuf = pOut-pBuf->aBuf; + assert( pBuf->nBuf<=pBuf->nAlloc ); + } +} + +/* +** This function is called when rebasing a local UPDATE change against one +** or more remote UPDATE changes. The aRec/nRec buffer contains the current +** old.* and new.* records for the change. The rebase buffer (a single +** record) is in aChange/nChange. The rebased change is appended to buffer +** pBuf. +** +** Rebasing the UPDATE involves: +** +** * Removing any changes to fields for which the corresponding field +** in the rebase buffer is set to "replaced" (type 0xFF). If this +** means the UPDATE change updates no fields, nothing is appended +** to the output buffer. +** +** * For each field modified by the local change for which the +** corresponding field in the rebase buffer is not "undefined" (0x00) +** or "replaced" (0xFF), the old.* value is replaced by the value +** in the rebase buffer. +*/ +static void sessionAppendPartialUpdate( + SessionBuffer *pBuf, /* Append record here */ + sqlite3_changeset_iter *pIter, /* Iterator pointed at local change */ + u8 *aRec, int nRec, /* Local change */ + u8 *aChange, int nChange, /* Record to rebase against */ + int *pRc /* IN/OUT: Return Code */ +){ + sessionBufferGrow(pBuf, 2+nRec+nChange, pRc); + if( *pRc==SQLITE_OK ){ + int bData = 0; + u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; + int i; + u8 *a1 = aRec; + u8 *a2 = aChange; + + *pOut++ = SQLITE_UPDATE; + *pOut++ = pIter->bIndirect; + for(i=0; i<pIter->nCol; i++){ + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( pIter->abPK[i] || a2[0]==0 ){ + if( !pIter->abPK[i] ) bData = 1; + memcpy(pOut, a1, n1); + pOut += n1; + }else if( a2[0]!=0xFF ){ + bData = 1; + memcpy(pOut, a2, n2); + pOut += n2; + }else{ + *pOut++ = '\0'; + } + a1 += n1; + a2 += n2; + } + if( bData ){ + a2 = aChange; + for(i=0; i<pIter->nCol; i++){ + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( pIter->abPK[i] || a2[0]!=0xFF ){ + memcpy(pOut, a1, n1); + pOut += n1; + }else{ + *pOut++ = '\0'; + } + a1 += n1; + a2 += n2; + } + pBuf->nBuf = (pOut - pBuf->aBuf); + } + } +} + +/* +** pIter is configured to iterate through a changeset. This function rebases +** that changeset according to the current configuration of the rebaser +** object passed as the first argument. If no error occurs and argument xOutput +** is not NULL, then the changeset is returned to the caller by invoking +** xOutput zero or more times and SQLITE_OK returned. Or, if xOutput is NULL, +** then (*ppOut) is set to point to a buffer containing the rebased changeset +** before this function returns. In this case (*pnOut) is set to the size of +** the buffer in bytes. It is the responsibility of the caller to eventually +** free the (*ppOut) buffer using sqlite3_free(). +** +** If an error occurs, an SQLite error code is returned. If ppOut and +** pnOut are not NULL, then the two output parameters are set to 0 before +** returning. +*/ +static int sessionRebase( + sqlite3_rebaser *p, /* Rebaser hash table */ + sqlite3_changeset_iter *pIter, /* Input data */ + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut, /* Context for xOutput callback */ + int *pnOut, /* OUT: Number of bytes in output changeset */ + void **ppOut /* OUT: Inverse of pChangeset */ +){ + int rc = SQLITE_OK; + u8 *aRec = 0; + int nRec = 0; + int bNew = 0; + SessionTable *pTab = 0; + SessionBuffer sOut = {0,0,0}; + + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){ + SessionChange *pChange = 0; + int bDone = 0; + + if( bNew ){ + const char *zTab = pIter->zTab; + for(pTab=p->grp.pList; pTab; pTab=pTab->pNext){ + if( 0==sqlite3_stricmp(pTab->zName, zTab) ) break; + } + bNew = 0; + + /* A patchset may not be rebased */ + if( pIter->bPatchset ){ + rc = SQLITE_ERROR; + } + + /* Append a table header to the output for this new table */ + sessionAppendByte(&sOut, pIter->bPatchset ? 'P' : 'T', &rc); + sessionAppendVarint(&sOut, pIter->nCol, &rc); + sessionAppendBlob(&sOut, pIter->abPK, pIter->nCol, &rc); + sessionAppendBlob(&sOut,(u8*)pIter->zTab,(int)strlen(pIter->zTab)+1,&rc); + } + + if( pTab && rc==SQLITE_OK ){ + int iHash = sessionChangeHash(pTab, 0, aRec, pTab->nChange); + + for(pChange=pTab->apChange[iHash]; pChange; pChange=pChange->pNext){ + if( sessionChangeEqual(pTab, 0, aRec, 0, pChange->aRecord) ){ + break; + } + } + } + + if( pChange ){ + assert( pChange->op==SQLITE_DELETE || pChange->op==SQLITE_INSERT ); + switch( pIter->op ){ + case SQLITE_INSERT: + if( pChange->op==SQLITE_INSERT ){ + bDone = 1; + if( pChange->bIndirect==0 ){ + sessionAppendByte(&sOut, SQLITE_UPDATE, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendBlob(&sOut, pChange->aRecord, pChange->nRecord, &rc); + sessionAppendBlob(&sOut, aRec, nRec, &rc); + } + } + break; + + case SQLITE_UPDATE: + bDone = 1; + if( pChange->op==SQLITE_DELETE ){ + if( pChange->bIndirect==0 ){ + u8 *pCsr = aRec; + sessionSkipRecord(&pCsr, pIter->nCol); + sessionAppendByte(&sOut, SQLITE_INSERT, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendRecordMerge(&sOut, pIter->nCol, + pCsr, nRec-(pCsr-aRec), + pChange->aRecord, pChange->nRecord, &rc + ); + } + }else{ + sessionAppendPartialUpdate(&sOut, pIter, + aRec, nRec, pChange->aRecord, pChange->nRecord, &rc + ); + } + break; + + default: + assert( pIter->op==SQLITE_DELETE ); + bDone = 1; + if( pChange->op==SQLITE_INSERT ){ + sessionAppendByte(&sOut, SQLITE_DELETE, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendRecordMerge(&sOut, pIter->nCol, + pChange->aRecord, pChange->nRecord, aRec, nRec, &rc + ); + } + break; + } + } + + if( bDone==0 ){ + sessionAppendByte(&sOut, pIter->op, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendBlob(&sOut, aRec, nRec, &rc); + } + if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + sOut.nBuf = 0; + } + if( rc ) break; + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(sOut.aBuf); + memset(&sOut, 0, sizeof(sOut)); + } + + if( rc==SQLITE_OK ){ + if( xOutput ){ + if( sOut.nBuf>0 ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + } + }else{ + *ppOut = (void*)sOut.aBuf; + *pnOut = sOut.nBuf; + sOut.aBuf = 0; + } + } + sqlite3_free(sOut.aBuf); + return rc; +} + +/* +** Create a new rebaser object. +*/ +SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew){ + int rc = SQLITE_OK; + sqlite3_rebaser *pNew; + + pNew = sqlite3_malloc(sizeof(sqlite3_rebaser)); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(sqlite3_rebaser)); + } + *ppNew = pNew; + return rc; +} + +/* +** Call this one or more times to configure a rebaser. +*/ +SQLITE_API int sqlite3rebaser_configure( + sqlite3_rebaser *p, + int nRebase, const void *pRebase +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ + int rc; /* Return code */ + rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase); + if( rc==SQLITE_OK ){ + rc = sessionChangesetToHash(pIter, &p->grp, 1); + } + sqlite3changeset_finalize(pIter); + return rc; +} + +/* +** Rebase a changeset according to current rebaser configuration +*/ +SQLITE_API int sqlite3rebaser_rebase( + sqlite3_rebaser *p, + int nIn, const void *pIn, + int *pnOut, void **ppOut +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */ + int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn); + + if( rc==SQLITE_OK ){ + rc = sessionRebase(p, pIter, 0, 0, pnOut, ppOut); + sqlite3changeset_finalize(pIter); + } + + return rc; +} + +/* +** Rebase a changeset according to current rebaser configuration +*/ +SQLITE_API int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *p, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */ + int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); + + if( rc==SQLITE_OK ){ + rc = sessionRebase(p, pIter, xOutput, pOut, 0, 0); + sqlite3changeset_finalize(pIter); + } + + return rc; +} + +/* +** Destroy a rebaser object +*/ +SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ + if( p ){ + sessionDeleteTable(p->grp.pList); + sqlite3_free(p); + } +} + #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ @@ -188257,7 +190574,8 @@ #endif do{ i = fts5yy_shift_ofst[stateno]; - assert( i>=0 && i+fts5YYNFTS5TOKEN<=sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]) ); + assert( i>=0 ); + assert( i+fts5YYNFTS5TOKEN<=(int)sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]) ); assert( iLookAhead!=fts5YYNOCODE ); assert( iLookAhead < fts5YYNFTS5TOKEN ); i += iLookAhead; @@ -192694,7 +195012,7 @@ ** no token characters at all. (e.g ... MATCH '""'). */ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); }else if( sCtx.pPhrase->nTerm ){ - sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix; + sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; } pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; } @@ -195157,6 +197475,7 @@ sqlite3_bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC); sqlite3_step(p->pWriter); p->rc = sqlite3_reset(p->pWriter); + sqlite3_bind_null(p->pWriter, 2); } /* @@ -196785,6 +199104,7 @@ bDlidx = (val & 0x0001); } p->rc = sqlite3_reset(pIdxSelect); + sqlite3_bind_null(pIdxSelect, 2); if( iPg<pSeg->pgnoFirst ){ iPg = pSeg->pgnoFirst; @@ -197997,6 +200317,7 @@ sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC); assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW ); p->rc = sqlite3_reset(pIdxSelect); + sqlite3_bind_null(pIdxSelect, 2); } } #endif @@ -198123,6 +200444,7 @@ sqlite3_bind_int64(p->pIdxWriter, 3, bFlag + ((i64)pWriter->iBtPage<<1)); sqlite3_step(p->pIdxWriter); p->rc = sqlite3_reset(p->pIdxWriter); + sqlite3_bind_null(p->pIdxWriter, 2); } pWriter->iBtPage = 0; } @@ -201436,6 +203758,12 @@ aColMap[1] = nCol; aColMap[2] = nCol+1; + assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH ); + /* Set idxFlags flags for all WHERE clause terms that will be used. */ for(i=0; i<pInfo->nConstraint; i++){ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; @@ -201454,11 +203782,11 @@ pInfo->estimatedCost = 1e50; return SQLITE_OK; } - }else{ + }else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){ int j; for(j=1; j<ArraySize(aConstraint); j++){ struct Constraint *pC = &aConstraint[j]; - if( iCol==aColMap[pC->iCol] && p->op & pC->op && p->usable ){ + if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){ pC->iConsIndex = i; idxFlags |= pC->fts5op; } @@ -203530,7 +205858,7 @@ ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2171d", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2018-04-10 17:39:29 4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd148b3b", -1, SQLITE_TRANSIENT); } static int fts5Init(sqlite3 *db){ @@ -204106,6 +206434,7 @@ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 2); } } return rc; @@ -204766,6 +207095,7 @@ } sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 1); } if( rc==SQLITE_OK && pVal ){ int iNew = p->pConfig->iCookie + 1; @@ -207798,9 +210128,9 @@ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=207801 +#if __LINE__!=210131 #undef SQLITE_SOURCE_ID -#define SQLITE_SOURCE_ID "2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2alt2" +#define SQLITE_SOURCE_ID "2018-04-10 17:39:29 4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd14alt2" #endif /* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
diff --git a/third_party/sqlite/amalgamation/sqlite3.h b/third_party/sqlite/amalgamation/sqlite3.h index c6607bd..8ddbd24 100644 --- a/third_party/sqlite/amalgamation/sqlite3.h +++ b/third_party/sqlite/amalgamation/sqlite3.h
@@ -123,9 +123,9 @@ ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.22.0" -#define SQLITE_VERSION_NUMBER 3022000 -#define SQLITE_SOURCE_ID "2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2alt1" +#define SQLITE_VERSION "3.23.1" +#define SQLITE_VERSION_NUMBER 3023001 +#define SQLITE_SOURCE_ID "2018-04-10 17:39:29 4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd14alt1" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1064,6 +1064,12 @@ ** so that all subsequent write operations are independent. ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. +** +** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] +** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain +** a file lock using the xLock or xShmLock methods of the VFS to wait +** for up to M milliseconds before failing, where M is the single +** unsigned integer parameter. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -1098,6 +1104,7 @@ #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 +#define SQLITE_FCNTL_LOCK_TIMEOUT 34 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -2054,11 +2061,13 @@ ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to ** override this behaviour. The first parameter passed to this operation -** is an integer - non-zero to disable checkpoints-on-close, or zero (the -** default) to enable them. The second parameter is a pointer to an integer +** is an integer - positive to disable checkpoints-on-close, or zero (the +** default) to enable them, and negative to leave the setting unchanged. +** The second parameter is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. ** </dd> +** ** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt> ** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, @@ -2068,13 +2077,20 @@ ** slower. But the QPSG has the advantage of more predictable behavior. With ** the QPSG active, SQLite will always use the same query plan in the field as ** was used during testing in the lab. +** The first argument to this setting is an integer which is 0 to disable +** the QPSG, positive to enable QPSG, or negative to leave the setting +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether the QPSG is disabled or enabled +** following this call. ** </dd> +** ** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt> ** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not ** include output for any operations performed by trigger programs. This ** option is used to set or clear (the default) a flag that governs this ** behavior. The first parameter passed to this operation is an integer - -** non-zero to enable output for trigger programs, or zero to disable it. +** positive to enable output for trigger programs, or zero to disable it, +** or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which is written ** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if ** it is not disabled, 1 if it is. @@ -2496,16 +2512,16 @@ ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. -** These routines understand most of the common K&R formatting options, -** plus some additional non-standard formats, detailed below. -** Note that some of the more obscure formatting options from recent -** C-library standards are omitted from this implementation. +** These routines understand most of the common formatting options from +** the standard library printf() +** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]). +** See the [built-in printf()] documentation for details. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their -** results into memory obtained from [sqlite3_malloc()]. +** results into memory obtained from [sqlite3_malloc64()]. ** The strings returned by these two routines should be ** released by [sqlite3_free()]. ^Both routines return a -** NULL pointer if [sqlite3_malloc()] is unable to allocate enough +** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough ** memory to hold the resulting string. ** ** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from @@ -2529,71 +2545,7 @@ ** ** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). ** -** These routines all implement some additional formatting -** options that are useful for constructing SQL statements. -** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", "%w" and "%z" options. -** -** ^(The %q option works like %s in that it substitutes a nul-terminated -** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal.)^ By doubling each '\'' -** character it escapes that character and allows it to be inserted into -** the string. -** -** For example, assume the string variable zText contains text as follows: -** -** <blockquote><pre> -** char *zText = "It's a happy day!"; -** </pre></blockquote> -** -** One can use this text in an SQL statement as follows: -** -** <blockquote><pre> -** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText); -** sqlite3_exec(db, zSQL, 0, 0, 0); -** sqlite3_free(zSQL); -** </pre></blockquote> -** -** Because the %q format string is used, the '\'' character in zText -** is escaped and the SQL generated is as follows: -** -** <blockquote><pre> -** INSERT INTO table1 VALUES('It''s a happy day!') -** </pre></blockquote> -** -** This is correct. Had we used %s instead of %q, the generated SQL -** would have looked like this: -** -** <blockquote><pre> -** INSERT INTO table1 VALUES('It's a happy day!'); -** </pre></blockquote> -** -** This second example is an SQL syntax error. As a general rule you should -** always use %q instead of %s when inserting text into a string literal. -** -** ^(The %Q option works like %q except it also adds single quotes around -** the outside of the total string. Additionally, if the parameter in the -** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes).)^ So, for example, one could say: -** -** <blockquote><pre> -** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText); -** sqlite3_exec(db, zSQL, 0, 0, 0); -** sqlite3_free(zSQL); -** </pre></blockquote> -** -** The code above will render a correct SQL statement in the zSQL -** variable even if the zText variable is a NULL pointer. -** -** ^(The "%w" formatting option is like "%q" except that it expects to -** be contained within double-quotes instead of single quotes, and it -** escapes the double-quote character instead of the single-quote -** character.)^ The "%w" formatting option is intended for safely inserting -** table and column names into a constructed SQL statement. -** -** ^(The "%z" formatting option works like "%s" but with the -** addition that after the string has been read and copied into -** the result, [sqlite3_free()] is called on the input string.)^ +** See also: [built-in printf()], [printf() SQL function] */ SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); @@ -3659,13 +3611,13 @@ ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** </li> +** </ol> ** ** <p>^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having ** the extra prepFlags parameter, which is a bit array consisting of zero or ** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The ** sqlite3_prepare_v2() interface works exactly the same as ** sqlite3_prepare_v3() with a zero prepFlags parameter. -** </ol> */ SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ @@ -7294,6 +7246,15 @@ ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** </dd> ** +** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> +** <dd>This parameter returns the number of dirty cache entries that have +** been written to disk in the middle of a transaction due to the page +** cache overflowing. Transactions are more efficient if they are written +** to disk all at once. When pages spill mid-transaction, that introduces +** additional overhead. This parameter can be used help identify +** inefficiencies that can be resolve by increasing the cache size. +** </dd> +** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been @@ -7313,7 +7274,8 @@ #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 -#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_CACHE_SPILL 12 +#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */ /* @@ -8817,6 +8779,128 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* +** CAPI3REF: Serialize a database +** +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory +** that is a serialization of the S database on [database connection] D. +** If P is not a NULL pointer, then the size of the database in bytes +** is written into *P. +** +** For an ordinary on-disk database file, the serialization is just a +** copy of the disk file. For an in-memory database or a "TEMP" database, +** the serialization is the same sequence of bytes which would be written +** to disk if that database where backed up to disk. +** +** The usual case is that sqlite3_serialize() copies the serialization of +** the database into memory obtained from [sqlite3_malloc64()] and returns +** a pointer to that memory. The caller is responsible for freeing the +** returned value to avoid a memory leak. However, if the F argument +** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations +** are made, and the sqlite3_serialize() function will return a pointer +** to the contiguous memory representation of the database that SQLite +** is currently using for that database, or NULL if the no such contiguous +** memory representation of the database exists. A contiguous memory +** representation of the database will usually only exist if there has +** been a prior call to [sqlite3_deserialize(D,S,...)] with the same +** values of D and S. +** The size of the database is written into *P even if the +** SQLITE_SERIALIZE_NOCOPY bit is set but no contigious copy +** of the database exists. +** +** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the +** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory +** allocation error occurs. +** +** This interface is only available if SQLite is compiled with the +** [SQLITE_ENABLE_DESERIALIZE] option. +*/ +SQLITE_API unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */ + sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */ + unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_serialize +** +** Zero or more of the following constants can be OR-ed together for +** the F argument to [sqlite3_serialize(D,S,P,F)]. +** +** SQLITE_SERIALIZE_NOCOPY means that [sqlite3_serialize()] will return +** a pointer to contiguous in-memory database that it is currently using, +** without making a copy of the database. If SQLite is not currently using +** a contiguous in-memory database, then this option causes +** [sqlite3_serialize()] to return a NULL pointer. SQLite will only be +** using a contiguous in-memory database if it has been initialized by a +** prior call to [sqlite3_deserialize()]. +*/ +#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ + +/* +** CAPI3REF: Deserialize a database +** +** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the +** [database connection] D to disconnect from database S and then +** reopen S as an in-memory database based on the serialization contained +** in P. The serialized database P is N bytes in size. M is the size of +** the buffer P, which might be larger than N. If M is larger than N, and +** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is +** permitted to add content to the in-memory database as long as the total +** size does not exceed M bytes. +** +** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will +** invoke sqlite3_free() on the serialization buffer when the database +** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then +** SQLite will try to increase the buffer size using sqlite3_realloc64() +** if writes on the database cause it to grow larger than M bytes. +** +** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the +** database is currently in a read transaction or is involved in a backup +** operation. +** +** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the +** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then +** [sqlite3_free()] is invoked on argument P prior to returning. +** +** This interface is only available if SQLite is compiled with the +** [SQLITE_ENABLE_DESERIALIZE] option. +*/ +SQLITE_API int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_deserialize() +** +** The following are allowed values for 6th argument (the F argument) to +** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. +** +** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization +** in the P argument is held in memory obtained from [sqlite3_malloc64()] +** and that SQLite should take ownership of this memory and automatically +** free it when it has finished using it. Without this flag, the caller +** is resposible for freeing any dynamically allocated memory. +** +** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to +** grow the size of the database using calls to [sqlite3_realloc64()]. This +** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used. +** Without this flag, the deserialized database cannot increase in size beyond +** the number of bytes specified by the M parameter. +** +** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database +** should be treated as read-only. +*/ +#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ +#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ +#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ @@ -8963,16 +9047,23 @@ /* ** CAPI3REF: Session Object Handle +** +** An instance of this object is a [session] that can be used to +** record changes to a database. */ typedef struct sqlite3_session sqlite3_session; /* ** CAPI3REF: Changeset Iterator Handle +** +** An instance of this object acts as a cursor for iterating +** over the elements of a [changeset] or [patchset]. */ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; /* ** CAPI3REF: Create A New Session Object +** CONSTRUCTOR: sqlite3_session ** ** Create a new session object attached to database handle db. If successful, ** a pointer to the new object is written to *ppSession and SQLITE_OK is @@ -9009,6 +9100,7 @@ /* ** CAPI3REF: Delete A Session Object +** DESTRUCTOR: sqlite3_session ** ** Delete a session object previously allocated using ** [sqlite3session_create()]. Once a session object has been deleted, the @@ -9024,6 +9116,7 @@ /* ** CAPI3REF: Enable Or Disable A Session Object +** METHOD: sqlite3_session ** ** Enable or disable the recording of changes by a session object. When ** enabled, a session object records changes made to the database. When @@ -9043,6 +9136,7 @@ /* ** CAPI3REF: Set Or Clear the Indirect Change Flag +** METHOD: sqlite3_session ** ** Each change recorded by a session object is marked as either direct or ** indirect. A change is marked as indirect if either: @@ -9072,6 +9166,7 @@ /* ** CAPI3REF: Attach A Table To A Session Object +** METHOD: sqlite3_session ** ** If argument zTab is not NULL, then it is the name of a table to attach ** to the session object passed as the first argument. All subsequent changes @@ -9134,6 +9229,7 @@ /* ** CAPI3REF: Set a table filter on a Session Object. +** METHOD: sqlite3_session ** ** The second argument (xFilter) is the "filter callback". For changes to rows ** in tables that are not attached to the Session object, the filter is called @@ -9152,6 +9248,7 @@ /* ** CAPI3REF: Generate A Changeset From A Session Object +** METHOD: sqlite3_session ** ** Obtain a changeset containing changes to the tables attached to the ** session object passed as the first argument. If successful, @@ -9262,6 +9359,7 @@ /* ** CAPI3REF: Load The Difference Between Tables Into A Session +** METHOD: sqlite3_session ** ** If it is not already attached to the session object passed as the first ** argument, this function attaches table zTbl in the same manner as the @@ -9326,6 +9424,7 @@ /* ** CAPI3REF: Generate A Patchset From A Session Object +** METHOD: sqlite3_session ** ** The differences between a patchset and a changeset are that: ** @@ -9377,6 +9476,7 @@ /* ** CAPI3REF: Create An Iterator To Traverse A Changeset +** CONSTRUCTOR: sqlite3_changeset_iter ** ** Create an iterator used to iterate through the contents of a changeset. ** If successful, *pp is set to point to the iterator handle and SQLITE_OK @@ -9417,6 +9517,7 @@ /* ** CAPI3REF: Advance A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function may only be used with iterators created by function ** [sqlite3changeset_start()]. If it is called on an iterator passed to @@ -9441,6 +9542,7 @@ /* ** CAPI3REF: Obtain The Current Operation From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -9475,6 +9577,7 @@ /* ** CAPI3REF: Obtain The Primary Key Definition Of A Table +** METHOD: sqlite3_changeset_iter ** ** For each modified table, a changeset includes the following: ** @@ -9506,6 +9609,7 @@ /* ** CAPI3REF: Obtain old.* Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -9536,6 +9640,7 @@ /* ** CAPI3REF: Obtain new.* Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -9569,6 +9674,7 @@ /* ** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function should only be used with iterator objects passed to a ** conflict-handler callback by [sqlite3changeset_apply()] with either @@ -9596,6 +9702,7 @@ /* ** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations +** METHOD: sqlite3_changeset_iter ** ** This function may only be called with an iterator passed to an ** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case @@ -9612,6 +9719,7 @@ /* ** CAPI3REF: Finalize A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function is used to finalize an iterator allocated with ** [sqlite3changeset_start()]. @@ -9628,6 +9736,7 @@ ** to that error is returned by this function. Otherwise, SQLITE_OK is ** returned. This is to allow the following pattern (pseudo-code): ** +** <pre> ** sqlite3changeset_start(); ** while( SQLITE_ROW==sqlite3changeset_next() ){ ** // Do something with change. @@ -9636,6 +9745,7 @@ ** if( rc!=SQLITE_OK ){ ** // An error has occurred ** } +** </pre> */ SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter); @@ -9683,6 +9793,7 @@ ** sqlite3_changegroup object. Calling it produces similar results as the ** following code fragment: ** +** <pre> ** sqlite3_changegroup *pGrp; ** rc = sqlite3_changegroup_new(&pGrp); ** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA); @@ -9693,6 +9804,7 @@ ** *ppOut = 0; ** *pnOut = 0; ** } +** </pre> ** ** Refer to the sqlite3_changegroup documentation below for details. */ @@ -9708,11 +9820,15 @@ /* ** CAPI3REF: Changegroup Handle +** +** A changegroup is an object used to combine two or more +** [changesets] or [patchsets] */ typedef struct sqlite3_changegroup sqlite3_changegroup; /* ** CAPI3REF: Create A New Changegroup Object +** CONSTRUCTOR: sqlite3_changegroup ** ** An sqlite3_changegroup object is used to combine two or more changesets ** (or patchsets) into a single changeset (or patchset). A single changegroup @@ -9750,6 +9866,7 @@ /* ** CAPI3REF: Add A Changeset To A Changegroup +** METHOD: sqlite3_changegroup ** ** Add all changes within the changeset (or patchset) in buffer pData (size ** nData bytes) to the changegroup. @@ -9827,6 +9944,7 @@ /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup +** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup @@ -9857,25 +9975,25 @@ /* ** CAPI3REF: Delete A Changegroup Object +** DESTRUCTOR: sqlite3_changegroup */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*); /* ** CAPI3REF: Apply A Changeset To A Database ** -** Apply a changeset to a database. This function attempts to update the -** "main" database attached to handle db with the changes found in the -** changeset passed via the second and third arguments. +** Apply a changeset or patchset to a database. These functions attempt to +** update the "main" database attached to handle db with the changes found in +** the changeset passed via the second and third arguments. ** -** The fourth argument (xFilter) passed to this function is the "filter +** The fourth argument (xFilter) passed to these functions is the "filter ** callback". If it is not NULL, then for each table affected by at least one ** change in the changeset, the filter callback is invoked with ** the table name as the second argument, and a copy of the context pointer -** passed as the sixth argument to this function as the first. If the "filter -** callback" returns zero, then no attempt is made to apply any changes to -** the table. Otherwise, if the return value is non-zero or the xFilter -** argument to this function is NULL, all changes related to the table are -** attempted. +** passed as the sixth argument as the first. If the "filter callback" +** returns zero, then no attempt is made to apply any changes to the table. +** Otherwise, if the return value is non-zero or the xFilter argument to +** is NULL, all changes related to the table are attempted. ** ** For each table that is not excluded by the filter callback, this function ** tests that the target database contains a compatible table. A table is @@ -9920,7 +10038,7 @@ ** ** <dl> ** <dt>DELETE Changes<dd> -** For each DELETE change, this function checks if the target database +** For each DELETE change, the function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all non-primary key columns also match the values stored in @@ -9965,7 +10083,7 @@ ** [SQLITE_CHANGESET_REPLACE]. ** ** <dt>UPDATE Changes<dd> -** For each UPDATE change, this function checks if the target database +** For each UPDATE change, the function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all modified non-primary key columns also match the values @@ -9996,11 +10114,28 @@ ** This can be used to further customize the applications conflict ** resolution strategy. ** -** All changes made by this function are enclosed in a savepoint transaction. +** All changes made by these functions are enclosed in a savepoint transaction. ** If any other error (aside from a constraint failure when attempting to ** write to the target database) occurs, then the savepoint transaction is ** rolled back, restoring the target database to its original state, and an ** SQLite error code returned. +** +** If the output parameters (ppRebase) and (pnRebase) are non-NULL and +** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() +** may set (*ppRebase) to point to a "rebase" that may be used with the +** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) +** is set to the size of the buffer in bytes. It is the responsibility of the +** caller to eventually free any such buffer using sqlite3_free(). The buffer +** is only allocated and populated if one or more conflicts were encountered +** while applying the patchset. See comments surrounding the sqlite3_rebaser +** APIs for further details. +** +** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent +** may be modified by passing a combination of +** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter. +** +** Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b> +** and therefore subject to change. */ SQLITE_API int sqlite3changeset_apply( sqlite3 *db, /* Apply change to "main" db of this handle */ @@ -10017,6 +10152,41 @@ ), void *pCtx /* First argument passed to xConflict */ ); +SQLITE_API int sqlite3changeset_apply_v2( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void *pChangeset, /* Changeset blob */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, /* OUT: Rebase data */ + int flags /* Combination of SESSION_APPLY_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3changeset_apply_v2 +** +** The following flags may passed via the 9th parameter to +** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]: +** +** <dl> +** <dt>SQLITE_CHANGESETAPPLY_NOSAVEPOINT <dd> +** Usually, the sessions module encloses all operations performed by +** a single call to apply_v2() or apply_v2_strm() in a [SAVEPOINT]. The +** SAVEPOINT is committed if the changeset or patchset is successfully +** applied, or rolled back if an error occurs. Specifying this flag +** causes the sessions module to omit this savepoint. In this case, if the +** caller has an open transaction or savepoint when apply_v2() is called, +** it may revert the partially applied changeset by rolling it back. +*/ +#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -10115,6 +10285,161 @@ #define SQLITE_CHANGESET_ABORT 2 /* +** CAPI3REF: Rebasing changesets +** EXPERIMENTAL +** +** Suppose there is a site hosting a database in state S0. And that +** modifications are made that move that database to state S1 and a +** changeset recorded (the "local" changeset). Then, a changeset based +** on S0 is received from another site (the "remote" changeset) and +** applied to the database. The database is then in state +** (S1+"remote"), where the exact state depends on any conflict +** resolution decisions (OMIT or REPLACE) made while applying "remote". +** Rebasing a changeset is to update it to take those conflict +** resolution decisions into account, so that the same conflicts +** do not have to be resolved elsewhere in the network. +** +** For example, if both the local and remote changesets contain an +** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)": +** +** local: INSERT INTO t1 VALUES(1, 'v1'); +** remote: INSERT INTO t1 VALUES(1, 'v2'); +** +** and the conflict resolution is REPLACE, then the INSERT change is +** removed from the local changeset (it was overridden). Or, if the +** conflict resolution was "OMIT", then the local changeset is modified +** to instead contain: +** +** UPDATE t1 SET b = 'v2' WHERE a=1; +** +** Changes within the local changeset are rebased as follows: +** +** <dl> +** <dt>Local INSERT<dd> +** This may only conflict with a remote INSERT. If the conflict +** resolution was OMIT, then add an UPDATE change to the rebased +** changeset. Or, if the conflict resolution was REPLACE, add +** nothing to the rebased changeset. +** +** <dt>Local DELETE<dd> +** This may conflict with a remote UPDATE or DELETE. In both cases the +** only possible resolution is OMIT. If the remote operation was a +** DELETE, then add no change to the rebased changeset. If the remote +** operation was an UPDATE, then the old.* fields of change are updated +** to reflect the new.* values in the UPDATE. +** +** <dt>Local UPDATE<dd> +** This may conflict with a remote UPDATE or DELETE. If it conflicts +** with a DELETE, and the conflict resolution was OMIT, then the update +** is changed into an INSERT. Any undefined values in the new.* record +** from the update change are filled in using the old.* values from +** the conflicting DELETE. Or, if the conflict resolution was REPLACE, +** the UPDATE change is simply omitted from the rebased changeset. +** +** If conflict is with a remote UPDATE and the resolution is OMIT, then +** the old.* values are rebased using the new.* values in the remote +** change. Or, if the resolution is REPLACE, then the change is copied +** into the rebased changeset with updates to columns also updated by +** the conflicting remote UPDATE removed. If this means no columns would +** be updated, the change is omitted. +** </dl> +** +** A local change may be rebased against multiple remote changes +** simultaneously. If a single key is modified by multiple remote +** changesets, they are combined as follows before the local changeset +** is rebased: +** +** <ul> +** <li> If there has been one or more REPLACE resolutions on a +** key, it is rebased according to a REPLACE. +** +** <li> If there have been no REPLACE resolutions on a key, then +** the local changeset is rebased according to the most recent +** of the OMIT resolutions. +** </ul> +** +** Note that conflict resolutions from multiple remote changesets are +** combined on a per-field basis, not per-row. This means that in the +** case of multiple remote UPDATE operations, some fields of a single +** local change may be rebased for REPLACE while others are rebased for +** OMIT. +** +** In order to rebase a local changeset, the remote changeset must first +** be applied to the local database using sqlite3changeset_apply_v2() and +** the buffer of rebase information captured. Then: +** +** <ol> +** <li> An sqlite3_rebaser object is created by calling +** sqlite3rebaser_create(). +** <li> The new object is configured with the rebase buffer obtained from +** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure(). +** If the local changeset is to be rebased against multiple remote +** changesets, then sqlite3rebaser_configure() should be called +** multiple times, in the same order that the multiple +** sqlite3changeset_apply_v2() calls were made. +** <li> Each local changeset is rebased by calling sqlite3rebaser_rebase(). +** <li> The sqlite3_rebaser object is deleted by calling +** sqlite3rebaser_delete(). +** </ol> +*/ +typedef struct sqlite3_rebaser sqlite3_rebaser; + +/* +** CAPI3REF: Create a changeset rebaser object. +** EXPERIMENTAL +** +** Allocate a new changeset rebaser object. If successful, set (*ppNew) to +** point to the new object and return SQLITE_OK. Otherwise, if an error +** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew) +** to NULL. +*/ +SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew); + +/* +** CAPI3REF: Configure a changeset rebaser object. +** EXPERIMENTAL +** +** Configure the changeset rebaser object to rebase changesets according +** to the conflict resolutions described by buffer pRebase (size nRebase +** bytes), which must have been obtained from a previous call to +** sqlite3changeset_apply_v2(). +*/ +SQLITE_API int sqlite3rebaser_configure( + sqlite3_rebaser*, + int nRebase, const void *pRebase +); + +/* +** CAPI3REF: Rebase a changeset +** EXPERIMENTAL +** +** Argument pIn must point to a buffer containing a changeset nIn bytes +** in size. This function allocates and populates a buffer with a copy +** of the changeset rebased rebased according to the configuration of the +** rebaser object passed as the first argument. If successful, (*ppOut) +** is set to point to the new buffer containing the rebased changset and +** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the +** responsibility of the caller to eventually free the new buffer using +** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) +** are set to zero and an SQLite error code returned. +*/ +SQLITE_API int sqlite3rebaser_rebase( + sqlite3_rebaser*, + int nIn, const void *pIn, + int *pnOut, void **ppOut +); + +/* +** CAPI3REF: Delete a changeset rebaser object. +** EXPERIMENTAL +** +** Delete the changeset rebaser object and all associated resources. There +** should be one call to this function for each successful invocation +** of sqlite3rebaser_create(). +*/ +SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p); + +/* ** CAPI3REF: Streaming Versions of API functions. ** ** The six streaming API xxx_strm() functions serve similar purposes to the @@ -10123,6 +10448,7 @@ ** <table border=1 style="margin-left:8ex;margin-right:8ex"> ** <tr><th>Streaming function<th>Non-streaming equivalent</th> ** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply] +** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2] ** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat] ** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert] ** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start] @@ -10218,6 +10544,23 @@ ), void *pCtx /* First argument passed to xConflict */ ); +SQLITE_API int sqlite3changeset_apply_v2_strm( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ + void *pIn, /* First arg for xInput */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +); SQLITE_API int sqlite3changeset_concat_strm( int (*xInputA)(void *pIn, void *pData, int *pnData), void *pInA, @@ -10255,6 +10598,13 @@ int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); +SQLITE_API int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *pRebaser, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +); /*
diff --git a/third_party/sqlite/patches/0001-test-SQLite-tests-compiling-on-Linux.patch b/third_party/sqlite/patches/0001-test-SQLite-tests-compiling-on-Linux.patch index 23338f47..42630f2 100644 --- a/third_party/sqlite/patches/0001-test-SQLite-tests-compiling-on-Linux.patch +++ b/third_party/sqlite/patches/0001-test-SQLite-tests-compiling-on-Linux.patch
@@ -1,10 +1,10 @@ -From 61a09e698c41f2aea5b7fbfd60386b073c813656 Mon Sep 17 00:00:00 2001 +From 4bd6a4e20fd5f40091c2043dee4715fc04b23ebd Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Fri, 16 Jan 2015 10:24:30 -0800 Subject: [PATCH 01/10] [test] SQLite tests compiling on Linux. --- - third_party/sqlite/src/Makefile.linux-gcc | 41 ++++++++++++++++++++++--------- + third_party/sqlite/src/Makefile.linux-gcc | 41 ++++++++++++++++------- third_party/sqlite/src/main.mk | 2 +- 2 files changed, 30 insertions(+), 13 deletions(-) @@ -18,7 +18,7 @@ # -TOP = ../sqlite +TOP = .. - + #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. @@ -32,19 +32,19 @@ USLEEP = -DHAVE_USLEEP=1 @@ -29,7 +29,7 @@ -THREADSAFE = -DTHREADSAFE=0 +THREADSAFE = -DTHREADSAFE=1 +#THREADSAFE = -DTHREADSAFE=0 - + #### Specify any extra linker options needed to make the library # thread safe # @@ -37,13 +37,13 @@ -THREADLIB = +THREADLIB = -lpthread +#THREADLIB = - + #### Specify any extra libraries needed to access required functions. # #TLIBS = -lrt # fdatasync on Solaris 8 -TLIBS = +TLIBS = -ldl - + #### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 # to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all @@ -58,7 +58,24 @@ TLIBS = @@ -69,7 +69,7 @@ + +# TODO(shess) I can't see why I need this setting. +OPTS += -DOS_UNIX=1 - + #### The suffix to add to executable files. ".exe" for windows. # Nothing for unix. @@ -70,7 +87,7 @@ EXE = @@ -82,7 +82,7 @@ #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage #TCC = /opt/mingw/bin/i386-mingw32-gcc -O6 @@ -91,16 +108,16 @@ SHPREFIX = lib - + #### Extra compiler options needed for programs that use the TCL library. # -#TCL_FLAGS = @@ -92,7 +92,7 @@ +#TCL_FLAGS = -I/home/drh/tcltk/8.5linux #TCL_FLAGS = -I/home/drh/tcltk/8.5win -DSTATIC_BUILD=1 #TCL_FLAGS = -I/home/drh/tcltk/8.3hpux - + #### Linker options needed to link against the TCL library. # -#LIBTCL = -ltcl -lm -ldl @@ -101,19 +101,20 @@ +#LIBTCL = /home/drh/tcltk/8.5linux/libtcl8.5g.a -lm -ldl #LIBTCL = /home/drh/tcltk/8.5win/libtcl85s.a -lmsvcrt #LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc - + diff --git a/third_party/sqlite/src/main.mk b/third_party/sqlite/src/main.mk -index 459adf6991c4..1ba4cda9a570 100644 +index d312545d16f3..77657f1a9201 100644 --- a/third_party/sqlite/src/main.mk +++ b/third_party/sqlite/src/main.mk -@@ -821,7 +821,7 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $ +@@ -827,7 +827,7 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $ tclsh $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c - + sqlite3_analyzer$(EXE): sqlite3_analyzer.c - $(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB) + $(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS) $(THREADLIB) - + sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl tclsh $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c --- -2.14.0 +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0002-Use-seperate-page-cache-pools-for-each-sqlite-connec.patch b/third_party/sqlite/patches/0002-Use-seperate-page-cache-pools-for-each-sqlite-connec.patch index f0e2ef9..f3c3836 100644 --- a/third_party/sqlite/patches/0002-Use-seperate-page-cache-pools-for-each-sqlite-connec.patch +++ b/third_party/sqlite/patches/0002-Use-seperate-page-cache-pools-for-each-sqlite-connec.patch
@@ -1,4 +1,4 @@ -From 98df487e6ab482e365a9f9013dc4318a59ed6216 Mon Sep 17 00:00:00 2001 +From e9aa46661e72451f7a418d76a1c0335338c2a43d Mon Sep 17 00:00:00 2001 From: rmcilroy <rmcilroy@chromium.org> Date: Thu, 20 Jun 2013 22:50:12 +0000 Subject: [PATCH 02/10] Use seperate page-cache pools for each sqlite @@ -39,5 +39,6 @@ pcache1.separateCache = 0; #elif SQLITE_THREADSAFE pcache1.separateCache = sqlite3GlobalConfig.pPage==0 --- -2.14.0 +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0003-Modify-default-VFS-to-support-WebDatabase.patch b/third_party/sqlite/patches/0003-Modify-default-VFS-to-support-WebDatabase.patch index 733bd8e2..95127f8 100644 --- a/third_party/sqlite/patches/0003-Modify-default-VFS-to-support-WebDatabase.patch +++ b/third_party/sqlite/patches/0003-Modify-default-VFS-to-support-WebDatabase.patch
@@ -1,4 +1,4 @@ -From e38ca21928e6b43aeee582d7c78036bb21a54d5c Mon Sep 17 00:00:00 2001 +From 41ae21b1b4cabce421e0d0454acee2d880183d65 Mon Sep 17 00:00:00 2001 From: dumi <dumi@chromium.org> Date: Mon, 20 Jul 2009 23:40:51 +0000 Subject: [PATCH 03/10] Modify default VFS to support WebDatabase. @@ -17,16 +17,16 @@ https://codereview.chromium.org/377039 [Possibly not a complete list.] --- - third_party/sqlite/src/src/os_unix.c | 50 ++++++++++++++++++++++++++++++++++ - third_party/sqlite/src/src/os_win.c | 8 ++++++ - third_party/sqlite/src/src/sqlite.h.in | 23 ++++++++++++++++ - 3 files changed, 81 insertions(+) + third_party/sqlite/src/src/os_unix.c | 51 ++++++++++++++++++++++++++ + third_party/sqlite/src/src/os_win.c | 8 ++++ + third_party/sqlite/src/src/sqlite.h.in | 23 ++++++++++++ + 3 files changed, 82 insertions(+) diff --git a/third_party/sqlite/src/src/os_unix.c b/third_party/sqlite/src/src/os_unix.c -index a55aa28d7104..74c2e08cd1ba 100644 +index 629c77b58b44..f19df3f0ba21 100644 --- a/third_party/sqlite/src/src/os_unix.c +++ b/third_party/sqlite/src/src/os_unix.c -@@ -1370,6 +1370,12 @@ static int fileHasMoved(unixFile *pFile){ +@@ -1378,6 +1378,12 @@ static int fileHasMoved(unixFile *pFile){ return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; #else struct stat buf; @@ -39,10 +39,10 @@ return pFile->pInode!=0 && (osStat(pFile->zPath, &buf)!=0 || (u64)buf.st_ino!=pFile->pInode->fileId.ino); -@@ -5746,6 +5752,45 @@ static int findCreateFileMode( +@@ -5795,6 +5801,45 @@ static int findCreateFileMode( return rc; } - + +/* +** Initialize |unixFile| internals of |file| on behalf of chromiumOpen() in +** WebDatabase SQLiteFileSystemPosix.cpp. Function is a subset of unixOpen(), @@ -85,15 +85,16 @@ /* ** Open the file zPath. ** -@@ -5847,6 +5892,7 @@ static int unixOpen( +@@ -5895,6 +5940,8 @@ static int unixOpen( + randomnessPid = osGetpid(0); sqlite3_randomness(0,0); } - ++ + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ memset(p, 0, sizeof(unixFile)); - + if( eType==SQLITE_OPEN_MAIN_DB ){ -@@ -5855,6 +5901,7 @@ static int unixOpen( +@@ -5903,6 +5950,7 @@ static int unixOpen( if( pUnused ){ fd = pUnused->fd; }else{ @@ -101,21 +102,21 @@ pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM_BKPT; -@@ -5939,6 +5986,7 @@ static int unixOpen( +@@ -5987,6 +6035,7 @@ static int unixOpen( } - + if( p->pPreallocatedUnused ){ + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ p->pPreallocatedUnused->fd = fd; p->pPreallocatedUnused->flags = flags; } -@@ -6020,10 +6068,12 @@ static int unixOpen( +@@ -6068,10 +6117,12 @@ static int unixOpen( assert( zPath==0 || zPath[0]=='/' || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); - + open_finished: if( rc!=SQLITE_OK ){ + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ @@ -123,13 +124,13 @@ } return rc; diff --git a/third_party/sqlite/src/src/os_win.c b/third_party/sqlite/src/src/os_win.c -index 2b2b8ebd564e..3b82da0bc39b 100644 +index 534426977f81..a69ca7be4d2d 100644 --- a/third_party/sqlite/src/src/os_win.c +++ b/third_party/sqlite/src/src/os_win.c -@@ -6075,4 +6075,12 @@ int sqlite3_os_end(void){ +@@ -6085,4 +6085,12 @@ int sqlite3_os_end(void){ return SQLITE_OK; } - + +CHROMIUM_SQLITE_API +void chromium_sqlite3_initialize_win_sqlite3_file(sqlite3_file* file, HANDLE handle) { + winFile* winSQLite3File = (winFile*)file; @@ -140,13 +141,13 @@ + #endif /* SQLITE_OS_WIN */ diff --git a/third_party/sqlite/src/src/sqlite.h.in b/third_party/sqlite/src/src/sqlite.h.in -index 9306d1c974ad..4f1b4a65d9d8 100644 +index 81e46024552b..d35b7d55f5a6 100644 --- a/third_party/sqlite/src/src/sqlite.h.in +++ b/third_party/sqlite/src/src/sqlite.h.in -@@ -7991,6 +7991,29 @@ int sqlite3_strnicmp(const char *, const char *, int); +@@ -7953,6 +7953,29 @@ int sqlite3_strnicmp(const char *, const char *, int); */ int sqlite3_strglob(const char *zGlob, const char *zStr); - + +/* Begin WebDatabase patch for Chromium */ +/* Expose some SQLite internals for the WebDatabase vfs. +** DO NOT EXTEND THE USE OF THIS. @@ -173,5 +174,6 @@ /* ** CAPI3REF: String LIKE Matching * --- -2.14.0 +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0004-Virtual-table-supporting-recovery-of-corrupted-datab.patch b/third_party/sqlite/patches/0004-Virtual-table-supporting-recovery-of-corrupted-datab.patch index 09290b4e9..529e3dd 100644 --- a/third_party/sqlite/patches/0004-Virtual-table-supporting-recovery-of-corrupted-datab.patch +++ b/third_party/sqlite/patches/0004-Virtual-table-supporting-recovery-of-corrupted-datab.patch
@@ -1,4 +1,4 @@ -From 303400994407af076f42448f14d9a9ff32b56c19 Mon Sep 17 00:00:00 2001 +From 2fa4c98f92d2d3896d8ac685e3a54c153d4990fa Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Sat, 20 Jul 2013 11:42:21 -0700 Subject: [PATCH 04/10] Virtual table supporting recovery of corrupted @@ -17,12 +17,12 @@ --- third_party/sqlite/src/main.mk | 5 + third_party/sqlite/src/src/main.c | 8 + - third_party/sqlite/src/src/recover.c | 2270 +++++++++++++++++++++++++++ + third_party/sqlite/src/src/recover.c | 2270 +++++++++++++++++++ third_party/sqlite/src/src/recover.h | 23 + - third_party/sqlite/src/src/recover_varint.c | 201 +++ + third_party/sqlite/src/src/recover_varint.c | 201 ++ third_party/sqlite/src/test/recover.test | 164 ++ - third_party/sqlite/src/test/recover0.test | 532 +++++++ - third_party/sqlite/src/test/recover1.test | 429 +++++ + third_party/sqlite/src/test/recover0.test | 532 +++++ + third_party/sqlite/src/test/recover1.test | 429 ++++ third_party/sqlite/src/test/recover2.test | 157 ++ 9 files changed, 3789 insertions(+) create mode 100644 third_party/sqlite/src/src/recover.c @@ -34,19 +34,19 @@ create mode 100644 third_party/sqlite/src/test/recover2.test diff --git a/third_party/sqlite/src/main.mk b/third_party/sqlite/src/main.mk -index 1ba4cda9a570..e6a6c93507ae 100644 +index 77657f1a9201..4b358c1f7c69 100644 --- a/third_party/sqlite/src/main.mk +++ b/third_party/sqlite/src/main.mk @@ -77,6 +77,8 @@ LIBOBJ+= vdbe.o parse.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ utf.o vtab.o - + +LIBOBJ += recover.o recover_varint.o + LIBOBJ += sqlite3session.o - + # All of the source code files. -@@ -401,6 +403,8 @@ TESTSRC2 = \ +@@ -403,6 +405,8 @@ TESTSRC2 = \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ @@ -55,22 +55,22 @@ $(TOP)/src/pcache.c \ $(TOP)/src/pcache1.c \ $(TOP)/src/select.c \ -@@ -861,6 +865,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 +@@ -867,6 +871,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit +TESTFIXTURE_FLAGS += -DDEFAULT_ENABLE_RECOVER=1 - + testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ diff --git a/third_party/sqlite/src/src/main.c b/third_party/sqlite/src/src/main.c -index 40c09348a745..aaf30bba232e 100644 +index 8fc687e5d87d..e1ea7632400e 100644 --- a/third_party/sqlite/src/src/main.c +++ b/third_party/sqlite/src/src/main.c -@@ -3052,6 +3052,14 @@ static int openDatabase( +@@ -3103,6 +3103,14 @@ static int openDatabase( } #endif - + +#ifdef DEFAULT_ENABLE_RECOVER + /* Initialize recover virtual table for testing. */ + extern int chrome_sqlite3_recoverVtableInit(sqlite3 *db); @@ -84,7 +84,7 @@ rc = sqlite3IcuInit(db); diff --git a/third_party/sqlite/src/src/recover.c b/third_party/sqlite/src/src/recover.c new file mode 100644 -index 000000000000..c22fd4d43166 +index 000000000000..ba239a507f9c --- /dev/null +++ b/third_party/sqlite/src/src/recover.c @@ -0,0 +1,2270 @@ @@ -2360,7 +2360,7 @@ +} diff --git a/third_party/sqlite/src/src/recover.h b/third_party/sqlite/src/src/recover.h new file mode 100644 -index 000000000000..691f2fdbab22 +index 000000000000..49b0d9e860db --- /dev/null +++ b/third_party/sqlite/src/src/recover.h @@ -0,0 +1,23 @@ @@ -3900,5 +3900,6 @@ +} [list 4 1024 1 text [string length $substr] $substr] + +finish_test --- -2.14.0 +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0005-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch b/third_party/sqlite/patches/0005-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch index af950ed1..b63f819a 100644 --- a/third_party/sqlite/patches/0005-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch +++ b/third_party/sqlite/patches/0005-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
@@ -1,4 +1,4 @@ -From e8b472eb93bf69304294b120f8ea5c0181941adb Mon Sep 17 00:00:00 2001 +From f63c9ffd71e362e10317ba95e0c10a300f317df8 Mon Sep 17 00:00:00 2001 From: "tc@google.com" <tc@google.com> Date: Tue, 6 Jan 2009 22:39:41 +0000 Subject: [PATCH 05/10] Custom shell.c helpers to load Chromium's ICU data. @@ -8,11 +8,11 @@ Original review URL: https://codereview.chromium.org/42250 --- - third_party/sqlite/src/Makefile.linux-gcc | 7 ++++++ + third_party/sqlite/src/Makefile.linux-gcc | 7 +++++ third_party/sqlite/src/main.mk | 2 +- - third_party/sqlite/src/src/shell.c.in | 10 +++++++++ - third_party/sqlite/src/src/shell_icu_linux.c | 27 +++++++++++++++++++++++ - third_party/sqlite/src/src/shell_icu_win.c | 32 ++++++++++++++++++++++++++++ + third_party/sqlite/src/src/shell.c.in | 10 ++++++ + third_party/sqlite/src/src/shell_icu_linux.c | 27 +++++++++++++++++ + third_party/sqlite/src/src/shell_icu_win.c | 32 ++++++++++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 third_party/sqlite/src/src/shell_icu_linux.c create mode 100644 third_party/sqlite/src/src/shell_icu_win.c @@ -24,7 +24,7 @@ @@ -77,6 +77,13 @@ OPTS += -DSQLITE_MEMDEBUG=1 # TODO(shess) I can't see why I need this setting. OPTS += -DOS_UNIX=1 - + +# Support for loading Chromium ICU data in sqlite3. +ifeq ($(shell uname -s),Darwin) +SHELL_ICU = @@ -36,26 +36,26 @@ # Nothing for unix. # diff --git a/third_party/sqlite/src/main.mk b/third_party/sqlite/src/main.mk -index e6a6c93507ae..7dafe79e718f 100644 +index 4b358c1f7c69..0f0e1f829d2b 100644 --- a/third_party/sqlite/src/main.mk +++ b/third_party/sqlite/src/main.mk -@@ -537,7 +537,7 @@ libsqlite3.a: $(LIBOBJ) - +@@ -540,7 +540,7 @@ libsqlite3.a: $(LIBOBJ) + sqlite3$(EXE): shell.c libsqlite3.a sqlite3.h $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \ - shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) + shell.c $(SHELL_ICU) libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) - + sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ diff --git a/third_party/sqlite/src/src/shell.c.in b/third_party/sqlite/src/src/shell.c.in -index 5f7408491666..8c61856a7e4f 100644 +index 28a059d5645f..18751fc75270 100644 --- a/third_party/sqlite/src/src/shell.c.in +++ b/third_party/sqlite/src/src/shell.c.in -@@ -8109,6 +8109,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ +@@ -8244,6 +8244,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } #endif - + + /* Begin evanm patch. */ +#if !defined(__APPLE__) + extern int sqlite_shell_init_icu(); @@ -140,5 +140,6 @@ + + return 1; +} --- -2.14.0 +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0006-fts3-Disable-fts3_tokenizer-and-fts4.patch b/third_party/sqlite/patches/0006-fts3-Disable-fts3_tokenizer-and-fts4.patch index 9761707..09522433 100644 --- a/third_party/sqlite/patches/0006-fts3-Disable-fts3_tokenizer-and-fts4.patch +++ b/third_party/sqlite/patches/0006-fts3-Disable-fts3_tokenizer-and-fts4.patch
@@ -1,4 +1,4 @@ -From 27b5c0b987de8959baa85f15055f64039ce1d3ef Mon Sep 17 00:00:00 2001 +From c4968d01d5115504420298361f030df19e8adcc9 Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Tue, 16 Dec 2014 13:02:27 -0800 Subject: [PATCH 06/10] [fts3] Disable fts3_tokenizer and fts4. @@ -22,7 +22,7 @@ ** older data. */ +#define CHROMIUM_FTS3_CHANGES 1 - + #include "fts3Int.h" #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) @@ -3972,7 +3973,11 @@ int sqlite3Fts3Init(sqlite3 *db){ @@ -54,6 +54,7 @@ +#endif return rc; } + +-- +2.16.0 --- -2.14.0
diff --git a/third_party/sqlite/patches/0007-fts3-Interior-node-corruption-detection.patch b/third_party/sqlite/patches/0007-fts3-Interior-node-corruption-detection.patch index 9a28a95..1ac2cbf 100644 --- a/third_party/sqlite/patches/0007-fts3-Interior-node-corruption-detection.patch +++ b/third_party/sqlite/patches/0007-fts3-Interior-node-corruption-detection.patch
@@ -1,4 +1,4 @@ -From 56eba46a886c1e966d54ea5c8610829350e8c3bf Mon Sep 17 00:00:00 2001 +From e23d8b238929d133f5962351345c62133d9b54eb Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Thu, 26 May 2011 18:44:46 +0000 Subject: [PATCH 07/10] [fts3] Interior node corruption detection. @@ -27,7 +27,7 @@ @@ -1859,8 +1859,13 @@ static int fts3ScanInteriorNode( isFirstTerm = 0; zCsr += fts3GetVarint32(zCsr, &nSuffix); - + - assert( nPrefix>=0 && nSuffix>=0 ); - if( &zCsr[nSuffix]>zEnd ){ + /* NOTE(shess): Previous code checked for negative nPrefix and @@ -40,5 +40,6 @@ rc = FTS_CORRUPT_VTAB; goto finish_scan; } --- -2.14.0 +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0008-fts3-Fix-uninit-variable-in-fts3EvalDeferredPhrase.patch b/third_party/sqlite/patches/0008-fts3-Fix-uninit-variable-in-fts3EvalDeferredPhrase.patch index b61507c..4d3b3f8 100644 --- a/third_party/sqlite/patches/0008-fts3-Fix-uninit-variable-in-fts3EvalDeferredPhrase.patch +++ b/third_party/sqlite/patches/0008-fts3-Fix-uninit-variable-in-fts3EvalDeferredPhrase.patch
@@ -1,4 +1,4 @@ -From 8a6058a07ca57de05365f80ca54e79920cb8822d Mon Sep 17 00:00:00 2001 +From 1b1d1f62ba0fa78cddc2567eaa3b25fe6660b537 Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Thu, 12 Feb 2015 15:01:26 -0800 Subject: [PATCH 08/10] [fts3] Fix uninit variable in fts3EvalDeferredPhrase. @@ -19,7 +19,7 @@ +++ b/third_party/sqlite/src/ext/fts3/fts3.c @@ -4187,8 +4187,8 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ Fts3DeferredToken *pDeferred = pToken->pDeferred; - + if( pDeferred ){ - char *pList; - int nList; @@ -27,6 +27,7 @@ + int nList = 0; int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); if( rc!=SQLITE_OK ) return rc; + +-- +2.16.0 --- -2.14.0
diff --git a/third_party/sqlite/patches/0009-Allow-auto-vacuum-to-work-with-chunks.patch b/third_party/sqlite/patches/0009-Allow-auto-vacuum-to-work-with-chunks.patch index 96ab4c9..38f0fdf 100644 --- a/third_party/sqlite/patches/0009-Allow-auto-vacuum-to-work-with-chunks.patch +++ b/third_party/sqlite/patches/0009-Allow-auto-vacuum-to-work-with-chunks.patch
@@ -1,4 +1,4 @@ -From 00c0d160dafbd48f1bec547fa5b2cd4954c8585d Mon Sep 17 00:00:00 2001 +From 7d1a25b7b37ad6fa8e11c2e2ff00bd4997266bb2 Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Thu, 2 Mar 2017 15:23:09 -0800 Subject: [PATCH 09/10] Allow auto-vacuum to work with chunks. @@ -17,22 +17,22 @@ BUG=698010 --- - third_party/sqlite/src/src/btree.c | 56 ++++++++++++++++- + third_party/sqlite/src/src/btree.c | 56 +++++++++++- third_party/sqlite/src/src/btree.h | 2 + third_party/sqlite/src/src/btreeInt.h | 1 + - third_party/sqlite/src/src/pragma.c | 21 +++++++ - third_party/sqlite/src/src/pragma.h | 98 +++++++++++++++-------------- - third_party/sqlite/src/tool/mkpragmatab.tcl | 4 ++ + third_party/sqlite/src/src/pragma.c | 21 +++++ + third_party/sqlite/src/src/pragma.h | 98 +++++++++++---------- + third_party/sqlite/src/tool/mkpragmatab.tcl | 4 + 6 files changed, 135 insertions(+), 47 deletions(-) diff --git a/third_party/sqlite/src/src/btree.c b/third_party/sqlite/src/src/btree.c -index 5d6cbfe90051..8e6907bdb351 100644 +index f4a70af79618..10cd9ad7b23c 100644 --- a/third_party/sqlite/src/src/btree.c +++ b/third_party/sqlite/src/src/btree.c -@@ -2971,6 +2971,46 @@ static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){ +@@ -2972,6 +2972,46 @@ static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){ # define setDefaultSyncFlag(pBt,safety_level) #endif - + +/* +** Change the 'auto-vacuum-slack-pages' property of the database. If auto vacuum +** is enabled, this is the number of chunks of slack to allow before @@ -76,14 +76,14 @@ /* ** Get a reference to pPage1 of the database file. This will ** also acquire a readlock on that file. -@@ -3800,13 +3840,27 @@ int sqlite3BtreeIncrVacuum(Btree *p){ +@@ -3802,13 +3842,27 @@ int sqlite3BtreeIncrVacuum(Btree *p){ */ static int autoVacuumCommit(BtShared *pBt){ int rc = SQLITE_OK; + int bShouldVacuum = pBt->autoVacuum && !pBt->incrVacuum; Pager *pPager = pBt->pPager; VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager); ) - + assert( sqlite3_mutex_held(pBt->mutex) ); invalidateAllOverflowCache(pBt); assert(pBt->autoVacuum); @@ -119,7 +119,7 @@ int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); int sqlite3BtreeCommitPhaseTwo(Btree*, int); diff --git a/third_party/sqlite/src/src/btreeInt.h b/third_party/sqlite/src/src/btreeInt.h -index 8b2a9af192bf..0694b31a78cc 100644 +index 8fe8e280fe5f..88f3b437b8ea 100644 --- a/third_party/sqlite/src/src/btreeInt.h +++ b/third_party/sqlite/src/src/btreeInt.h @@ -412,6 +412,7 @@ struct BtShared { @@ -137,7 +137,7 @@ @@ -756,6 +756,27 @@ void sqlite3Pragma( } #endif - + + /* + ** PRAGMA [schema.]auto_vacuum_slack_pages(N) + ** @@ -261,7 +261,7 @@ +#define PragTyp_LOCK_STATUS 45 +#define PragTyp_PARSER_TRACE 46 +#define PragTyp_STATS 47 - + /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -152,6 +153,11 @@ static const PragmaName aPragmaName[] = { @@ -287,7 +287,7 @@ --- a/third_party/sqlite/src/tool/mkpragmatab.tcl +++ b/third_party/sqlite/src/tool/mkpragmatab.tcl @@ -382,6 +382,10 @@ set pragma_def { - + NAME: optimize FLAG: Result1 NeedSchema + @@ -295,7 +295,8 @@ + FLAG: NeedSchema Result0 SchemaReq NoColumns1 + IF: !defined(SQLITE_OMIT_AUTOVACUUM) } - + # Open the output file --- -2.14.0. +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0010-fuchsia-Use-dot-file-locking-for-sqlite.patch b/third_party/sqlite/patches/0010-fuchsia-Use-dot-file-locking-for-sqlite.patch index 255cd403..d25c938 100644 --- a/third_party/sqlite/patches/0010-fuchsia-Use-dot-file-locking-for-sqlite.patch +++ b/third_party/sqlite/patches/0010-fuchsia-Use-dot-file-locking-for-sqlite.patch
@@ -1,4 +1,4 @@ -From 5bc429a1b8206673c2ab4bf1046c1fad65ffcfc5 Mon Sep 17 00:00:00 2001 +From c27c563a4fe5dce1be27d57b137b2fe5bfc88b9e Mon Sep 17 00:00:00 2001 From: Scott Graham <scottmg@chromium.org> Date: Mon, 11 Sep 2017 13:37:46 -0700 Subject: [PATCH 10/10] fuchsia: Use dot-file locking for sqlite @@ -8,10 +8,10 @@ 1 file changed, 4 insertions(+) diff --git a/third_party/sqlite/src/src/os_unix.c b/third_party/sqlite/src/src/os_unix.c -index 74c2e08cd1ba..e2ae7e5525ec 100644 +index f19df3f0ba21..31085d6dea1e 100644 --- a/third_party/sqlite/src/src/os_unix.c +++ b/third_party/sqlite/src/src/os_unix.c -@@ -7740,6 +7740,10 @@ int sqlite3_os_init(void){ +@@ -7789,6 +7789,10 @@ int sqlite3_os_init(void){ UNIXVFS("unix", autolockIoFinder ), #elif OS_VXWORKS UNIXVFS("unix", vxworksIoFinder ), @@ -22,5 +22,6 @@ #else UNIXVFS("unix", posixIoFinder ), #endif --- -2.14.0. +-- +2.16.0 +
diff --git a/third_party/sqlite/patches/0011-Fix-thread-sanitizer-TSAN-error-when-SQLITE_ENABLE_A.patch b/third_party/sqlite/patches/0011-Fix-thread-sanitizer-TSAN-error-when-SQLITE_ENABLE_A.patch deleted file mode 100644 index af46a53..0000000 --- a/third_party/sqlite/patches/0011-Fix-thread-sanitizer-TSAN-error-when-SQLITE_ENABLE_A.patch +++ /dev/null
@@ -1,106 +0,0 @@ -From 5eb2089812c879a12159e26668b0bf16b6602adc Mon Sep 17 00:00:00 2001 -From: Victor Costan <pwnall@chromium.org> -Date: Fri, 16 Feb 2018 18:37:52 -0800 -Subject: [PATCH] Fix thread sanitizer (TSAN) error when - SQLITE_ENABLE_API_ARMOR is on. - -This patch can be removed in the next release of SQLite that contains -https://www.sqlite.org/src/info/f53b8a573bfbb487 - -When SQLITE_ENABLE_API_ARMOR is on, the default switch branch in -pthreadMutexAlloc writes the id field of the statically allocated -mutexes in staticMutexes without any synchronization. - -Given that the id field is always set to the same value, the race can be -harmless under some conditions (atomic pointer writes, sequential -consistency memory model). However, it is still a race, and TSAN flags -it accordingly. - -This CL avoids the data race by initializing the static mutexes' id -fields at static initialization time. ---- - third_party/sqlite/src/src/mutex_unix.c | 30 +++++++++++++------------ - 1 file changed, 16 insertions(+), 14 deletions(-) - -diff --git a/third_party/sqlite/src/src/mutex_unix.c b/third_party/sqlite/src/src/mutex_unix.c -index 87258114ebdd..f790301b4ddd 100644 ---- a/third_party/sqlite/src/src/mutex_unix.c -+++ b/third_party/sqlite/src/src/mutex_unix.c -@@ -50,11 +50,12 @@ struct sqlite3_mutex { - #endif - }; - #if SQLITE_MUTEX_NREF --#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0} -+# define SQLITE3_MUTEX_INITIALIZER(id) \ -+ {PTHREAD_MUTEX_INITIALIZER,id,0,(pthread_t)0,0} - #elif defined(SQLITE_ENABLE_API_ARMOR) --#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 } -+# define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER, id } - #else --#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } -+#define SQLITE3_MUTEX_INITIALIZER(id) { PTHREAD_MUTEX_INITIALIZER } - #endif - - /* -@@ -151,18 +152,18 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; } - */ - static sqlite3_mutex *pthreadMutexAlloc(int iType){ - static sqlite3_mutex staticMutexes[] = { -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER, -- SQLITE3_MUTEX_INITIALIZER -+ SQLITE3_MUTEX_INITIALIZER(2), -+ SQLITE3_MUTEX_INITIALIZER(3), -+ SQLITE3_MUTEX_INITIALIZER(4), -+ SQLITE3_MUTEX_INITIALIZER(5), -+ SQLITE3_MUTEX_INITIALIZER(6), -+ SQLITE3_MUTEX_INITIALIZER(7), -+ SQLITE3_MUTEX_INITIALIZER(8), -+ SQLITE3_MUTEX_INITIALIZER(9), -+ SQLITE3_MUTEX_INITIALIZER(10), -+ SQLITE3_MUTEX_INITIALIZER(11), -+ SQLITE3_MUTEX_INITIALIZER(12), -+ SQLITE3_MUTEX_INITIALIZER(13) - }; - sqlite3_mutex *p; - switch( iType ){ -@@ -180,6 +181,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ - pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&p->mutex, &recursiveAttr); - pthread_mutexattr_destroy(&recursiveAttr); -+#endif -+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) -+ p->id = SQLITE_MUTEX_RECURSIVE; - #endif - } - break; -@@ -188,6 +192,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ - p = sqlite3MallocZero( sizeof(*p) ); - if( p ){ - pthread_mutex_init(&p->mutex, 0); -+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) -+ p->id = SQLITE_MUTEX_FAST; -+#endif - } - break; - } -@@ -203,7 +210,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ - } - } - #if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) -- if( p ) p->id = iType; -+ assert( p==0 || p->id==iType ); - #endif - return p; - } --- -2.14.0.
diff --git a/third_party/sqlite/patches/0011-Handle-TRUE-and-FALSE-in-DEFAULT-expression-for-ALTE.patch b/third_party/sqlite/patches/0011-Handle-TRUE-and-FALSE-in-DEFAULT-expression-for-ALTE.patch new file mode 100644 index 0000000..ee20a168 --- /dev/null +++ b/third_party/sqlite/patches/0011-Handle-TRUE-and-FALSE-in-DEFAULT-expression-for-ALTE.patch
@@ -0,0 +1,44 @@ +From 6e08f30068d6c4dea0d704c6658a238fbbfe2ec7 Mon Sep 17 00:00:00 2001 +From: Victor Costan <pwnall@chromium.org> +Date: Wed, 18 Apr 2018 02:19:08 -0700 +Subject: [PATCH] Handle TRUE and FALSE in DEFAULT expression for ALTER TABLE. + +sqlite 3.23 recognizes TRUE and FALSE as aliases for 0 and 1. This is +done by creating TK_TRUEFALSE expression nodes for these constants. +Unfortunately, the ALTER TABLE code path (sqlite3AlterFinishAddColumn -> +-> sqlite3ValueFromExpr -> valueFromExpr) does not handle the +TK_TRUEFALSE case. This fixes the bug. + +The problem can be seen by running the following commands in a SQLite +shell: +> CREATE TABLE tbl (id INTEGER NOT NULL DEFAULT 0, + flag BOOL NOT NULL DEFAULT FALSE); +> ALTER TABLE tbl ADD COLUMN flag2 BOOL NOT NULL DEFAULT FALSE; + +When the bug is present, the shell produces the error message below: +Error: Cannot add a column with non-constant default + +This is a backport of https://www.sqlite.org/src/info/594ebc69557095c9 +--- + third_party/sqlite/src/src/vdbemem.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/third_party/sqlite/src/src/vdbemem.c b/third_party/sqlite/src/src/vdbemem.c +index bb934a8186d0..5c6dda17b6d5 100644 +--- a/third_party/sqlite/src/src/vdbemem.c ++++ b/third_party/sqlite/src/src/vdbemem.c +@@ -1484,6 +1484,11 @@ static int valueFromExpr( + rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); + } + #endif ++ else if( op==TK_TRUEFALSE ){ ++ pVal = valueNew(db, pCtx); ++ pVal->flags = MEM_Int; ++ pVal->u.i = pExpr->u.zToken[4]==0; ++ } + + *ppVal = pVal; + return rc; +-- +2.16.0 +
diff --git a/third_party/sqlite/src/Makefile.in b/third_party/sqlite/src/Makefile.in index e4b9aede..4e6ed014 100644 --- a/third_party/sqlite/src/Makefile.in +++ b/third_party/sqlite/src/Makefile.in
@@ -180,7 +180,7 @@ func.lo global.lo hash.lo \ icu.lo insert.lo json1.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ - memjournal.lo \ + memdb.lo memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ @@ -240,6 +240,7 @@ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ + $(TOP)/src/memdb.c \ $(TOP)/src/memjournal.c \ $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ @@ -440,6 +441,7 @@ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ + $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ @@ -590,6 +592,7 @@ FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 +FUZZCHECK_OPT += -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c DBFUZZ_OPT = @@ -647,6 +650,9 @@ $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \ $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) +sessionfuzz$(TEXE): $(TOP)/test/sessionfuzz.c sqlite3.c sqlite3.h + $(CC) $(CFLAGS) -I. -o $@ $(TOP)/test/sessionfuzz.c $(TLIBS) + dbfuzz$(TEXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c $(TLIBS) @@ -827,6 +833,9 @@ mem5.lo: $(TOP)/src/mem5.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem5.c +memdb.lo: $(TOP)/src/memdb.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/memdb.c + memjournal.lo: $(TOP)/src/memjournal.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/memjournal.c @@ -1160,14 +1169,17 @@ ./testfixture$(TEXE) $(TOP)/test/full.test # Fuzz testing -fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) +fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db ./fuzzcheck$(TEXE) $(FUZZDATA) + ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db -fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA) +fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db ./fuzzcheck$(TEXE) --limit-mem 100M $(FUZZDATA) + ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db -valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) +valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA) + valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db # The veryquick.test TCL tests. #
diff --git a/third_party/sqlite/src/Makefile.msc b/third_party/sqlite/src/Makefile.msc index 73c8df2..43d4a37 100644 --- a/third_party/sqlite/src/Makefile.msc +++ b/third_party/sqlite/src/Makefile.msc
@@ -1186,7 +1186,7 @@ func.lo global.lo hash.lo \ icu.lo insert.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ - memjournal.lo \ + memdb.lo memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ @@ -1259,6 +1259,7 @@ $(TOP)\src\mem2.c \ $(TOP)\src\mem3.c \ $(TOP)\src\mem5.c \ + $(TOP)\src\memdb.c \ $(TOP)\src\memjournal.c \ $(TOP)\src\mutex.c \ $(TOP)\src\mutex_noop.c \ @@ -1500,6 +1501,7 @@ $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\mmapwarm.c \ $(TOP)\ext\misc\nextchar.c \ + $(TOP)\ext\misc\normalize.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\remember.c \ @@ -1614,12 +1616,11 @@ # MPTESTER_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 FUZZERSHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ -DSQLITE_MAX_MEMORY=50000000 +FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ -DSQLITE_MAX_MEMORY=50000000 -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ -DBSELFTEST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 ST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 # Standard options to testfixture. @@ -1687,7 +1688,7 @@ $(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H) - $(LTLINK) $(NO_WARN) $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) + $(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) srcck1.exe: $(TOP)\tool\srcck1.c $(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c @@ -1707,6 +1708,9 @@ ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) +sessionfuzz.exe: zlib $(TOP)\test\sessionfuzz.c $(SQLITE3C) $(SQLITE3H) + $(LTLINK) $(NO_WARN) -I$(ZLIBINCDIR) $(TOP)\test\sessionfuzz.c /link $(LDFLAGS) $(LTLINKOPTS) /LIBPATH:$(ZLIBLIBDIR) $(ZLIBLIB) + mptester.exe: $(TOP)\mptest\mptest.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(MPTESTER_COMPILE_OPTS) $(TOP)\mptest\mptest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) @@ -1908,6 +1912,9 @@ mem5.lo: $(TOP)\src\mem5.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\mem5.c +memdb.lo: $(TOP)\src\memdb.c $(HDR) + $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\memdb.c + memjournal.lo: $(TOP)\src\memjournal.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\memjournal.c @@ -2084,7 +2091,7 @@ # Source files that go into making shell.c SHELL_SRC = \ $(TOP)\src\shell.c.in \ - $(TOP)\ext\misc\appendvfs.c \ + $(TOP)\ext\misc\appendvfs.c \ $(TOP)\ext\misc\shathree.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\completion.c \ @@ -2251,6 +2258,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_JSON1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS) TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) @@ -2364,11 +2372,11 @@ $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_checker.c \ /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS) -dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) +dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS) $(LTLINK) $(NO_WARN) -DDBDUMP_STANDALONE $(TOP)\ext\misc\dbdump.c $(SQLITE3C) \ /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) -testloadext.lo: $(TOP)\src\test_loadext.c +testloadext.lo: $(TOP)\src\test_loadext.c $(SQLITE3H) $(LTCOMPILE) $(NO_WARN) -c $(TOP)\src\test_loadext.c testloadext.dll: testloadext.lo @@ -2421,9 +2429,6 @@ $(LTLINK) $(NO_WARN) $(KV_COMPILE_OPTS) \ $(TOP)\test\kvtest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) -dbselftest.exe: $(TOP)\test\dbselftest.c $(SQLITE3C) $(SQLITE3H) - $(LTLINK) $(NO_WARN) $(DBSELFTEST_COMPILE_OPTS) $(TOP)\test\dbselftest.c $(SQLITE3C) - rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \ $(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) @@ -2463,7 +2468,10 @@ del /Q sqlite3_analyzer.exe sqlite3_analyzer.c 2>NUL del /Q sqlite-*-output.vsix 2>NUL del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe dbhash.exe 2>NUL - del /Q sqltclsh.exe 2>NUL + del /Q sqltclsh.* 2>NUL + del /Q dbfuzz.exe sessionfuzz.exe 2>NUL + del /Q kvtest.exe ossshell.exe scrub.exe 2>NUL + del /Q showshm.exe sqlite3_checker.* sqlite3_expert.exe 2>NUL del /Q fts5.* fts5parse.* 2>NUL del /Q lsm.h lsm1.c 2>NUL # <</mark>>
diff --git a/third_party/sqlite/src/README.md b/third_party/sqlite/src/README.md index 011eb60a..9426721 100644 --- a/third_party/sqlite/src/README.md +++ b/third_party/sqlite/src/README.md
@@ -4,9 +4,10 @@ engine. Some test scripts are also included. However, many other test scripts and most of the documentation are managed separately. -If you are reading this on a Git mirror someplace, you are doing it wrong. -The [official repository](https://www.sqlite.org/src/) is better. Go there -now. +SQLite [does not use Git](https://sqlite.org/whynotgit.html). +If you are reading this on GitHub, then you are looking at an +unofficial mirror. See <https://sqlite.org/src> for the official +repository. ## Obtaining The Code @@ -14,15 +15,17 @@ [Fossil](https://www.fossil-scm.org/), a distributed version control system that was specifically designed to support SQLite development. If you do not want to use Fossil, you can download tarballs or ZIP -archives as follows: +archives or [SQLite archives](https://sqlite.org/cli.html#sqlar) as follows: - * Lastest trunk check-in: - <https://www.sqlite.org/src/tarball/sqlite.tar.gz> or - <https://www.sqlite.org/src/zip/sqlite.zip>. + * Lastest trunk check-in as + [Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz), + [ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip), or + [SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar). - * Latest release: - <https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=release> or - <https://www.sqlite.org/src/zip/sqlite.zip?r=release>. + * Latest release as + [Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=release), + [ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip?r=release), or + [SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar?r=release). * For other check-ins, substitute an appropriate branch name or tag or hash prefix for "release" in the URLs of the previous
diff --git a/third_party/sqlite/src/VERSION b/third_party/sqlite/src/VERSION index a7e7070f..9b2f2a1 100644 --- a/third_party/sqlite/src/VERSION +++ b/third_party/sqlite/src/VERSION
@@ -1 +1 @@ -3.22.0 +3.23.1
diff --git a/third_party/sqlite/src/autoconf/Makefile.am b/third_party/sqlite/src/autoconf/Makefile.am index 0f07068..db246924 100644 --- a/third_party/sqlite/src/autoconf/Makefile.am +++ b/third_party/sqlite/src/autoconf/Makefile.am
@@ -10,7 +10,7 @@ EXTRA_sqlite3_SOURCES = sqlite3.c sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@ sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@ -sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB +sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS) include_HEADERS = sqlite3.h sqlite3ext.h
diff --git a/third_party/sqlite/src/autoconf/Makefile.msc b/third_party/sqlite/src/autoconf/Makefile.msc index 5f7c693..d99549b9 100644 --- a/third_party/sqlite/src/autoconf/Makefile.msc +++ b/third_party/sqlite/src/autoconf/Makefile.msc
@@ -931,6 +931,7 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTVTAB SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_DBSTAT_VTAB SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC -DSQLITE_INTROSPECTION_PRAGMAS +SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_RTREE !ENDIF
diff --git a/third_party/sqlite/src/autoconf/configure.ac b/third_party/sqlite/src/autoconf/configure.ac index 2e8f65e..fa99ae8 100644 --- a/third_party/sqlite/src/autoconf/configure.ac +++ b/third_party/sqlite/src/autoconf/configure.ac
@@ -169,6 +169,9 @@ ]) AC_SUBST(ZLIB_FLAGS) +AC_SEARCH_LIBS(system,,,[SHELL_CFLAGS="-DSQLITE_NOHAVE_SYSTEM"]) +AC_SUBST(SHELL_CFLAGS) + #----------------------------------------------------------------------- # UPDATE: Maybe it's better if users just set CFLAGS before invoking # configure. This option doesn't really add much...
diff --git a/third_party/sqlite/src/configure b/third_party/sqlite/src/configure index 8e735c6..baead2e 100755 --- a/third_party/sqlite/src/configure +++ b/third_party/sqlite/src/configure
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite 3.22.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.23.1. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -726,8 +726,8 @@ # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.22.0' -PACKAGE_STRING='sqlite 3.22.0' +PACKAGE_VERSION='3.23.1' +PACKAGE_STRING='sqlite 3.23.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1465,7 +1465,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.22.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.23.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1530,7 +1530,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.22.0:";; + short | recursive ) echo "Configuration of sqlite 3.23.1:";; esac cat <<\_ACEOF @@ -1655,7 +1655,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.22.0 +sqlite configure 3.23.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2074,7 +2074,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.22.0, which was +It was created by sqlite $as_me 3.23.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -12242,7 +12242,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.22.0, which was +This file was extended by sqlite $as_me 3.23.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12308,7 +12308,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite config.status 3.22.0 +sqlite config.status 3.23.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\"
diff --git a/third_party/sqlite/src/ext/expert/expert.c b/third_party/sqlite/src/ext/expert/expert.c index 13fc87e..051480f8 100644 --- a/third_party/sqlite/src/ext/expert/expert.c +++ b/third_party/sqlite/src/ext/expert/expert.c
@@ -93,8 +93,9 @@ }else{ for(i=1; i<(argc-1); i++){ char *zArg = argv[i]; + int nArg; if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++; - int nArg = (int)strlen(zArg); + nArg = (int)strlen(zArg); if( nArg>=2 && 0==sqlite3_strnicmp(zArg, "-file", nArg) ){ if( ++i==(argc-1) ) option_requires_argument("-file"); rc = readSqlFromFile(p, argv[i], &zErr);
diff --git a/third_party/sqlite/src/ext/fts3/fts3_write.c b/third_party/sqlite/src/ext/fts3/fts3_write.c index 0174626..0c63cb0 100644 --- a/third_party/sqlite/src/ext/fts3/fts3_write.c +++ b/third_party/sqlite/src/ext/fts3/fts3_write.c
@@ -1908,6 +1908,7 @@ sqlite3_bind_blob(pStmt, 2, z, n, SQLITE_STATIC); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); + sqlite3_bind_null(pStmt, 2); } return rc; } @@ -1964,6 +1965,7 @@ sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); + sqlite3_bind_null(pStmt, 6); } return rc; } @@ -3443,6 +3445,7 @@ sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, SQLITE_STATIC); sqlite3_step(pStmt); *pRC = sqlite3_reset(pStmt); + sqlite3_bind_null(pStmt, 2); sqlite3_free(a); } @@ -4631,6 +4634,7 @@ sqlite3_bind_int(pChomp, 4, iIdx); sqlite3_step(pChomp); rc = sqlite3_reset(pChomp); + sqlite3_bind_null(pChomp, 2); } } @@ -4710,6 +4714,7 @@ sqlite3_bind_blob(pReplace, 2, pHint->a, pHint->n, SQLITE_STATIC); sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 2); } return rc; @@ -5524,7 +5529,6 @@ ){ Fts3Table *p = (Fts3Table *)pVtab; int rc = SQLITE_OK; /* Return Code */ - int isRemove = 0; /* True for an UPDATE or DELETE */ u32 *aSzIns = 0; /* Sizes of inserted documents */ u32 *aSzDel = 0; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ @@ -5622,7 +5626,6 @@ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ); rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel); - isRemove = 1; } /* If this is an INSERT or UPDATE operation, insert the new record. */ @@ -5634,7 +5637,7 @@ rc = FTS_CORRUPT_VTAB; } } - if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){ + if( rc==SQLITE_OK ){ rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid); } if( rc==SQLITE_OK ){
diff --git a/third_party/sqlite/src/ext/fts5/fts5_expr.c b/third_party/sqlite/src/ext/fts5/fts5_expr.c index ce0144b..7b20000 100644 --- a/third_party/sqlite/src/ext/fts5/fts5_expr.c +++ b/third_party/sqlite/src/ext/fts5/fts5_expr.c
@@ -1676,7 +1676,7 @@ ** no token characters at all. (e.g ... MATCH '""'). */ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); }else if( sCtx.pPhrase->nTerm ){ - sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix; + sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; } pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; }
diff --git a/third_party/sqlite/src/ext/fts5/fts5_index.c b/third_party/sqlite/src/ext/fts5/fts5_index.c index 1b76236..2e1bbbf 100644 --- a/third_party/sqlite/src/ext/fts5/fts5_index.c +++ b/third_party/sqlite/src/ext/fts5/fts5_index.c
@@ -758,6 +758,7 @@ sqlite3_bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC); sqlite3_step(p->pWriter); p->rc = sqlite3_reset(p->pWriter); + sqlite3_bind_null(p->pWriter, 2); } /* @@ -2386,6 +2387,7 @@ bDlidx = (val & 0x0001); } p->rc = sqlite3_reset(pIdxSelect); + sqlite3_bind_null(pIdxSelect, 2); if( iPg<pSeg->pgnoFirst ){ iPg = pSeg->pgnoFirst; @@ -3598,6 +3600,7 @@ sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC); assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW ); p->rc = sqlite3_reset(pIdxSelect); + sqlite3_bind_null(pIdxSelect, 2); } } #endif @@ -3724,6 +3727,7 @@ sqlite3_bind_int64(p->pIdxWriter, 3, bFlag + ((i64)pWriter->iBtPage<<1)); sqlite3_step(p->pIdxWriter); p->rc = sqlite3_reset(p->pIdxWriter); + sqlite3_bind_null(p->pIdxWriter, 2); } pWriter->iBtPage = 0; }
diff --git a/third_party/sqlite/src/ext/fts5/fts5_main.c b/third_party/sqlite/src/ext/fts5/fts5_main.c index 2e92d61..da51c5e 100644 --- a/third_party/sqlite/src/ext/fts5/fts5_main.c +++ b/third_party/sqlite/src/ext/fts5/fts5_main.c
@@ -535,6 +535,12 @@ aColMap[1] = nCol; aColMap[2] = nCol+1; + assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH ); + /* Set idxFlags flags for all WHERE clause terms that will be used. */ for(i=0; i<pInfo->nConstraint; i++){ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; @@ -553,11 +559,11 @@ pInfo->estimatedCost = 1e50; return SQLITE_OK; } - }else{ + }else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){ int j; for(j=1; j<ArraySize(aConstraint); j++){ struct Constraint *pC = &aConstraint[j]; - if( iCol==aColMap[pC->iCol] && p->op & pC->op && p->usable ){ + if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){ pC->iConsIndex = i; idxFlags |= pC->fts5op; }
diff --git a/third_party/sqlite/src/ext/fts5/fts5_storage.c b/third_party/sqlite/src/ext/fts5/fts5_storage.c index bb04986..8424f9d 100644 --- a/third_party/sqlite/src/ext/fts5/fts5_storage.c +++ b/third_party/sqlite/src/ext/fts5/fts5_storage.c
@@ -458,6 +458,7 @@ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 2); } } return rc; @@ -1118,6 +1119,7 @@ } sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 1); } if( rc==SQLITE_OK && pVal ){ int iNew = p->pConfig->iCookie + 1;
diff --git a/third_party/sqlite/src/ext/fts5/test/fts5aa.test b/third_party/sqlite/src/ext/fts5/test/fts5aa.test index 03e4aa5..7ea2002 100644 --- a/third_party/sqlite/src/ext/fts5/test/fts5aa.test +++ b/third_party/sqlite/src/ext/fts5/test/fts5aa.test
@@ -591,7 +591,19 @@ SELECT rowid FROM t9('a*') } {1} +#------------------------------------------------------------------------- +do_execsql_test 23.0 { + CREATE VIRTUAL TABLE t10 USING fts5(x, detail=%DETAIL%); + CREATE TABLE t11(x); +} +do_execsql_test 23.1 { + SELECT * FROM t11, t10 WHERE t11.x = t10.x AND t10.rowid IS NULL; +} +do_execsql_test 23.2 { + SELECT * FROM t11, t10 WHERE t10.rowid IS NULL; } +} +expand_all_sql db finish_test
diff --git a/third_party/sqlite/src/ext/icu/README.txt b/third_party/sqlite/src/ext/icu/README.txt index 2e7480fa..e3460a8 100644 --- a/third_party/sqlite/src/ext/icu/README.txt +++ b/third_party/sqlite/src/ext/icu/README.txt
@@ -39,8 +39,8 @@ To utilise "general" case mapping, the upper() or lower() scalar functions are invoked with one argument: - upper('ABC') -> 'abc' - lower('abc') -> 'ABC' + upper('abc') -> 'ABC' + lower('ABC') -> 'abc' To access ICU "language specific" case mapping, upper() or lower() should be invoked with two arguments. The second argument is the name
diff --git a/third_party/sqlite/src/ext/misc/btreeinfo.c b/third_party/sqlite/src/ext/misc/btreeinfo.c index 806b8d4..de0392bc 100644 --- a/third_party/sqlite/src/ext/misc/btreeinfo.c +++ b/third_party/sqlite/src/ext/misc/btreeinfo.c
@@ -339,7 +339,8 @@ sqlite3 *db = sqlite3_context_db_handle(ctx); int rc = binfoCompute(db, pgno, pCsr); if( rc ){ - return rc; + pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); + return SQLITE_ERROR; } } switch( i ){
diff --git a/third_party/sqlite/src/ext/misc/completion.c b/third_party/sqlite/src/ext/misc/completion.c index 5bb9672..2b7d8d73 100644 --- a/third_party/sqlite/src/ext/misc/completion.c +++ b/third_party/sqlite/src/ext/misc/completion.c
@@ -78,7 +78,7 @@ #define COMPLETION_INDEXES 5 #define COMPLETION_TRIGGERS 6 #define COMPLETION_DATABASES 7 -#define COMPLETION_TABLES 8 +#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */ #define COMPLETION_COLUMNS 9 #define COMPLETION_MODULES 10 #define COMPLETION_EOF 11 @@ -250,8 +250,7 @@ const char *zDb = (const char*)sqlite3_column_text(pS2, 1); zSql = sqlite3_mprintf( "%z%s" - "SELECT name FROM \"%w\".sqlite_master" - " WHERE type='table'", + "SELECT name FROM \"%w\".sqlite_master", zSql, zSep, zDb ); if( zSql==0 ) return SQLITE_NOMEM;
diff --git a/third_party/sqlite/src/ext/misc/dbdump.c b/third_party/sqlite/src/ext/misc/dbdump.c index 5e9b609e6..9244aa6 100644 --- a/third_party/sqlite/src/ext/misc/dbdump.c +++ b/third_party/sqlite/src/ext/misc/dbdump.c
@@ -293,7 +293,6 @@ ** ordinary column in the table. Verify that azRowid[j] is a valid ** name for the rowid before adding it to azCol[0]. WITHOUT ROWID ** tables will fail this last check */ - int rc; rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0); if( rc==SQLITE_OK ) azCol[0] = azRowid[j]; break; @@ -455,12 +454,12 @@ if( strcmp(zType, "table")==0 ){ DText sSelect; DText sTable; - char **azCol; + char **azTCol; int i; int nCol; - azCol = tableColumnList(p, zTable); - if( azCol==0 ) return 0; + azTCol = tableColumnList(p, zTable); + if( azTCol==0 ) return 0; initText(&sTable); appendText(&sTable, "INSERT INTO ", 0); @@ -473,12 +472,12 @@ ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)" ** instead of the usual "INSERT INTO tab VALUES(...)". */ - if( azCol[0] ){ + if( azTCol[0] ){ appendText(&sTable, "(", 0); - appendText(&sTable, azCol[0], 0); - for(i=1; azCol[i]; i++){ + appendText(&sTable, azTCol[0], 0); + for(i=1; azTCol[i]; i++){ appendText(&sTable, ",", 0); - appendText(&sTable, azCol[i], quoteChar(azCol[i])); + appendText(&sTable, azTCol[i], quoteChar(azTCol[i])); } appendText(&sTable, ")", 0); } @@ -487,19 +486,19 @@ /* Build an appropriate SELECT statement */ initText(&sSelect); appendText(&sSelect, "SELECT ", 0); - if( azCol[0] ){ - appendText(&sSelect, azCol[0], 0); + if( azTCol[0] ){ + appendText(&sSelect, azTCol[0], 0); appendText(&sSelect, ",", 0); } - for(i=1; azCol[i]; i++){ - appendText(&sSelect, azCol[i], quoteChar(azCol[i])); - if( azCol[i+1] ){ + for(i=1; azTCol[i]; i++){ + appendText(&sSelect, azTCol[i], quoteChar(azTCol[i])); + if( azTCol[i+1] ){ appendText(&sSelect, ",", 0); } } nCol = i; - if( azCol[0]==0 ) nCol--; - freeColumnList(azCol); + if( azTCol[0]==0 ) nCol--; + freeColumnList(azTCol); appendText(&sSelect, " FROM ", 0); appendText(&sSelect, zTable, quoteChar(zTable));
diff --git a/third_party/sqlite/src/ext/misc/eval.c b/third_party/sqlite/src/ext/misc/eval.c index 2a036a9..9094e8f 100644 --- a/third_party/sqlite/src/ext/misc/eval.c +++ b/third_party/sqlite/src/ext/misc/eval.c
@@ -34,6 +34,7 @@ static int callback(void *pCtx, int argc, char **argv, char **colnames){ struct EvalResult *p = (struct EvalResult*)pCtx; int i; + if( argv==0 ) return 0; for(i=0; i<argc; i++){ const char *z = argv[i] ? argv[i] : ""; size_t sz = strlen(z);
diff --git a/third_party/sqlite/src/ext/misc/fileio.c b/third_party/sqlite/src/ext/misc/fileio.c index e609a26..ae68eb978 100644 --- a/third_party/sqlite/src/ext/misc/fileio.c +++ b/third_party/sqlite/src/ext/misc/fileio.c
@@ -93,6 +93,9 @@ # include <direct.h> # include "test_windirent.h" # define dirent DIRENT +# ifndef chmod +# define chmod _chmod +# endif # ifndef stat # define stat _stat # endif @@ -159,6 +162,97 @@ va_end(ap); } +#if defined(_WIN32) +/* +** This function is designed to convert a Win32 FILETIME structure into the +** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC). +*/ +static sqlite3_uint64 fileTimeToUnixTime( + LPFILETIME pFileTime +){ + SYSTEMTIME epochSystemTime; + ULARGE_INTEGER epochIntervals; + FILETIME epochFileTime; + ULARGE_INTEGER fileIntervals; + + memset(&epochSystemTime, 0, sizeof(SYSTEMTIME)); + epochSystemTime.wYear = 1970; + epochSystemTime.wMonth = 1; + epochSystemTime.wDay = 1; + SystemTimeToFileTime(&epochSystemTime, &epochFileTime); + epochIntervals.LowPart = epochFileTime.dwLowDateTime; + epochIntervals.HighPart = epochFileTime.dwHighDateTime; + + fileIntervals.LowPart = pFileTime->dwLowDateTime; + fileIntervals.HighPart = pFileTime->dwHighDateTime; + + return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; +} + +/* +** This function attempts to normalize the time values found in the stat() +** buffer to UTC. This is necessary on Win32, where the runtime library +** appears to return these values as local times. +*/ +static void statTimesToUtc( + const char *zPath, + struct stat *pStatBuf +){ + HANDLE hFindFile; + WIN32_FIND_DATAW fd; + LPWSTR zUnicodeName; + extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); + zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath); + if( zUnicodeName ){ + memset(&fd, 0, sizeof(WIN32_FIND_DATA)); + hFindFile = FindFirstFileW(zUnicodeName, &fd); + if( hFindFile!=NULL ){ + pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime); + pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime); + pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime); + FindClose(hFindFile); + } + sqlite3_free(zUnicodeName); + } +} +#endif + +/* +** This function is used in place of stat(). On Windows, special handling +** is required in order for the included time to be returned as UTC. On all +** other systems, this function simply calls stat(). +*/ +static int fileStat( + const char *zPath, + struct stat *pStatBuf +){ +#if defined(_WIN32) + int rc = stat(zPath, pStatBuf); + if( rc==0 ) statTimesToUtc(zPath, pStatBuf); + return rc; +#else + return stat(zPath, pStatBuf); +#endif +} + +/* +** This function is used in place of lstat(). On Windows, special handling +** is required in order for the included time to be returned as UTC. On all +** other systems, this function simply calls lstat(). +*/ +static int fileLinkStat( + const char *zPath, + struct stat *pStatBuf +){ +#if defined(_WIN32) + int rc = lstat(zPath, pStatBuf); + if( rc==0 ) statTimesToUtc(zPath, pStatBuf); + return rc; +#else + return lstat(zPath, pStatBuf); +#endif +} + /* ** Argument zFile is the name of a file that will be created and/or written ** by SQL function writefile(). This function ensures that the directory @@ -190,7 +284,7 @@ if( i==nCopy ) break; zCopy[i] = '\0'; - rc2 = stat(zCopy, &sStat); + rc2 = fileStat(zCopy, &sStat); if( rc2!=0 ){ if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR; }else{ @@ -232,7 +326,7 @@ ** to do so using chmod(), it is not an error. */ struct stat sStat; if( errno!=EEXIST - || 0!=stat(zFile, &sStat) + || 0!=fileStat(zFile, &sStat) || !S_ISDIR(sStat.st_mode) || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777)) ){ @@ -270,15 +364,23 @@ SYSTEMTIME currentTime; LONGLONG intervals; HANDLE hFile; + LPWSTR zUnicodeName; + extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); + GetSystemTime(¤tTime); SystemTimeToFileTime(¤tTime, &lastAccess); intervals = Int32x32To64(mtime, 10000000) + 116444736000000000; lastWrite.dwLowDateTime = (DWORD)intervals; lastWrite.dwHighDateTime = intervals >> 32; - hFile = CreateFile( - zFile, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, + zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile); + if( zUnicodeName==0 ){ + return 1; + } + hFile = CreateFileW( + zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); + sqlite3_free(zUnicodeName); if( hFile!=INVALID_HANDLE_VALUE ){ BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite); CloseHandle(hFile); @@ -286,7 +388,7 @@ }else{ return 1; } -#elif defined(AT_FDCWD) && 0 /* utimensat() is not univerally available */ +#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */ /* Recent unix */ struct timespec times[2]; times[0].tv_nsec = times[1].tv_nsec = 0; @@ -486,10 +588,12 @@ sqlite3_free(pLvl->zDir); } sqlite3_free(pCur->zPath); + sqlite3_free(pCur->aLvl); pCur->aLvl = 0; pCur->zPath = 0; pCur->zBase = 0; pCur->nBase = 0; + pCur->nLvl = 0; pCur->iLvl = -1; pCur->iRowid = 1; } @@ -501,7 +605,6 @@ fsdir_cursor *pCur = (fsdir_cursor*)cur; fsdirResetCursor(pCur); - sqlite3_free(pCur->aLvl); sqlite3_free(pCur); return SQLITE_OK; } @@ -562,7 +665,7 @@ sqlite3_free(pCur->zPath); pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name); if( pCur->zPath==0 ) return SQLITE_NOMEM; - if( lstat(pCur->zPath, &pCur->sStat) ){ + if( fileLinkStat(pCur->zPath, &pCur->sStat) ){ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); return SQLITE_ERROR; } @@ -696,7 +799,7 @@ if( pCur->zPath==0 ){ return SQLITE_NOMEM; } - if( lstat(pCur->zPath, &pCur->sStat) ){ + if( fileLinkStat(pCur->zPath, &pCur->sStat) ){ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); return SQLITE_ERROR; }
diff --git a/third_party/sqlite/src/ext/misc/normalize.c b/third_party/sqlite/src/ext/misc/normalize.c new file mode 100644 index 0000000..8b54fdb --- /dev/null +++ b/third_party/sqlite/src/ext/misc/normalize.c
@@ -0,0 +1,707 @@ +/* +** 2018-01-08 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code to implement the sqlite3_normalize() function. +** +** char *sqlite3_normalize(const char *zSql); +** +** This function takes an SQL string as input and returns a "normalized" +** version of that string in memory obtained from sqlite3_malloc64(). The +** caller is responsible for ensuring that the returned memory is freed. +** +** If a memory allocation error occurs, this routine returns NULL. +** +** The normalization consists of the following transformations: +** +** (1) Convert every literal (string, blob literal, numeric constant, +** or "NULL" constant) into a ? +** +** (2) Remove all superfluous whitespace, including comments. Change +** all required whitespace to a single space character. +** +** (3) Lowercase all ASCII characters. +** +** (4) If an IN or NOT IN operator is followed by a list of 1 or more +** values, convert that list into "(?,?,?)". +** +** The purpose of normalization is two-fold: +** +** (1) Sanitize queries by removing potentially private or sensitive +** information contained in literals. +** +** (2) Identify structurally identical queries by comparing their +** normalized forms. +** +** Command-Line Utility +** -------------------- +** +** This file also contains code for a command-line utility that converts +** SQL queries in text files into their normalized forms. To build the +** command-line program, compile this file with -DSQLITE_NORMALIZE_CLI +** and link it against the SQLite library. +*/ +#include <sqlite3.h> +#include <string.h> + +/* +** Implementation note: +** +** Much of the tokenizer logic is copied out of the tokenize.c source file +** of SQLite. That logic could be simplified for this particular application, +** but that would impose a risk of introducing subtle errors. It is best to +** keep the code as close to the original as possible. +** +** The tokenize code is in sync with the SQLite core as of 2018-01-08. +** Any future changes to the core tokenizer might require corresponding +** adjustments to the tokenizer logic in this module. +*/ + + +/* Character classes for tokenizing +** +** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented +** using a lookup table, whereas a switch() directly on c uses a binary search. +** The lookup table is much faster. To maximize speed, and to ensure that +** a lookup table is used, all of the classes need to be small integers and +** all of them need to be used within the switch. +*/ +#define CC_X 0 /* The letter 'x', or start of BLOB literal */ +#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */ +#define CC_ID 2 /* unicode characters usable in IDs */ +#define CC_DIGIT 3 /* Digits */ +#define CC_DOLLAR 4 /* '$' */ +#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */ +#define CC_VARNUM 6 /* '?'. Numeric SQL variables */ +#define CC_SPACE 7 /* Space characters */ +#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */ +#define CC_QUOTE2 9 /* '['. [...] style quoted ids */ +#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */ +#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */ +#define CC_LT 12 /* '<'. Part of < or <= or <> */ +#define CC_GT 13 /* '>'. Part of > or >= */ +#define CC_EQ 14 /* '='. Part of = or == */ +#define CC_BANG 15 /* '!'. Part of != */ +#define CC_SLASH 16 /* '/'. / or c-style comment */ +#define CC_LP 17 /* '(' */ +#define CC_RP 18 /* ')' */ +#define CC_SEMI 19 /* ';' */ +#define CC_PLUS 20 /* '+' */ +#define CC_STAR 21 /* '*' */ +#define CC_PERCENT 22 /* '%' */ +#define CC_COMMA 23 /* ',' */ +#define CC_AND 24 /* '&' */ +#define CC_TILDA 25 /* '~' */ +#define CC_DOT 26 /* '.' */ +#define CC_ILLEGAL 27 /* Illegal character */ + +static const unsigned char aiClass[] = { +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ +/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27, +/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16, +/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6, +/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1, +/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27, +/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; + +/* An array to map all upper-case characters into their corresponding +** lower-case character. +** +** SQLite only considers US-ASCII (or EBCDIC) characters. We do not +** handle case conversions for the UTF character set since the tables +** involved are nearly as big or bigger than SQLite itself. +*/ +static const unsigned char sqlite3UpperToLower[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, + 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, + 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, + 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, + 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, + 252,253,254,255 +}; + +/* +** The following 256 byte lookup table is used to support SQLites built-in +** equivalents to the following standard library functions: +** +** isspace() 0x01 +** isalpha() 0x02 +** isdigit() 0x04 +** isalnum() 0x06 +** isxdigit() 0x08 +** toupper() 0x20 +** SQLite identifier character 0x40 +** Quote character 0x80 +** +** Bit 0x20 is set if the mapped character requires translation to upper +** case. i.e. if the character is a lower-case ASCII character. +** If x is a lower-case ASCII character, then its upper-case equivalent +** is (x - 0x20). Therefore toupper() can be implemented as: +** +** (x & ~(map[x]&0x20)) +** +** The equivalent of tolower() is implemented using the sqlite3UpperToLower[] +** array. tolower() is used more often than toupper() by SQLite. +** +** Bit 0x40 is set if the character is non-alphanumeric and can be used in an +** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any +** non-ASCII UTF character. Hence the test for whether or not a character is +** part of an identifier is 0x46. +*/ +static const unsigned char sqlite3CtypeMap[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */ + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ + 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ + 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ + + 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ + 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ + 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ + 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ +}; +#define sqlite3Toupper(x) ((x)&~(sqlite3CtypeMap[(unsigned char)(x)]&0x20)) +#define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01) +#define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06) +#define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) +#define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) +#define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) +#define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) +#define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) + + +/* +** If X is a character that can be used in an identifier then +** IdChar(X) will be true. Otherwise it is false. +** +** For ASCII, any character with the high-order bit set is +** allowed in an identifier. For 7-bit characters, +** sqlite3IsIdChar[X] must be 1. +** +** For EBCDIC, the rules are more complex but have the same +** end result. +** +** Ticket #1066. the SQL standard does not allow '$' in the +** middle of identifiers. But many SQL implementations do. +** SQLite will allow '$' in identifiers for compatibility. +** But the feature is undocumented. +*/ +#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) + +/* +** Ignore testcase() macros +*/ +#define testcase(X) + +/* +** Token values +*/ +#define TK_SPACE 0 +#define TK_NAME 1 +#define TK_LITERAL 2 +#define TK_PUNCT 3 +#define TK_ERROR 4 + +#define TK_MINUS TK_PUNCT +#define TK_LP TK_PUNCT +#define TK_RP TK_PUNCT +#define TK_SEMI TK_PUNCT +#define TK_PLUS TK_PUNCT +#define TK_STAR TK_PUNCT +#define TK_SLASH TK_PUNCT +#define TK_REM TK_PUNCT +#define TK_EQ TK_PUNCT +#define TK_LE TK_PUNCT +#define TK_NE TK_PUNCT +#define TK_LSHIFT TK_PUNCT +#define TK_LT TK_PUNCT +#define TK_GE TK_PUNCT +#define TK_RSHIFT TK_PUNCT +#define TK_GT TK_PUNCT +#define TK_GE TK_PUNCT +#define TK_BITOR TK_PUNCT +#define TK_CONCAT TK_PUNCT +#define TK_COMMA TK_PUNCT +#define TK_BITAND TK_PUNCT +#define TK_BITNOT TK_PUNCT +#define TK_STRING TK_LITERAL +#define TK_ID TK_NAME +#define TK_ILLEGAL TK_ERROR +#define TK_DOT TK_PUNCT +#define TK_INTEGER TK_LITERAL +#define TK_FLOAT TK_LITERAL +#define TK_VARIABLE TK_LITERAL +#define TK_BLOB TK_LITERAL + +/* +** Return the length (in bytes) of the token that begins at z[0]. +** Store the token type in *tokenType before returning. +*/ +static int sqlite3GetToken(const unsigned char *z, int *tokenType){ + int i, c; + switch( aiClass[*z] ){ /* Switch on the character-class of the first byte + ** of the token. See the comment on the CC_ defines + ** above. */ + case CC_SPACE: { + for(i=1; sqlite3Isspace(z[i]); i++){} + *tokenType = TK_SPACE; + return i; + } + case CC_MINUS: { + if( z[1]=='-' ){ + for(i=2; (c=z[i])!=0 && c!='\n'; i++){} + *tokenType = TK_SPACE; + return i; + } + *tokenType = TK_MINUS; + return 1; + } + case CC_LP: { + *tokenType = TK_LP; + return 1; + } + case CC_RP: { + *tokenType = TK_RP; + return 1; + } + case CC_SEMI: { + *tokenType = TK_SEMI; + return 1; + } + case CC_PLUS: { + *tokenType = TK_PLUS; + return 1; + } + case CC_STAR: { + *tokenType = TK_STAR; + return 1; + } + case CC_SLASH: { + if( z[1]!='*' || z[2]==0 ){ + *tokenType = TK_SLASH; + return 1; + } + for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} + if( c ) i++; + *tokenType = TK_SPACE; + return i; + } + case CC_PERCENT: { + *tokenType = TK_REM; + return 1; + } + case CC_EQ: { + *tokenType = TK_EQ; + return 1 + (z[1]=='='); + } + case CC_LT: { + if( (c=z[1])=='=' ){ + *tokenType = TK_LE; + return 2; + }else if( c=='>' ){ + *tokenType = TK_NE; + return 2; + }else if( c=='<' ){ + *tokenType = TK_LSHIFT; + return 2; + }else{ + *tokenType = TK_LT; + return 1; + } + } + case CC_GT: { + if( (c=z[1])=='=' ){ + *tokenType = TK_GE; + return 2; + }else if( c=='>' ){ + *tokenType = TK_RSHIFT; + return 2; + }else{ + *tokenType = TK_GT; + return 1; + } + } + case CC_BANG: { + if( z[1]!='=' ){ + *tokenType = TK_ILLEGAL; + return 1; + }else{ + *tokenType = TK_NE; + return 2; + } + } + case CC_PIPE: { + if( z[1]!='|' ){ + *tokenType = TK_BITOR; + return 1; + }else{ + *tokenType = TK_CONCAT; + return 2; + } + } + case CC_COMMA: { + *tokenType = TK_COMMA; + return 1; + } + case CC_AND: { + *tokenType = TK_BITAND; + return 1; + } + case CC_TILDA: { + *tokenType = TK_BITNOT; + return 1; + } + case CC_QUOTE: { + int delim = z[0]; + testcase( delim=='`' ); + testcase( delim=='\'' ); + testcase( delim=='"' ); + for(i=1; (c=z[i])!=0; i++){ + if( c==delim ){ + if( z[i+1]==delim ){ + i++; + }else{ + break; + } + } + } + if( c=='\'' ){ + *tokenType = TK_STRING; + return i+1; + }else if( c!=0 ){ + *tokenType = TK_ID; + return i+1; + }else{ + *tokenType = TK_ILLEGAL; + return i; + } + } + case CC_DOT: { + if( !sqlite3Isdigit(z[1]) ){ + *tokenType = TK_DOT; + return 1; + } + /* If the next character is a digit, this is a floating point + ** number that begins with ".". Fall thru into the next case */ + } + case CC_DIGIT: { + *tokenType = TK_INTEGER; + if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ + for(i=3; sqlite3Isxdigit(z[i]); i++){} + return i; + } + for(i=0; sqlite3Isdigit(z[i]); i++){} + if( z[i]=='.' ){ + i++; + while( sqlite3Isdigit(z[i]) ){ i++; } + *tokenType = TK_FLOAT; + } + if( (z[i]=='e' || z[i]=='E') && + ( sqlite3Isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) + ) + ){ + i += 2; + while( sqlite3Isdigit(z[i]) ){ i++; } + *tokenType = TK_FLOAT; + } + while( IdChar(z[i]) ){ + *tokenType = TK_ILLEGAL; + i++; + } + return i; + } + case CC_QUOTE2: { + for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} + *tokenType = c==']' ? TK_ID : TK_ILLEGAL; + return i; + } + case CC_VARNUM: { + *tokenType = TK_VARIABLE; + for(i=1; sqlite3Isdigit(z[i]); i++){} + return i; + } + case CC_DOLLAR: + case CC_VARALPHA: { + int n = 0; + testcase( z[0]=='$' ); testcase( z[0]=='@' ); + testcase( z[0]==':' ); testcase( z[0]=='#' ); + *tokenType = TK_VARIABLE; + for(i=1; (c=z[i])!=0; i++){ + if( IdChar(c) ){ + n++; + }else if( c=='(' && n>0 ){ + do{ + i++; + }while( (c=z[i])!=0 && !sqlite3Isspace(c) && c!=')' ); + if( c==')' ){ + i++; + }else{ + *tokenType = TK_ILLEGAL; + } + break; + }else if( c==':' && z[i+1]==':' ){ + i++; + }else{ + break; + } + } + if( n==0 ) *tokenType = TK_ILLEGAL; + return i; + } + case CC_KYWD: { + for(i=1; aiClass[z[i]]<=CC_KYWD; i++){} + if( IdChar(z[i]) ){ + /* This token started out using characters that can appear in keywords, + ** but z[i] is a character not allowed within keywords, so this must + ** be an identifier instead */ + i++; + break; + } + *tokenType = TK_ID; + return i; + } + case CC_X: { + testcase( z[0]=='x' ); testcase( z[0]=='X' ); + if( z[1]=='\'' ){ + *tokenType = TK_BLOB; + for(i=2; sqlite3Isxdigit(z[i]); i++){} + if( z[i]!='\'' || i%2 ){ + *tokenType = TK_ILLEGAL; + while( z[i] && z[i]!='\'' ){ i++; } + } + if( z[i] ) i++; + return i; + } + /* If it is not a BLOB literal, then it must be an ID, since no + ** SQL keywords start with the letter 'x'. Fall through */ + } + case CC_ID: { + i = 1; + break; + } + default: { + *tokenType = TK_ILLEGAL; + return 1; + } + } + while( IdChar(z[i]) ){ i++; } + *tokenType = TK_ID; + return i; +} + +char *sqlite3_normalize(const char *zSql){ + char *z; /* The output string */ + sqlite3_int64 nZ; /* Size of the output string in bytes */ + sqlite3_int64 nSql; /* Size of the input string in bytes */ + int i; /* Next character to read from zSql[] */ + int j; /* Next slot to fill in on z[] */ + int tokenType; /* Type of the next token */ + int n; /* Size of the next token */ + int k; /* Loop counter */ + + nSql = strlen(zSql); + nZ = nSql; + z = sqlite3_malloc64( nZ+2 ); + if( z==0 ) return 0; + for(i=j=0; zSql[i]; i += n){ + n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); + switch( tokenType ){ + case TK_SPACE: { + break; + } + case TK_ERROR: { + sqlite3_free(z); + return 0; + } + case TK_LITERAL: { + z[j++] = '?'; + break; + } + case TK_PUNCT: + case TK_NAME: { + if( n==4 && sqlite3_strnicmp(zSql+i,"NULL",4)==0 ){ + if( (j>=3 && strncmp(z+j-2,"is",2)==0 && !IdChar(z[j-3])) + || (j>=4 && strncmp(z+j-3,"not",3)==0 && !IdChar(z[j-4])) + ){ + /* NULL is a keyword in this case, not a literal value */ + }else{ + /* Here the NULL is a literal value */ + z[j++] = '?'; + break; + } + } + if( j>0 && IdChar(z[j-1]) && IdChar(zSql[i]) ) z[j++] = ' '; + for(k=0; k<n; k++){ + z[j++] = sqlite3Tolower(zSql[i+k]); + } + break; + } + } + } + while( j>0 && z[j-1]==' ' ){ j--; } + if( i>0 && z[j-1]!=';' ){ z[j++] = ';'; } + z[j] = 0; + + /* Make a second pass converting "in(...)" where the "..." is not a + ** SELECT statement into "in(?,?,?)" */ + for(i=0; i<j; i=n){ + char *zIn = strstr(z+i, "in("); + int nParen; + if( zIn==0 ) break; + n = (int)(zIn-z)+3; /* Index of first char past "in(" */ + if( n && IdChar(zIn[-1]) ) continue; + if( strncmp(zIn, "in(select",9)==0 && !IdChar(zIn[9]) ) continue; + if( strncmp(zIn, "in(with",7)==0 && !IdChar(zIn[7]) ) continue; + for(nParen=1, k=0; z[n+k]; k++){ + if( z[n+k]=='(' ) nParen++; + if( z[n+k]==')' ){ + nParen--; + if( nParen==0 ) break; + } + } + /* k is the number of bytes in the "..." within "in(...)" */ + if( k<5 ){ + z = sqlite3_realloc64(z, j+(5-k)+1); + if( z==0 ) return 0; + memmove(z+n+5, z+n+k, j-(n+k)); + }else if( k>5 ){ + memmove(z+n+5, z+n+k, j-(n+k)); + } + j = j-k+5; + z[j] = 0; + memcpy(z+n, "?,?,?", 5); + } + return z; +} + +/* +** For testing purposes, or to build a stand-alone SQL normalizer program, +** compile this one source file with the -DSQLITE_NORMALIZE_CLI and link +** it against any SQLite library. The resulting command-line program will +** run sqlite3_normalize() over the text of all files named on the command- +** line and show the result on standard output. +*/ +#ifdef SQLITE_NORMALIZE_CLI +#include <stdio.h> +#include <stdlib.h> + +/* +** Break zIn up into separate SQL statements and run sqlite3_normalize() +** on each one. Print the result of each run. +*/ +static void normalizeFile(char *zIn){ + int i; + if( zIn==0 ) return; + for(i=0; zIn[i]; i++){ + char cSaved; + if( zIn[i]!=';' ) continue; + cSaved = zIn[i+1]; + zIn[i+1] = 0; + if( sqlite3_complete(zIn) ){ + char *zOut = sqlite3_normalize(zIn); + if( zOut ){ + printf("%s\n", zOut); + sqlite3_free(zOut); + }else{ + fprintf(stderr, "ERROR: %s\n", zIn); + } + zIn[i+1] = cSaved; + zIn += i+1; + i = -1; + }else{ + zIn[i+1] = cSaved; + } + } +} + +/* +** The main routine for "sql_normalize". Read files named on the +** command-line and run the text of each through sqlite3_normalize(). +*/ +int main(int argc, char **argv){ + int i; + FILE *in; + char *zBuf = 0; + sqlite3_int64 sz, got; + + for(i=1; i<argc; i++){ + in = fopen(argv[i], "rb"); + if( in==0 ){ + fprintf(stderr, "cannot open \"%s\"\n", argv[i]); + continue; + } + fseek(in, 0, SEEK_END); + sz = ftell(in); + rewind(in); + zBuf = sqlite3_realloc64(zBuf, sz+1); + if( zBuf==0 ){ + fprintf(stderr, "failed to malloc for %lld bytes\n", sz); + exit(1); + } + got = fread(zBuf, 1, sz, in); + fclose(in); + if( got!=sz ){ + fprintf(stderr, "only able to read %lld of %lld bytes from \"%s\"\n", + got, sz, argv[i]); + }else{ + zBuf[got] = 0; + normalizeFile(zBuf); + } + } + sqlite3_free(zBuf); +} +#endif /* SQLITE_NORMALIZE_CLI */
diff --git a/third_party/sqlite/src/ext/misc/scrub.c b/third_party/sqlite/src/ext/misc/scrub.c index 88316a2..2de11f7 100644 --- a/third_party/sqlite/src/ext/misc/scrub.c +++ b/third_party/sqlite/src/ext/misc/scrub.c
@@ -131,7 +131,7 @@ scrubBackupErr(p, "write failed for page %d", pgno); p->rcErr = SQLITE_IOERR; } - if( pgno>p->iLastPage ) p->iLastPage = pgno; + if( (u32)pgno>p->iLastPage ) p->iLastPage = pgno; } /* Prepare a statement against the "db" database. */ @@ -459,7 +459,7 @@ nLocal = K<=X ? K : M; if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; } iChild = scrubBackupInt32(&a[pc+nLocal]); - scrubBackupOverflow(p, iChild, P-nLocal); + scrubBackupOverflow(p, iChild, (u32)(P-nLocal)); } /* Walk the right-most tree */
diff --git a/third_party/sqlite/src/ext/misc/series.c b/third_party/sqlite/src/ext/misc/series.c index d02a690..cf591bf 100644 --- a/third_party/sqlite/src/ext/misc/series.c +++ b/third_party/sqlite/src/ext/misc/series.c
@@ -270,6 +270,15 @@ }else{ pCur->iStep = 1; } + for(i=0; i<argc; i++){ + if( sqlite3_value_type(argv[i])==SQLITE_NULL ){ + /* If any of the constraints have a NULL value, then return no rows. + ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */ + pCur->mnValue = 1; + pCur->mxValue = 0; + break; + } + } if( idxNum & 8 ){ pCur->isDesc = 1; pCur->iValue = pCur->mxValue;
diff --git a/third_party/sqlite/src/ext/misc/spellfix.c b/third_party/sqlite/src/ext/misc/spellfix.c index 58995ac..981bd391 100644 --- a/third_party/sqlite/src/ext/misc/spellfix.c +++ b/third_party/sqlite/src/ext/misc/spellfix.c
@@ -18,6 +18,12 @@ SQLITE_EXTENSION_INIT1 #ifndef SQLITE_AMALGAMATION +# if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +# define NDEBUG 1 +# endif +# if defined(NDEBUG) && defined(SQLITE_DEBUG) +# undef NDEBUG +# endif # include <string.h> # include <stdio.h> # include <stdlib.h> @@ -652,6 +658,79 @@ sqlite3_free(p); } +/* Compare the FROM values of two EditDist3Cost objects, for sorting. +** Return negative, zero, or positive if the A is less than, equal to, +** or greater than B. +*/ +static int editDist3CostCompare(EditDist3Cost *pA, EditDist3Cost *pB){ + int n = pA->nFrom; + int rc; + if( n>pB->nFrom ) n = pB->nFrom; + rc = strncmp(pA->a, pB->a, n); + if( rc==0 ) rc = pA->nFrom - pB->nFrom; + return rc; +} + +/* +** Merge together two sorted lists of EditDist3Cost objects, in order +** of increasing FROM. +*/ +static EditDist3Cost *editDist3CostMerge( + EditDist3Cost *pA, + EditDist3Cost *pB +){ + EditDist3Cost *pHead = 0; + EditDist3Cost **ppTail = &pHead; + EditDist3Cost *p; + while( pA && pB ){ + if( editDist3CostCompare(pA,pB)<=0 ){ + p = pA; + pA = pA->pNext; + }else{ + p = pB; + pB = pB->pNext; + } + *ppTail = p; + ppTail = &p->pNext; + } + if( pA ){ + *ppTail = pA; + }else{ + *ppTail = pB; + } + return pHead; +} + +/* +** Sort a list of EditDist3Cost objects into order of increasing FROM +*/ +static EditDist3Cost *editDist3CostSort(EditDist3Cost *pList){ + EditDist3Cost *ap[60], *p; + int i; + int mx = 0; + ap[0] = 0; + ap[1] = 0; + while( pList ){ + p = pList; + pList = p->pNext; + p->pNext = 0; + for(i=0; ap[i]; i++){ + p = editDist3CostMerge(ap[i],p); + ap[i] = 0; + } + ap[i] = p; + if( i>mx ){ + mx = i; + ap[i+1] = 0; + } + } + p = 0; + for(i=0; i<=mx; i++){ + if( ap[i] ) p = editDist3CostMerge(p,ap[i]); + } + return p; +} + /* ** Load all edit-distance weights from a table. */ @@ -685,6 +764,7 @@ assert( zTo!=0 || nTo==0 ); if( nFrom>100 || nTo>100 ) continue; if( iCost<0 ) continue; + if( iCost>=10000 ) continue; /* Costs above 10K are considered infinite */ if( pLang==0 || iLang!=iLangPrev ){ EditDist3Lang *pNew; pNew = sqlite3_realloc64(p->a, (p->nLang+1)*sizeof(p->a[0])); @@ -722,6 +802,12 @@ } rc2 = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ) rc = rc2; + if( rc==SQLITE_OK ){ + int iLang; + for(iLang=0; iLang<p->nLang; iLang++){ + p->a[iLang].pCost = editDist3CostSort(p->a[iLang].pCost); + } + } return rc; } @@ -749,6 +835,8 @@ ** the given string. */ static int matchTo(EditDist3Cost *p, const char *z, int n){ + assert( n>0 ); + if( p->a[p->nFrom]!=z[0] ) return 0; if( p->nTo>n ) return 0; if( strncmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0; return 1; @@ -760,7 +848,10 @@ */ static int matchFrom(EditDist3Cost *p, const char *z, int n){ assert( p->nFrom<=n ); - if( strncmp(p->a, z, p->nFrom)!=0 ) return 0; + if( p->nFrom ){ + if( p->a[0]!=z[0] ) return 0; + if( strncmp(p->a, z, p->nFrom)!=0 ) return 0; + } return 1; } @@ -776,7 +867,9 @@ ){ int b1 = pStr->a[n1].nByte; if( b1>n2 ) return 0; - if( memcmp(pStr->z+n1, z2, b1)!=0 ) return 0; + assert( b1>0 ); + if( pStr->z[n1]!=z2[0] ) return 0; + if( strncmp(pStr->z+n1, z2, b1)!=0 ) return 0; return 1; } @@ -858,9 +951,6 @@ /* ** Update entry m[i] such that it is the minimum of its current value ** and m[j]+iCost. -** -** If the iCost is 1,000,000 or greater, then consider the cost to be -** infinite and skip the update. */ static void updateCost( unsigned int *m, @@ -868,11 +958,11 @@ int j, int iCost ){ + unsigned int b; assert( iCost>=0 ); - if( iCost<10000 ){ - unsigned int b = m[j] + iCost; - if( b<m[i] ) m[i] = b; - } + assert( iCost<10000 ); + b = m[j] + iCost; + if( b<m[i] ) m[i] = b; } /* @@ -936,8 +1026,9 @@ a2[i2].nByte = utf8Len((unsigned char)z2[i2], n2-i2); for(p=pLang->pCost; p; p=p->pNext){ EditDist3Cost **apNew; - if( p->nFrom>0 ) continue; + if( p->nFrom>0 ) break; if( i2+p->nTo>n2 ) continue; + if( p->a[0]>z2[i2] ) break; if( matchTo(p, z2+i2, n2-i2)==0 ) continue; a2[i2].nIns++; apNew = sqlite3_realloc64(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns); @@ -1200,404 +1291,412 @@ return nChar; } +typedef struct Transliteration Transliteration; +struct Transliteration { + unsigned short int cFrom; + unsigned char cTo0, cTo1, cTo2, cTo3; +}; + /* ** Table of translations from unicode characters into ASCII. */ -static const struct { - unsigned short int cFrom; - unsigned char cTo0, cTo1; -} translit[] = { - { 0x00A0, 0x20, 0x00 }, /* to */ - { 0x00B5, 0x75, 0x00 }, /* µ to u */ - { 0x00C0, 0x41, 0x00 }, /* À to A */ - { 0x00C1, 0x41, 0x00 }, /* Á to A */ - { 0x00C2, 0x41, 0x00 }, /*  to A */ - { 0x00C3, 0x41, 0x00 }, /* à to A */ - { 0x00C4, 0x41, 0x65 }, /* Ä to Ae */ - { 0x00C5, 0x41, 0x61 }, /* Å to Aa */ - { 0x00C6, 0x41, 0x45 }, /* Æ to AE */ - { 0x00C7, 0x43, 0x00 }, /* Ç to C */ - { 0x00C8, 0x45, 0x00 }, /* È to E */ - { 0x00C9, 0x45, 0x00 }, /* É to E */ - { 0x00CA, 0x45, 0x00 }, /* Ê to E */ - { 0x00CB, 0x45, 0x00 }, /* Ë to E */ - { 0x00CC, 0x49, 0x00 }, /* Ì to I */ - { 0x00CD, 0x49, 0x00 }, /* Í to I */ - { 0x00CE, 0x49, 0x00 }, /* Î to I */ - { 0x00CF, 0x49, 0x00 }, /* Ï to I */ - { 0x00D0, 0x44, 0x00 }, /* Ð to D */ - { 0x00D1, 0x4E, 0x00 }, /* Ñ to N */ - { 0x00D2, 0x4F, 0x00 }, /* Ò to O */ - { 0x00D3, 0x4F, 0x00 }, /* Ó to O */ - { 0x00D4, 0x4F, 0x00 }, /* Ô to O */ - { 0x00D5, 0x4F, 0x00 }, /* Õ to O */ - { 0x00D6, 0x4F, 0x65 }, /* Ö to Oe */ - { 0x00D7, 0x78, 0x00 }, /* × to x */ - { 0x00D8, 0x4F, 0x00 }, /* Ø to O */ - { 0x00D9, 0x55, 0x00 }, /* Ù to U */ - { 0x00DA, 0x55, 0x00 }, /* Ú to U */ - { 0x00DB, 0x55, 0x00 }, /* Û to U */ - { 0x00DC, 0x55, 0x65 }, /* Ü to Ue */ - { 0x00DD, 0x59, 0x00 }, /* Ý to Y */ - { 0x00DE, 0x54, 0x68 }, /* Þ to Th */ - { 0x00DF, 0x73, 0x73 }, /* ß to ss */ - { 0x00E0, 0x61, 0x00 }, /* à to a */ - { 0x00E1, 0x61, 0x00 }, /* á to a */ - { 0x00E2, 0x61, 0x00 }, /* â to a */ - { 0x00E3, 0x61, 0x00 }, /* ã to a */ - { 0x00E4, 0x61, 0x65 }, /* ä to ae */ - { 0x00E5, 0x61, 0x61 }, /* å to aa */ - { 0x00E6, 0x61, 0x65 }, /* æ to ae */ - { 0x00E7, 0x63, 0x00 }, /* ç to c */ - { 0x00E8, 0x65, 0x00 }, /* è to e */ - { 0x00E9, 0x65, 0x00 }, /* é to e */ - { 0x00EA, 0x65, 0x00 }, /* ê to e */ - { 0x00EB, 0x65, 0x00 }, /* ë to e */ - { 0x00EC, 0x69, 0x00 }, /* ì to i */ - { 0x00ED, 0x69, 0x00 }, /* í to i */ - { 0x00EE, 0x69, 0x00 }, /* î to i */ - { 0x00EF, 0x69, 0x00 }, /* ï to i */ - { 0x00F0, 0x64, 0x00 }, /* ð to d */ - { 0x00F1, 0x6E, 0x00 }, /* ñ to n */ - { 0x00F2, 0x6F, 0x00 }, /* ò to o */ - { 0x00F3, 0x6F, 0x00 }, /* ó to o */ - { 0x00F4, 0x6F, 0x00 }, /* ô to o */ - { 0x00F5, 0x6F, 0x00 }, /* õ to o */ - { 0x00F6, 0x6F, 0x65 }, /* ö to oe */ - { 0x00F7, 0x3A, 0x00 }, /* ÷ to : */ - { 0x00F8, 0x6F, 0x00 }, /* ø to o */ - { 0x00F9, 0x75, 0x00 }, /* ù to u */ - { 0x00FA, 0x75, 0x00 }, /* ú to u */ - { 0x00FB, 0x75, 0x00 }, /* û to u */ - { 0x00FC, 0x75, 0x65 }, /* ü to ue */ - { 0x00FD, 0x79, 0x00 }, /* ý to y */ - { 0x00FE, 0x74, 0x68 }, /* þ to th */ - { 0x00FF, 0x79, 0x00 }, /* ÿ to y */ - { 0x0100, 0x41, 0x00 }, /* Ā to A */ - { 0x0101, 0x61, 0x00 }, /* ā to a */ - { 0x0102, 0x41, 0x00 }, /* Ă to A */ - { 0x0103, 0x61, 0x00 }, /* ă to a */ - { 0x0104, 0x41, 0x00 }, /* Ą to A */ - { 0x0105, 0x61, 0x00 }, /* ą to a */ - { 0x0106, 0x43, 0x00 }, /* Ć to C */ - { 0x0107, 0x63, 0x00 }, /* ć to c */ - { 0x0108, 0x43, 0x68 }, /* Ĉ to Ch */ - { 0x0109, 0x63, 0x68 }, /* ĉ to ch */ - { 0x010A, 0x43, 0x00 }, /* Ċ to C */ - { 0x010B, 0x63, 0x00 }, /* ċ to c */ - { 0x010C, 0x43, 0x00 }, /* Č to C */ - { 0x010D, 0x63, 0x00 }, /* č to c */ - { 0x010E, 0x44, 0x00 }, /* Ď to D */ - { 0x010F, 0x64, 0x00 }, /* ď to d */ - { 0x0110, 0x44, 0x00 }, /* Đ to D */ - { 0x0111, 0x64, 0x00 }, /* đ to d */ - { 0x0112, 0x45, 0x00 }, /* Ē to E */ - { 0x0113, 0x65, 0x00 }, /* ē to e */ - { 0x0114, 0x45, 0x00 }, /* Ĕ to E */ - { 0x0115, 0x65, 0x00 }, /* ĕ to e */ - { 0x0116, 0x45, 0x00 }, /* Ė to E */ - { 0x0117, 0x65, 0x00 }, /* ė to e */ - { 0x0118, 0x45, 0x00 }, /* Ę to E */ - { 0x0119, 0x65, 0x00 }, /* ę to e */ - { 0x011A, 0x45, 0x00 }, /* Ě to E */ - { 0x011B, 0x65, 0x00 }, /* ě to e */ - { 0x011C, 0x47, 0x68 }, /* Ĝ to Gh */ - { 0x011D, 0x67, 0x68 }, /* ĝ to gh */ - { 0x011E, 0x47, 0x00 }, /* Ğ to G */ - { 0x011F, 0x67, 0x00 }, /* ğ to g */ - { 0x0120, 0x47, 0x00 }, /* Ġ to G */ - { 0x0121, 0x67, 0x00 }, /* ġ to g */ - { 0x0122, 0x47, 0x00 }, /* Ģ to G */ - { 0x0123, 0x67, 0x00 }, /* ģ to g */ - { 0x0124, 0x48, 0x68 }, /* Ĥ to Hh */ - { 0x0125, 0x68, 0x68 }, /* ĥ to hh */ - { 0x0126, 0x48, 0x00 }, /* Ħ to H */ - { 0x0127, 0x68, 0x00 }, /* ħ to h */ - { 0x0128, 0x49, 0x00 }, /* Ĩ to I */ - { 0x0129, 0x69, 0x00 }, /* ĩ to i */ - { 0x012A, 0x49, 0x00 }, /* Ī to I */ - { 0x012B, 0x69, 0x00 }, /* ī to i */ - { 0x012C, 0x49, 0x00 }, /* Ĭ to I */ - { 0x012D, 0x69, 0x00 }, /* ĭ to i */ - { 0x012E, 0x49, 0x00 }, /* Į to I */ - { 0x012F, 0x69, 0x00 }, /* į to i */ - { 0x0130, 0x49, 0x00 }, /* İ to I */ - { 0x0131, 0x69, 0x00 }, /* ı to i */ - { 0x0132, 0x49, 0x4A }, /* IJ to IJ */ - { 0x0133, 0x69, 0x6A }, /* ij to ij */ - { 0x0134, 0x4A, 0x68 }, /* Ĵ to Jh */ - { 0x0135, 0x6A, 0x68 }, /* ĵ to jh */ - { 0x0136, 0x4B, 0x00 }, /* Ķ to K */ - { 0x0137, 0x6B, 0x00 }, /* ķ to k */ - { 0x0138, 0x6B, 0x00 }, /* ĸ to k */ - { 0x0139, 0x4C, 0x00 }, /* Ĺ to L */ - { 0x013A, 0x6C, 0x00 }, /* ĺ to l */ - { 0x013B, 0x4C, 0x00 }, /* Ļ to L */ - { 0x013C, 0x6C, 0x00 }, /* ļ to l */ - { 0x013D, 0x4C, 0x00 }, /* Ľ to L */ - { 0x013E, 0x6C, 0x00 }, /* ľ to l */ - { 0x013F, 0x4C, 0x2E }, /* Ŀ to L. */ - { 0x0140, 0x6C, 0x2E }, /* ŀ to l. */ - { 0x0141, 0x4C, 0x00 }, /* Ł to L */ - { 0x0142, 0x6C, 0x00 }, /* ł to l */ - { 0x0143, 0x4E, 0x00 }, /* Ń to N */ - { 0x0144, 0x6E, 0x00 }, /* ń to n */ - { 0x0145, 0x4E, 0x00 }, /* Ņ to N */ - { 0x0146, 0x6E, 0x00 }, /* ņ to n */ - { 0x0147, 0x4E, 0x00 }, /* Ň to N */ - { 0x0148, 0x6E, 0x00 }, /* ň to n */ - { 0x0149, 0x27, 0x6E }, /* ʼn to 'n */ - { 0x014A, 0x4E, 0x47 }, /* Ŋ to NG */ - { 0x014B, 0x6E, 0x67 }, /* ŋ to ng */ - { 0x014C, 0x4F, 0x00 }, /* Ō to O */ - { 0x014D, 0x6F, 0x00 }, /* ō to o */ - { 0x014E, 0x4F, 0x00 }, /* Ŏ to O */ - { 0x014F, 0x6F, 0x00 }, /* ŏ to o */ - { 0x0150, 0x4F, 0x00 }, /* Ő to O */ - { 0x0151, 0x6F, 0x00 }, /* ő to o */ - { 0x0152, 0x4F, 0x45 }, /* Œ to OE */ - { 0x0153, 0x6F, 0x65 }, /* œ to oe */ - { 0x0154, 0x52, 0x00 }, /* Ŕ to R */ - { 0x0155, 0x72, 0x00 }, /* ŕ to r */ - { 0x0156, 0x52, 0x00 }, /* Ŗ to R */ - { 0x0157, 0x72, 0x00 }, /* ŗ to r */ - { 0x0158, 0x52, 0x00 }, /* Ř to R */ - { 0x0159, 0x72, 0x00 }, /* ř to r */ - { 0x015A, 0x53, 0x00 }, /* Ś to S */ - { 0x015B, 0x73, 0x00 }, /* ś to s */ - { 0x015C, 0x53, 0x68 }, /* Ŝ to Sh */ - { 0x015D, 0x73, 0x68 }, /* ŝ to sh */ - { 0x015E, 0x53, 0x00 }, /* Ş to S */ - { 0x015F, 0x73, 0x00 }, /* ş to s */ - { 0x0160, 0x53, 0x00 }, /* Š to S */ - { 0x0161, 0x73, 0x00 }, /* š to s */ - { 0x0162, 0x54, 0x00 }, /* Ţ to T */ - { 0x0163, 0x74, 0x00 }, /* ţ to t */ - { 0x0164, 0x54, 0x00 }, /* Ť to T */ - { 0x0165, 0x74, 0x00 }, /* ť to t */ - { 0x0166, 0x54, 0x00 }, /* Ŧ to T */ - { 0x0167, 0x74, 0x00 }, /* ŧ to t */ - { 0x0168, 0x55, 0x00 }, /* Ũ to U */ - { 0x0169, 0x75, 0x00 }, /* ũ to u */ - { 0x016A, 0x55, 0x00 }, /* Ū to U */ - { 0x016B, 0x75, 0x00 }, /* ū to u */ - { 0x016C, 0x55, 0x00 }, /* Ŭ to U */ - { 0x016D, 0x75, 0x00 }, /* ŭ to u */ - { 0x016E, 0x55, 0x00 }, /* Ů to U */ - { 0x016F, 0x75, 0x00 }, /* ů to u */ - { 0x0170, 0x55, 0x00 }, /* Ű to U */ - { 0x0171, 0x75, 0x00 }, /* ű to u */ - { 0x0172, 0x55, 0x00 }, /* Ų to U */ - { 0x0173, 0x75, 0x00 }, /* ų to u */ - { 0x0174, 0x57, 0x00 }, /* Ŵ to W */ - { 0x0175, 0x77, 0x00 }, /* ŵ to w */ - { 0x0176, 0x59, 0x00 }, /* Ŷ to Y */ - { 0x0177, 0x79, 0x00 }, /* ŷ to y */ - { 0x0178, 0x59, 0x00 }, /* Ÿ to Y */ - { 0x0179, 0x5A, 0x00 }, /* Ź to Z */ - { 0x017A, 0x7A, 0x00 }, /* ź to z */ - { 0x017B, 0x5A, 0x00 }, /* Ż to Z */ - { 0x017C, 0x7A, 0x00 }, /* ż to z */ - { 0x017D, 0x5A, 0x00 }, /* Ž to Z */ - { 0x017E, 0x7A, 0x00 }, /* ž to z */ - { 0x017F, 0x73, 0x00 }, /* ſ to s */ - { 0x0192, 0x66, 0x00 }, /* ƒ to f */ - { 0x0218, 0x53, 0x00 }, /* Ș to S */ - { 0x0219, 0x73, 0x00 }, /* ș to s */ - { 0x021A, 0x54, 0x00 }, /* Ț to T */ - { 0x021B, 0x74, 0x00 }, /* ț to t */ - { 0x0386, 0x41, 0x00 }, /* Ά to A */ - { 0x0388, 0x45, 0x00 }, /* Έ to E */ - { 0x0389, 0x49, 0x00 }, /* Ή to I */ - { 0x038A, 0x49, 0x00 }, /* Ί to I */ - { 0x038C, 0x4f, 0x00 }, /* Ό to O */ - { 0x038E, 0x59, 0x00 }, /* Ύ to Y */ - { 0x038F, 0x4f, 0x00 }, /* Ώ to O */ - { 0x0390, 0x69, 0x00 }, /* ΐ to i */ - { 0x0391, 0x41, 0x00 }, /* Α to A */ - { 0x0392, 0x42, 0x00 }, /* Β to B */ - { 0x0393, 0x47, 0x00 }, /* Γ to G */ - { 0x0394, 0x44, 0x00 }, /* Δ to D */ - { 0x0395, 0x45, 0x00 }, /* Ε to E */ - { 0x0396, 0x5a, 0x00 }, /* Ζ to Z */ - { 0x0397, 0x49, 0x00 }, /* Η to I */ - { 0x0398, 0x54, 0x68 }, /* Θ to Th */ - { 0x0399, 0x49, 0x00 }, /* Ι to I */ - { 0x039A, 0x4b, 0x00 }, /* Κ to K */ - { 0x039B, 0x4c, 0x00 }, /* Λ to L */ - { 0x039C, 0x4d, 0x00 }, /* Μ to M */ - { 0x039D, 0x4e, 0x00 }, /* Ν to N */ - { 0x039E, 0x58, 0x00 }, /* Ξ to X */ - { 0x039F, 0x4f, 0x00 }, /* Ο to O */ - { 0x03A0, 0x50, 0x00 }, /* Π to P */ - { 0x03A1, 0x52, 0x00 }, /* Ρ to R */ - { 0x03A3, 0x53, 0x00 }, /* Σ to S */ - { 0x03A4, 0x54, 0x00 }, /* Τ to T */ - { 0x03A5, 0x59, 0x00 }, /* Υ to Y */ - { 0x03A6, 0x46, 0x00 }, /* Φ to F */ - { 0x03A7, 0x43, 0x68 }, /* Χ to Ch */ - { 0x03A8, 0x50, 0x73 }, /* Ψ to Ps */ - { 0x03A9, 0x4f, 0x00 }, /* Ω to O */ - { 0x03AA, 0x49, 0x00 }, /* Ϊ to I */ - { 0x03AB, 0x59, 0x00 }, /* Ϋ to Y */ - { 0x03AC, 0x61, 0x00 }, /* ά to a */ - { 0x03AD, 0x65, 0x00 }, /* έ to e */ - { 0x03AE, 0x69, 0x00 }, /* ή to i */ - { 0x03AF, 0x69, 0x00 }, /* ί to i */ - { 0x03B1, 0x61, 0x00 }, /* α to a */ - { 0x03B2, 0x62, 0x00 }, /* β to b */ - { 0x03B3, 0x67, 0x00 }, /* γ to g */ - { 0x03B4, 0x64, 0x00 }, /* δ to d */ - { 0x03B5, 0x65, 0x00 }, /* ε to e */ - { 0x03B6, 0x7a, 0x00 }, /* ζ to z */ - { 0x03B7, 0x69, 0x00 }, /* η to i */ - { 0x03B8, 0x74, 0x68 }, /* θ to th */ - { 0x03B9, 0x69, 0x00 }, /* ι to i */ - { 0x03BA, 0x6b, 0x00 }, /* κ to k */ - { 0x03BB, 0x6c, 0x00 }, /* λ to l */ - { 0x03BC, 0x6d, 0x00 }, /* μ to m */ - { 0x03BD, 0x6e, 0x00 }, /* ν to n */ - { 0x03BE, 0x78, 0x00 }, /* ξ to x */ - { 0x03BF, 0x6f, 0x00 }, /* ο to o */ - { 0x03C0, 0x70, 0x00 }, /* π to p */ - { 0x03C1, 0x72, 0x00 }, /* ρ to r */ - { 0x03C3, 0x73, 0x00 }, /* σ to s */ - { 0x03C4, 0x74, 0x00 }, /* τ to t */ - { 0x03C5, 0x79, 0x00 }, /* υ to y */ - { 0x03C6, 0x66, 0x00 }, /* φ to f */ - { 0x03C7, 0x63, 0x68 }, /* χ to ch */ - { 0x03C8, 0x70, 0x73 }, /* ψ to ps */ - { 0x03C9, 0x6f, 0x00 }, /* ω to o */ - { 0x03CA, 0x69, 0x00 }, /* ϊ to i */ - { 0x03CB, 0x79, 0x00 }, /* ϋ to y */ - { 0x03CC, 0x6f, 0x00 }, /* ό to o */ - { 0x03CD, 0x79, 0x00 }, /* ύ to y */ - { 0x03CE, 0x69, 0x00 }, /* ώ to i */ - { 0x0400, 0x45, 0x00 }, /* Ѐ to E */ - { 0x0401, 0x45, 0x00 }, /* Ё to E */ - { 0x0402, 0x44, 0x00 }, /* Ђ to D */ - { 0x0403, 0x47, 0x00 }, /* Ѓ to G */ - { 0x0404, 0x45, 0x00 }, /* Є to E */ - { 0x0405, 0x5a, 0x00 }, /* Ѕ to Z */ - { 0x0406, 0x49, 0x00 }, /* І to I */ - { 0x0407, 0x49, 0x00 }, /* Ї to I */ - { 0x0408, 0x4a, 0x00 }, /* Ј to J */ - { 0x0409, 0x49, 0x00 }, /* Љ to I */ - { 0x040A, 0x4e, 0x00 }, /* Њ to N */ - { 0x040B, 0x44, 0x00 }, /* Ћ to D */ - { 0x040C, 0x4b, 0x00 }, /* Ќ to K */ - { 0x040D, 0x49, 0x00 }, /* Ѝ to I */ - { 0x040E, 0x55, 0x00 }, /* Ў to U */ - { 0x040F, 0x44, 0x00 }, /* Џ to D */ - { 0x0410, 0x41, 0x00 }, /* А to A */ - { 0x0411, 0x42, 0x00 }, /* Б to B */ - { 0x0412, 0x56, 0x00 }, /* В to V */ - { 0x0413, 0x47, 0x00 }, /* Г to G */ - { 0x0414, 0x44, 0x00 }, /* Д to D */ - { 0x0415, 0x45, 0x00 }, /* Е to E */ - { 0x0416, 0x5a, 0x68 }, /* Ж to Zh */ - { 0x0417, 0x5a, 0x00 }, /* З to Z */ - { 0x0418, 0x49, 0x00 }, /* И to I */ - { 0x0419, 0x49, 0x00 }, /* Й to I */ - { 0x041A, 0x4b, 0x00 }, /* К to K */ - { 0x041B, 0x4c, 0x00 }, /* Л to L */ - { 0x041C, 0x4d, 0x00 }, /* М to M */ - { 0x041D, 0x4e, 0x00 }, /* Н to N */ - { 0x041E, 0x4f, 0x00 }, /* О to O */ - { 0x041F, 0x50, 0x00 }, /* П to P */ - { 0x0420, 0x52, 0x00 }, /* Р to R */ - { 0x0421, 0x53, 0x00 }, /* С to S */ - { 0x0422, 0x54, 0x00 }, /* Т to T */ - { 0x0423, 0x55, 0x00 }, /* У to U */ - { 0x0424, 0x46, 0x00 }, /* Ф to F */ - { 0x0425, 0x4b, 0x68 }, /* Х to Kh */ - { 0x0426, 0x54, 0x63 }, /* Ц to Tc */ - { 0x0427, 0x43, 0x68 }, /* Ч to Ch */ - { 0x0428, 0x53, 0x68 }, /* Ш to Sh */ - { 0x0429, 0x53, 0x68 }, /* Щ to Shch */ - { 0x042A, 0x61, 0x00 }, /* to A */ - { 0x042B, 0x59, 0x00 }, /* Ы to Y */ - { 0x042C, 0x59, 0x00 }, /* to Y */ - { 0x042D, 0x45, 0x00 }, /* Э to E */ - { 0x042E, 0x49, 0x75 }, /* Ю to Iu */ - { 0x042F, 0x49, 0x61 }, /* Я to Ia */ - { 0x0430, 0x61, 0x00 }, /* а to a */ - { 0x0431, 0x62, 0x00 }, /* б to b */ - { 0x0432, 0x76, 0x00 }, /* в to v */ - { 0x0433, 0x67, 0x00 }, /* г to g */ - { 0x0434, 0x64, 0x00 }, /* д to d */ - { 0x0435, 0x65, 0x00 }, /* е to e */ - { 0x0436, 0x7a, 0x68 }, /* ж to zh */ - { 0x0437, 0x7a, 0x00 }, /* з to z */ - { 0x0438, 0x69, 0x00 }, /* и to i */ - { 0x0439, 0x69, 0x00 }, /* й to i */ - { 0x043A, 0x6b, 0x00 }, /* к to k */ - { 0x043B, 0x6c, 0x00 }, /* л to l */ - { 0x043C, 0x6d, 0x00 }, /* м to m */ - { 0x043D, 0x6e, 0x00 }, /* н to n */ - { 0x043E, 0x6f, 0x00 }, /* о to o */ - { 0x043F, 0x70, 0x00 }, /* п to p */ - { 0x0440, 0x72, 0x00 }, /* р to r */ - { 0x0441, 0x73, 0x00 }, /* с to s */ - { 0x0442, 0x74, 0x00 }, /* т to t */ - { 0x0443, 0x75, 0x00 }, /* у to u */ - { 0x0444, 0x66, 0x00 }, /* ф to f */ - { 0x0445, 0x6b, 0x68 }, /* х to kh */ - { 0x0446, 0x74, 0x63 }, /* ц to tc */ - { 0x0447, 0x63, 0x68 }, /* ч to ch */ - { 0x0448, 0x73, 0x68 }, /* ш to sh */ - { 0x0449, 0x73, 0x68 }, /* щ to shch */ - { 0x044A, 0x61, 0x00 }, /* to a */ - { 0x044B, 0x79, 0x00 }, /* ы to y */ - { 0x044C, 0x79, 0x00 }, /* to y */ - { 0x044D, 0x65, 0x00 }, /* э to e */ - { 0x044E, 0x69, 0x75 }, /* ю to iu */ - { 0x044F, 0x69, 0x61 }, /* я to ia */ - { 0x0450, 0x65, 0x00 }, /* ѐ to e */ - { 0x0451, 0x65, 0x00 }, /* ё to e */ - { 0x0452, 0x64, 0x00 }, /* ђ to d */ - { 0x0453, 0x67, 0x00 }, /* ѓ to g */ - { 0x0454, 0x65, 0x00 }, /* є to e */ - { 0x0455, 0x7a, 0x00 }, /* ѕ to z */ - { 0x0456, 0x69, 0x00 }, /* і to i */ - { 0x0457, 0x69, 0x00 }, /* ї to i */ - { 0x0458, 0x6a, 0x00 }, /* ј to j */ - { 0x0459, 0x69, 0x00 }, /* љ to i */ - { 0x045A, 0x6e, 0x00 }, /* њ to n */ - { 0x045B, 0x64, 0x00 }, /* ћ to d */ - { 0x045C, 0x6b, 0x00 }, /* ќ to k */ - { 0x045D, 0x69, 0x00 }, /* ѝ to i */ - { 0x045E, 0x75, 0x00 }, /* ў to u */ - { 0x045F, 0x64, 0x00 }, /* џ to d */ - { 0x1E02, 0x42, 0x00 }, /* Ḃ to B */ - { 0x1E03, 0x62, 0x00 }, /* ḃ to b */ - { 0x1E0A, 0x44, 0x00 }, /* Ḋ to D */ - { 0x1E0B, 0x64, 0x00 }, /* ḋ to d */ - { 0x1E1E, 0x46, 0x00 }, /* Ḟ to F */ - { 0x1E1F, 0x66, 0x00 }, /* ḟ to f */ - { 0x1E40, 0x4D, 0x00 }, /* Ṁ to M */ - { 0x1E41, 0x6D, 0x00 }, /* ṁ to m */ - { 0x1E56, 0x50, 0x00 }, /* Ṗ to P */ - { 0x1E57, 0x70, 0x00 }, /* ṗ to p */ - { 0x1E60, 0x53, 0x00 }, /* Ṡ to S */ - { 0x1E61, 0x73, 0x00 }, /* ṡ to s */ - { 0x1E6A, 0x54, 0x00 }, /* Ṫ to T */ - { 0x1E6B, 0x74, 0x00 }, /* ṫ to t */ - { 0x1E80, 0x57, 0x00 }, /* Ẁ to W */ - { 0x1E81, 0x77, 0x00 }, /* ẁ to w */ - { 0x1E82, 0x57, 0x00 }, /* Ẃ to W */ - { 0x1E83, 0x77, 0x00 }, /* ẃ to w */ - { 0x1E84, 0x57, 0x00 }, /* Ẅ to W */ - { 0x1E85, 0x77, 0x00 }, /* ẅ to w */ - { 0x1EF2, 0x59, 0x00 }, /* Ỳ to Y */ - { 0x1EF3, 0x79, 0x00 }, /* ỳ to y */ - { 0xFB00, 0x66, 0x66 }, /* ff to ff */ - { 0xFB01, 0x66, 0x69 }, /* fi to fi */ - { 0xFB02, 0x66, 0x6C }, /* fl to fl */ - { 0xFB05, 0x73, 0x74 }, /* ſt to st */ - { 0xFB06, 0x73, 0x74 }, /* st to st */ +static const Transliteration translit[] = { + { 0x00A0, 0x20, 0x00, 0x00, 0x00 }, /* to */ + { 0x00B5, 0x75, 0x00, 0x00, 0x00 }, /* µ to u */ + { 0x00C0, 0x41, 0x00, 0x00, 0x00 }, /* À to A */ + { 0x00C1, 0x41, 0x00, 0x00, 0x00 }, /* Á to A */ + { 0x00C2, 0x41, 0x00, 0x00, 0x00 }, /*  to A */ + { 0x00C3, 0x41, 0x00, 0x00, 0x00 }, /* à to A */ + { 0x00C4, 0x41, 0x65, 0x00, 0x00 }, /* Ä to Ae */ + { 0x00C5, 0x41, 0x61, 0x00, 0x00 }, /* Å to Aa */ + { 0x00C6, 0x41, 0x45, 0x00, 0x00 }, /* Æ to AE */ + { 0x00C7, 0x43, 0x00, 0x00, 0x00 }, /* Ç to C */ + { 0x00C8, 0x45, 0x00, 0x00, 0x00 }, /* È to E */ + { 0x00C9, 0x45, 0x00, 0x00, 0x00 }, /* É to E */ + { 0x00CA, 0x45, 0x00, 0x00, 0x00 }, /* Ê to E */ + { 0x00CB, 0x45, 0x00, 0x00, 0x00 }, /* Ë to E */ + { 0x00CC, 0x49, 0x00, 0x00, 0x00 }, /* Ì to I */ + { 0x00CD, 0x49, 0x00, 0x00, 0x00 }, /* Í to I */ + { 0x00CE, 0x49, 0x00, 0x00, 0x00 }, /* Î to I */ + { 0x00CF, 0x49, 0x00, 0x00, 0x00 }, /* Ï to I */ + { 0x00D0, 0x44, 0x00, 0x00, 0x00 }, /* Ð to D */ + { 0x00D1, 0x4E, 0x00, 0x00, 0x00 }, /* Ñ to N */ + { 0x00D2, 0x4F, 0x00, 0x00, 0x00 }, /* Ò to O */ + { 0x00D3, 0x4F, 0x00, 0x00, 0x00 }, /* Ó to O */ + { 0x00D4, 0x4F, 0x00, 0x00, 0x00 }, /* Ô to O */ + { 0x00D5, 0x4F, 0x00, 0x00, 0x00 }, /* Õ to O */ + { 0x00D6, 0x4F, 0x65, 0x00, 0x00 }, /* Ö to Oe */ + { 0x00D7, 0x78, 0x00, 0x00, 0x00 }, /* × to x */ + { 0x00D8, 0x4F, 0x00, 0x00, 0x00 }, /* Ø to O */ + { 0x00D9, 0x55, 0x00, 0x00, 0x00 }, /* Ù to U */ + { 0x00DA, 0x55, 0x00, 0x00, 0x00 }, /* Ú to U */ + { 0x00DB, 0x55, 0x00, 0x00, 0x00 }, /* Û to U */ + { 0x00DC, 0x55, 0x65, 0x00, 0x00 }, /* Ü to Ue */ + { 0x00DD, 0x59, 0x00, 0x00, 0x00 }, /* Ý to Y */ + { 0x00DE, 0x54, 0x68, 0x00, 0x00 }, /* Þ to Th */ + { 0x00DF, 0x73, 0x73, 0x00, 0x00 }, /* ß to ss */ + { 0x00E0, 0x61, 0x00, 0x00, 0x00 }, /* à to a */ + { 0x00E1, 0x61, 0x00, 0x00, 0x00 }, /* á to a */ + { 0x00E2, 0x61, 0x00, 0x00, 0x00 }, /* â to a */ + { 0x00E3, 0x61, 0x00, 0x00, 0x00 }, /* ã to a */ + { 0x00E4, 0x61, 0x65, 0x00, 0x00 }, /* ä to ae */ + { 0x00E5, 0x61, 0x61, 0x00, 0x00 }, /* å to aa */ + { 0x00E6, 0x61, 0x65, 0x00, 0x00 }, /* æ to ae */ + { 0x00E7, 0x63, 0x00, 0x00, 0x00 }, /* ç to c */ + { 0x00E8, 0x65, 0x00, 0x00, 0x00 }, /* è to e */ + { 0x00E9, 0x65, 0x00, 0x00, 0x00 }, /* é to e */ + { 0x00EA, 0x65, 0x00, 0x00, 0x00 }, /* ê to e */ + { 0x00EB, 0x65, 0x00, 0x00, 0x00 }, /* ë to e */ + { 0x00EC, 0x69, 0x00, 0x00, 0x00 }, /* ì to i */ + { 0x00ED, 0x69, 0x00, 0x00, 0x00 }, /* í to i */ + { 0x00EE, 0x69, 0x00, 0x00, 0x00 }, /* î to i */ + { 0x00EF, 0x69, 0x00, 0x00, 0x00 }, /* ï to i */ + { 0x00F0, 0x64, 0x00, 0x00, 0x00 }, /* ð to d */ + { 0x00F1, 0x6E, 0x00, 0x00, 0x00 }, /* ñ to n */ + { 0x00F2, 0x6F, 0x00, 0x00, 0x00 }, /* ò to o */ + { 0x00F3, 0x6F, 0x00, 0x00, 0x00 }, /* ó to o */ + { 0x00F4, 0x6F, 0x00, 0x00, 0x00 }, /* ô to o */ + { 0x00F5, 0x6F, 0x00, 0x00, 0x00 }, /* õ to o */ + { 0x00F6, 0x6F, 0x65, 0x00, 0x00 }, /* ö to oe */ + { 0x00F7, 0x3A, 0x00, 0x00, 0x00 }, /* ÷ to : */ + { 0x00F8, 0x6F, 0x00, 0x00, 0x00 }, /* ø to o */ + { 0x00F9, 0x75, 0x00, 0x00, 0x00 }, /* ù to u */ + { 0x00FA, 0x75, 0x00, 0x00, 0x00 }, /* ú to u */ + { 0x00FB, 0x75, 0x00, 0x00, 0x00 }, /* û to u */ + { 0x00FC, 0x75, 0x65, 0x00, 0x00 }, /* ü to ue */ + { 0x00FD, 0x79, 0x00, 0x00, 0x00 }, /* ý to y */ + { 0x00FE, 0x74, 0x68, 0x00, 0x00 }, /* þ to th */ + { 0x00FF, 0x79, 0x00, 0x00, 0x00 }, /* ÿ to y */ + { 0x0100, 0x41, 0x00, 0x00, 0x00 }, /* Ā to A */ + { 0x0101, 0x61, 0x00, 0x00, 0x00 }, /* ā to a */ + { 0x0102, 0x41, 0x00, 0x00, 0x00 }, /* Ă to A */ + { 0x0103, 0x61, 0x00, 0x00, 0x00 }, /* ă to a */ + { 0x0104, 0x41, 0x00, 0x00, 0x00 }, /* Ą to A */ + { 0x0105, 0x61, 0x00, 0x00, 0x00 }, /* ą to a */ + { 0x0106, 0x43, 0x00, 0x00, 0x00 }, /* Ć to C */ + { 0x0107, 0x63, 0x00, 0x00, 0x00 }, /* ć to c */ + { 0x0108, 0x43, 0x68, 0x00, 0x00 }, /* Ĉ to Ch */ + { 0x0109, 0x63, 0x68, 0x00, 0x00 }, /* ĉ to ch */ + { 0x010A, 0x43, 0x00, 0x00, 0x00 }, /* Ċ to C */ + { 0x010B, 0x63, 0x00, 0x00, 0x00 }, /* ċ to c */ + { 0x010C, 0x43, 0x00, 0x00, 0x00 }, /* Č to C */ + { 0x010D, 0x63, 0x00, 0x00, 0x00 }, /* č to c */ + { 0x010E, 0x44, 0x00, 0x00, 0x00 }, /* Ď to D */ + { 0x010F, 0x64, 0x00, 0x00, 0x00 }, /* ď to d */ + { 0x0110, 0x44, 0x00, 0x00, 0x00 }, /* Đ to D */ + { 0x0111, 0x64, 0x00, 0x00, 0x00 }, /* đ to d */ + { 0x0112, 0x45, 0x00, 0x00, 0x00 }, /* Ē to E */ + { 0x0113, 0x65, 0x00, 0x00, 0x00 }, /* ē to e */ + { 0x0114, 0x45, 0x00, 0x00, 0x00 }, /* Ĕ to E */ + { 0x0115, 0x65, 0x00, 0x00, 0x00 }, /* ĕ to e */ + { 0x0116, 0x45, 0x00, 0x00, 0x00 }, /* Ė to E */ + { 0x0117, 0x65, 0x00, 0x00, 0x00 }, /* ė to e */ + { 0x0118, 0x45, 0x00, 0x00, 0x00 }, /* Ę to E */ + { 0x0119, 0x65, 0x00, 0x00, 0x00 }, /* ę to e */ + { 0x011A, 0x45, 0x00, 0x00, 0x00 }, /* Ě to E */ + { 0x011B, 0x65, 0x00, 0x00, 0x00 }, /* ě to e */ + { 0x011C, 0x47, 0x68, 0x00, 0x00 }, /* Ĝ to Gh */ + { 0x011D, 0x67, 0x68, 0x00, 0x00 }, /* ĝ to gh */ + { 0x011E, 0x47, 0x00, 0x00, 0x00 }, /* Ğ to G */ + { 0x011F, 0x67, 0x00, 0x00, 0x00 }, /* ğ to g */ + { 0x0120, 0x47, 0x00, 0x00, 0x00 }, /* Ġ to G */ + { 0x0121, 0x67, 0x00, 0x00, 0x00 }, /* ġ to g */ + { 0x0122, 0x47, 0x00, 0x00, 0x00 }, /* Ģ to G */ + { 0x0123, 0x67, 0x00, 0x00, 0x00 }, /* ģ to g */ + { 0x0124, 0x48, 0x68, 0x00, 0x00 }, /* Ĥ to Hh */ + { 0x0125, 0x68, 0x68, 0x00, 0x00 }, /* ĥ to hh */ + { 0x0126, 0x48, 0x00, 0x00, 0x00 }, /* Ħ to H */ + { 0x0127, 0x68, 0x00, 0x00, 0x00 }, /* ħ to h */ + { 0x0128, 0x49, 0x00, 0x00, 0x00 }, /* Ĩ to I */ + { 0x0129, 0x69, 0x00, 0x00, 0x00 }, /* ĩ to i */ + { 0x012A, 0x49, 0x00, 0x00, 0x00 }, /* Ī to I */ + { 0x012B, 0x69, 0x00, 0x00, 0x00 }, /* ī to i */ + { 0x012C, 0x49, 0x00, 0x00, 0x00 }, /* Ĭ to I */ + { 0x012D, 0x69, 0x00, 0x00, 0x00 }, /* ĭ to i */ + { 0x012E, 0x49, 0x00, 0x00, 0x00 }, /* Į to I */ + { 0x012F, 0x69, 0x00, 0x00, 0x00 }, /* į to i */ + { 0x0130, 0x49, 0x00, 0x00, 0x00 }, /* İ to I */ + { 0x0131, 0x69, 0x00, 0x00, 0x00 }, /* ı to i */ + { 0x0132, 0x49, 0x4A, 0x00, 0x00 }, /* IJ to IJ */ + { 0x0133, 0x69, 0x6A, 0x00, 0x00 }, /* ij to ij */ + { 0x0134, 0x4A, 0x68, 0x00, 0x00 }, /* Ĵ to Jh */ + { 0x0135, 0x6A, 0x68, 0x00, 0x00 }, /* ĵ to jh */ + { 0x0136, 0x4B, 0x00, 0x00, 0x00 }, /* Ķ to K */ + { 0x0137, 0x6B, 0x00, 0x00, 0x00 }, /* ķ to k */ + { 0x0138, 0x6B, 0x00, 0x00, 0x00 }, /* ĸ to k */ + { 0x0139, 0x4C, 0x00, 0x00, 0x00 }, /* Ĺ to L */ + { 0x013A, 0x6C, 0x00, 0x00, 0x00 }, /* ĺ to l */ + { 0x013B, 0x4C, 0x00, 0x00, 0x00 }, /* Ļ to L */ + { 0x013C, 0x6C, 0x00, 0x00, 0x00 }, /* ļ to l */ + { 0x013D, 0x4C, 0x00, 0x00, 0x00 }, /* Ľ to L */ + { 0x013E, 0x6C, 0x00, 0x00, 0x00 }, /* ľ to l */ + { 0x013F, 0x4C, 0x2E, 0x00, 0x00 }, /* Ŀ to L. */ + { 0x0140, 0x6C, 0x2E, 0x00, 0x00 }, /* ŀ to l. */ + { 0x0141, 0x4C, 0x00, 0x00, 0x00 }, /* Ł to L */ + { 0x0142, 0x6C, 0x00, 0x00, 0x00 }, /* ł to l */ + { 0x0143, 0x4E, 0x00, 0x00, 0x00 }, /* Ń to N */ + { 0x0144, 0x6E, 0x00, 0x00, 0x00 }, /* ń to n */ + { 0x0145, 0x4E, 0x00, 0x00, 0x00 }, /* Ņ to N */ + { 0x0146, 0x6E, 0x00, 0x00, 0x00 }, /* ņ to n */ + { 0x0147, 0x4E, 0x00, 0x00, 0x00 }, /* Ň to N */ + { 0x0148, 0x6E, 0x00, 0x00, 0x00 }, /* ň to n */ + { 0x0149, 0x27, 0x6E, 0x00, 0x00 }, /* ʼn to 'n */ + { 0x014A, 0x4E, 0x47, 0x00, 0x00 }, /* Ŋ to NG */ + { 0x014B, 0x6E, 0x67, 0x00, 0x00 }, /* ŋ to ng */ + { 0x014C, 0x4F, 0x00, 0x00, 0x00 }, /* Ō to O */ + { 0x014D, 0x6F, 0x00, 0x00, 0x00 }, /* ō to o */ + { 0x014E, 0x4F, 0x00, 0x00, 0x00 }, /* Ŏ to O */ + { 0x014F, 0x6F, 0x00, 0x00, 0x00 }, /* ŏ to o */ + { 0x0150, 0x4F, 0x00, 0x00, 0x00 }, /* Ő to O */ + { 0x0151, 0x6F, 0x00, 0x00, 0x00 }, /* ő to o */ + { 0x0152, 0x4F, 0x45, 0x00, 0x00 }, /* Œ to OE */ + { 0x0153, 0x6F, 0x65, 0x00, 0x00 }, /* œ to oe */ + { 0x0154, 0x52, 0x00, 0x00, 0x00 }, /* Ŕ to R */ + { 0x0155, 0x72, 0x00, 0x00, 0x00 }, /* ŕ to r */ + { 0x0156, 0x52, 0x00, 0x00, 0x00 }, /* Ŗ to R */ + { 0x0157, 0x72, 0x00, 0x00, 0x00 }, /* ŗ to r */ + { 0x0158, 0x52, 0x00, 0x00, 0x00 }, /* Ř to R */ + { 0x0159, 0x72, 0x00, 0x00, 0x00 }, /* ř to r */ + { 0x015A, 0x53, 0x00, 0x00, 0x00 }, /* Ś to S */ + { 0x015B, 0x73, 0x00, 0x00, 0x00 }, /* ś to s */ + { 0x015C, 0x53, 0x68, 0x00, 0x00 }, /* Ŝ to Sh */ + { 0x015D, 0x73, 0x68, 0x00, 0x00 }, /* ŝ to sh */ + { 0x015E, 0x53, 0x00, 0x00, 0x00 }, /* Ş to S */ + { 0x015F, 0x73, 0x00, 0x00, 0x00 }, /* ş to s */ + { 0x0160, 0x53, 0x00, 0x00, 0x00 }, /* Š to S */ + { 0x0161, 0x73, 0x00, 0x00, 0x00 }, /* š to s */ + { 0x0162, 0x54, 0x00, 0x00, 0x00 }, /* Ţ to T */ + { 0x0163, 0x74, 0x00, 0x00, 0x00 }, /* ţ to t */ + { 0x0164, 0x54, 0x00, 0x00, 0x00 }, /* Ť to T */ + { 0x0165, 0x74, 0x00, 0x00, 0x00 }, /* ť to t */ + { 0x0166, 0x54, 0x00, 0x00, 0x00 }, /* Ŧ to T */ + { 0x0167, 0x74, 0x00, 0x00, 0x00 }, /* ŧ to t */ + { 0x0168, 0x55, 0x00, 0x00, 0x00 }, /* Ũ to U */ + { 0x0169, 0x75, 0x00, 0x00, 0x00 }, /* ũ to u */ + { 0x016A, 0x55, 0x00, 0x00, 0x00 }, /* Ū to U */ + { 0x016B, 0x75, 0x00, 0x00, 0x00 }, /* ū to u */ + { 0x016C, 0x55, 0x00, 0x00, 0x00 }, /* Ŭ to U */ + { 0x016D, 0x75, 0x00, 0x00, 0x00 }, /* ŭ to u */ + { 0x016E, 0x55, 0x00, 0x00, 0x00 }, /* Ů to U */ + { 0x016F, 0x75, 0x00, 0x00, 0x00 }, /* ů to u */ + { 0x0170, 0x55, 0x00, 0x00, 0x00 }, /* Ű to U */ + { 0x0171, 0x75, 0x00, 0x00, 0x00 }, /* ű to u */ + { 0x0172, 0x55, 0x00, 0x00, 0x00 }, /* Ų to U */ + { 0x0173, 0x75, 0x00, 0x00, 0x00 }, /* ų to u */ + { 0x0174, 0x57, 0x00, 0x00, 0x00 }, /* Ŵ to W */ + { 0x0175, 0x77, 0x00, 0x00, 0x00 }, /* ŵ to w */ + { 0x0176, 0x59, 0x00, 0x00, 0x00 }, /* Ŷ to Y */ + { 0x0177, 0x79, 0x00, 0x00, 0x00 }, /* ŷ to y */ + { 0x0178, 0x59, 0x00, 0x00, 0x00 }, /* Ÿ to Y */ + { 0x0179, 0x5A, 0x00, 0x00, 0x00 }, /* Ź to Z */ + { 0x017A, 0x7A, 0x00, 0x00, 0x00 }, /* ź to z */ + { 0x017B, 0x5A, 0x00, 0x00, 0x00 }, /* Ż to Z */ + { 0x017C, 0x7A, 0x00, 0x00, 0x00 }, /* ż to z */ + { 0x017D, 0x5A, 0x00, 0x00, 0x00 }, /* Ž to Z */ + { 0x017E, 0x7A, 0x00, 0x00, 0x00 }, /* ž to z */ + { 0x017F, 0x73, 0x00, 0x00, 0x00 }, /* ſ to s */ + { 0x0192, 0x66, 0x00, 0x00, 0x00 }, /* ƒ to f */ + { 0x0218, 0x53, 0x00, 0x00, 0x00 }, /* Ș to S */ + { 0x0219, 0x73, 0x00, 0x00, 0x00 }, /* ș to s */ + { 0x021A, 0x54, 0x00, 0x00, 0x00 }, /* Ț to T */ + { 0x021B, 0x74, 0x00, 0x00, 0x00 }, /* ț to t */ + { 0x0386, 0x41, 0x00, 0x00, 0x00 }, /* Ά to A */ + { 0x0388, 0x45, 0x00, 0x00, 0x00 }, /* Έ to E */ + { 0x0389, 0x49, 0x00, 0x00, 0x00 }, /* Ή to I */ + { 0x038A, 0x49, 0x00, 0x00, 0x00 }, /* Ί to I */ + { 0x038C, 0x4f, 0x00, 0x00, 0x00 }, /* Ό to O */ + { 0x038E, 0x59, 0x00, 0x00, 0x00 }, /* Ύ to Y */ + { 0x038F, 0x4f, 0x00, 0x00, 0x00 }, /* Ώ to O */ + { 0x0390, 0x69, 0x00, 0x00, 0x00 }, /* ΐ to i */ + { 0x0391, 0x41, 0x00, 0x00, 0x00 }, /* Α to A */ + { 0x0392, 0x42, 0x00, 0x00, 0x00 }, /* Β to B */ + { 0x0393, 0x47, 0x00, 0x00, 0x00 }, /* Γ to G */ + { 0x0394, 0x44, 0x00, 0x00, 0x00 }, /* Δ to D */ + { 0x0395, 0x45, 0x00, 0x00, 0x00 }, /* Ε to E */ + { 0x0396, 0x5a, 0x00, 0x00, 0x00 }, /* Ζ to Z */ + { 0x0397, 0x49, 0x00, 0x00, 0x00 }, /* Η to I */ + { 0x0398, 0x54, 0x68, 0x00, 0x00 }, /* Θ to Th */ + { 0x0399, 0x49, 0x00, 0x00, 0x00 }, /* Ι to I */ + { 0x039A, 0x4b, 0x00, 0x00, 0x00 }, /* Κ to K */ + { 0x039B, 0x4c, 0x00, 0x00, 0x00 }, /* Λ to L */ + { 0x039C, 0x4d, 0x00, 0x00, 0x00 }, /* Μ to M */ + { 0x039D, 0x4e, 0x00, 0x00, 0x00 }, /* Ν to N */ + { 0x039E, 0x58, 0x00, 0x00, 0x00 }, /* Ξ to X */ + { 0x039F, 0x4f, 0x00, 0x00, 0x00 }, /* Ο to O */ + { 0x03A0, 0x50, 0x00, 0x00, 0x00 }, /* Π to P */ + { 0x03A1, 0x52, 0x00, 0x00, 0x00 }, /* Ρ to R */ + { 0x03A3, 0x53, 0x00, 0x00, 0x00 }, /* Σ to S */ + { 0x03A4, 0x54, 0x00, 0x00, 0x00 }, /* Τ to T */ + { 0x03A5, 0x59, 0x00, 0x00, 0x00 }, /* Υ to Y */ + { 0x03A6, 0x46, 0x00, 0x00, 0x00 }, /* Φ to F */ + { 0x03A7, 0x43, 0x68, 0x00, 0x00 }, /* Χ to Ch */ + { 0x03A8, 0x50, 0x73, 0x00, 0x00 }, /* Ψ to Ps */ + { 0x03A9, 0x4f, 0x00, 0x00, 0x00 }, /* Ω to O */ + { 0x03AA, 0x49, 0x00, 0x00, 0x00 }, /* Ϊ to I */ + { 0x03AB, 0x59, 0x00, 0x00, 0x00 }, /* Ϋ to Y */ + { 0x03AC, 0x61, 0x00, 0x00, 0x00 }, /* ά to a */ + { 0x03AD, 0x65, 0x00, 0x00, 0x00 }, /* έ to e */ + { 0x03AE, 0x69, 0x00, 0x00, 0x00 }, /* ή to i */ + { 0x03AF, 0x69, 0x00, 0x00, 0x00 }, /* ί to i */ + { 0x03B1, 0x61, 0x00, 0x00, 0x00 }, /* α to a */ + { 0x03B2, 0x62, 0x00, 0x00, 0x00 }, /* β to b */ + { 0x03B3, 0x67, 0x00, 0x00, 0x00 }, /* γ to g */ + { 0x03B4, 0x64, 0x00, 0x00, 0x00 }, /* δ to d */ + { 0x03B5, 0x65, 0x00, 0x00, 0x00 }, /* ε to e */ + { 0x03B6, 0x7a, 0x00, 0x00, 0x00 }, /* ζ to z */ + { 0x03B7, 0x69, 0x00, 0x00, 0x00 }, /* η to i */ + { 0x03B8, 0x74, 0x68, 0x00, 0x00 }, /* θ to th */ + { 0x03B9, 0x69, 0x00, 0x00, 0x00 }, /* ι to i */ + { 0x03BA, 0x6b, 0x00, 0x00, 0x00 }, /* κ to k */ + { 0x03BB, 0x6c, 0x00, 0x00, 0x00 }, /* λ to l */ + { 0x03BC, 0x6d, 0x00, 0x00, 0x00 }, /* μ to m */ + { 0x03BD, 0x6e, 0x00, 0x00, 0x00 }, /* ν to n */ + { 0x03BE, 0x78, 0x00, 0x00, 0x00 }, /* ξ to x */ + { 0x03BF, 0x6f, 0x00, 0x00, 0x00 }, /* ο to o */ + { 0x03C0, 0x70, 0x00, 0x00, 0x00 }, /* π to p */ + { 0x03C1, 0x72, 0x00, 0x00, 0x00 }, /* ρ to r */ + { 0x03C3, 0x73, 0x00, 0x00, 0x00 }, /* σ to s */ + { 0x03C4, 0x74, 0x00, 0x00, 0x00 }, /* τ to t */ + { 0x03C5, 0x79, 0x00, 0x00, 0x00 }, /* υ to y */ + { 0x03C6, 0x66, 0x00, 0x00, 0x00 }, /* φ to f */ + { 0x03C7, 0x63, 0x68, 0x00, 0x00 }, /* χ to ch */ + { 0x03C8, 0x70, 0x73, 0x00, 0x00 }, /* ψ to ps */ + { 0x03C9, 0x6f, 0x00, 0x00, 0x00 }, /* ω to o */ + { 0x03CA, 0x69, 0x00, 0x00, 0x00 }, /* ϊ to i */ + { 0x03CB, 0x79, 0x00, 0x00, 0x00 }, /* ϋ to y */ + { 0x03CC, 0x6f, 0x00, 0x00, 0x00 }, /* ό to o */ + { 0x03CD, 0x79, 0x00, 0x00, 0x00 }, /* ύ to y */ + { 0x03CE, 0x69, 0x00, 0x00, 0x00 }, /* ώ to i */ + { 0x0400, 0x45, 0x00, 0x00, 0x00 }, /* Ѐ to E */ + { 0x0401, 0x45, 0x00, 0x00, 0x00 }, /* Ё to E */ + { 0x0402, 0x44, 0x00, 0x00, 0x00 }, /* Ђ to D */ + { 0x0403, 0x47, 0x00, 0x00, 0x00 }, /* Ѓ to G */ + { 0x0404, 0x45, 0x00, 0x00, 0x00 }, /* Є to E */ + { 0x0405, 0x5a, 0x00, 0x00, 0x00 }, /* Ѕ to Z */ + { 0x0406, 0x49, 0x00, 0x00, 0x00 }, /* І to I */ + { 0x0407, 0x49, 0x00, 0x00, 0x00 }, /* Ї to I */ + { 0x0408, 0x4a, 0x00, 0x00, 0x00 }, /* Ј to J */ + { 0x0409, 0x49, 0x00, 0x00, 0x00 }, /* Љ to I */ + { 0x040A, 0x4e, 0x00, 0x00, 0x00 }, /* Њ to N */ + { 0x040B, 0x44, 0x00, 0x00, 0x00 }, /* Ћ to D */ + { 0x040C, 0x4b, 0x00, 0x00, 0x00 }, /* Ќ to K */ + { 0x040D, 0x49, 0x00, 0x00, 0x00 }, /* Ѝ to I */ + { 0x040E, 0x55, 0x00, 0x00, 0x00 }, /* Ў to U */ + { 0x040F, 0x44, 0x00, 0x00, 0x00 }, /* Џ to D */ + { 0x0410, 0x41, 0x00, 0x00, 0x00 }, /* А to A */ + { 0x0411, 0x42, 0x00, 0x00, 0x00 }, /* Б to B */ + { 0x0412, 0x56, 0x00, 0x00, 0x00 }, /* В to V */ + { 0x0413, 0x47, 0x00, 0x00, 0x00 }, /* Г to G */ + { 0x0414, 0x44, 0x00, 0x00, 0x00 }, /* Д to D */ + { 0x0415, 0x45, 0x00, 0x00, 0x00 }, /* Е to E */ + { 0x0416, 0x5a, 0x68, 0x00, 0x00 }, /* Ж to Zh */ + { 0x0417, 0x5a, 0x00, 0x00, 0x00 }, /* З to Z */ + { 0x0418, 0x49, 0x00, 0x00, 0x00 }, /* И to I */ + { 0x0419, 0x49, 0x00, 0x00, 0x00 }, /* Й to I */ + { 0x041A, 0x4b, 0x00, 0x00, 0x00 }, /* К to K */ + { 0x041B, 0x4c, 0x00, 0x00, 0x00 }, /* Л to L */ + { 0x041C, 0x4d, 0x00, 0x00, 0x00 }, /* М to M */ + { 0x041D, 0x4e, 0x00, 0x00, 0x00 }, /* Н to N */ + { 0x041E, 0x4f, 0x00, 0x00, 0x00 }, /* О to O */ + { 0x041F, 0x50, 0x00, 0x00, 0x00 }, /* П to P */ + { 0x0420, 0x52, 0x00, 0x00, 0x00 }, /* Р to R */ + { 0x0421, 0x53, 0x00, 0x00, 0x00 }, /* С to S */ + { 0x0422, 0x54, 0x00, 0x00, 0x00 }, /* Т to T */ + { 0x0423, 0x55, 0x00, 0x00, 0x00 }, /* У to U */ + { 0x0424, 0x46, 0x00, 0x00, 0x00 }, /* Ф to F */ + { 0x0425, 0x4b, 0x68, 0x00, 0x00 }, /* Х to Kh */ + { 0x0426, 0x54, 0x63, 0x00, 0x00 }, /* Ц to Tc */ + { 0x0427, 0x43, 0x68, 0x00, 0x00 }, /* Ч to Ch */ + { 0x0428, 0x53, 0x68, 0x00, 0x00 }, /* Ш to Sh */ + { 0x0429, 0x53, 0x68, 0x63, 0x68 }, /* Щ to Shch */ + { 0x042A, 0x61, 0x00, 0x00, 0x00 }, /* to A */ + { 0x042B, 0x59, 0x00, 0x00, 0x00 }, /* Ы to Y */ + { 0x042C, 0x59, 0x00, 0x00, 0x00 }, /* to Y */ + { 0x042D, 0x45, 0x00, 0x00, 0x00 }, /* Э to E */ + { 0x042E, 0x49, 0x75, 0x00, 0x00 }, /* Ю to Iu */ + { 0x042F, 0x49, 0x61, 0x00, 0x00 }, /* Я to Ia */ + { 0x0430, 0x61, 0x00, 0x00, 0x00 }, /* а to a */ + { 0x0431, 0x62, 0x00, 0x00, 0x00 }, /* б to b */ + { 0x0432, 0x76, 0x00, 0x00, 0x00 }, /* в to v */ + { 0x0433, 0x67, 0x00, 0x00, 0x00 }, /* г to g */ + { 0x0434, 0x64, 0x00, 0x00, 0x00 }, /* д to d */ + { 0x0435, 0x65, 0x00, 0x00, 0x00 }, /* е to e */ + { 0x0436, 0x7a, 0x68, 0x00, 0x00 }, /* ж to zh */ + { 0x0437, 0x7a, 0x00, 0x00, 0x00 }, /* з to z */ + { 0x0438, 0x69, 0x00, 0x00, 0x00 }, /* и to i */ + { 0x0439, 0x69, 0x00, 0x00, 0x00 }, /* й to i */ + { 0x043A, 0x6b, 0x00, 0x00, 0x00 }, /* к to k */ + { 0x043B, 0x6c, 0x00, 0x00, 0x00 }, /* л to l */ + { 0x043C, 0x6d, 0x00, 0x00, 0x00 }, /* м to m */ + { 0x043D, 0x6e, 0x00, 0x00, 0x00 }, /* н to n */ + { 0x043E, 0x6f, 0x00, 0x00, 0x00 }, /* о to o */ + { 0x043F, 0x70, 0x00, 0x00, 0x00 }, /* п to p */ + { 0x0440, 0x72, 0x00, 0x00, 0x00 }, /* р to r */ + { 0x0441, 0x73, 0x00, 0x00, 0x00 }, /* с to s */ + { 0x0442, 0x74, 0x00, 0x00, 0x00 }, /* т to t */ + { 0x0443, 0x75, 0x00, 0x00, 0x00 }, /* у to u */ + { 0x0444, 0x66, 0x00, 0x00, 0x00 }, /* ф to f */ + { 0x0445, 0x6b, 0x68, 0x00, 0x00 }, /* х to kh */ + { 0x0446, 0x74, 0x63, 0x00, 0x00 }, /* ц to tc */ + { 0x0447, 0x63, 0x68, 0x00, 0x00 }, /* ч to ch */ + { 0x0448, 0x73, 0x68, 0x00, 0x00 }, /* ш to sh */ + { 0x0449, 0x73, 0x68, 0x63, 0x68 }, /* щ to shch */ + { 0x044A, 0x61, 0x00, 0x00, 0x00 }, /* to a */ + { 0x044B, 0x79, 0x00, 0x00, 0x00 }, /* ы to y */ + { 0x044C, 0x79, 0x00, 0x00, 0x00 }, /* to y */ + { 0x044D, 0x65, 0x00, 0x00, 0x00 }, /* э to e */ + { 0x044E, 0x69, 0x75, 0x00, 0x00 }, /* ю to iu */ + { 0x044F, 0x69, 0x61, 0x00, 0x00 }, /* я to ia */ + { 0x0450, 0x65, 0x00, 0x00, 0x00 }, /* ѐ to e */ + { 0x0451, 0x65, 0x00, 0x00, 0x00 }, /* ё to e */ + { 0x0452, 0x64, 0x00, 0x00, 0x00 }, /* ђ to d */ + { 0x0453, 0x67, 0x00, 0x00, 0x00 }, /* ѓ to g */ + { 0x0454, 0x65, 0x00, 0x00, 0x00 }, /* є to e */ + { 0x0455, 0x7a, 0x00, 0x00, 0x00 }, /* ѕ to z */ + { 0x0456, 0x69, 0x00, 0x00, 0x00 }, /* і to i */ + { 0x0457, 0x69, 0x00, 0x00, 0x00 }, /* ї to i */ + { 0x0458, 0x6a, 0x00, 0x00, 0x00 }, /* ј to j */ + { 0x0459, 0x69, 0x00, 0x00, 0x00 }, /* љ to i */ + { 0x045A, 0x6e, 0x00, 0x00, 0x00 }, /* њ to n */ + { 0x045B, 0x64, 0x00, 0x00, 0x00 }, /* ћ to d */ + { 0x045C, 0x6b, 0x00, 0x00, 0x00 }, /* ќ to k */ + { 0x045D, 0x69, 0x00, 0x00, 0x00 }, /* ѝ to i */ + { 0x045E, 0x75, 0x00, 0x00, 0x00 }, /* ў to u */ + { 0x045F, 0x64, 0x00, 0x00, 0x00 }, /* џ to d */ + { 0x1E02, 0x42, 0x00, 0x00, 0x00 }, /* Ḃ to B */ + { 0x1E03, 0x62, 0x00, 0x00, 0x00 }, /* ḃ to b */ + { 0x1E0A, 0x44, 0x00, 0x00, 0x00 }, /* Ḋ to D */ + { 0x1E0B, 0x64, 0x00, 0x00, 0x00 }, /* ḋ to d */ + { 0x1E1E, 0x46, 0x00, 0x00, 0x00 }, /* Ḟ to F */ + { 0x1E1F, 0x66, 0x00, 0x00, 0x00 }, /* ḟ to f */ + { 0x1E40, 0x4D, 0x00, 0x00, 0x00 }, /* Ṁ to M */ + { 0x1E41, 0x6D, 0x00, 0x00, 0x00 }, /* ṁ to m */ + { 0x1E56, 0x50, 0x00, 0x00, 0x00 }, /* Ṗ to P */ + { 0x1E57, 0x70, 0x00, 0x00, 0x00 }, /* ṗ to p */ + { 0x1E60, 0x53, 0x00, 0x00, 0x00 }, /* Ṡ to S */ + { 0x1E61, 0x73, 0x00, 0x00, 0x00 }, /* ṡ to s */ + { 0x1E6A, 0x54, 0x00, 0x00, 0x00 }, /* Ṫ to T */ + { 0x1E6B, 0x74, 0x00, 0x00, 0x00 }, /* ṫ to t */ + { 0x1E80, 0x57, 0x00, 0x00, 0x00 }, /* Ẁ to W */ + { 0x1E81, 0x77, 0x00, 0x00, 0x00 }, /* ẁ to w */ + { 0x1E82, 0x57, 0x00, 0x00, 0x00 }, /* Ẃ to W */ + { 0x1E83, 0x77, 0x00, 0x00, 0x00 }, /* ẃ to w */ + { 0x1E84, 0x57, 0x00, 0x00, 0x00 }, /* Ẅ to W */ + { 0x1E85, 0x77, 0x00, 0x00, 0x00 }, /* ẅ to w */ + { 0x1EF2, 0x59, 0x00, 0x00, 0x00 }, /* Ỳ to Y */ + { 0x1EF3, 0x79, 0x00, 0x00, 0x00 }, /* ỳ to y */ + { 0xFB00, 0x66, 0x66, 0x00, 0x00 }, /* ff to ff */ + { 0xFB01, 0x66, 0x69, 0x00, 0x00 }, /* fi to fi */ + { 0xFB02, 0x66, 0x6C, 0x00, 0x00 }, /* fl to fl */ + { 0xFB05, 0x73, 0x74, 0x00, 0x00 }, /* ſt to st */ + { 0xFB06, 0x73, 0x74, 0x00, 0x00 }, /* st to st */ }; +static const Transliteration *spellfixFindTranslit(int c, int *pxTop){ + *pxTop = (sizeof(translit)/sizeof(translit[0])) - 1; + return translit; +} + /* ** Convert the input string from UTF-8 into pure ASCII by converting ** all non-ASCII characters to some combination of characters in the @@ -1621,23 +1720,24 @@ zOut[nOut++] = (unsigned char)c; }else{ int xTop, xBtm, x; - xTop = sizeof(translit)/sizeof(translit[0]) - 1; + const Transliteration *tbl = spellfixFindTranslit(c, &xTop); xBtm = 0; while( xTop>=xBtm ){ x = (xTop + xBtm)/2; - if( translit[x].cFrom==c ){ - zOut[nOut++] = translit[x].cTo0; - if( translit[x].cTo1 ){ - zOut[nOut++] = translit[x].cTo1; - /* Add an extra "ch" after the "sh" for Щ and щ */ - if( c==0x0429 || c== 0x0449 ){ - zOut[nOut++] = 'c'; - zOut[nOut++] = 'h'; + if( tbl[x].cFrom==c ){ + zOut[nOut++] = tbl[x].cTo0; + if( tbl[x].cTo1 ){ + zOut[nOut++] = tbl[x].cTo1; + if( tbl[x].cTo2 ){ + zOut[nOut++] = tbl[x].cTo2; + if( tbl[x].cTo3 ){ + zOut[nOut++] = tbl[x].cTo3; + } } } c = 0; break; - }else if( translit[x].cFrom>c ){ + }else if( tbl[x].cFrom>c ){ xTop = x-1; }else{ xBtm = x+1; @@ -1668,15 +1768,22 @@ nOut++; if( c>=128 ){ int xTop, xBtm, x; - xTop = sizeof(translit)/sizeof(translit[0]) - 1; + const Transliteration *tbl = spellfixFindTranslit(c, &xTop); xBtm = 0; while( xTop>=xBtm ){ x = (xTop + xBtm)/2; - if( translit[x].cFrom==c ){ - if( translit[x].cTo1 ) nOut++; - if( c==0x0429 || c== 0x0449 ) nOut += 2; + if( tbl[x].cFrom==c ){ + if( tbl[x].cTo1 ){ + nOut++; + if( tbl[x].cTo2 ){ + nOut++; + if( tbl[x].cTo3 ){ + nOut++; + } + } + } break; - }else if( translit[x].cFrom>c ){ + }else if( tbl[x].cFrom>c ){ xTop = x-1; }else{ xBtm = x+1; @@ -2476,7 +2583,7 @@ nPattern = (int)strlen(zPattern); if( zPattern[nPattern-1]=='*' ) nPattern--; zSql = sqlite3_mprintf( - "SELECT id, word, rank, k1" + "SELECT id, word, rank, coalesce(k1,word)" " FROM \"%w\".\"%w_vocab\"" " WHERE langid=%d AND k2>=?1 AND k2<?2", p->zDbName, p->zTableName, iLang @@ -2810,17 +2917,17 @@ if( sqlite3_value_type(argv[1])==SQLITE_NULL ){ spellfix1DbExec(&rc, db, "INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) " - "VALUES(%d,%d,%Q,%Q,%Q)", + "VALUES(%d,%d,%Q,nullif(%Q,%Q),%Q)", p->zDbName, p->zTableName, - iRank, iLang, zWord, zK1, zK2 + iRank, iLang, zWord, zK1, zWord, zK2 ); }else{ newRowid = sqlite3_value_int64(argv[1]); spellfix1DbExec(&rc, db, "INSERT OR %s INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) " - "VALUES(%lld,%d,%d,%Q,%Q,%Q)", + "VALUES(%lld,%d,%d,%Q,nullif(%Q,%Q),%Q)", zConflict, p->zDbName, p->zTableName, - newRowid, iRank, iLang, zWord, zK1, zK2 + newRowid, iRank, iLang, zWord, zK1, zWord, zK2 ); } *pRowid = sqlite3_last_insert_rowid(db); @@ -2829,9 +2936,9 @@ newRowid = *pRowid = sqlite3_value_int64(argv[1]); spellfix1DbExec(&rc, db, "UPDATE OR %s \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d," - " word=%Q, k1=%Q, k2=%Q WHERE id=%lld", + " word=%Q, k1=nullif(%Q,%Q), k2=%Q WHERE id=%lld", zConflict, p->zDbName, p->zTableName, newRowid, iRank, iLang, - zWord, zK1, zK2, rowid + zWord, zK1, zWord, zK2, rowid ); } sqlite3_free(zK1);
diff --git a/third_party/sqlite/src/ext/misc/zipfile.c b/third_party/sqlite/src/ext/misc/zipfile.c index 246ae9b7..0466c81 100644 --- a/third_party/sqlite/src/ext/misc/zipfile.c +++ b/third_party/sqlite/src/ext/misc/zipfile.c
@@ -30,29 +30,48 @@ #include <string.h> #include <assert.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#if !defined(_WIN32) && !defined(WIN32) -# include <unistd.h> -# include <dirent.h> -# include <utime.h> -#else -# include <io.h> -#endif -#include <time.h> -#include <errno.h> - #include <zlib.h> #ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_AMALGAMATION + typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned long u32; #define MIN(a,b) ((a)<(b) ? (a) : (b)) + +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) +# define ALWAYS(X) (1) +# define NEVER(X) (0) +#elif !defined(NDEBUG) +# define ALWAYS(X) ((X)?1:(assert(0),0)) +# define NEVER(X) ((X)?(assert(0),1):0) +#else +# define ALWAYS(X) (X) +# define NEVER(X) (X) +#endif + +#endif /* SQLITE_AMALGAMATION */ + +/* +** Definitions for mode bitmasks S_IFDIR, S_IFREG and S_IFLNK. +** +** In some ways it would be better to obtain these values from system +** header files. But, the dependency is undesirable and (a) these +** have been stable for decades, (b) the values are part of POSIX and +** are also made explicit in [man stat], and (c) are part of the +** file format for zip archives. +*/ +#ifndef S_IFDIR +# define S_IFDIR 0040000 +#endif +#ifndef S_IFREG +# define S_IFREG 0100000 +#endif +#ifndef S_IFLNK +# define S_IFLNK 0120000 #endif static const char ZIPFILE_SCHEMA[] = @@ -93,6 +112,9 @@ ** ** ZIPFILE_SIGNATURE_LFH: ** First 4 bytes of a valid LFH record. +** +** ZIPFILE_SIGNATURE_EOCD +** First 4 bytes of a valid EOCD record. */ #define ZIPFILE_EXTRA_TIMESTAMP 0x5455 #define ZIPFILE_NEWENTRY_MADEBY ((3<<8) + 30) @@ -101,22 +123,14 @@ #define ZIPFILE_SIGNATURE_CDS 0x02014b50 #define ZIPFILE_SIGNATURE_LFH 0x04034b50 #define ZIPFILE_SIGNATURE_EOCD 0x06054b50 -#define ZIPFILE_LFH_FIXED_SZ 30 /* -** Set the error message contained in context ctx to the results of -** vprintf(zFmt, ...). +** The sizes of the fixed-size part of each of the three main data +** structures in a zip archive. */ -static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ - char *zMsg = 0; - va_list ap; - va_start(ap, zFmt); - zMsg = sqlite3_vmprintf(zFmt, ap); - sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); - va_end(ap); -} - +#define ZIPFILE_LFH_FIXED_SZ 30 +#define ZIPFILE_EOCD_FIXED_SZ 22 +#define ZIPFILE_CDS_FIXED_SZ 46 /* *** 4.3.16 End of central directory record: @@ -222,47 +236,39 @@ typedef struct ZipfileEntry ZipfileEntry; struct ZipfileEntry { - char *zPath; /* Path of zipfile entry */ - u8 *aCdsEntry; /* Buffer containing entire CDS entry */ - int nCdsEntry; /* Size of buffer aCdsEntry[] in bytes */ - int bDeleted; /* True if entry has been deleted */ + ZipfileCDS cds; /* Parsed CDS record */ + u32 mUnixTime; /* Modification time, in UNIX format */ + u8 *aExtra; /* cds.nExtra+cds.nComment bytes of extra data */ + i64 iDataOff; /* Offset to data in file (if aData==0) */ + u8 *aData; /* cds.szCompressed bytes of compressed data */ ZipfileEntry *pNext; /* Next element in in-memory CDS */ }; /* -** Cursor type for recursively iterating through a directory structure. +** Cursor type for zipfile tables. */ typedef struct ZipfileCsr ZipfileCsr; struct ZipfileCsr { sqlite3_vtab_cursor base; /* Base class - must be first */ i64 iId; /* Cursor ID */ - int bEof; /* True when at EOF */ + u8 bEof; /* True when at EOF */ + u8 bNoop; /* If next xNext() call is no-op */ /* Used outside of write transactions */ FILE *pFile; /* Zip file */ i64 iNextOff; /* Offset of next record in central directory */ ZipfileEOCD eocd; /* Parse of central directory record */ - /* Used inside write transactions */ - ZipfileEntry *pCurrent; - - ZipfileCDS cds; /* Central Directory Structure */ - ZipfileLFH lfh; /* Local File Header for current entry */ - i64 iDataOff; /* Offset in zipfile to data */ - u32 mTime; /* Extended mtime value */ - int flags; /* Flags byte (see below for bits) */ + ZipfileEntry *pFreeEntry; /* Free this list when cursor is closed or reset */ + ZipfileEntry *pCurrent; /* Current entry */ ZipfileCsr *pCsrNext; /* Next cursor on same virtual table */ }; -/* -** Values for ZipfileCsr.flags. -*/ -#define ZIPFILE_MTIME_VALID 0x0001 - typedef struct ZipfileTab ZipfileTab; struct ZipfileTab { sqlite3_vtab base; /* Base class - must be first */ char *zFile; /* Zip file this table accesses (may be NULL) */ + sqlite3 *db; /* Host database connection */ u8 *aBuffer; /* Temporary buffer used for various tasks */ ZipfileCsr *pCsrList; /* List of cursors */ @@ -276,17 +282,33 @@ i64 szOrig; /* Size of archive at start of transaction */ }; +/* +** Set the error message contained in context ctx to the results of +** vprintf(zFmt, ...). +*/ +static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ + char *zMsg = 0; + va_list ap; + va_start(ap, zFmt); + zMsg = sqlite3_vmprintf(zFmt, ap); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); + va_end(ap); +} + +/* +** If string zIn is quoted, dequote it in place. Otherwise, if the string +** is not quoted, do nothing. +*/ static void zipfileDequote(char *zIn){ char q = zIn[0]; if( q=='"' || q=='\'' || q=='`' || q=='[' ){ - char c; int iIn = 1; int iOut = 0; if( q=='[' ) q = ']'; - while( (c = zIn[iIn++]) ){ - if( c==q ){ - if( zIn[iIn++]!=q ) break; - } + while( ALWAYS(zIn[iIn]) ){ + char c = zIn[iIn++]; + if( c==q && zIn[iIn++]!=q ) break; zIn[iOut++] = c; } zIn[iOut] = '\0'; @@ -314,6 +336,21 @@ ZipfileTab *pNew = 0; int rc; + /* If the table name is not "zipfile", require that the argument be + ** specified. This stops zipfile tables from being created as: + ** + ** CREATE VIRTUAL TABLE zzz USING zipfile(); + ** + ** It does not prevent: + ** + ** CREATE VIRTUAL TABLE zipfile USING zipfile(); + */ + assert( 0==sqlite3_stricmp(argv[0], "zipfile") ); + if( (0!=sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){ + *pzErr = sqlite3_mprintf("zipfile constructor requires one argument"); + return SQLITE_ERROR; + } + if( argc>3 ){ zFile = argv[3]; nFile = (int)strlen(zFile)+1; @@ -324,6 +361,7 @@ pNew = (ZipfileTab*)sqlite3_malloc(nByte+nFile); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, nByte+nFile); + pNew->db = db; pNew->aBuffer = (u8*)&pNew[1]; if( zFile ){ pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE]; @@ -336,9 +374,42 @@ } /* +** Free the ZipfileEntry structure indicated by the only argument. +*/ +static void zipfileEntryFree(ZipfileEntry *p){ + if( p ){ + sqlite3_free(p->cds.zFile); + sqlite3_free(p); + } +} + +/* +** Release resources that should be freed at the end of a write +** transaction. +*/ +static void zipfileCleanupTransaction(ZipfileTab *pTab){ + ZipfileEntry *pEntry; + ZipfileEntry *pNext; + + if( pTab->pWriteFd ){ + fclose(pTab->pWriteFd); + pTab->pWriteFd = 0; + } + for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){ + pNext = pEntry->pNext; + zipfileEntryFree(pEntry); + } + pTab->pFirstEntry = 0; + pTab->pLastEntry = 0; + pTab->szCurrent = 0; + pTab->szOrig = 0; +} + +/* ** This method is the destructor for zipfile vtab objects. */ static int zipfileDisconnect(sqlite3_vtab *pVtab){ + zipfileCleanupTransaction((ZipfileTab*)pVtab); sqlite3_free(pVtab); return SQLITE_OK; } @@ -366,12 +437,20 @@ ** by zipfileOpen(). */ static void zipfileResetCursor(ZipfileCsr *pCsr){ - sqlite3_free(pCsr->cds.zFile); - pCsr->cds.zFile = 0; + ZipfileEntry *p; + ZipfileEntry *pNext; + pCsr->bEof = 0; if( pCsr->pFile ){ fclose(pCsr->pFile); pCsr->pFile = 0; + zipfileEntryFree(pCsr->pCurrent); + pCsr->pCurrent = 0; + } + + for(p=pCsr->pFreeEntry; p; p=pNext){ + pNext = p->pNext; + zipfileEntryFree(p); } } @@ -385,12 +464,8 @@ zipfileResetCursor(pCsr); /* Remove this cursor from the ZipfileTab.pCsrList list. */ - for(pp=&pTab->pCsrList; *pp; pp=&((*pp)->pCsrNext)){ - if( *pp==pCsr ){ - *pp = pCsr->pCsrNext; - break; - } - } + for(pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext)); + *pp = pCsr->pCsrNext; sqlite3_free(pCsr); return SQLITE_OK; @@ -400,13 +475,31 @@ ** Set the error message for the virtual table associated with cursor ** pCsr to the results of vprintf(zFmt, ...). */ -static void zipfileSetErrmsg(ZipfileCsr *pCsr, const char *zFmt, ...){ +static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){ va_list ap; va_start(ap, zFmt); + sqlite3_free(pTab->base.zErrMsg); + pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap); + va_end(ap); +} +static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){ + va_list ap; + va_start(ap, zFmt); + sqlite3_free(pCsr->base.pVtab->zErrMsg); pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap); va_end(ap); } +/* +** Read nRead bytes of data from offset iOff of file pFile into buffer +** aRead[]. Return SQLITE_OK if successful, or an SQLite error code +** otherwise. +** +** If an error does occur, output variable (*pzErrmsg) may be set to point +** to an English language error message. It is the responsibility of the +** caller to eventually free this buffer using +** sqlite3_free(). +*/ static int zipfileReadData( FILE *pFile, /* Read from this file */ u8 *aRead, /* Read into this buffer */ @@ -440,9 +533,16 @@ return SQLITE_OK; } +/* +** Read and return a 16-bit little-endian unsigned integer from buffer aBuf. +*/ static u16 zipfileGetU16(const u8 *aBuf){ return (aBuf[1] << 8) + aBuf[0]; } + +/* +** Read and return a 32-bit little-endian unsigned integer from buffer aBuf. +*/ static u32 zipfileGetU32(const u8 *aBuf){ return ((u32)(aBuf[3]) << 24) + ((u32)(aBuf[2]) << 16) @@ -450,10 +550,17 @@ + ((u32)(aBuf[0]) << 0); } +/* +** Write a 16-bit little endiate integer into buffer aBuf. +*/ static void zipfilePutU16(u8 *aBuf, u16 val){ aBuf[0] = val & 0xFF; aBuf[1] = (val>>8) & 0xFF; } + +/* +** Write a 32-bit little endiate integer into buffer aBuf. +*/ static void zipfilePutU32(u8 *aBuf, u32 val){ aBuf[0] = val & 0xFF; aBuf[1] = (val>>8) & 0xFF; @@ -467,15 +574,11 @@ #define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; } #define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; } -static u8* zipfileCsrBuffer(ZipfileCsr *pCsr){ - return ((ZipfileTab*)(pCsr->base.pVtab))->aBuffer; -} - /* ** Magic numbers used to read CDS records. */ -#define ZIPFILE_CDS_FIXED_SZ 46 #define ZIPFILE_CDS_NFILE_OFF 28 +#define ZIPFILE_CDS_SZCOMPRESSED_OFF 20 /* ** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR @@ -512,168 +615,80 @@ } /* -** Read the CDS record for the current entry from disk into pCsr->cds. +** Decode the LFH record in buffer aBuf into (*pLFH). Return SQLITE_ERROR +** if the record is not well-formed, or SQLITE_OK otherwise. */ -static int zipfileCsrReadCDS(ZipfileCsr *pCsr){ - char **pzErr = &pCsr->base.pVtab->zErrMsg; - u8 *aRead; - int rc = SQLITE_OK; - - sqlite3_free(pCsr->cds.zFile); - pCsr->cds.zFile = 0; - - if( pCsr->pCurrent==0 ){ - aRead = zipfileCsrBuffer(pCsr); - rc = zipfileReadData( - pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr - ); - }else{ - aRead = pCsr->pCurrent->aCdsEntry; - } - - if( rc==SQLITE_OK ){ - rc = zipfileReadCDS(aRead, &pCsr->cds); - if( rc!=SQLITE_OK ){ - assert( pCsr->pCurrent==0 ); - zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff); - }else{ - int nRead; - if( pCsr->pCurrent==0 ){ - nRead = pCsr->cds.nFile + pCsr->cds.nExtra; - aRead = zipfileCsrBuffer(pCsr); - pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ; - rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr); - }else{ - aRead = &aRead[ZIPFILE_CDS_FIXED_SZ]; - } - - if( rc==SQLITE_OK ){ - pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead); - pCsr->iNextOff += pCsr->cds.nFile; - pCsr->iNextOff += pCsr->cds.nExtra; - pCsr->iNextOff += pCsr->cds.nComment; - } - - /* Scan the cds.nExtra bytes of "extra" fields for any that can - ** be interpreted. The general format of an extra field is: - ** - ** Header ID 2 bytes - ** Data Size 2 bytes - ** Data N bytes - ** - */ - if( rc==SQLITE_OK ){ - u8 *p = &aRead[pCsr->cds.nFile]; - u8 *pEnd = &p[pCsr->cds.nExtra]; - - while( p<pEnd ){ - u16 id = zipfileRead16(p); - u16 nByte = zipfileRead16(p); - - switch( id ){ - case ZIPFILE_EXTRA_TIMESTAMP: { - u8 b = p[0]; - if( b & 0x01 ){ /* 0x01 -> modtime is present */ - pCsr->mTime = zipfileGetU32(&p[1]); - pCsr->flags |= ZIPFILE_MTIME_VALID; - } - break; - } - } - - p += nByte; - } - } - } - } - - return rc; -} - -static FILE *zipfileGetFd(ZipfileCsr *pCsr){ - if( pCsr->pFile ) return pCsr->pFile; - return ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd; -} - static int zipfileReadLFH( - FILE *pFd, - i64 iOffset, - u8 *aTmp, - ZipfileLFH *pLFH, - char **pzErr + u8 *aBuffer, + ZipfileLFH *pLFH ){ - u8 *aRead = aTmp; - static const int szFix = ZIPFILE_LFH_FIXED_SZ; - int rc; - - rc = zipfileReadData(pFd, aRead, szFix, iOffset, pzErr); - if( rc==SQLITE_OK ){ - u32 sig = zipfileRead32(aRead); - if( sig!=ZIPFILE_SIGNATURE_LFH ){ - *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", (int)iOffset); - rc = SQLITE_ERROR; - }else{ - pLFH->iVersionExtract = zipfileRead16(aRead); - pLFH->flags = zipfileRead16(aRead); - pLFH->iCompression = zipfileRead16(aRead); - pLFH->mTime = zipfileRead16(aRead); - pLFH->mDate = zipfileRead16(aRead); - pLFH->crc32 = zipfileRead32(aRead); - pLFH->szCompressed = zipfileRead32(aRead); - pLFH->szUncompressed = zipfileRead32(aRead); - pLFH->nFile = zipfileRead16(aRead); - pLFH->nExtra = zipfileRead16(aRead); - assert( aRead==&aTmp[szFix] ); - } - } - return rc; -} - -static int zipfileCsrReadLFH(ZipfileCsr *pCsr){ - FILE *pFile = zipfileGetFd(pCsr); - char **pzErr = &pCsr->base.pVtab->zErrMsg; - u8 *aRead = zipfileCsrBuffer(pCsr); - int rc = zipfileReadLFH(pFile, pCsr->cds.iOffset, aRead, &pCsr->lfh, pzErr); - pCsr->iDataOff = pCsr->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; - pCsr->iDataOff += pCsr->lfh.nFile+pCsr->lfh.nExtra; - return rc; -} - - -/* -** Advance an ZipfileCsr to its next row of output. -*/ -static int zipfileNext(sqlite3_vtab_cursor *cur){ - ZipfileCsr *pCsr = (ZipfileCsr*)cur; + u8 *aRead = aBuffer; int rc = SQLITE_OK; - pCsr->flags = 0; - if( pCsr->pCurrent==0 ){ - i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; - if( pCsr->iNextOff>=iEof ){ - pCsr->bEof = 1; - } + u32 sig = zipfileRead32(aRead); + if( sig!=ZIPFILE_SIGNATURE_LFH ){ + rc = SQLITE_ERROR; }else{ - assert( pCsr->pFile==0 ); - do { - pCsr->pCurrent = pCsr->pCurrent->pNext; - }while( pCsr->pCurrent && pCsr->pCurrent->bDeleted ); - if( pCsr->pCurrent==0 ){ - pCsr->bEof = 1; - } + pLFH->iVersionExtract = zipfileRead16(aRead); + pLFH->flags = zipfileRead16(aRead); + pLFH->iCompression = zipfileRead16(aRead); + pLFH->mTime = zipfileRead16(aRead); + pLFH->mDate = zipfileRead16(aRead); + pLFH->crc32 = zipfileRead32(aRead); + pLFH->szCompressed = zipfileRead32(aRead); + pLFH->szUncompressed = zipfileRead32(aRead); + pLFH->nFile = zipfileRead16(aRead); + pLFH->nExtra = zipfileRead16(aRead); } - - if( pCsr->bEof==0 ){ - rc = zipfileCsrReadCDS(pCsr); - if( rc==SQLITE_OK ){ - rc = zipfileCsrReadLFH(pCsr); - } - } - return rc; } + /* +** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields. +** Scan through this buffer to find an "extra-timestamp" field. If one +** exists, extract the 32-bit modification-timestamp from it and store +** the value in output parameter *pmTime. +** +** Zero is returned if no extra-timestamp record could be found (and so +** *pmTime is left unchanged), or non-zero otherwise. +** +** The general format of an extra field is: +** +** Header ID 2 bytes +** Data Size 2 bytes +** Data N bytes +*/ +static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){ + int ret = 0; + u8 *p = aExtra; + u8 *pEnd = &aExtra[nExtra]; + + while( p<pEnd ){ + u16 id = zipfileRead16(p); + u16 nByte = zipfileRead16(p); + + switch( id ){ + case ZIPFILE_EXTRA_TIMESTAMP: { + u8 b = p[0]; + if( b & 0x01 ){ /* 0x01 -> modtime is present */ + *pmTime = zipfileGetU32(&p[1]); + ret = 1; + } + break; + } + } + + p += nByte; + } + return ret; +} + +/* +** Convert the standard MS-DOS timestamp stored in the mTime and mDate +** fields of the CDS structure passed as the only argument to a 32-bit +** UNIX seconds-since-the-epoch timestamp. Return the result. +** ** "Standard" MS-DOS time format: ** ** File modification time: @@ -684,44 +699,237 @@ ** Bits 00-04: day ** Bits 05-08: month (1-12) ** Bits 09-15: years from 1980 +** +** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx */ -static time_t zipfileMtime(ZipfileCsr *pCsr){ - struct tm t; - memset(&t, 0, sizeof(t)); - t.tm_sec = (pCsr->cds.mTime & 0x1F)*2; - t.tm_min = (pCsr->cds.mTime >> 5) & 0x2F; - t.tm_hour = (pCsr->cds.mTime >> 11) & 0x1F; +static u32 zipfileMtime(ZipfileCDS *pCDS){ + int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F)); + int M = ((pCDS->mDate >> 5) & 0x0F); + int D = (pCDS->mDate & 0x1F); + int B = -13; - t.tm_mday = (pCsr->cds.mDate & 0x1F); - t.tm_mon = ((pCsr->cds.mDate >> 5) & 0x0F) - 1; - t.tm_year = 80 + ((pCsr->cds.mDate >> 9) & 0x7F); + int sec = (pCDS->mTime & 0x1F)*2; + int min = (pCDS->mTime >> 5) & 0x3F; + int hr = (pCDS->mTime >> 11) & 0x1F; + i64 JD; - return mktime(&t); + /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */ + + /* Calculate the JD in seconds for noon on the day in question */ + if( M<3 ){ + Y = Y-1; + M = M+12; + } + JD = (i64)(24*60*60) * ( + (int)(365.25 * (Y + 4716)) + + (int)(30.6001 * (M + 1)) + + D + B - 1524 + ); + + /* Correct the JD for the time within the day */ + JD += (hr-12) * 3600 + min * 60 + sec; + + /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */ + return (u32)(JD - (i64)(24405875) * 24*60*6); } -static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mTime){ - time_t t = (time_t)mTime; - struct tm res; +/* +** The opposite of zipfileMtime(). This function populates the mTime and +** mDate fields of the CDS structure passed as the first argument according +** to the UNIX timestamp value passed as the second. +*/ +static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){ + /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */ + i64 JD = (i64)2440588 + mUnixTime / (24*60*60); -#if !defined(_WIN32) && !defined(WIN32) - localtime_r(&t, &res); -#else - memcpy(&res, localtime(&t), sizeof(struct tm)); -#endif + int A, B, C, D, E; + int yr, mon, day; + int hr, min, sec; - pCds->mTime = (u16)( - (res.tm_sec / 2) + - (res.tm_min << 5) + - (res.tm_hour << 11)); + A = (int)((JD - 1867216.25)/36524.25); + A = (int)(JD + 1 + A - (A/4)); + B = A + 1524; + C = (int)((B - 122.1)/365.25); + D = (36525*(C&32767))/100; + E = (int)((B-D)/30.6001); - pCds->mDate = (u16)( - (res.tm_mday-1) + - ((res.tm_mon+1) << 5) + - ((res.tm_year-80) << 9)); + day = B - D - (int)(30.6001*E); + mon = (E<14 ? E-1 : E-13); + yr = mon>2 ? C-4716 : C-4715; + + hr = (mUnixTime % (24*60*60)) / (60*60); + min = (mUnixTime % (60*60)) / 60; + sec = (mUnixTime % 60); + + if( yr>=1980 ){ + pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9)); + pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11)); + }else{ + pCds->mDate = pCds->mTime = 0; + } + + assert( mUnixTime<315507600 + || mUnixTime==zipfileMtime(pCds) + || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds)) + /* || (mUnixTime % 2) */ + ); } +/* +** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in +** size) containing an entire zip archive image. Or, if aBlob is NULL, +** then pFile is a file-handle open on a zip file. In either case, this +** function creates a ZipfileEntry object based on the zip archive entry +** for which the CDS record is at offset iOff. +** +** If successful, SQLITE_OK is returned and (*ppEntry) set to point to +** the new object. Otherwise, an SQLite error code is returned and the +** final value of (*ppEntry) undefined. +*/ +static int zipfileGetEntry( + ZipfileTab *pTab, /* Store any error message here */ + const u8 *aBlob, /* Pointer to in-memory file image */ + int nBlob, /* Size of aBlob[] in bytes */ + FILE *pFile, /* If aBlob==0, read from this file */ + i64 iOff, /* Offset of CDS record */ + ZipfileEntry **ppEntry /* OUT: Pointer to new object */ +){ + u8 *aRead; + char **pzErr = &pTab->base.zErrMsg; + int rc = SQLITE_OK; + + if( aBlob==0 ){ + aRead = pTab->aBuffer; + rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr); + }else{ + aRead = (u8*)&aBlob[iOff]; + } + + if( rc==SQLITE_OK ){ + int nAlloc; + ZipfileEntry *pNew; + + int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]); + int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]); + nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]); + + nAlloc = sizeof(ZipfileEntry) + nExtra; + if( aBlob ){ + nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]); + } + + pNew = (ZipfileEntry*)sqlite3_malloc(nAlloc); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(ZipfileEntry)); + rc = zipfileReadCDS(aRead, &pNew->cds); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff); + }else if( aBlob==0 ){ + rc = zipfileReadData( + pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr + ); + }else{ + aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ]; + } + } + + if( rc==SQLITE_OK ){ + u32 *pt = &pNew->mUnixTime; + pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead); + pNew->aExtra = (u8*)&pNew[1]; + memcpy(pNew->aExtra, &aRead[nFile], nExtra); + if( pNew->cds.zFile==0 ){ + rc = SQLITE_NOMEM; + }else if( 0==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){ + pNew->mUnixTime = zipfileMtime(&pNew->cds); + } + } + + if( rc==SQLITE_OK ){ + static const int szFix = ZIPFILE_LFH_FIXED_SZ; + ZipfileLFH lfh; + if( pFile ){ + rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr); + }else{ + aRead = (u8*)&aBlob[pNew->cds.iOffset]; + } + + rc = zipfileReadLFH(aRead, &lfh); + if( rc==SQLITE_OK ){ + pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; + pNew->iDataOff += lfh.nFile + lfh.nExtra; + if( aBlob && pNew->cds.szCompressed ){ + pNew->aData = &pNew->aExtra[nExtra]; + memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed); + } + }else{ + *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", + (int)pNew->cds.iOffset + ); + } + } + + if( rc!=SQLITE_OK ){ + zipfileEntryFree(pNew); + }else{ + *ppEntry = pNew; + } + } + + return rc; +} + +/* +** Advance an ZipfileCsr to its next row of output. +*/ +static int zipfileNext(sqlite3_vtab_cursor *cur){ + ZipfileCsr *pCsr = (ZipfileCsr*)cur; + int rc = SQLITE_OK; + + if( pCsr->pFile ){ + i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize; + zipfileEntryFree(pCsr->pCurrent); + pCsr->pCurrent = 0; + if( pCsr->iNextOff>=iEof ){ + pCsr->bEof = 1; + }else{ + ZipfileEntry *p = 0; + ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab); + rc = zipfileGetEntry(pTab, 0, 0, pCsr->pFile, pCsr->iNextOff, &p); + if( rc==SQLITE_OK ){ + pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ; + pCsr->iNextOff += (int)p->cds.nExtra + p->cds.nFile + p->cds.nComment; + } + pCsr->pCurrent = p; + } + }else{ + if( !pCsr->bNoop ){ + pCsr->pCurrent = pCsr->pCurrent->pNext; + } + if( pCsr->pCurrent==0 ){ + pCsr->bEof = 1; + } + } + + pCsr->bNoop = 0; + return rc; +} + +static void zipfileFree(void *p) { + sqlite3_free(p); +} + +/* +** Buffer aIn (size nIn bytes) contains compressed data. Uncompressed, the +** size is nOut bytes. This function uncompresses the data and sets the +** return value in context pCtx to the result (a blob). +** +** If an error occurs, an error code is left in pCtx instead. +*/ static void zipfileInflate( - sqlite3_context *pCtx, /* Store error here, if any */ + sqlite3_context *pCtx, /* Store result here */ const u8 *aIn, /* Compressed data */ int nIn, /* Size of buffer aIn[] in bytes */ int nOut /* Expected output size */ @@ -747,7 +955,8 @@ if( err!=Z_STREAM_END ){ zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err); }else{ - sqlite3_result_blob(pCtx, aRes, nOut, SQLITE_TRANSIENT); + sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree); + aRes = 0; } } sqlite3_free(aRes); @@ -755,10 +964,22 @@ } } +/* +** Buffer aIn (size nIn bytes) contains uncompressed data. This function +** compresses it and sets (*ppOut) to point to a buffer containing the +** compressed data. The caller is responsible for eventually calling +** sqlite3_free() to release buffer (*ppOut). Before returning, (*pnOut) +** is set to the size of buffer (*ppOut) in bytes. +** +** If no error occurs, SQLITE_OK is returned. Otherwise, an SQLite error +** code is returned and an error message left in virtual-table handle +** pTab. The values of (*ppOut) and (*pnOut) are left unchanged in this +** case. +*/ static int zipfileDeflate( - ZipfileTab *pTab, /* Set error message here */ const u8 *aIn, int nIn, /* Input */ - u8 **ppOut, int *pnOut /* Output */ + u8 **ppOut, int *pnOut, /* Output */ + char **pzErr /* OUT: Error message */ ){ int nAlloc = (int)compressBound(nIn); u8 *aOut; @@ -784,7 +1005,7 @@ *pnOut = (int)str.total_out; }else{ sqlite3_free(aOut); - pTab->base.zErrMsg = sqlite3_mprintf("zipfile: deflate() error"); + *pzErr = sqlite3_mprintf("zipfile: deflate() error"); rc = SQLITE_ERROR; } deflateEnd(&str); @@ -804,60 +1025,66 @@ int i /* Which column to return */ ){ ZipfileCsr *pCsr = (ZipfileCsr*)cur; + ZipfileCDS *pCDS = &pCsr->pCurrent->cds; int rc = SQLITE_OK; switch( i ){ case 0: /* name */ - sqlite3_result_text(ctx, pCsr->cds.zFile, -1, SQLITE_TRANSIENT); + sqlite3_result_text(ctx, pCDS->zFile, -1, SQLITE_TRANSIENT); break; case 1: /* mode */ /* TODO: Whether or not the following is correct surely depends on ** the platform on which the archive was created. */ - sqlite3_result_int(ctx, pCsr->cds.iExternalAttr >> 16); + sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16); break; case 2: { /* mtime */ - if( pCsr->flags & ZIPFILE_MTIME_VALID ){ - sqlite3_result_int64(ctx, pCsr->mTime); - }else{ - sqlite3_result_int64(ctx, zipfileMtime(pCsr)); - } + sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime); break; } case 3: { /* sz */ if( sqlite3_vtab_nochange(ctx)==0 ){ - sqlite3_result_int64(ctx, pCsr->cds.szUncompressed); + sqlite3_result_int64(ctx, pCDS->szUncompressed); } break; } case 4: /* rawdata */ if( sqlite3_vtab_nochange(ctx) ) break; case 5: { /* data */ - if( i==4 || pCsr->cds.iCompression==0 || pCsr->cds.iCompression==8 ){ - int sz = pCsr->cds.szCompressed; - int szFinal = pCsr->cds.szUncompressed; + if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){ + int sz = pCDS->szCompressed; + int szFinal = pCDS->szUncompressed; if( szFinal>0 ){ - u8 *aBuf = sqlite3_malloc(sz); - if( aBuf==0 ){ - rc = SQLITE_NOMEM; + u8 *aBuf; + u8 *aFree = 0; + if( pCsr->pCurrent->aData ){ + aBuf = pCsr->pCurrent->aData; }else{ - FILE *pFile = zipfileGetFd(pCsr); - rc = zipfileReadData(pFile, aBuf, sz, pCsr->iDataOff, - &pCsr->base.pVtab->zErrMsg - ); + aBuf = aFree = sqlite3_malloc(sz); + if( aBuf==0 ){ + rc = SQLITE_NOMEM; + }else{ + FILE *pFile = pCsr->pFile; + if( pFile==0 ){ + pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd; + } + rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff, + &pCsr->base.pVtab->zErrMsg + ); + } } if( rc==SQLITE_OK ){ - if( i==5 && pCsr->cds.iCompression ){ + if( i==5 && pCDS->iCompression ){ zipfileInflate(ctx, aBuf, sz, szFinal); }else{ sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT); } - sqlite3_free(aBuf); } + sqlite3_free(aFree); }else{ /* Figure out if this is a directory or a zero-sized file. Consider ** it to be a directory either if the mode suggests so, or if ** the final character in the name is '/'. */ - u32 mode = pCsr->cds.iExternalAttr >> 16; - if( !(mode & S_IFDIR) && pCsr->cds.zFile[pCsr->cds.nFile-1]!='/' ){ + u32 mode = pCDS->iExternalAttr >> 16; + if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){ sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC); } } @@ -865,9 +1092,10 @@ break; } case 6: /* method */ - sqlite3_result_int(ctx, pCsr->cds.iCompression); + sqlite3_result_int(ctx, pCDS->iCompression); break; - case 7: /* z */ + default: /* z */ + assert( i==7 ); sqlite3_result_int64(ctx, pCsr->iId); break; } @@ -876,16 +1104,7 @@ } /* -** Return the rowid for the current row. -*/ -static int zipfileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - assert( 0 ); - return SQLITE_OK; -} - -/* -** Return TRUE if the cursor has been moved off of the last -** row of output. +** Return TRUE if the cursor is at EOF. */ static int zipfileEof(sqlite3_vtab_cursor *cur){ ZipfileCsr *pCsr = (ZipfileCsr*)cur; @@ -893,28 +1112,43 @@ } /* +** If aBlob is not NULL, then it points to a buffer nBlob bytes in size +** containing an entire zip archive image. Or, if aBlob is NULL, then pFile +** is guaranteed to be a file-handle open on a zip file. +** +** This function attempts to locate the EOCD record within the zip archive +** and populate *pEOCD with the results of decoding it. SQLITE_OK is +** returned if successful. Otherwise, an SQLite error code is returned and +** an English language error message may be left in virtual-table pTab. */ static int zipfileReadEOCD( ZipfileTab *pTab, /* Return errors here */ - FILE *pFile, /* Read from this file */ + const u8 *aBlob, /* Pointer to in-memory file image */ + int nBlob, /* Size of aBlob[] in bytes */ + FILE *pFile, /* Read from this file if aBlob==0 */ ZipfileEOCD *pEOCD /* Object to populate */ ){ u8 *aRead = pTab->aBuffer; /* Temporary buffer */ - i64 szFile; /* Total size of file in bytes */ int nRead; /* Bytes to read from file */ - i64 iOff; /* Offset to read from */ - int rc; + int rc = SQLITE_OK; - fseek(pFile, 0, SEEK_END); - szFile = (i64)ftell(pFile); - if( szFile==0 ){ - memset(pEOCD, 0, sizeof(ZipfileEOCD)); - return SQLITE_OK; + if( aBlob==0 ){ + i64 iOff; /* Offset to read from */ + i64 szFile; /* Total size of file in bytes */ + fseek(pFile, 0, SEEK_END); + szFile = (i64)ftell(pFile); + if( szFile==0 ){ + memset(pEOCD, 0, sizeof(ZipfileEOCD)); + return SQLITE_OK; + } + nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); + iOff = szFile - nRead; + rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg); + }else{ + nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE)); + aRead = (u8*)&aBlob[nBlob-nRead]; } - nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); - iOff = szFile - nRead; - rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg); if( rc==SQLITE_OK ){ int i; @@ -940,17 +1174,59 @@ pEOCD->nEntryTotal = zipfileRead16(aRead); pEOCD->nSize = zipfileRead32(aRead); pEOCD->iOffset = zipfileRead32(aRead); - -#if 0 - printf("iDisk=%d iFirstDisk=%d nEntry=%d " - "nEntryTotal=%d nSize=%d iOffset=%d", - (int)pEOCD->iDisk, (int)pEOCD->iFirstDisk, (int)pEOCD->nEntry, - (int)pEOCD->nEntryTotal, (int)pEOCD->nSize, (int)pEOCD->iOffset - ); -#endif } - return SQLITE_OK; + return rc; +} + +/* +** Add object pNew to the linked list that begins at ZipfileTab.pFirstEntry +** and ends with pLastEntry. If argument pBefore is NULL, then pNew is added +** to the end of the list. Otherwise, it is added to the list immediately +** before pBefore (which is guaranteed to be a part of said list). +*/ +static void zipfileAddEntry( + ZipfileTab *pTab, + ZipfileEntry *pBefore, + ZipfileEntry *pNew +){ + assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) ); + assert( pNew->pNext==0 ); + if( pBefore==0 ){ + if( pTab->pFirstEntry==0 ){ + pTab->pFirstEntry = pTab->pLastEntry = pNew; + }else{ + assert( pTab->pLastEntry->pNext==0 ); + pTab->pLastEntry->pNext = pNew; + pTab->pLastEntry = pNew; + } + }else{ + ZipfileEntry **pp; + for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext)); + pNew->pNext = pBefore; + *pp = pNew; + } +} + +static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){ + ZipfileEOCD eocd; + int rc; + int i; + i64 iOff; + + rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd); + iOff = eocd.iOffset; + for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){ + ZipfileEntry *pNew = 0; + rc = zipfileGetEntry(pTab, aBlob, nBlob, pTab->pWriteFd, iOff, &pNew); + + if( rc==SQLITE_OK ){ + zipfileAddEntry(pTab, 0, pNew); + iOff += ZIPFILE_CDS_FIXED_SZ; + iOff += (int)pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment; + } + } + return rc; } /* @@ -963,29 +1239,37 @@ ){ ZipfileTab *pTab = (ZipfileTab*)cur->pVtab; ZipfileCsr *pCsr = (ZipfileCsr*)cur; - const char *zFile; /* Zip file to scan */ + const char *zFile = 0; /* Zip file to scan */ int rc = SQLITE_OK; /* Return Code */ + int bInMemory = 0; /* True for an in-memory zipfile */ zipfileResetCursor(pCsr); if( pTab->zFile ){ zFile = pTab->zFile; }else if( idxNum==0 ){ - /* Error. This is an eponymous virtual table and the user has not - ** supplied a file name. */ - zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument"); + zipfileCursorErr(pCsr, "zipfile() function requires an argument"); return SQLITE_ERROR; + }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]); + int nBlob = sqlite3_value_bytes(argv[0]); + assert( pTab->pFirstEntry==0 ); + rc = zipfileLoadDirectory(pTab, aBlob, nBlob); + pCsr->pFreeEntry = pTab->pFirstEntry; + pTab->pFirstEntry = pTab->pLastEntry = 0; + if( rc!=SQLITE_OK ) return rc; + bInMemory = 1; }else{ zFile = (const char*)sqlite3_value_text(argv[0]); } - if( pTab->pWriteFd==0 ){ + if( 0==pTab->pWriteFd && 0==bInMemory ){ pCsr->pFile = fopen(zFile, "rb"); if( pCsr->pFile==0 ){ - zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile); + zipfileCursorErr(pCsr, "cannot open file: %s", zFile); rc = SQLITE_ERROR; }else{ - rc = zipfileReadEOCD(pTab, pCsr->pFile, &pCsr->eocd); + rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd); if( rc==SQLITE_OK ){ if( pCsr->eocd.nEntry==0 ){ pCsr->bEof = 1; @@ -996,12 +1280,9 @@ } } }else{ - ZipfileEntry e; - memset(&e, 0, sizeof(e)); - e.pNext = pTab->pFirstEntry; - pCsr->pCurrent = &e; + pCsr->bNoop = 1; + pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry; rc = zipfileNext(cur); - assert( pCsr->pCurrent!=&e ); } return rc; @@ -1037,182 +1318,67 @@ return SQLITE_OK; } -/* -** Add object pNew to the end of the linked list that begins at -** ZipfileTab.pFirstEntry and ends with pLastEntry. -*/ -static void zipfileAddEntry( - ZipfileTab *pTab, - ZipfileEntry *pBefore, - ZipfileEntry *pNew -){ - assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) ); - assert( pNew->pNext==0 ); - if( pBefore==0 ){ - if( pTab->pFirstEntry==0 ){ - pTab->pFirstEntry = pTab->pLastEntry = pNew; - }else{ - assert( pTab->pLastEntry->pNext==0 ); - pTab->pLastEntry->pNext = pNew; - pTab->pLastEntry = pNew; - } - }else{ - ZipfileEntry **pp; - for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext)); - pNew->pNext = pBefore; - *pp = pNew; - } -} - -static int zipfileLoadDirectory(ZipfileTab *pTab){ - ZipfileEOCD eocd; - int rc; - - rc = zipfileReadEOCD(pTab, pTab->pWriteFd, &eocd); - if( rc==SQLITE_OK && eocd.nEntry>0 ){ - int i; - int iOff = 0; - u8 *aBuf = sqlite3_malloc(eocd.nSize); - if( aBuf==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = zipfileReadData( - pTab->pWriteFd, aBuf, eocd.nSize, eocd.iOffset, &pTab->base.zErrMsg - ); - } - - for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){ - u16 nFile; - u16 nExtra; - u16 nComment; - ZipfileEntry *pNew; - u8 *aRec = &aBuf[iOff]; - - nFile = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF]); - nExtra = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF+2]); - nComment = zipfileGetU16(&aRec[ZIPFILE_CDS_NFILE_OFF+4]); - - pNew = sqlite3_malloc( - sizeof(ZipfileEntry) - + nFile+1 - + ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment - ); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(ZipfileEntry)); - pNew->zPath = (char*)&pNew[1]; - memcpy(pNew->zPath, &aRec[ZIPFILE_CDS_FIXED_SZ], nFile); - pNew->zPath[nFile] = '\0'; - pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1]; - pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; - memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry); - zipfileAddEntry(pTab, 0, pNew); - } - - iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; - } - - sqlite3_free(aBuf); - } - - return rc; -} - -static ZipfileEntry *zipfileNewEntry( - ZipfileCDS *pCds, /* Values for fixed size part of CDS */ - const char *zPath, /* Path for new entry */ - int nPath, /* strlen(zPath) */ - u32 mTime /* Modification time (or 0) */ -){ - u8 *aWrite; +static ZipfileEntry *zipfileNewEntry(const char *zPath){ ZipfileEntry *pNew; - pCds->nFile = (u16)nPath; - pCds->nExtra = mTime ? 9 : 0; - pNew = (ZipfileEntry*)sqlite3_malloc( - sizeof(ZipfileEntry) + - nPath+1 + - ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra - ); - + pNew = sqlite3_malloc(sizeof(ZipfileEntry)); if( pNew ){ memset(pNew, 0, sizeof(ZipfileEntry)); - pNew->zPath = (char*)&pNew[1]; - pNew->aCdsEntry = (u8*)&pNew->zPath[nPath+1]; - pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra; - memcpy(pNew->zPath, zPath, nPath+1); - - aWrite = pNew->aCdsEntry; - zipfileWrite32(aWrite, ZIPFILE_SIGNATURE_CDS); - zipfileWrite16(aWrite, pCds->iVersionMadeBy); - zipfileWrite16(aWrite, pCds->iVersionExtract); - zipfileWrite16(aWrite, pCds->flags); - zipfileWrite16(aWrite, pCds->iCompression); - zipfileWrite16(aWrite, pCds->mTime); - zipfileWrite16(aWrite, pCds->mDate); - zipfileWrite32(aWrite, pCds->crc32); - zipfileWrite32(aWrite, pCds->szCompressed); - zipfileWrite32(aWrite, pCds->szUncompressed); - zipfileWrite16(aWrite, pCds->nFile); - zipfileWrite16(aWrite, pCds->nExtra); - zipfileWrite16(aWrite, pCds->nComment); assert( pCds->nComment==0 ); - zipfileWrite16(aWrite, pCds->iDiskStart); - zipfileWrite16(aWrite, pCds->iInternalAttr); - zipfileWrite32(aWrite, pCds->iExternalAttr); - zipfileWrite32(aWrite, pCds->iOffset); - assert( aWrite==&pNew->aCdsEntry[ZIPFILE_CDS_FIXED_SZ] ); - memcpy(aWrite, zPath, nPath); - if( pCds->nExtra ){ - aWrite += nPath; - zipfileWrite16(aWrite, ZIPFILE_EXTRA_TIMESTAMP); - zipfileWrite16(aWrite, 5); - *aWrite++ = 0x01; - zipfileWrite32(aWrite, mTime); + pNew->cds.zFile = sqlite3_mprintf("%s", zPath); + if( pNew->cds.zFile==0 ){ + sqlite3_free(pNew); + pNew = 0; } } - return pNew; } +static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){ + ZipfileCDS *pCds = &pEntry->cds; + u8 *a = aBuf; + + pCds->nExtra = 9; + + /* Write the LFH itself */ + zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH); + zipfileWrite16(a, pCds->iVersionExtract); + zipfileWrite16(a, pCds->flags); + zipfileWrite16(a, pCds->iCompression); + zipfileWrite16(a, pCds->mTime); + zipfileWrite16(a, pCds->mDate); + zipfileWrite32(a, pCds->crc32); + zipfileWrite32(a, pCds->szCompressed); + zipfileWrite32(a, pCds->szUncompressed); + zipfileWrite16(a, (u16)pCds->nFile); + zipfileWrite16(a, pCds->nExtra); + assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] ); + + /* Add the file name */ + memcpy(a, pCds->zFile, (int)pCds->nFile); + a += (int)pCds->nFile; + + /* The "extra" data */ + zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP); + zipfileWrite16(a, 5); + *a++ = 0x01; + zipfileWrite32(a, pEntry->mUnixTime); + + return a-aBuf; +} + static int zipfileAppendEntry( ZipfileTab *pTab, - ZipfileCDS *pCds, - const char *zPath, /* Path for new entry */ - int nPath, /* strlen(zPath) */ + ZipfileEntry *pEntry, const u8 *pData, - int nData, - u32 mTime + int nData ){ u8 *aBuf = pTab->aBuffer; + int nBuf; int rc; - zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_LFH); - zipfileWrite16(aBuf, pCds->iVersionExtract); - zipfileWrite16(aBuf, pCds->flags); - zipfileWrite16(aBuf, pCds->iCompression); - zipfileWrite16(aBuf, pCds->mTime); - zipfileWrite16(aBuf, pCds->mDate); - zipfileWrite32(aBuf, pCds->crc32); - zipfileWrite32(aBuf, pCds->szCompressed); - zipfileWrite32(aBuf, pCds->szUncompressed); - zipfileWrite16(aBuf, (u16)nPath); - zipfileWrite16(aBuf, pCds->nExtra); - assert( aBuf==&pTab->aBuffer[ZIPFILE_LFH_FIXED_SZ] ); - rc = zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer)); + nBuf = zipfileSerializeLFH(pEntry, aBuf); + rc = zipfileAppendData(pTab, aBuf, nBuf); if( rc==SQLITE_OK ){ - rc = zipfileAppendData(pTab, (const u8*)zPath, nPath); - } - - if( rc==SQLITE_OK && pCds->nExtra ){ - aBuf = pTab->aBuffer; - zipfileWrite16(aBuf, ZIPFILE_EXTRA_TIMESTAMP); - zipfileWrite16(aBuf, 5); - *aBuf++ = 0x01; - zipfileWrite32(aBuf, mTime); - rc = zipfileAppendData(pTab, pTab->aBuffer, 9); - } - - if( rc==SQLITE_OK ){ + pEntry->iDataOff = pTab->szCurrent; rc = zipfileAppendData(pTab, pData, nData); } @@ -1220,15 +1386,15 @@ } static int zipfileGetMode( - ZipfileTab *pTab, sqlite3_value *pVal, - u32 defaultMode, /* Value to use if pVal IS NULL */ - u32 *pMode + int bIsDir, /* If true, default to directory */ + u32 *pMode, /* OUT: Mode value */ + char **pzErr /* OUT: Error message */ ){ const char *z = (const char*)sqlite3_value_text(pVal); u32 mode = 0; if( z==0 ){ - mode = defaultMode; + mode = (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)); }else if( z[0]>='0' && z[0]<='9' ){ mode = (unsigned int)sqlite3_value_int(pVal); }else{ @@ -1238,9 +1404,7 @@ switch( z[0] ){ case '-': mode |= S_IFREG; break; case 'd': mode |= S_IFDIR; break; -#if !defined(_WIN32) && !defined(WIN32) case 'l': mode |= S_IFLNK; break; -#endif default: goto parse_error; } for(i=1; i<10; i++){ @@ -1248,11 +1412,17 @@ else if( z[i]!='-' ) goto parse_error; } } + if( ((mode & S_IFDIR)==0)==bIsDir ){ + /* The "mode" attribute is a directory, but data has been specified. + ** Or vice-versa - no data but "mode" is a file or symlink. */ + *pzErr = sqlite3_mprintf("zipfile: mode does not match data"); + return SQLITE_CONSTRAINT; + } *pMode = mode; return SQLITE_OK; parse_error: - pTab->base.zErrMsg = sqlite3_mprintf("zipfile: parse error in mode: %s", z); + *pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s", z); return SQLITE_ERROR; } @@ -1268,6 +1438,81 @@ return 1; } +static int zipfileBegin(sqlite3_vtab *pVtab){ + ZipfileTab *pTab = (ZipfileTab*)pVtab; + int rc = SQLITE_OK; + + assert( pTab->pWriteFd==0 ); + + /* Open a write fd on the file. Also load the entire central directory + ** structure into memory. During the transaction any new file data is + ** appended to the archive file, but the central directory is accumulated + ** in main-memory until the transaction is committed. */ + pTab->pWriteFd = fopen(pTab->zFile, "ab+"); + if( pTab->pWriteFd==0 ){ + pTab->base.zErrMsg = sqlite3_mprintf( + "zipfile: failed to open file %s for writing", pTab->zFile + ); + rc = SQLITE_ERROR; + }else{ + fseek(pTab->pWriteFd, 0, SEEK_END); + pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); + rc = zipfileLoadDirectory(pTab, 0, 0); + } + + if( rc!=SQLITE_OK ){ + zipfileCleanupTransaction(pTab); + } + + return rc; +} + +/* +** Return the current time as a 32-bit timestamp in UNIX epoch format (like +** time(2)). +*/ +static u32 zipfileTime(void){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(0); + u32 ret; + if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ + i64 ms; + pVfs->xCurrentTimeInt64(pVfs, &ms); + ret = (u32)((ms/1000) - ((i64)24405875 * 8640)); + }else{ + double day; + pVfs->xCurrentTime(pVfs, &day); + ret = (u32)((day - 2440587.5) * 86400); + } + return ret; +} + +/* +** Return a 32-bit timestamp in UNIX epoch format. +** +** If the value passed as the only argument is either NULL or an SQL NULL, +** return the current time. Otherwise, return the value stored in (*pVal) +** cast to a 32-bit unsigned integer. +*/ +static u32 zipfileGetTime(sqlite3_value *pVal){ + if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ + return zipfileTime(); + } + return (u32)sqlite3_value_int64(pVal); +} + +/* +** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry +** linked list. Remove it from the list and free the object. +*/ +static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ + if( pOld ){ + ZipfileEntry **pp; + for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext)); + *pp = (*pp)->pNext; + zipfileEntryFree(pOld); + } +} + /* ** xUpdate method. */ @@ -1282,7 +1527,7 @@ ZipfileEntry *pNew = 0; /* New in-memory CDS entry */ u32 mode = 0; /* Mode for new entry */ - i64 mTime = 0; /* Modification time for new entry */ + u32 mTime = 0; /* Modification time for new entry */ i64 sz = 0; /* Uncompressed size */ const char *zPath = 0; /* Path for new entry */ int nPath = 0; /* strlen(zPath) */ @@ -1291,217 +1536,239 @@ int iMethod = 0; /* Compression method for new entry */ u8 *pFree = 0; /* Free this */ char *zFree = 0; /* Also free this */ - ZipfileCDS cds; /* New Central Directory Structure entry */ ZipfileEntry *pOld = 0; + ZipfileEntry *pOld2 = 0; + int bUpdate = 0; /* True for an update that modifies "name" */ int bIsDir = 0; u32 iCrc32 = 0; - assert( pTab->zFile ); - assert( pTab->pWriteFd ); + if( pTab->pWriteFd==0 ){ + rc = zipfileBegin(pVtab); + if( rc!=SQLITE_OK ) return rc; + } + /* If this is a DELETE or UPDATE, find the archive entry to delete. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ const char *zDelete = (const char*)sqlite3_value_text(apVal[0]); int nDelete = (int)strlen(zDelete); + if( nVal>1 ){ + const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]); + if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){ + bUpdate = 1; + } + } for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){ - if( pOld->bDeleted ) continue; - if( zipfileComparePath(pOld->zPath, zDelete, nDelete)==0 ){ - pOld->bDeleted = 1; + if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){ break; } assert( pOld->pNext ); } - if( nVal==1 ) return SQLITE_OK; } - /* Check that "sz" and "rawdata" are both NULL: */ - if( sqlite3_value_type(apVal[5])!=SQLITE_NULL - || sqlite3_value_type(apVal[6])!=SQLITE_NULL - ){ - rc = SQLITE_CONSTRAINT; - } - - if( rc==SQLITE_OK ){ - if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){ - /* data=NULL. A directory */ - bIsDir = 1; - }else{ - /* Value specified for "data", and possibly "method". This must be - ** a regular file or a symlink. */ - const u8 *aIn = sqlite3_value_blob(apVal[7]); - int nIn = sqlite3_value_bytes(apVal[7]); - int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; - - iMethod = sqlite3_value_int(apVal[8]); - sz = nIn; - pData = aIn; - nData = nIn; - if( iMethod!=0 && iMethod!=8 ){ - rc = SQLITE_CONSTRAINT; - }else{ - if( bAuto || iMethod ){ - int nCmp; - rc = zipfileDeflate(pTab, aIn, nIn, &pFree, &nCmp); - if( rc==SQLITE_OK ){ - if( iMethod || nCmp<nIn ){ - iMethod = 8; - pData = pFree; - nData = nCmp; - } - } - } - iCrc32 = crc32(0, aIn, nIn); - } - } - } - - if( rc==SQLITE_OK ){ - rc = zipfileGetMode(pTab, apVal[3], - (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)), &mode - ); - if( rc==SQLITE_OK && (bIsDir == ((mode & S_IFDIR)==0)) ){ - /* The "mode" attribute is a directory, but data has been specified. - ** Or vice-versa - no data but "mode" is a file or symlink. */ + if( nVal>1 ){ + /* Check that "sz" and "rawdata" are both NULL: */ + if( sqlite3_value_type(apVal[5])!=SQLITE_NULL ){ + zipfileTableErr(pTab, "sz must be NULL"); rc = SQLITE_CONSTRAINT; } - } - - if( rc==SQLITE_OK ){ - zPath = (const char*)sqlite3_value_text(apVal[2]); - nPath = (int)strlen(zPath); - if( sqlite3_value_type(apVal[4])==SQLITE_NULL ){ - mTime = (sqlite3_int64)time(0); - }else{ - mTime = sqlite3_value_int64(apVal[4]); + if( sqlite3_value_type(apVal[6])!=SQLITE_NULL ){ + zipfileTableErr(pTab, "rawdata must be NULL"); + rc = SQLITE_CONSTRAINT; } - } - if( rc==SQLITE_OK && bIsDir ){ - /* For a directory, check that the last character in the path is a - ** '/'. This appears to be required for compatibility with info-zip - ** (the unzip command on unix). It does not create directories - ** otherwise. */ - if( zPath[nPath-1]!='/' ){ - zFree = sqlite3_mprintf("%s/", zPath); - if( zFree==0 ){ rc = SQLITE_NOMEM; } - zPath = (const char*)zFree; - nPath++; + if( rc==SQLITE_OK ){ + if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){ + /* data=NULL. A directory */ + bIsDir = 1; + }else{ + /* Value specified for "data", and possibly "method". This must be + ** a regular file or a symlink. */ + const u8 *aIn = sqlite3_value_blob(apVal[7]); + int nIn = sqlite3_value_bytes(apVal[7]); + int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; + + iMethod = sqlite3_value_int(apVal[8]); + sz = nIn; + pData = aIn; + nData = nIn; + if( iMethod!=0 && iMethod!=8 ){ + zipfileTableErr(pTab, "unknown compression method: %d", iMethod); + rc = SQLITE_CONSTRAINT; + }else{ + if( bAuto || iMethod ){ + int nCmp; + rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg); + if( rc==SQLITE_OK ){ + if( iMethod || nCmp<nIn ){ + iMethod = 8; + pData = pFree; + nData = nCmp; + } + } + } + iCrc32 = crc32(0, aIn, nIn); + } + } } - } - /* Check that we're not inserting a duplicate entry */ - if( rc==SQLITE_OK ){ - ZipfileEntry *p; - for(p=pTab->pFirstEntry; p; p=p->pNext){ - if( p->bDeleted ) continue; - if( zipfileComparePath(p->zPath, zPath, nPath)==0 ){ - rc = SQLITE_CONSTRAINT; - break; + if( rc==SQLITE_OK ){ + rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg); + } + + if( rc==SQLITE_OK ){ + zPath = (const char*)sqlite3_value_text(apVal[2]); + nPath = (int)strlen(zPath); + mTime = zipfileGetTime(apVal[4]); + } + + if( rc==SQLITE_OK && bIsDir ){ + /* For a directory, check that the last character in the path is a + ** '/'. This appears to be required for compatibility with info-zip + ** (the unzip command on unix). It does not create directories + ** otherwise. */ + if( zPath[nPath-1]!='/' ){ + zFree = sqlite3_mprintf("%s/", zPath); + if( zFree==0 ){ rc = SQLITE_NOMEM; } + zPath = (const char*)zFree; + nPath++; + } + } + + /* Check that we're not inserting a duplicate entry -OR- updating an + ** entry with a path, thereby making it into a duplicate. */ + if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){ + ZipfileEntry *p; + for(p=pTab->pFirstEntry; p; p=p->pNext){ + if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){ + switch( sqlite3_vtab_on_conflict(pTab->db) ){ + case SQLITE_IGNORE: { + goto zipfile_update_done; + } + case SQLITE_REPLACE: { + pOld2 = p; + break; + } + default: { + zipfileTableErr(pTab, "duplicate name: \"%s\"", zPath); + rc = SQLITE_CONSTRAINT; + break; + } + } + break; + } + } + } + + if( rc==SQLITE_OK ){ + /* Create the new CDS record. */ + pNew = zipfileNewEntry(zPath); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; + pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; + pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS; + pNew->cds.iCompression = (u16)iMethod; + zipfileMtimeToDos(&pNew->cds, mTime); + pNew->cds.crc32 = iCrc32; + pNew->cds.szCompressed = nData; + pNew->cds.szUncompressed = (u32)sz; + pNew->cds.iExternalAttr = (mode<<16); + pNew->cds.iOffset = (u32)pTab->szCurrent; + pNew->cds.nFile = (u16)nPath; + pNew->mUnixTime = (u32)mTime; + rc = zipfileAppendEntry(pTab, pNew, pData, nData); + zipfileAddEntry(pTab, pOld, pNew); } } } - if( rc==SQLITE_OK ){ - /* Create the new CDS record. */ - memset(&cds, 0, sizeof(cds)); - cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; - cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; - cds.flags = ZIPFILE_NEWENTRY_FLAGS; - cds.iCompression = (u16)iMethod; - zipfileMtimeToDos(&cds, (u32)mTime); - cds.crc32 = iCrc32; - cds.szCompressed = nData; - cds.szUncompressed = (u32)sz; - cds.iExternalAttr = (mode<<16); - cds.iOffset = (u32)pTab->szCurrent; - pNew = zipfileNewEntry(&cds, zPath, nPath, (u32)mTime); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - zipfileAddEntry(pTab, pOld, pNew); + if( rc==SQLITE_OK && (pOld || pOld2) ){ + ZipfileCsr *pCsr; + for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){ + if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){ + pCsr->pCurrent = pCsr->pCurrent->pNext; + pCsr->bNoop = 1; + } } + + zipfileRemoveEntryFromList(pTab, pOld); + zipfileRemoveEntryFromList(pTab, pOld2); } - /* Append the new header+file to the archive */ - if( rc==SQLITE_OK ){ - rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime); - } - - if( rc!=SQLITE_OK && pOld ){ - pOld->bDeleted = 0; - } +zipfile_update_done: sqlite3_free(pFree); sqlite3_free(zFree); return rc; } +static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){ + u8 *a = aBuf; + zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD); + zipfileWrite16(a, p->iDisk); + zipfileWrite16(a, p->iFirstDisk); + zipfileWrite16(a, p->nEntry); + zipfileWrite16(a, p->nEntryTotal); + zipfileWrite32(a, p->nSize); + zipfileWrite32(a, p->iOffset); + zipfileWrite16(a, 0); /* Size of trailing comment in bytes*/ + + return a-aBuf; +} + static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){ - u8 *aBuf = pTab->aBuffer; - - zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_EOCD); - zipfileWrite16(aBuf, p->iDisk); - zipfileWrite16(aBuf, p->iFirstDisk); - zipfileWrite16(aBuf, p->nEntry); - zipfileWrite16(aBuf, p->nEntryTotal); - zipfileWrite32(aBuf, p->nSize); - zipfileWrite32(aBuf, p->iOffset); - zipfileWrite16(aBuf, 0); /* Size of trailing comment in bytes*/ - - assert( (aBuf-pTab->aBuffer)==22 ); - return zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer)); + int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer); + assert( nBuf==ZIPFILE_EOCD_FIXED_SZ ); + return zipfileAppendData(pTab, pTab->aBuffer, nBuf); } -static void zipfileCleanupTransaction(ZipfileTab *pTab){ - ZipfileEntry *pEntry; - ZipfileEntry *pNext; +/* +** Serialize the CDS structure into buffer aBuf[]. Return the number +** of bytes written. +*/ +static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){ + u8 *a = aBuf; + ZipfileCDS *pCDS = &pEntry->cds; - for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){ - pNext = pEntry->pNext; - sqlite3_free(pEntry); - } - pTab->pFirstEntry = 0; - pTab->pLastEntry = 0; - fclose(pTab->pWriteFd); - pTab->pWriteFd = 0; - pTab->szCurrent = 0; - pTab->szOrig = 0; -} - -static int zipfileBegin(sqlite3_vtab *pVtab){ - ZipfileTab *pTab = (ZipfileTab*)pVtab; - int rc = SQLITE_OK; - - assert( pTab->pWriteFd==0 ); - - /* This table is only writable if a default archive path was specified - ** as part of the CREATE VIRTUAL TABLE statement. */ - if( pTab->zFile==0 ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "zipfile: writing requires a default archive" - ); - return SQLITE_ERROR; + if( pEntry->aExtra==0 ){ + pCDS->nExtra = 9; } - /* Open a write fd on the file. Also load the entire central directory - ** structure into memory. During the transaction any new file data is - ** appended to the archive file, but the central directory is accumulated - ** in main-memory until the transaction is committed. */ - pTab->pWriteFd = fopen(pTab->zFile, "ab+"); - if( pTab->pWriteFd==0 ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "zipfile: failed to open file %s for writing", pTab->zFile - ); - rc = SQLITE_ERROR; + zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS); + zipfileWrite16(a, pCDS->iVersionMadeBy); + zipfileWrite16(a, pCDS->iVersionExtract); + zipfileWrite16(a, pCDS->flags); + zipfileWrite16(a, pCDS->iCompression); + zipfileWrite16(a, pCDS->mTime); + zipfileWrite16(a, pCDS->mDate); + zipfileWrite32(a, pCDS->crc32); + zipfileWrite32(a, pCDS->szCompressed); + zipfileWrite32(a, pCDS->szUncompressed); + assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] ); + zipfileWrite16(a, pCDS->nFile); + zipfileWrite16(a, pCDS->nExtra); + zipfileWrite16(a, pCDS->nComment); + zipfileWrite16(a, pCDS->iDiskStart); + zipfileWrite16(a, pCDS->iInternalAttr); + zipfileWrite32(a, pCDS->iExternalAttr); + zipfileWrite32(a, pCDS->iOffset); + + memcpy(a, pCDS->zFile, pCDS->nFile); + a += pCDS->nFile; + + if( pEntry->aExtra ){ + int n = (int)pCDS->nExtra + (int)pCDS->nComment; + memcpy(a, pEntry->aExtra, n); + a += n; }else{ - fseek(pTab->pWriteFd, 0, SEEK_END); - pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd); - rc = zipfileLoadDirectory(pTab); + assert( pCDS->nExtra==9 ); + zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP); + zipfileWrite16(a, 5); + *a++ = 0x01; + zipfileWrite32(a, pEntry->mUnixTime); } - if( rc!=SQLITE_OK ){ - zipfileCleanupTransaction(pTab); - } - - return rc; + return a-aBuf; } static int zipfileCommit(sqlite3_vtab *pVtab){ @@ -1513,10 +1780,10 @@ ZipfileEOCD eocd; int nEntry = 0; - /* Write out all undeleted entries */ + /* Write out all entries */ for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){ - if( p->bDeleted ) continue; - rc = zipfileAppendData(pTab, p->aCdsEntry, p->nCdsEntry); + int n = zipfileSerializeCDS(p, pTab->aBuffer); + rc = zipfileAppendData(pTab, pTab->aBuffer, n); nEntry++; } @@ -1557,7 +1824,7 @@ pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0])); if( pCsr ){ - ZipfileCDS *p = &pCsr->cds; + ZipfileCDS *p = &pCsr->pCurrent->cds; char *zRes = sqlite3_mprintf("{" "\"version-made-by\" : %u, " "\"version-to-extract\" : %u, " @@ -1594,7 +1861,6 @@ } } - /* ** xFindFunction method. */ @@ -1605,18 +1871,259 @@ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ void **ppArg /* OUT: User data for *pxFunc */ ){ - if( nArg>0 ){ - if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ - *pxFunc = zipfileFunctionCds; - *ppArg = (void*)pVtab; - return 1; + if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ + *pxFunc = zipfileFunctionCds; + *ppArg = (void*)pVtab; + return 1; + } + return 0; +} + +typedef struct ZipfileBuffer ZipfileBuffer; +struct ZipfileBuffer { + u8 *a; /* Pointer to buffer */ + int n; /* Size of buffer in bytes */ + int nAlloc; /* Byte allocated at a[] */ +}; + +typedef struct ZipfileCtx ZipfileCtx; +struct ZipfileCtx { + int nEntry; + ZipfileBuffer body; + ZipfileBuffer cds; +}; + +static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){ + if( pBuf->n+nByte>pBuf->nAlloc ){ + u8 *aNew; + int nNew = pBuf->n ? pBuf->n*2 : 512; + int nReq = pBuf->n + nByte; + + while( nNew<nReq ) nNew = nNew*2; + aNew = sqlite3_realloc(pBuf->a, nNew); + if( aNew==0 ) return SQLITE_NOMEM; + pBuf->a = aNew; + pBuf->nAlloc = nNew; + } + return SQLITE_OK; +} + +/* +** xStep() callback for the zipfile() aggregate. This can be called in +** any of the following ways: +** +** SELECT zipfile(name,data) ... +** SELECT zipfile(name,mode,mtime,data) ... +** SELECT zipfile(name,mode,mtime,data,method) ... +*/ +void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){ + ZipfileCtx *p; /* Aggregate function context */ + ZipfileEntry e; /* New entry to add to zip archive */ + + sqlite3_value *pName = 0; + sqlite3_value *pMode = 0; + sqlite3_value *pMtime = 0; + sqlite3_value *pData = 0; + sqlite3_value *pMethod = 0; + + int bIsDir = 0; + u32 mode; + int rc = SQLITE_OK; + char *zErr = 0; + + int iMethod = -1; /* Compression method to use (0 or 8) */ + + const u8 *aData = 0; /* Possibly compressed data for new entry */ + int nData = 0; /* Size of aData[] in bytes */ + int szUncompressed = 0; /* Size of data before compression */ + u8 *aFree = 0; /* Free this before returning */ + u32 iCrc32 = 0; /* crc32 of uncompressed data */ + + char *zName = 0; /* Path (name) of new entry */ + int nName = 0; /* Size of zName in bytes */ + char *zFree = 0; /* Free this before returning */ + int nByte; + + memset(&e, 0, sizeof(e)); + p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); + if( p==0 ) return; + + /* Martial the arguments into stack variables */ + if( nVal!=2 && nVal!=4 && nVal!=5 ){ + zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()"); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + pName = apVal[0]; + if( nVal==2 ){ + pData = apVal[1]; + }else{ + pMode = apVal[1]; + pMtime = apVal[2]; + pData = apVal[3]; + if( nVal==5 ){ + pMethod = apVal[4]; } } - return 0; + /* Check that the 'name' parameter looks ok. */ + zName = (char*)sqlite3_value_text(pName); + nName = sqlite3_value_bytes(pName); + if( zName==0 ){ + zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL"); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + + /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use + ** deflate compression) or NULL (choose automatically). */ + if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){ + iMethod = (int)sqlite3_value_int64(pMethod); + if( iMethod!=0 && iMethod!=8 ){ + zErr = sqlite3_mprintf("illegal method value: %d", iMethod); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + } + + /* Now inspect the data. If this is NULL, then the new entry must be a + ** directory. Otherwise, figure out whether or not the data should + ** be deflated or simply stored in the zip archive. */ + if( sqlite3_value_type(pData)==SQLITE_NULL ){ + bIsDir = 1; + iMethod = 0; + }else{ + aData = sqlite3_value_blob(pData); + szUncompressed = nData = sqlite3_value_bytes(pData); + iCrc32 = crc32(0, aData, nData); + if( iMethod<0 || iMethod==8 ){ + int nOut = 0; + rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr); + if( rc!=SQLITE_OK ){ + goto zipfile_step_out; + } + if( iMethod==8 || nOut<nData ){ + aData = aFree; + nData = nOut; + iMethod = 8; + }else{ + iMethod = 0; + } + } + } + + /* Decode the "mode" argument. */ + rc = zipfileGetMode(pMode, bIsDir, &mode, &zErr); + if( rc ) goto zipfile_step_out; + + /* Decode the "mtime" argument. */ + e.mUnixTime = zipfileGetTime(pMtime); + + /* If this is a directory entry, ensure that there is exactly one '/' + ** at the end of the path. Or, if this is not a directory and the path + ** ends in '/' it is an error. */ + if( bIsDir==0 ){ + if( zName[nName-1]=='/' ){ + zErr = sqlite3_mprintf("non-directory name must not end with /"); + rc = SQLITE_ERROR; + goto zipfile_step_out; + } + }else{ + if( zName[nName-1]!='/' ){ + zName = zFree = sqlite3_mprintf("%s/", zName); + nName++; + if( zName==0 ){ + rc = SQLITE_NOMEM; + goto zipfile_step_out; + } + }else{ + while( nName>1 && zName[nName-2]=='/' ) nName--; + } + } + + /* Assemble the ZipfileEntry object for the new zip archive entry */ + e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; + e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; + e.cds.flags = ZIPFILE_NEWENTRY_FLAGS; + e.cds.iCompression = (u16)iMethod; + zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime); + e.cds.crc32 = iCrc32; + e.cds.szCompressed = nData; + e.cds.szUncompressed = szUncompressed; + e.cds.iExternalAttr = (mode<<16); + e.cds.iOffset = p->body.n; + e.cds.nFile = (u16)nName; + e.cds.zFile = zName; + + /* Append the LFH to the body of the new archive */ + nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9; + if( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out; + p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]); + + /* Append the data to the body of the new archive */ + if( nData>0 ){ + if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out; + memcpy(&p->body.a[p->body.n], aData, nData); + p->body.n += nData; + } + + /* Append the CDS record to the directory of the new archive */ + nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9; + if( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out; + p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]); + + /* Increment the count of entries in the archive */ + p->nEntry++; + + zipfile_step_out: + sqlite3_free(aFree); + sqlite3_free(zFree); + if( rc ){ + if( zErr ){ + sqlite3_result_error(pCtx, zErr, -1); + }else{ + sqlite3_result_error_code(pCtx, rc); + } + } + sqlite3_free(zErr); } /* +** xFinalize() callback for zipfile aggregate function. +*/ +void zipfileFinal(sqlite3_context *pCtx){ + ZipfileCtx *p; + ZipfileEOCD eocd; + int nZip; + u8 *aZip; + + p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx)); + if( p==0 ) return; + if( p->nEntry>0 ){ + memset(&eocd, 0, sizeof(eocd)); + eocd.nEntry = (u16)p->nEntry; + eocd.nEntryTotal = (u16)p->nEntry; + eocd.nSize = p->cds.n; + eocd.iOffset = p->body.n; + + nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ; + aZip = (u8*)sqlite3_malloc(nZip); + if( aZip==0 ){ + sqlite3_result_error_nomem(pCtx); + }else{ + memcpy(aZip, p->body.a, p->body.n); + memcpy(&aZip[p->body.n], p->cds.a, p->cds.n); + zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]); + sqlite3_result_blob(pCtx, aZip, nZip, zipfileFree); + } + } + + sqlite3_free(p->body.a); + sqlite3_free(p->cds.a); +} + + +/* ** Register the "zipfile" virtual table. */ static int zipfileRegister(sqlite3 *db){ @@ -1633,7 +2140,7 @@ zipfileNext, /* xNext - advance a cursor */ zipfileEof, /* xEof - check for end of scan */ zipfileColumn, /* xColumn - read data */ - zipfileRowid, /* xRowid - read data */ + 0, /* xRowid - read data */ zipfileUpdate, /* xUpdate */ zipfileBegin, /* xBegin */ 0, /* xSync */ @@ -1644,8 +2151,11 @@ }; int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0); + if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1); if( rc==SQLITE_OK ){ - rc = sqlite3_overload_function(db, "zipfile_cds", -1); + rc = sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0, + zipfileStep, zipfileFinal + ); } return rc; }
diff --git a/third_party/sqlite/src/ext/misc/zorder.c b/third_party/sqlite/src/ext/misc/zorder.c new file mode 100644 index 0000000..f9f2f9b --- /dev/null +++ b/third_party/sqlite/src/ext/misc/zorder.c
@@ -0,0 +1,102 @@ +/* +** 2018-02-09 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** SQL functions for z-order (Morton code) transformations. +** +** zorder(X0,X0,..,xN) Generate an N+1 dimension Morton code +** +** unzorder(Z,N,I) Extract the I-th dimension from N-dimensional +** Morton code Z. +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include <assert.h> +#include <string.h> + +/* +** Functions: zorder(X0,X1,....) +** +** Convert integers X0, X1, ... into morton code. +** +** The output is a signed 64-bit integer. If any argument is too large, +** an error is thrown. +*/ +static void zorderFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_int64 z, x[63]; + int i, j; + z = 0; + for(i=0; i<argc; i++){ + x[i] = sqlite3_value_int64(argv[i]); + } + if( argc>0 ){ + for(i=0; i<63; i++){ + j = i%argc; + z |= (x[j]&1)<<i; + x[j] >>= 1; + } + } + sqlite3_result_int64(context, z); + for(i=0; i<argc; i++){ + if( x[i] ){ + sqlite3_result_error(context, "parameter too large", -1); + } + } +} + + +/* +** Functions: unzorder(Z,N,I) +** +** Assuming that Z is an N-dimensional Morton code, extract the I-th +** dimension. +*/ +static void unzorderFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_int64 z, n, i, x; + int j, k; + z = sqlite3_value_int64(argv[0]); + n = sqlite3_value_int64(argv[1]); + i = sqlite3_value_int64(argv[2]); + x = 0; + for(k=0, j=i; j<63; j+=n, k++){ + x |= ((z>>j)&1)<<k; + } + sqlite3_result_int64(context, x); +} + + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_zorder_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + rc = sqlite3_create_function(db, "zorder", -1, SQLITE_UTF8, 0, + zorderFunc, 0, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "unzorder", 3, SQLITE_UTF8, 0, + unzorderFunc, 0, 0); + } + return rc; +}
diff --git a/third_party/sqlite/src/ext/rbu/rbu_common.tcl b/third_party/sqlite/src/ext/rbu/rbu_common.tcl index a58c3aa..b216fe0 100644 --- a/third_party/sqlite/src/ext/rbu/rbu_common.tcl +++ b/third_party/sqlite/src/ext/rbu/rbu_common.tcl
@@ -71,6 +71,7 @@ } proc do_rbu_vacuum_test {tn step} { + forcedelete state.db uplevel [list do_test $tn.1 { if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db } while 1 {
diff --git a/third_party/sqlite/src/ext/rbu/rbucollate.test b/third_party/sqlite/src/ext/rbu/rbucollate.test new file mode 100644 index 0000000..a9c3b4d --- /dev/null +++ b/third_party/sqlite/src/ext/rbu/rbucollate.test
@@ -0,0 +1,63 @@ +# 2018 March 22 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +source [file join [file dirname [info script]] rbu_common.tcl] +set ::testprefix rbucollate + +ifcapable !icu_collations { + finish_test + return +} + +db close +sqlite3_shutdown +sqlite3_config_uri 1 +reset_db + +# Create a simple RBU database. That expects to write to a table: +# +# CREATE TABLE t1(a PRIMARY KEY, b, c); +# +proc create_rbu1 {filename} { + forcedelete $filename + sqlite3 rbu1 $filename + rbu1 eval { + CREATE TABLE data_t1(a, b, c, rbu_control); + INSERT INTO data_t1 VALUES('a', 'one', 1, 0); + INSERT INTO data_t1 VALUES('b', 'two', 2, 0); + INSERT INTO data_t1 VALUES('c', 'three', 3, 0); + } + rbu1 close + return $filename +} + +do_execsql_test 1.0 { + SELECT icu_load_collation('en_US', 'my-collate'); + CREATE TABLE t1(a COLLATE "my-collate" PRIMARY KEY, b, c); +} {{}} + +do_test 1.2 { + create_rbu1 testrbu.db + sqlite3rbu rbu test.db testrbu.db + rbu dbMain_eval { SELECT icu_load_collation('en_US', 'my-collate') } + rbu dbRbu_eval { SELECT icu_load_collation('en_US', 'my-collate') } + while 1 { + set rc [rbu step] + if {$rc!="SQLITE_OK"} break + } + rbu close + db eval { SELECT * FROM t1 } +} {a one 1 b two 2 c three 3} + +#forcedelete testrbu.db +finish_test +
diff --git a/third_party/sqlite/src/ext/rbu/sqlite3rbu.c b/third_party/sqlite/src/ext/rbu/sqlite3rbu.c index 361d335..ebae212 100644 --- a/third_party/sqlite/src/ext/rbu/sqlite3rbu.c +++ b/third_party/sqlite/src/ext/rbu/sqlite3rbu.c
@@ -1806,7 +1806,7 @@ int iCid = sqlite3_column_int(pXInfo, 1); int bDesc = sqlite3_column_int(pXInfo, 3); const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4); - zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma, + zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma, iCid, pIter->azTblType[iCid], zCollate ); zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":""); @@ -1867,7 +1867,7 @@ ** "PRIMARY KEY" to the imposter table column declaration. */ zPk = "PRIMARY KEY "; } - zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %s%s", + zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s", zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl, (pIter->abNotNull[iCol] ? " NOT NULL" : "") );
diff --git a/third_party/sqlite/src/ext/rbu/test_rbu.c b/third_party/sqlite/src/ext/rbu/test_rbu.c index 0973fba8..1c1c9b4 100644 --- a/third_party/sqlite/src/ext/rbu/test_rbu.c +++ b/third_party/sqlite/src/ext/rbu/test_rbu.c
@@ -81,6 +81,7 @@ {"close_no_error", 2, ""}, /* 9 */ {"temp_size_limit", 3, "LIMIT"}, /* 10 */ {"temp_size", 2, ""}, /* 11 */ + {"dbRbu_eval", 3, "SQL"}, /* 12 */ {0,0,0} }; int iCmd; @@ -146,8 +147,9 @@ break; } - case 4: /* dbMain_eval */ { - sqlite3 *db = sqlite3rbu_db(pRbu, 0); + case 12: /* dbRbu_eval */ + case 4: /* dbMain_eval */ { + sqlite3 *db = sqlite3rbu_db(pRbu, (iCmd==12)); int rc = sqlite3_exec(db, Tcl_GetString(objv[2]), 0, 0, 0); if( rc!=SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(db), -1));
diff --git a/third_party/sqlite/src/ext/repair/sqlite3_checker.tcl b/third_party/sqlite/src/ext/repair/sqlite3_checker.tcl index 8396872..f708d7f 100644 --- a/third_party/sqlite/src/ext/repair/sqlite3_checker.tcl +++ b/third_party/sqlite/src/ext/repair/sqlite3_checker.tcl
@@ -220,7 +220,9 @@ if {$bFreelistCheck || $bAll} { puts -nonewline "freelist-check: " flush stdout + db eval BEGIN puts [db one {SELECT checkfreelist('main')}] + db eval END } if {$bSummary} { set scale 0
diff --git a/third_party/sqlite/src/ext/rtree/rtree.c b/third_party/sqlite/src/ext/rtree/rtree.c index 2f98a160..ba40ddd 100644 --- a/third_party/sqlite/src/ext/rtree/rtree.c +++ b/third_party/sqlite/src/ext/rtree/rtree.c
@@ -785,6 +785,7 @@ sqlite3_step(p); pNode->isDirty = 0; rc = sqlite3_reset(p); + sqlite3_bind_null(p, 2); if( pNode->iNode==0 && rc==SQLITE_OK ){ pNode->iNode = sqlite3_last_insert_rowid(pRtree->db); nodeHashInsert(pRtree, pNode);
diff --git a/third_party/sqlite/src/ext/rtree/rtree1.test b/third_party/sqlite/src/ext/rtree/rtree1.test index 67cdac4..c4498d3 100644 --- a/third_party/sqlite/src/ext/rtree/rtree1.test +++ b/third_party/sqlite/src/ext/rtree/rtree1.test
@@ -609,4 +609,5 @@ COMMIT; } +expand_all_sql db finish_test
diff --git a/third_party/sqlite/src/ext/rtree/rtree4.test b/third_party/sqlite/src/ext/rtree/rtree4.test index c10098f..0b4b0cb 100644 --- a/third_party/sqlite/src/ext/rtree/rtree4.test +++ b/third_party/sqlite/src/ext/rtree/rtree4.test
@@ -250,4 +250,5 @@ do_rtree_integrity_test rtree4-$nDim.3 rx } +expand_all_sql db finish_test
diff --git a/third_party/sqlite/src/ext/rtree/rtree5.test b/third_party/sqlite/src/ext/rtree/rtree5.test index fca1be47..6d47a72a 100644 --- a/third_party/sqlite/src/ext/rtree/rtree5.test +++ b/third_party/sqlite/src/ext/rtree/rtree5.test
@@ -79,4 +79,5 @@ } {2 2147483643 2147483647 -2147483648 -2147483643} do_rtree_integrity_test rtree5-1.14 t1 +expand_all_sql db finish_test
diff --git a/third_party/sqlite/src/ext/rtree/rtree6.test b/third_party/sqlite/src/ext/rtree/rtree6.test index 994a460..9d8f673f2 100644 --- a/third_party/sqlite/src/ext/rtree/rtree6.test +++ b/third_party/sqlite/src/ext/rtree/rtree6.test
@@ -158,5 +158,5 @@ x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>1.1 } {} - +expand_all_sql db finish_test
diff --git a/third_party/sqlite/src/ext/rtree/rtreeG.test b/third_party/sqlite/src/ext/rtree/rtreeG.test index 5ede9c0..ba4f667 100644 --- a/third_party/sqlite/src/ext/rtree/rtreeG.test +++ b/third_party/sqlite/src/ext/rtree/rtreeG.test
@@ -59,6 +59,7 @@ set ::log } {} +expand_all_sql db db close sqlite3_shutdown test_sqlite3_log
diff --git a/third_party/sqlite/src/ext/session/session1.test b/third_party/sqlite/src/ext/session/session1.test index 17d2b50..f3556167 100644 --- a/third_party/sqlite/src/ext/session/session1.test +++ b/third_party/sqlite/src/ext/session/session1.test
@@ -612,6 +612,49 @@ {UPDATE t1 0 X.. {i 3 {} {} i 3} {{} {} {} {} t one}} } +#------------------------------------------------------------------------- +# Test that no savepoint is used if -nosavepoint is specified. +# +do_execsql_test $tn.13.1 { + CREATE TABLE x1(a INTEGER PRIMARY KEY, b)%WR%; +} +do_test $tn.13.2 { + execsql BEGIN + set C [changeset_from_sql { + INSERT INTO x1 VALUES(1, 'one'); + INSERT INTO x1 VALUES(2, 'two'); + INSERT INTO x1 VALUES(3, 'three'); + }] + execsql ROLLBACK + execsql { + INSERT INTO x1 VALUES(1, 'i'); + INSERT INTO x1 VALUES(2, 'ii'); + INSERT INTO x1 VALUES(3, 'iii'); + } +} {} + +proc xConflict {args} { + set ret [lindex $::CONFLICT_HANDLERS 0] + set ::CONFLICT_HANDLERS [lrange $::CONFLICT_HANDLERS 1 end] + set ret +} +do_test $tn.13.3 { + set CONFLICT_HANDLERS [list REPLACE REPLACE ABORT] + execsql BEGIN + catch { sqlite3changeset_apply_v2 db $C xConflict } msg + execsql { + SELECT * FROM x1 + } +} {1 i 2 ii 3 iii} +do_test $tn.13.3 { + set CONFLICT_HANDLERS [list REPLACE REPLACE ABORT] + execsql ROLLBACK + execsql BEGIN + catch { sqlite3changeset_apply_v2 -nosavepoint db $C xConflict } msg + execsql { SELECT * FROM x1 } +} {1 one 2 two 3 iii} +execsql ROLLBACK + }] }
diff --git a/third_party/sqlite/src/ext/session/session4.test b/third_party/sqlite/src/ext/session/session4.test index 7e5b6c7..20b506c 100644 --- a/third_party/sqlite/src/ext/session/session4.test +++ b/third_party/sqlite/src/ext/session/session4.test
@@ -11,6 +11,8 @@ # This file implements regression tests for the session module. # +package require Tcl 8.6 + if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } @@ -64,4 +66,81 @@ list [catch { sqlite3changeset_apply db $x xConflict } msg] $msg } {1 SQLITE_CORRUPT} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 2.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d); + CREATE TABLE t2(e TEXT PRIMARY KEY NOT NULL,f,g); + CREATE TABLE t3(w REAL PRIMARY KEY NOT NULL,x,y); + CREATE TABLE t4(z PRIMARY KEY) WITHOUT ROWID; +} + +foreach {tn blob} { + 1 54010174340012000000 + 2 54fefe8bcb0012000300 + 3 5480809280808001017434001200fb + 4 50af9c939c9c9cb09c9c6400b09c9c6400 + 5 12000300 + 6 09847304 + 7 5401017434001208 + 8 54010174340012fc0386868600 + 9 54010174340012FC0386868600 + 10 548894FEFE + 11 54010171340012E703ABFA7433FD1200 + 12 540101743400120003FFED00010000000000000002120002400C00000000000054040100000074310017000100000000000000050100000000000000030100000000000000040000010000000000000004010000000000000003001700010000000000000007030378797A01000000000000000F000001000000000000000F030378797A005403010000743200090003037838790100000000800000000200000000000000000900030378327902400C0000000000000304666F7572 + 13 540101743400120003001200010000000000000002120002400C0000000000005404010000007431001700010000000000000005010000000000000003010000000000000004000001000000000000000401000000000000000300170001000000000000000703FC87797A01000000000000000F000001000000000000000F030378797A005403010000743200090003037838790100000000800000000200000000000000000900030378327902400C0000000000000304666F7572 + 14 540101743400120003001200010000000000000002120002400C00000000000054040100000074310017000100000000000000050100000000000000030100000000000000040000010000000000000004010000000000000003001700010000000000000007030378797A01000000000000000F000001000000000000000F03FC87797A005403010000743200090003037838790100000000800000000200000000000000000900030378327902400C0000000000000304666F7572 + 15 540101743400120003001200010000000000000002120002400C00000000000054040100000074310017000100000000000000050100000000000000030100000000000000040000010000000000000004010000000000000003001700010000000000000007030378797A01000000000000000F000001000000000000000F030378797A005403010000743200090003FC8738790100000000800000000200000000000000000900030378327902400C0000000000000304666F7572 + 16 540101743400120003001200010000000000000002120002400C00000000000054040100000074310017000100000000000000050100000000000000030100000000000000040000010000000000000004010000000000000003001700010000000000000007030378797A01000000000000000F000001000000000000000F030378797A00540301000074320009000303783879010000000080000000020000000000000000090003FC87327902400C0000000000000304666F7572 + 17 540101743400120003FFE3000412F7010000E600000000021202120002400C0000000000005B0401000000743100171C0304646F750002400C000000000000540401000000D3310017000100000000000000050100000000000378797A405403000002F10100000100000000000004090001000100000007030378797A0100000000000D0007000001000000002300000F1B0378797A405403013900743200090003038C3879010000000000000000000002120002400C0000000000005B0401000000743117170003047C5E00FF + 18 54010174340012000300120001000000E6FF100000120002401E00000000000054040100000074310017000100040000010000000000000004FFFF7FFF0000000000010000010000001000000007030378797A01000000000000000F000000000000FA0304666F7572 + 19 540101743400120003001200010000000000000002121B02400C00000000000054040000000074310017000100000000000000050100000000000000030100000000000000040000010000000000000004010000000000000003001700010000000000000007030378817A01000000000000000F000001000000000100000F030378797A005403010000743200090003FFE809000303780000000000000304666F7572 + 20 5401017D3400120003001200010000000000000002120002400CFC00000000005404010000007431001700010000000000000005010000000000000003010000000000000004000001000000000000000401000000000000000300170001000000000000000703FFFF797A01000000000000000F000001000000000000000F030378797A005403010000743200090003037838790100000000800000000200000000000000000900030378326C02400C0000000000000304666F7572 + 21 5401017434001200030012000100FFE20000000002120002400C00000000000054040100E0007431001700010000E99D000000020000000003FFE70009000303783279020004000001030000000000002117000003001700012701000100000000743100000100000000008000090003037F387901000000008000000002000000000400000009005303010A00FF7FFFFF00000000000304664F6572 + 22 540101743400120003FFFF7FFF0000000000000002120002400C00000000000054040100000074310017000100000000000000050100000000000000030100010000000000000000040000010000000000000004010000000000000003001700010000000000000007030378797A01000000000000000F000001000000000000000F030378797A005403010000743200090003037838790100000000800000000200000000000000000900030378327902400C0000000000000304666F7572 + 23 540101742700120100120003F5FF0300 + 24 5401017434E312540101743400120003FFFC00 + 25 540101743400540101743D3D3D3D3D3D3D3D3D3D3D3D3D3400120003FFED000300 + 26 5401017446EA5301743D1D3D3D01743D1D3D3DCF3D3D3D1A3D3D3D3D3400120003FFFF000000 + 27 540101743400540101743D3D3D3D3D3D3D3D3D3D251000120003FF81000000000000 + 28 540101340012000397FF3D7F3D3400120003001200540101743D3D3D3D3D3D393D3D3D12000300 + 29 500174340050010F74340012000300120003FFE5 + 30 5004007233E900177FEF0054257F0002EF001200031E12000300 + 31 5001015001015252525250010174340012EF039A9A0100E351525D52525252525252525252525252525252525250010174340012EF039A0100009A9A9A9A9A9BA3B200120003010040743400 + 32 5401017400123400120003FFFC00 + 33 540101743400120003001200010000000000004002120002400C0000000000005404010000007431001700010000000000000005010000000000000003010000000000000004000001000000000000000401000000000000000300170001000000000000000703FC87797A01000000000000000F000001000000000000000F030378797A005403010000743200090003037838790100000000800000000200000000000000000900030378327902400C0000000000000304666F7572 + 34 54040100000074310017000100000002000015050100000000000000030100000000140000040000010000000000000004010000000000000003001700010000000000000007030378797A01000000000000000F000001000000000000000F030378797A0054030100007432000900030378387901000000008E000000020000000000000000090003FFFF000002400C0000000000000304666F7572 + 35 540101743400120003001200010000000000000002120002400C00000000000050060100000074310017000100000000000000050100000000000000030100000003001700010000666F7572 + 36 540101743400120003001200010000000000000002120002400C00000000000050050100000074310017000100000000000000050100000000000000030100000003001700010000666F7572 + 37 540101743400120003001200010000000000000002120002400C00000000000050040100008074310017000100000000000000050100000000000000030100000003001700010000666F7572 + 38 540101743400120003001200010000000000000002120002400C00000000000050040100000074310017000000000000000000050100000000000000030100000003001700010000666F7572 + 39 540101743400120003001200010000000000000002120002400C00000000000050040100018074310017000100000000000000050100000000000000030100000003001700010000666F7572 + 40 540101743400120003001200010000000000000002120002400C0000000000005004FEFFFFFF74310017000100000000000000050100000000000000030100000003001700010000666F7572 + 41 540101743400120003001200010000000000000002120002400C00000000000050040100000074310017000004000000000000050100000000000000030100000003001700010000666F7572 + 42 540101743400120003001200010000000000000002120002400C0000000000005005FFFF050074310017000100000000000000050100000000000000030100000003001700010000666F7572 + 43 540101743400120003001200010000000000000002120002400C000000000000500401006E0074310017000300000000001221050100000000000000030100000003001700010000666F7572 + 44 540101743400120003001200010000000000020000120002400C00000000000050050100000074310017000100000000000000050100004000000000030100000025001700010000666F7572 + 45 540101743400120003001200010000000000ECFF02120002400C000000000000500401F9FF00743100170001000000000000000500E1000000000000030100000003000000000000666F7572 + 46 54010174340B0B0B0B0B0B0B0B0B0B0B0B0B0B0B00120003001200010000000000000002120002400C00000000000050040100000074310017010000000000000000050100FFE900000000030100000003007F00000000666F7572 + 47 54010103001200010000000000020002120002400C0000000000005004010000F374310017000100000000000000050100000000000000030100000003001700010000666F8E72 + 48 540101743400120003001200010000000000000002120002400C00000000000050030012000174310017000700000000000000050100002000000001000000000003001700010000666F7572 + 49 540101743400120004001200010000000000000002120002400C0000000000005004010000FC733100170001000000000000000501000000000000000301000000F6FF17000100007C6F7572 + 50 54010174FFDDFF8003001200010000100000000002120002400C000000000000500401000000743100170000000005010000000000000000000003010072 + 51 540101743200120003001200010000000000000002120002400C00000000000050040100001074310017000000000003010000120300170100000000000000050100000000000000030100000003001700010000666F7572 + 52 540101745401017434001200010000000000001702120002400C00000000000050040100001A74310017000100000000000100000100000000000000030100000003001700010000666F7572 + 53 540101743400120003001200010000000000000002120002400C000000000000500401000000743100170001000002400C00000000000050040110000074310017000000000000050100000000000000030100000003001700010000666F7572 + 54 540101743400120003001200010000000000000002120002400C000000000002120002400C00000000000050040100000074310017FF0050040100000074310017FF7F00000000000000050100000000000000030100000003001700010000666F7572 + 55 540101743400120003001200010000000000000002120002400C00000000000050040100000074310017000100010080000001000000020003010100000300170100000003001700010000666F7572 + 56 5487ffffff7f +} { + do_test 2.$tn { + set changeset [binary decode hex $blob] +#set fd [open x.change w+] +#fconfigure $fd -encoding binary -translation binary +#puts -nonewline $fd $changeset +#close $fd + list [catch { sqlite3changeset_apply db $changeset xConflict } msg] $msg + } {1 SQLITE_CORRUPT} +} + finish_test
diff --git a/third_party/sqlite/src/ext/session/sessionG.test b/third_party/sqlite/src/ext/session/sessionG.test index 51829b5..465e330 100644 --- a/third_party/sqlite/src/ext/session/sessionG.test +++ b/third_party/sqlite/src/ext/session/sessionG.test
@@ -204,7 +204,47 @@ } } {1 2 3 7 8 9} +#------------------------------------------------------------------------- +reset_db +db func number_name number_name +do_execsql_test 6.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE UNIQUE INDEX t1b ON t1(b); + WITH s(i) AS ( + SELECT 1 + UNION ALL + SELECT i+1 FROM s WHERE i<1000 + ) + INSERT INTO t1 SELECT i, number_name(i) FROM s; +} + +do_test 6.1 { + db eval BEGIN + set ::C [changeset_from_sql { + DELETE FROM t1; + WITH s(i) AS ( + SELECT 1 + UNION ALL + SELECT i+1 FROM s WHERE i<1000 + ) + INSERT INTO t1 SELECT i, number_name(i+1) FROM s; + }] + db eval ROLLBACK + execsql { SELECT count(*) FROM t1 WHERE number_name(a) IS NOT b } +} {0} + +proc xConflict {args} { exit ; return "OMIT" } +do_test 6.2 { + sqlite3changeset_apply db $C xConflict +} {} + +do_execsql_test 6.3 { SELECT count(*) FROM t1; } {1000} +do_execsql_test 6.4 { + SELECT count(*) FROM t1 WHERE number_name(a+1) IS NOT b; +} {0} + +# db eval { SELECT * FROM t1 } { puts "$a || $b" } finish_test
diff --git a/third_party/sqlite/src/ext/session/session_common.tcl b/third_party/sqlite/src/ext/session/session_common.tcl index ebb3aa41..350cba6e 100644 --- a/third_party/sqlite/src/ext/session/session_common.tcl +++ b/third_party/sqlite/src/ext/session/session_common.tcl
@@ -169,3 +169,30 @@ sqlite3session_foreach elem $c { lappend list $elem } lsort $list } + +set ones {zero one two three four five six seven eight nine + ten eleven twelve thirteen fourteen fifteen sixteen seventeen + eighteen nineteen} +set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety} +proc number_name {n} { + if {$n>=1000} { + set txt "[number_name [expr {$n/1000}]] thousand" + set n [expr {$n%1000}] + } else { + set txt {} + } + if {$n>=100} { + append txt " [lindex $::ones [expr {$n/100}]] hundred" + set n [expr {$n%100}] + } + if {$n>=20} { + append txt " [lindex $::tens [expr {$n/10}]]" + set n [expr {$n%10}] + } + if {$n>0} { + append txt " [lindex $::ones $n]" + } + set txt [string trim $txt] + if {$txt==""} {set txt zero} + return $txt +}
diff --git a/third_party/sqlite/src/ext/session/sessionfault2.test b/third_party/sqlite/src/ext/session/sessionfault2.test index b250c8d..b91fb831 100644 --- a/third_party/sqlite/src/ext/session/sessionfault2.test +++ b/third_party/sqlite/src/ext/session/sessionfault2.test
@@ -20,6 +20,8 @@ ifcapable !session {finish_test; return} set testprefix sessionfault2 +if 1 { + do_execsql_test 1.0.0 { CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES(1, 1); @@ -103,5 +105,181 @@ faultsim_integrity_check } +#------------------------------------------------------------------------- +# OOM when collecting and apply a changeset that uses sqlite_stat1. +# +reset_db +forcedelete test.db2 +sqlite3 db2 test.db2 +do_common_sql { + CREATE TABLE t1(a PRIMARY KEY, b UNIQUE, c); + CREATE INDEX i1 ON t1(c); + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t1 VALUES(7, 8, 9); + CREATE TABLE t2(a, b, c); + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); + ANALYZE; +} +faultsim_save_and_close +db2 close + +do_faultsim_test 1.1 -faults oom-* -prep { + catch {db2 close} + catch {db close} + faultsim_restore_and_reopen + sqlite3 db2 test.db2 +} -body { + do_then_apply_sql { + INSERT INTO sqlite_stat1 VALUES('x', 'y', 45); + UPDATE sqlite_stat1 SET stat = 123 WHERE tbl='t1' AND idx='i1'; + UPDATE sqlite_stat1 SET stat = 456 WHERE tbl='t2'; + } +} -test { + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} + faultsim_integrity_check + if {$testrc==0} { compare_db db db2 } +} + +#------------------------------------------------------------------------- +# OOM when collecting and using a rebase changeset. +# +reset_db +do_execsql_test 2.0 { + CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c)); + CREATE TABLE t4(x PRIMARY KEY, y, z); + + INSERT INTO t3 VALUES(1, 2, 3); + INSERT INTO t3 VALUES(4, 2, 5); + INSERT INTO t3 VALUES(7, 2, 9); + + INSERT INTO t4 VALUES('a', 'b', 'c'); + INSERT INTO t4 VALUES('d', 'e', 'f'); + INSERT INTO t4 VALUES('g', 'h', 'i'); +} +faultsim_save_and_close +db2 close + +proc xConflict {ret args} { return $ret } + +do_test 2.1 { + faultsim_restore_and_reopen + set C1 [changeset_from_sql { + INSERT INTO t3 VALUES(10, 11, 12); + UPDATE t4 SET y='j' WHERE x='g'; + DELETE FROM t4 WHERE x='a'; + }] + + faultsim_restore_and_reopen + set C2 [changeset_from_sql { + INSERT INTO t3 VALUES(1000, 11, 12); + DELETE FROM t4 WHERE x='g'; + }] + + faultsim_restore_and_reopen + sqlite3changeset_apply db $C1 [list xConflict OMIT] + faultsim_save_and_close +} {} + +do_faultsim_test 2.2 -faults oom* -prep { + catch {db2 close} + catch {db close} + faultsim_restore_and_reopen + sqlite3 db2 test.db2 +} -body { + set rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]] + set {} {} +} -test { + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} +} +do_faultsim_test 2.3 -faults oom* -prep { + catch {db2 close} + catch {db close} + faultsim_restore_and_reopen + sqlite3 db2 test.db2 +} -body { + set rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict REPLACE]] + set {} {} +} -test { + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} +} +do_faultsim_test 2.4 -faults oom* -prep { + catch {db2 close} + catch {db close} + faultsim_restore_and_reopen + set ::rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict REPLACE]] +} -body { + sqlite3rebaser_create R + R configure $::rebase + R rebase $::C1 + set {} {} +} -test { + catch { R delete } + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} +} +do_faultsim_test 2.5 -faults oom* -prep { + catch {db2 close} + catch {db close} + faultsim_restore_and_reopen + set ::rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]] +} -body { + sqlite3rebaser_create R + R configure $::rebase + R rebase $::C1 + set {} {} +} -test { + catch { R delete } + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} +} + +} + +reset_db +do_execsql_test 3.0 { + CREATE TABLE t1(x PRIMARY KEY, y, z); + INSERT INTO t1 VALUES(3, 1, 4); + INSERT INTO t1 VALUES(1, 5, 9); +} +faultsim_save_and_close + +proc xConflict {ret args} { return $ret } + +do_test 3.1 { + faultsim_restore_and_reopen + + execsql { BEGIN; UPDATE t1 SET z=11; } + set C1 [changeset_from_sql { + UPDATE t1 SET z=10 WHERE x=1; + }] + execsql { ROLLBACK } + + execsql { BEGIN; UPDATE t1 SET z=11; } + set C2 [changeset_from_sql { + UPDATE t1 SET z=55 WHERE x=1; + }] + execsql { ROLLBACK } + + set ::rebase1 [sqlite3changeset_apply_v2 db $::C1 [list xConflict OMIT]] + set ::rebase2 [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]] + set {} {} + execsql { SELECT * FROM t1 } +} {3 1 4 1 5 9} + + +do_faultsim_test 3.2 -faults oom* -prep { + faultsim_restore_and_reopen +} -body { + sqlite3rebaser_create R + R configure $::rebase1 + R configure $::rebase2 + set {} {} +} -test { + catch { R delete } + faultsim_test_result {0 {}} {1 SQLITE_NOMEM} +} + + finish_test
diff --git a/third_party/sqlite/src/ext/session/sessionrebase.test b/third_party/sqlite/src/ext/session/sessionrebase.test new file mode 100644 index 0000000..cf267fa --- /dev/null +++ b/third_party/sqlite/src/ext/session/sessionrebase.test
@@ -0,0 +1,477 @@ +# 2018 March 14 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] session_common.tcl] +source $testdir/tester.tcl +ifcapable !session {finish_test; return} + +set testprefix sessionrebase + +set ::lConflict [list] +proc xConflict {args} { + set res [lindex $::lConflict 0] + set ::lConflict [lrange $::lConflict 1 end] + return $res +} + +#------------------------------------------------------------------------- +# The following test cases - 1.* - test that the rebase blobs output by +# sqlite3_changeset_apply_v2 look correct in some simple cases. The blob +# is itself a changeset, containing records determined as follows: +# +# * For each conflict resolved with REPLACE, the rebase blob contains +# a DELETE record. All fields other than the PK fields are undefined. +# +# * For each conflict resolved with OMIT, the rebase blob contains an +# INSERT record. For an INSERT or UPDATE operation, the indirect flag +# is clear and all updated fields are defined. For a DELETE operation, +# the indirect flag is set and all non-PK fields left undefined. +# +proc do_apply_v2_test {tn sql modsql conflict_handler res} { + + execsql BEGIN + sqlite3session S db main + S attach * + execsql $sql + set changeset [S changeset] + S delete + execsql ROLLBACK + + execsql BEGIN + execsql $modsql + set ::lConflict $conflict_handler + set blob [sqlite3changeset_apply_v2 db $changeset xConflict] + execsql ROLLBACK + + uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]] +} + + +set ::lConflict [list] +proc xConflict {args} { + set res [lindex $::lConflict 0] + set ::lConflict [lrange $::lConflict 1 end] + return $res +} + +# Take a copy of database test.db in file test.db2. Execute $sql1 +# against test.db and $sql2 against test.db2. Capture a changeset +# for each. Then send the test.db2 changeset to test.db and apply +# it with the conflict handlers in $conflict_handler. Patch the +# test.db changeset and then execute it against test.db2. Test that +# the two databases come out the same. +# +proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} { + + for {set i 1} {$i <= 2} {incr i} { + forcedelete test.db2 test.db2-journal test.db2-wal + forcecopy test.db test.db2 + sqlite3 db2 test.db2 + + db eval BEGIN + + sqlite3session S1 db main + S1 attach * + execsql $sql1 db + set c1 [S1 changeset] + S1 delete + + if {$i==1} { + sqlite3session S2 db2 main + S2 attach * + execsql $sql2 db2 + set c2 [S2 changeset] + S2 delete + } else { + set c2 [list] + foreach sql [split $sql2 ";"] { + if {[string is space $sql]} continue + sqlite3session S2 db2 main + S2 attach * + execsql $sql db2 + lappend c2 [S2 changeset] + S2 delete + } + } + + set ::lConflict $conflict_handler + set rebase [list] + if {$i==1} { + lappend rebase [sqlite3changeset_apply_v2 db $c2 xConflict] + } else { + foreach c $c2 { +#puts "apply_v2: [changeset_to_list $c]" + lappend rebase [sqlite3changeset_apply_v2 db $c xConflict] + } + #puts "llength: [llength $rebase]" + } + #if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint } + #puts [changeset_to_list [lindex $rebase 0]] ; breakpoint + #puts [llength $rebase] + + sqlite3rebaser_create R + foreach r $rebase { +#puts [changeset_to_list $r] + R configure $r + } + set c1r [R rebase $c1] + R delete + #if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] } + + sqlite3changeset_apply_v2 db2 $c1r xConflictAbort + + if {[string range $tn end end]!="*"} { + uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}] + } + db2 close + + if {$testsql!=""} { + uplevel [list do_execsql_test $tn.$i.2 $testsql $testres] + } + + db eval ROLLBACK + } +} + +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'value A'); +} + +do_apply_v2_test 1.1.1 { + UPDATE t1 SET b = 'value B' WHERE a=1; +} { + UPDATE t1 SET b = 'value C' WHERE a=1; +} { + OMIT +} { + {INSERT t1 0 X. {} {i 1 t {value B}}} +} + +do_apply_v2_test 1.1.2 { + UPDATE t1 SET b = 'value B' WHERE a=1; +} { + UPDATE t1 SET b = 'value C' WHERE a=1; +} { + REPLACE +} { + {INSERT t1 1 X. {} {i 1 t {value B}}} +} + +do_apply_v2_test 1.2.1 { + INSERT INTO t1 VALUES(2, 'first'); +} { + INSERT INTO t1 VALUES(2, 'second'); +} { + OMIT +} { + {INSERT t1 0 X. {} {i 2 t first}} +} +do_apply_v2_test 1.2.2 { + INSERT INTO t1 VALUES(2, 'first'); +} { + INSERT INTO t1 VALUES(2, 'second'); +} { + REPLACE +} { + {INSERT t1 1 X. {} {i 2 t first}} +} + +do_apply_v2_test 1.3.1 { + DELETE FROM t1 WHERE a=1; +} { + UPDATE t1 SET b='value D' WHERE a=1; +} { + OMIT +} { + {DELETE t1 0 X. {i 1 t {value A}} {}} +} +do_apply_v2_test 1.3.2 { + DELETE FROM t1 WHERE a=1; +} { + UPDATE t1 SET b='value D' WHERE a=1; +} { + REPLACE +} { + {DELETE t1 1 X. {i 1 t {value A}} {}} +} + +#------------------------------------------------------------------------- +# Test cases 2.* - simple tests of rebasing actual changesets. +# +# 2.1.1 - 1u2u1r +# 2.1.2 - 1u2u2r +# 2.1.3 - 1d2d +# 2.1.4 - 1d2u1r +# 2.1.5 - 1d2u2r !! +# 2.1.6 - 1u2d1r +# 2.1.7 - 1u2d2r +# +# 2.1.8 - 1i2i2r +# 2.1.9 - 1i2i1r +# + +proc xConflictAbort {args} { + return "ABORT" +} + +reset_db +do_execsql_test 2.1.0 { + CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t1 VALUES(3, 'three'); +} +do_rebase_test 2.1.1 { + UPDATE t1 SET b = 'two.1' WHERE a=2 +} { + UPDATE t1 SET b = 'two.2' WHERE a=2; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two.1 3 three} + +do_rebase_test 2.1.2 { + UPDATE t1 SET b = 'two.1' WHERE a=2 +} { + UPDATE t1 SET b = 'two.2' WHERE a=2; +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two.2 3 three} + +do_rebase_test 2.1.3 { + DELETE FROM t1 WHERE a=3 +} { + DELETE FROM t1 WHERE a=3; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two} + +do_rebase_test 2.1.4 { + DELETE FROM t1 WHERE a=1 +} { + UPDATE t1 SET b='one.2' WHERE a=1 +} { + OMIT +} { SELECT * FROM t1 } {2 two 3 three} + +#do_rebase_test 2.1.5 { +# DELETE FROM t1 WHERE a=1; +#} { +# UPDATE t1 SET b='one.2' WHERE a=1 +#} { +# REPLACE +#} { SELECT * FROM t1 } {2 two 3 three} + +do_rebase_test 2.1.6 { + UPDATE t1 SET b='three.1' WHERE a=3 +} { + DELETE FROM t1 WHERE a=3; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two 3 three.1} + +do_rebase_test 2.1.7 { + UPDATE t1 SET b='three.1' WHERE a=3 +} { + DELETE FROM t1 WHERE a=3; +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two} + +do_rebase_test 2.1.8 { + INSERT INTO t1 VALUES(4, 'four.1') +} { + INSERT INTO t1 VALUES(4, 'four.2'); +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2} + +do_rebase_test 2.1.9 { + INSERT INTO t1 VALUES(4, 'four.1') +} { + INSERT INTO t1 VALUES(4, 'four.2'); +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1} + +do_execsql_test 2.2.0 { + CREATE TABLE t2(x, y, z PRIMARY KEY); + INSERT INTO t2 VALUES('i', 'a', 'A'); + INSERT INTO t2 VALUES('ii', 'b', 'B'); + INSERT INTO t2 VALUES('iii', 'c', 'C'); + + CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t3 VALUES(-1, 'z', 'Z'); + INSERT INTO t3 VALUES(-2, 'y', 'Y'); +} + +do_rebase_test 2.2.1 { + UPDATE t2 SET x=1 WHERE z='A' +} { + UPDATE t2 SET y='one' WHERE z='A'; +} { +} { SELECT * FROM t2 WHERE z='A' } { 1 one A } + +do_rebase_test 2.2.2 { + UPDATE t2 SET x=1, y='one' WHERE z='B' +} { + UPDATE t2 SET y='two' WHERE z='B'; +} { + REPLACE +} { SELECT * FROM t2 WHERE z='B' } { 1 two B } + +do_rebase_test 2.2.3 { + UPDATE t2 SET x=1, y='one' WHERE z='B' +} { + UPDATE t2 SET y='two' WHERE z='B'; +} { + OMIT +} { SELECT * FROM t2 WHERE z='B' } { 1 one B } + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 3.0 { + CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c)); + CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z); + + INSERT INTO t3 VALUES(1, 2, 3); + INSERT INTO t3 VALUES(4, 2, 5); + INSERT INTO t3 VALUES(7, 2, 9); + + INSERT INTO abcdefghijkl VALUES('a', 'b', 'c'); + INSERT INTO abcdefghijkl VALUES('d', 'e', 'f'); + INSERT INTO abcdefghijkl VALUES('g', 'h', 'i'); +} + +breakpoint +# do_rebase_test 3.6.tn { +# UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d'; +# } { +# UPDATE abcdefghijkl SET y=1 WHERE x='d'; +# UPDATE abcdefghijkl SET z=1 WHERE x='d'; +# } [list REPLACE REPLACE REPLACE] + +foreach {tn p} { + 1 OMIT 2 REPLACE +} { + do_rebase_test 3.1.$tn { + INSERT INTO t3 VALUES(1, 1, 1); + UPDATE abcdefghijkl SET y=2; + } { + INSERT INTO t3 VALUES(4, 1, 1); + DELETE FROM abcdefghijkl; + } [list $p $p $p $p $p $p $p $p] + + do_rebase_test 3.2.$tn { + INSERT INTO abcdefghijkl SELECT * FROM t3; + UPDATE t3 SET b=b+1; + } { + INSERT INTO t3 VALUES(3, 3, 3); + INSERT INTO abcdefghijkl SELECT * FROM t3; + } [list $p $p $p $p $p $p $p $p] + + do_rebase_test 3.3.$tn { + INSERT INTO abcdefghijkl VALUES(22, 23, 24); + } { + INSERT INTO abcdefghijkl VALUES(22, 25, 26); + UPDATE abcdefghijkl SET y=400 WHERE x=22; + } [list $p $p $p $p $p $p $p $p] + + do_rebase_test 3.4.$tn { + INSERT INTO abcdefghijkl VALUES(22, 23, 24); + } { + INSERT INTO abcdefghijkl VALUES(22, 25, 26); + UPDATE abcdefghijkl SET y=400 WHERE x=22; + } [list REPLACE $p] + + do_rebase_test 3.5.$tn* { + UPDATE abcdefghijkl SET y='X' WHERE x='d'; + } { + DELETE FROM abcdefghijkl WHERE x='d'; + INSERT INTO abcdefghijkl VALUES('d', NULL, NULL); + } [list $p $p $p] + do_rebase_test 3.5.$tn { + UPDATE abcdefghijkl SET y='X' WHERE x='d'; + } { + DELETE FROM abcdefghijkl WHERE x='d'; + INSERT INTO abcdefghijkl VALUES('d', NULL, NULL); + } [list REPLACE $p $p] + + do_rebase_test 3.6.$tn { + UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d'; + } { + UPDATE abcdefghijkl SET y=1 WHERE x='d'; + UPDATE abcdefghijkl SET z=1 WHERE x='d'; + } [list REPLACE $p $p] +} + +#------------------------------------------------------------------------- +# Check that apply_v2() does not create a rebase buffer for a patchset. +# And that it is not possible to rebase a patchset. +# +do_execsql_test 4.0 { + CREATE TABLE t5(o PRIMARY KEY, p, q); + INSERT INTO t5 VALUES(1, 2, 3); + INSERT INTO t5 VALUES(4, 5, 6); +} +foreach {tn cmd rebasable} { + 1 patchset 0 + 2 changeset 1 +} { + proc xConflict {args} { return "OMIT" } + do_test 4.1.$tn { + execsql { + BEGIN; + DELETE FROM t5 WHERE o=4; + } + + sqlite3session S db main + S attach * + execsql { + INSERT INTO t5 VALUES(4, 'five', 'six'); + } + set P [S $cmd] + S delete + + execsql ROLLBACK; + + set ::rebase [sqlite3changeset_apply_v2 db $P xConflict] + expr [llength $::rebase]>0 + } $rebasable +} + +foreach {tn cmd rebasable} { + 1 patchset 0 + 2 changeset 1 +} { + do_test 4.2.$tn { + sqlite3session S db main + S attach * + execsql { + INSERT INTO t5 VALUES(5+$tn, 'five', 'six'); + } + set P [S $cmd] + S delete + + sqlite3rebaser_create R + R configure $::rebase + expr [catch {R rebase $P}]==0 + } $rebasable + + catch { R delete } +} +finish_test +
diff --git a/third_party/sqlite/src/ext/session/sqlite3session.c b/third_party/sqlite/src/ext/session/sqlite3session.c index 39654b2..f7e7910 100644 --- a/third_party/sqlite/src/ext/session/sqlite3session.c +++ b/third_party/sqlite/src/ext/session/sqlite3session.c
@@ -68,7 +68,7 @@ ** sqlite3changeset_start_strm()). */ struct SessionInput { - int bNoDiscard; /* If true, discard no data */ + int bNoDiscard; /* If true, do not discard in InputBuffer() */ int iCurrent; /* Offset in aData[] of current change */ int iNext; /* Offset in aData[] of next change */ u8 *aData; /* Pointer to buffer containing changeset */ @@ -232,8 +232,8 @@ ** statement. ** ** For a DELETE change, all fields within the record except those associated -** with PRIMARY KEY columns are set to "undefined". The PRIMARY KEY fields -** contain the values identifying the row to delete. +** with PRIMARY KEY columns are omitted. The PRIMARY KEY fields contain the +** values identifying the row to delete. ** ** For an UPDATE change, all fields except those associated with PRIMARY KEY ** columns and columns that are modified by the UPDATE are set to "undefined". @@ -516,7 +516,7 @@ static int sessionSerialLen(u8 *a){ int e = *a; int n; - if( e==0 ) return 1; + if( e==0 || e==0xFF ) return 1; if( e==SQLITE_NULL ) return 1; if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; return sessionVarintGet(&a[1], &n) + 1 + n; @@ -596,7 +596,7 @@ int n1 = sessionSerialLen(a1); int n2 = sessionSerialLen(a2); - if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){ + if( n1!=n2 || memcmp(a1, a2, n1) ){ return 0; } a1 += n1; @@ -839,7 +839,7 @@ }else{ z = sqlite3_value_blob(pVal); } - if( memcmp(a, z, n) ) return 0; + if( n>0 && memcmp(a, z, n) ) return 0; a += n; } } @@ -1115,7 +1115,7 @@ int iHash; int bNull = 0; int rc = SQLITE_OK; - SessionStat1Ctx stat1; + SessionStat1Ctx stat1 = {0}; if( pSession->rc ) return; @@ -2183,6 +2183,7 @@ "SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND " "idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb ); + if( zSql==0 ) rc = SQLITE_NOMEM; }else{ int i; const char *zSep = ""; @@ -2592,7 +2593,7 @@ ** object and the buffer is full, discard some data to free up space. */ static void sessionDiscardData(SessionInput *pIn){ - if( pIn->bEof && pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){ + if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){ int nMove = pIn->buf.nBuf - pIn->iNext; assert( nMove>=0 ); if( nMove>0 ){ @@ -2720,13 +2721,16 @@ if( abPK && abPK[i]==0 ) continue; rc = sessionInputBuffer(pIn, 9); if( rc==SQLITE_OK ){ - eType = pIn->aData[pIn->iNext++]; - } - - assert( apOut[i]==0 ); - if( eType ){ - apOut[i] = sqlite3ValueNew(0); - if( !apOut[i] ) rc = SQLITE_NOMEM; + if( pIn->iNext>=pIn->nData ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + eType = pIn->aData[pIn->iNext++]; + assert( apOut[i]==0 ); + if( eType ){ + apOut[i] = sqlite3ValueNew(0); + if( !apOut[i] ) rc = SQLITE_NOMEM; + } + } } if( rc==SQLITE_OK ){ @@ -2736,10 +2740,14 @@ pIn->iNext += sessionVarintGet(aVal, &nByte); rc = sessionInputBuffer(pIn, nByte); if( rc==SQLITE_OK ){ - u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0); - rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc); + if( nByte<0 || nByte>pIn->nData-pIn->iNext ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0); + rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc); + pIn->iNext += nByte; + } } - pIn->iNext += nByte; } if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ sqlite3_int64 v = sessionGetI64(aVal); @@ -2779,8 +2787,19 @@ rc = sessionInputBuffer(pIn, 9); if( rc==SQLITE_OK ){ nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol); - rc = sessionInputBuffer(pIn, nRead+nCol+100); - nRead += nCol; + /* The hard upper limit for the number of columns in an SQLite + ** database table is, according to sqliteLimit.h, 32676. So + ** consider any table-header that purports to have more than 65536 + ** columns to be corrupt. This is convenient because otherwise, + ** if the (nCol>65536) condition below were omitted, a sufficiently + ** large value for nCol may cause nRead to wrap around and become + ** negative. Leading to a crash. */ + if( nCol<0 || nCol>65536 ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = sessionInputBuffer(pIn, nRead+nCol+100); + nRead += nCol; + } } while( rc==SQLITE_OK ){ @@ -2857,11 +2876,15 @@ int nByte; int nVarint; nVarint = sessionVarintGet(&p->in.aData[p->in.iNext], &p->nCol); - nCopy -= nVarint; - p->in.iNext += nVarint; - nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy; - p->tblhdr.nBuf = 0; - sessionBufferGrow(&p->tblhdr, nByte, &rc); + if( p->nCol>0 ){ + nCopy -= nVarint; + p->in.iNext += nVarint; + nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy; + p->tblhdr.nBuf = 0; + sessionBufferGrow(&p->tblhdr, nByte, &rc); + }else{ + rc = SQLITE_CORRUPT_BKPT; + } } if( rc==SQLITE_OK ){ @@ -2896,7 +2919,8 @@ static int sessionChangesetNext( sqlite3_changeset_iter *p, /* Changeset iterator */ u8 **paRec, /* If non-NULL, store record pointer here */ - int *pnRec /* If non-NULL, store size of record here */ + int *pnRec, /* If non-NULL, store size of record here */ + int *pbNew /* If non-NULL, true if new table */ ){ int i; u8 op; @@ -2931,6 +2955,7 @@ op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ + if( pbNew ) *pbNew = 1; p->bPatchset = (op=='P'); if( sessionChangesetReadTblhdr(p) ) return p->rc; if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc; @@ -2939,6 +2964,13 @@ op = p->in.aData[p->in.iNext++]; } + if( p->zTab==0 ){ + /* The first record in the changeset is not a table header. Must be a + ** corrupt changeset. */ + assert( p->in.iNext==1 ); + return (p->rc = SQLITE_CORRUPT_BKPT); + } + p->op = op; p->bIndirect = p->in.aData[p->in.iNext++]; if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ @@ -2981,9 +3013,9 @@ ** new.* to old.*, to accommodate the code that reads these arrays. */ for(i=0; i<p->nCol; i++){ assert( p->apValue[i]==0 ); - assert( p->abPK[i]==0 || p->apValue[i+p->nCol] ); if( p->abPK[i] ){ p->apValue[i] = p->apValue[i+p->nCol]; + if( p->apValue[i]==0 ) return (p->rc = SQLITE_CORRUPT_BKPT); p->apValue[i+p->nCol] = 0; } } @@ -3002,7 +3034,7 @@ ** callback by changeset_apply(). */ int sqlite3changeset_next(sqlite3_changeset_iter *p){ - return sessionChangesetNext(p, 0, 0); + return sessionChangesetNext(p, 0, 0, 0); } /* @@ -3381,6 +3413,8 @@ int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ SessionBuffer constraints; /* Deferred constraints are stored here */ + SessionBuffer rebase; /* Rebase information (if any) here */ + int bRebaseStarted; /* If table header is already in rebase */ }; /* @@ -3647,7 +3681,6 @@ "AND (?4 OR stat IS ?3)" ); } - assert( rc==SQLITE_OK ); return rc; } @@ -3708,7 +3741,13 @@ if( !abPK || abPK[i] ){ sqlite3_value *pVal; (void)xValue(pIter, i, &pVal); - rc = sessionBindValue(pStmt, i+1, pVal); + if( pVal==0 ){ + /* The value in the changeset was "undefined". This indicates a + ** corrupt changeset blob. */ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = sessionBindValue(pStmt, i+1, pVal); + } } } return rc; @@ -3757,6 +3796,54 @@ } /* +** This function is called from within sqlite3changset_apply_v2() when +** a conflict is encountered and resolved using conflict resolution +** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE).. +** It adds a conflict resolution record to the buffer in +** SessionApplyCtx.rebase, which will eventually be returned to the caller +** of apply_v2() as the "rebase" buffer. +** +** Return SQLITE_OK if successful, or an SQLite error code otherwise. +*/ +static int sessionRebaseAdd( + SessionApplyCtx *p, /* Apply context */ + int eType, /* Conflict resolution (OMIT or REPLACE) */ + sqlite3_changeset_iter *pIter /* Iterator pointing at current change */ +){ + int rc = SQLITE_OK; + int i; + int eOp = pIter->op; + if( p->bRebaseStarted==0 ){ + /* Append a table-header to the rebase buffer */ + const char *zTab = pIter->zTab; + sessionAppendByte(&p->rebase, 'T', &rc); + sessionAppendVarint(&p->rebase, p->nCol, &rc); + sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc); + sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc); + p->bRebaseStarted = 1; + } + + assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT ); + assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE ); + + sessionAppendByte(&p->rebase, + (eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc + ); + sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc); + for(i=0; i<p->nCol; i++){ + sqlite3_value *pVal = 0; + if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){ + sqlite3changeset_old(pIter, i, &pVal); + }else{ + sqlite3changeset_new(pIter, i, &pVal); + } + sessionAppendValue(&p->rebase, pVal, &rc); + } + + return rc; +} + +/* ** Invoke the conflict handler for the change that the changeset iterator ** currently points to. ** @@ -3831,7 +3918,7 @@ u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent]; int nBlob = pIter->in.iNext - pIter->in.iCurrent; sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc); - res = SQLITE_CHANGESET_OMIT; + return SQLITE_OK; }else{ /* No other row with the new.* primary key. */ res = xConflict(pCtx, eType+1, pIter); @@ -3857,6 +3944,9 @@ rc = SQLITE_MISUSE; break; } + if( rc==SQLITE_OK ){ + rc = sessionRebaseAdd(p, res, pIter); + } } return rc; @@ -4032,42 +4122,42 @@ int rc; rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry); - assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) ); - - /* If the bRetry flag is set, the change has not been applied due to an - ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and - ** a row with the correct PK is present in the db, but one or more other - ** fields do not contain the expected values) and the conflict handler - ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation, - ** but pass NULL as the final argument so that sessionApplyOneOp() ignores - ** the SQLITE_CHANGESET_DATA problem. */ - if( bRetry ){ - assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE ); - rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0); - } - - /* If the bReplace flag is set, the change is an INSERT that has not - ** been performed because the database already contains a row with the - ** specified primary key and the conflict handler returned - ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row - ** before reattempting the INSERT. */ - else if( bReplace ){ - assert( pIter->op==SQLITE_INSERT ); - rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = sessionBindRow(pIter, - sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete); - sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1); - } - if( rc==SQLITE_OK ){ - sqlite3_step(pApply->pDelete); - rc = sqlite3_reset(pApply->pDelete); - } - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ + /* If the bRetry flag is set, the change has not been applied due to an + ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and + ** a row with the correct PK is present in the db, but one or more other + ** fields do not contain the expected values) and the conflict handler + ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation, + ** but pass NULL as the final argument so that sessionApplyOneOp() ignores + ** the SQLITE_CHANGESET_DATA problem. */ + if( bRetry ){ + assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE ); rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0); } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0); + + /* If the bReplace flag is set, the change is an INSERT that has not + ** been performed because the database already contains a row with the + ** specified primary key and the conflict handler returned + ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row + ** before reattempting the INSERT. */ + else if( bReplace ){ + assert( pIter->op==SQLITE_INSERT ); + rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0); + if( rc==SQLITE_OK ){ + rc = sessionBindRow(pIter, + sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete); + sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1); + } + if( rc==SQLITE_OK ){ + sqlite3_step(pApply->pDelete); + rc = sqlite3_reset(pApply->pDelete); + } + if( rc==SQLITE_OK ){ + rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0); + } } } @@ -4143,10 +4233,12 @@ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ sqlite3_changeset_iter *p /* Handle describing change and conflict */ ), - void *pCtx /* First argument passed to xConflict */ + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, /* OUT: Rebase information */ + int flags /* SESSION_APPLY_XXX flags */ ){ int schemaMismatch = 0; - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ const char *zTab = 0; /* Name of current table */ int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ SessionApplyCtx sApply; /* changeset_apply() context object */ @@ -4157,7 +4249,9 @@ pIter->in.bNoDiscard = 1; memset(&sApply, 0, sizeof(sApply)); sqlite3_mutex_enter(sqlite3_db_mutex(db)); - rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); + if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ + rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); + } if( rc==SQLITE_OK ){ rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0); } @@ -4181,9 +4275,18 @@ sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pSelect); - memset(&sApply, 0, sizeof(sApply)); sApply.db = db; + sApply.pDelete = 0; + sApply.pUpdate = 0; + sApply.pInsert = 0; + sApply.pSelect = 0; + sApply.nCol = 0; + sApply.azCol = 0; + sApply.abPK = 0; + sApply.bStat1 = 0; sApply.bDeferConstraints = 1; + sApply.bRebaseStarted = 0; + memset(&sApply.constraints, 0, sizeof(SessionBuffer)); /* If an xFilter() callback was specified, invoke it now. If the ** xFilter callback returns zero, skip this table. If it returns @@ -4286,24 +4389,63 @@ } sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); - }else{ - sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0); - sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); + if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); + }else{ + sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0); + sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); + } } + if( rc==SQLITE_OK && bPatchset==0 && ppRebase && pnRebase ){ + *ppRebase = (void*)sApply.rebase.aBuf; + *pnRebase = sApply.rebase.nBuf; + sApply.rebase.aBuf = 0; + } sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pDelete); sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pSelect); sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_free((char*)sApply.constraints.aBuf); + sqlite3_free((char*)sApply.rebase.aBuf); sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } /* +** Apply the changeset passed via pChangeset/nChangeset to the main +** database attached to handle "db". +*/ +int sqlite3changeset_apply_v2( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void *pChangeset, /* Changeset blob */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +){ + sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ + int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset); + if( rc==SQLITE_OK ){ + rc = sessionChangesetApply( + db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags + ); + } + return rc; +} + +/* ** Apply the changeset passed via pChangeset/nChangeset to the main database ** attached to handle "db". Invoke the supplied conflict handler callback ** to resolve any conflicts encountered while applying the change. @@ -4323,12 +4465,9 @@ ), void *pCtx /* First argument passed to xConflict */ ){ - sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset); - if( rc==SQLITE_OK ){ - rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx); - } - return rc; + return sqlite3changeset_apply_v2( + db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0 + ); } /* @@ -4336,6 +4475,32 @@ ** attached to handle "db". Invoke the supplied conflict handler callback ** to resolve any conflicts encountered while applying the change. */ +int sqlite3changeset_apply_v2_strm( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ + void *pIn, /* First arg for xInput */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +){ + sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ + int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); + if( rc==SQLITE_OK ){ + rc = sessionChangesetApply( + db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags + ); + } + return rc; +} int sqlite3changeset_apply_strm( sqlite3 *db, /* Apply change to "main" db of this handle */ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ @@ -4351,12 +4516,9 @@ ), void *pCtx /* First argument passed to xConflict */ ){ - sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ - int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); - if( rc==SQLITE_OK ){ - rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx); - } - return rc; + return sqlite3changeset_apply_v2_strm( + db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0 + ); } /* @@ -4375,6 +4537,7 @@ */ static int sessionChangeMerge( SessionTable *pTab, /* Table structure */ + int bRebase, /* True for a rebase hash-table */ int bPatchset, /* True for patchsets */ SessionChange *pExist, /* Existing change */ int op2, /* Second change operation */ @@ -4384,6 +4547,7 @@ SessionChange **ppNew /* OUT: Merged change */ ){ SessionChange *pNew = 0; + int rc = SQLITE_OK; if( !pExist ){ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec); @@ -4393,9 +4557,66 @@ memset(pNew, 0, sizeof(SessionChange)); pNew->op = op2; pNew->bIndirect = bIndirect; - pNew->nRecord = nRec; pNew->aRecord = (u8*)&pNew[1]; - memcpy(pNew->aRecord, aRec, nRec); + if( bIndirect==0 || bRebase==0 ){ + pNew->nRecord = nRec; + memcpy(pNew->aRecord, aRec, nRec); + }else{ + int i; + u8 *pIn = aRec; + u8 *pOut = pNew->aRecord; + for(i=0; i<pTab->nCol; i++){ + int nIn = sessionSerialLen(pIn); + if( *pIn==0 ){ + *pOut++ = 0; + }else if( pTab->abPK[i]==0 ){ + *pOut++ = 0xFF; + }else{ + memcpy(pOut, pIn, nIn); + pOut += nIn; + } + pIn += nIn; + } + pNew->nRecord = pOut - pNew->aRecord; + } + }else if( bRebase ){ + if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){ + *ppNew = pExist; + }else{ + int nByte = nRec + pExist->nRecord + sizeof(SessionChange); + pNew = (SessionChange*)sqlite3_malloc(nByte); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + int i; + u8 *a1 = pExist->aRecord; + u8 *a2 = aRec; + u8 *pOut; + + memset(pNew, 0, nByte); + pNew->bIndirect = bIndirect || pExist->bIndirect; + pNew->op = op2; + pOut = pNew->aRecord = (u8*)&pNew[1]; + + for(i=0; i<pTab->nCol; i++){ + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( *a1==0xFF || (pTab->abPK[i]==0 && bIndirect) ){ + *pOut++ = 0xFF; + }else if( *a2==0 ){ + memcpy(pOut, a1, n1); + pOut += n1; + }else{ + memcpy(pOut, a2, n2); + pOut += n2; + } + a1 += n1; + a2 += n2; + } + pNew->nRecord = pOut - pNew->aRecord; + } + sqlite3_free(pExist); + } }else{ int op1 = pExist->op; @@ -4489,7 +4710,7 @@ } *ppNew = pNew; - return SQLITE_OK; + return rc; } /* @@ -4498,15 +4719,15 @@ */ static int sessionChangesetToHash( sqlite3_changeset_iter *pIter, /* Iterator to read from */ - sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */ + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ + int bRebase /* True if hash table is for rebasing */ ){ u8 *aRec; int nRec; int rc = SQLITE_OK; SessionTable *pTab = 0; - - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){ + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ const char *zNew; int nCol; int op; @@ -4586,7 +4807,7 @@ } } - rc = sessionChangeMerge(pTab, + rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); if( rc ) break; @@ -4694,7 +4915,7 @@ rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ - rc = sessionChangesetToHash(pIter, pGrp); + rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; @@ -4725,7 +4946,7 @@ rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); if( rc==SQLITE_OK ){ - rc = sessionChangesetToHash(pIter, pGrp); + rc = sessionChangesetToHash(pIter, pGrp, 0); } sqlite3changeset_finalize(pIter); return rc; @@ -4810,4 +5031,347 @@ return rc; } +/* +** Changeset rebaser handle. +*/ +struct sqlite3_rebaser { + sqlite3_changegroup grp; /* Hash table */ +}; + +/* +** Buffers a1 and a2 must both contain a sessions module record nCol +** fields in size. This function appends an nCol sessions module +** record to buffer pBuf that is a copy of a1, except that for +** each field that is undefined in a1[], swap in the field from a2[]. +*/ +static void sessionAppendRecordMerge( + SessionBuffer *pBuf, /* Buffer to append to */ + int nCol, /* Number of columns in each record */ + u8 *a1, int n1, /* Record 1 */ + u8 *a2, int n2, /* Record 2 */ + int *pRc /* IN/OUT: error code */ +){ + sessionBufferGrow(pBuf, n1+n2, pRc); + if( *pRc==SQLITE_OK ){ + int i; + u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; + for(i=0; i<nCol; i++){ + int nn1 = sessionSerialLen(a1); + int nn2 = sessionSerialLen(a2); + if( *a1==0 || *a1==0xFF ){ + memcpy(pOut, a2, nn2); + pOut += nn2; + }else{ + memcpy(pOut, a1, nn1); + pOut += nn1; + } + a1 += nn1; + a2 += nn2; + } + + pBuf->nBuf = pOut-pBuf->aBuf; + assert( pBuf->nBuf<=pBuf->nAlloc ); + } +} + +/* +** This function is called when rebasing a local UPDATE change against one +** or more remote UPDATE changes. The aRec/nRec buffer contains the current +** old.* and new.* records for the change. The rebase buffer (a single +** record) is in aChange/nChange. The rebased change is appended to buffer +** pBuf. +** +** Rebasing the UPDATE involves: +** +** * Removing any changes to fields for which the corresponding field +** in the rebase buffer is set to "replaced" (type 0xFF). If this +** means the UPDATE change updates no fields, nothing is appended +** to the output buffer. +** +** * For each field modified by the local change for which the +** corresponding field in the rebase buffer is not "undefined" (0x00) +** or "replaced" (0xFF), the old.* value is replaced by the value +** in the rebase buffer. +*/ +static void sessionAppendPartialUpdate( + SessionBuffer *pBuf, /* Append record here */ + sqlite3_changeset_iter *pIter, /* Iterator pointed at local change */ + u8 *aRec, int nRec, /* Local change */ + u8 *aChange, int nChange, /* Record to rebase against */ + int *pRc /* IN/OUT: Return Code */ +){ + sessionBufferGrow(pBuf, 2+nRec+nChange, pRc); + if( *pRc==SQLITE_OK ){ + int bData = 0; + u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; + int i; + u8 *a1 = aRec; + u8 *a2 = aChange; + + *pOut++ = SQLITE_UPDATE; + *pOut++ = pIter->bIndirect; + for(i=0; i<pIter->nCol; i++){ + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( pIter->abPK[i] || a2[0]==0 ){ + if( !pIter->abPK[i] ) bData = 1; + memcpy(pOut, a1, n1); + pOut += n1; + }else if( a2[0]!=0xFF ){ + bData = 1; + memcpy(pOut, a2, n2); + pOut += n2; + }else{ + *pOut++ = '\0'; + } + a1 += n1; + a2 += n2; + } + if( bData ){ + a2 = aChange; + for(i=0; i<pIter->nCol; i++){ + int n1 = sessionSerialLen(a1); + int n2 = sessionSerialLen(a2); + if( pIter->abPK[i] || a2[0]!=0xFF ){ + memcpy(pOut, a1, n1); + pOut += n1; + }else{ + *pOut++ = '\0'; + } + a1 += n1; + a2 += n2; + } + pBuf->nBuf = (pOut - pBuf->aBuf); + } + } +} + +/* +** pIter is configured to iterate through a changeset. This function rebases +** that changeset according to the current configuration of the rebaser +** object passed as the first argument. If no error occurs and argument xOutput +** is not NULL, then the changeset is returned to the caller by invoking +** xOutput zero or more times and SQLITE_OK returned. Or, if xOutput is NULL, +** then (*ppOut) is set to point to a buffer containing the rebased changeset +** before this function returns. In this case (*pnOut) is set to the size of +** the buffer in bytes. It is the responsibility of the caller to eventually +** free the (*ppOut) buffer using sqlite3_free(). +** +** If an error occurs, an SQLite error code is returned. If ppOut and +** pnOut are not NULL, then the two output parameters are set to 0 before +** returning. +*/ +static int sessionRebase( + sqlite3_rebaser *p, /* Rebaser hash table */ + sqlite3_changeset_iter *pIter, /* Input data */ + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut, /* Context for xOutput callback */ + int *pnOut, /* OUT: Number of bytes in output changeset */ + void **ppOut /* OUT: Inverse of pChangeset */ +){ + int rc = SQLITE_OK; + u8 *aRec = 0; + int nRec = 0; + int bNew = 0; + SessionTable *pTab = 0; + SessionBuffer sOut = {0,0,0}; + + while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){ + SessionChange *pChange = 0; + int bDone = 0; + + if( bNew ){ + const char *zTab = pIter->zTab; + for(pTab=p->grp.pList; pTab; pTab=pTab->pNext){ + if( 0==sqlite3_stricmp(pTab->zName, zTab) ) break; + } + bNew = 0; + + /* A patchset may not be rebased */ + if( pIter->bPatchset ){ + rc = SQLITE_ERROR; + } + + /* Append a table header to the output for this new table */ + sessionAppendByte(&sOut, pIter->bPatchset ? 'P' : 'T', &rc); + sessionAppendVarint(&sOut, pIter->nCol, &rc); + sessionAppendBlob(&sOut, pIter->abPK, pIter->nCol, &rc); + sessionAppendBlob(&sOut,(u8*)pIter->zTab,(int)strlen(pIter->zTab)+1,&rc); + } + + if( pTab && rc==SQLITE_OK ){ + int iHash = sessionChangeHash(pTab, 0, aRec, pTab->nChange); + + for(pChange=pTab->apChange[iHash]; pChange; pChange=pChange->pNext){ + if( sessionChangeEqual(pTab, 0, aRec, 0, pChange->aRecord) ){ + break; + } + } + } + + if( pChange ){ + assert( pChange->op==SQLITE_DELETE || pChange->op==SQLITE_INSERT ); + switch( pIter->op ){ + case SQLITE_INSERT: + if( pChange->op==SQLITE_INSERT ){ + bDone = 1; + if( pChange->bIndirect==0 ){ + sessionAppendByte(&sOut, SQLITE_UPDATE, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendBlob(&sOut, pChange->aRecord, pChange->nRecord, &rc); + sessionAppendBlob(&sOut, aRec, nRec, &rc); + } + } + break; + + case SQLITE_UPDATE: + bDone = 1; + if( pChange->op==SQLITE_DELETE ){ + if( pChange->bIndirect==0 ){ + u8 *pCsr = aRec; + sessionSkipRecord(&pCsr, pIter->nCol); + sessionAppendByte(&sOut, SQLITE_INSERT, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendRecordMerge(&sOut, pIter->nCol, + pCsr, nRec-(pCsr-aRec), + pChange->aRecord, pChange->nRecord, &rc + ); + } + }else{ + sessionAppendPartialUpdate(&sOut, pIter, + aRec, nRec, pChange->aRecord, pChange->nRecord, &rc + ); + } + break; + + default: + assert( pIter->op==SQLITE_DELETE ); + bDone = 1; + if( pChange->op==SQLITE_INSERT ){ + sessionAppendByte(&sOut, SQLITE_DELETE, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendRecordMerge(&sOut, pIter->nCol, + pChange->aRecord, pChange->nRecord, aRec, nRec, &rc + ); + } + break; + } + } + + if( bDone==0 ){ + sessionAppendByte(&sOut, pIter->op, &rc); + sessionAppendByte(&sOut, pIter->bIndirect, &rc); + sessionAppendBlob(&sOut, aRec, nRec, &rc); + } + if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + sOut.nBuf = 0; + } + if( rc ) break; + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(sOut.aBuf); + memset(&sOut, 0, sizeof(sOut)); + } + + if( rc==SQLITE_OK ){ + if( xOutput ){ + if( sOut.nBuf>0 ){ + rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); + } + }else{ + *ppOut = (void*)sOut.aBuf; + *pnOut = sOut.nBuf; + sOut.aBuf = 0; + } + } + sqlite3_free(sOut.aBuf); + return rc; +} + +/* +** Create a new rebaser object. +*/ +int sqlite3rebaser_create(sqlite3_rebaser **ppNew){ + int rc = SQLITE_OK; + sqlite3_rebaser *pNew; + + pNew = sqlite3_malloc(sizeof(sqlite3_rebaser)); + if( pNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pNew, 0, sizeof(sqlite3_rebaser)); + } + *ppNew = pNew; + return rc; +} + +/* +** Call this one or more times to configure a rebaser. +*/ +int sqlite3rebaser_configure( + sqlite3_rebaser *p, + int nRebase, const void *pRebase +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ + int rc; /* Return code */ + rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase); + if( rc==SQLITE_OK ){ + rc = sessionChangesetToHash(pIter, &p->grp, 1); + } + sqlite3changeset_finalize(pIter); + return rc; +} + +/* +** Rebase a changeset according to current rebaser configuration +*/ +int sqlite3rebaser_rebase( + sqlite3_rebaser *p, + int nIn, const void *pIn, + int *pnOut, void **ppOut +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */ + int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn); + + if( rc==SQLITE_OK ){ + rc = sessionRebase(p, pIter, 0, 0, pnOut, ppOut); + sqlite3changeset_finalize(pIter); + } + + return rc; +} + +/* +** Rebase a changeset according to current rebaser configuration +*/ +int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *p, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +){ + sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */ + int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); + + if( rc==SQLITE_OK ){ + rc = sessionRebase(p, pIter, xOutput, pOut, 0, 0); + sqlite3changeset_finalize(pIter); + } + + return rc; +} + +/* +** Destroy a rebaser object +*/ +void sqlite3rebaser_delete(sqlite3_rebaser *p){ + if( p ){ + sessionDeleteTable(p->grp.pList); + sqlite3_free(p); + } +} + #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
diff --git a/third_party/sqlite/src/ext/session/sqlite3session.h b/third_party/sqlite/src/ext/session/sqlite3session.h index f5db0b4..f0f785a3 100644 --- a/third_party/sqlite/src/ext/session/sqlite3session.h +++ b/third_party/sqlite/src/ext/session/sqlite3session.h
@@ -13,16 +13,23 @@ /* ** CAPI3REF: Session Object Handle +** +** An instance of this object is a [session] that can be used to +** record changes to a database. */ typedef struct sqlite3_session sqlite3_session; /* ** CAPI3REF: Changeset Iterator Handle +** +** An instance of this object acts as a cursor for iterating +** over the elements of a [changeset] or [patchset]. */ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter; /* ** CAPI3REF: Create A New Session Object +** CONSTRUCTOR: sqlite3_session ** ** Create a new session object attached to database handle db. If successful, ** a pointer to the new object is written to *ppSession and SQLITE_OK is @@ -59,6 +66,7 @@ /* ** CAPI3REF: Delete A Session Object +** DESTRUCTOR: sqlite3_session ** ** Delete a session object previously allocated using ** [sqlite3session_create()]. Once a session object has been deleted, the @@ -74,6 +82,7 @@ /* ** CAPI3REF: Enable Or Disable A Session Object +** METHOD: sqlite3_session ** ** Enable or disable the recording of changes by a session object. When ** enabled, a session object records changes made to the database. When @@ -93,6 +102,7 @@ /* ** CAPI3REF: Set Or Clear the Indirect Change Flag +** METHOD: sqlite3_session ** ** Each change recorded by a session object is marked as either direct or ** indirect. A change is marked as indirect if either: @@ -122,6 +132,7 @@ /* ** CAPI3REF: Attach A Table To A Session Object +** METHOD: sqlite3_session ** ** If argument zTab is not NULL, then it is the name of a table to attach ** to the session object passed as the first argument. All subsequent changes @@ -184,6 +195,7 @@ /* ** CAPI3REF: Set a table filter on a Session Object. +** METHOD: sqlite3_session ** ** The second argument (xFilter) is the "filter callback". For changes to rows ** in tables that are not attached to the Session object, the filter is called @@ -202,6 +214,7 @@ /* ** CAPI3REF: Generate A Changeset From A Session Object +** METHOD: sqlite3_session ** ** Obtain a changeset containing changes to the tables attached to the ** session object passed as the first argument. If successful, @@ -312,6 +325,7 @@ /* ** CAPI3REF: Load The Difference Between Tables Into A Session +** METHOD: sqlite3_session ** ** If it is not already attached to the session object passed as the first ** argument, this function attaches table zTbl in the same manner as the @@ -376,6 +390,7 @@ /* ** CAPI3REF: Generate A Patchset From A Session Object +** METHOD: sqlite3_session ** ** The differences between a patchset and a changeset are that: ** @@ -427,6 +442,7 @@ /* ** CAPI3REF: Create An Iterator To Traverse A Changeset +** CONSTRUCTOR: sqlite3_changeset_iter ** ** Create an iterator used to iterate through the contents of a changeset. ** If successful, *pp is set to point to the iterator handle and SQLITE_OK @@ -467,6 +483,7 @@ /* ** CAPI3REF: Advance A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function may only be used with iterators created by function ** [sqlite3changeset_start()]. If it is called on an iterator passed to @@ -491,6 +508,7 @@ /* ** CAPI3REF: Obtain The Current Operation From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -525,6 +543,7 @@ /* ** CAPI3REF: Obtain The Primary Key Definition Of A Table +** METHOD: sqlite3_changeset_iter ** ** For each modified table, a changeset includes the following: ** @@ -556,6 +575,7 @@ /* ** CAPI3REF: Obtain old.* Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -586,6 +606,7 @@ /* ** CAPI3REF: Obtain new.* Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator @@ -619,6 +640,7 @@ /* ** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function should only be used with iterator objects passed to a ** conflict-handler callback by [sqlite3changeset_apply()] with either @@ -646,6 +668,7 @@ /* ** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations +** METHOD: sqlite3_changeset_iter ** ** This function may only be called with an iterator passed to an ** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case @@ -662,6 +685,7 @@ /* ** CAPI3REF: Finalize A Changeset Iterator +** METHOD: sqlite3_changeset_iter ** ** This function is used to finalize an iterator allocated with ** [sqlite3changeset_start()]. @@ -678,6 +702,7 @@ ** to that error is returned by this function. Otherwise, SQLITE_OK is ** returned. This is to allow the following pattern (pseudo-code): ** +** <pre> ** sqlite3changeset_start(); ** while( SQLITE_ROW==sqlite3changeset_next() ){ ** // Do something with change. @@ -686,6 +711,7 @@ ** if( rc!=SQLITE_OK ){ ** // An error has occurred ** } +** </pre> */ int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter); @@ -733,6 +759,7 @@ ** sqlite3_changegroup object. Calling it produces similar results as the ** following code fragment: ** +** <pre> ** sqlite3_changegroup *pGrp; ** rc = sqlite3_changegroup_new(&pGrp); ** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA); @@ -743,6 +770,7 @@ ** *ppOut = 0; ** *pnOut = 0; ** } +** </pre> ** ** Refer to the sqlite3_changegroup documentation below for details. */ @@ -758,11 +786,15 @@ /* ** CAPI3REF: Changegroup Handle +** +** A changegroup is an object used to combine two or more +** [changesets] or [patchsets] */ typedef struct sqlite3_changegroup sqlite3_changegroup; /* ** CAPI3REF: Create A New Changegroup Object +** CONSTRUCTOR: sqlite3_changegroup ** ** An sqlite3_changegroup object is used to combine two or more changesets ** (or patchsets) into a single changeset (or patchset). A single changegroup @@ -800,6 +832,7 @@ /* ** CAPI3REF: Add A Changeset To A Changegroup +** METHOD: sqlite3_changegroup ** ** Add all changes within the changeset (or patchset) in buffer pData (size ** nData bytes) to the changegroup. @@ -877,6 +910,7 @@ /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup +** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup @@ -907,25 +941,25 @@ /* ** CAPI3REF: Delete A Changegroup Object +** DESTRUCTOR: sqlite3_changegroup */ void sqlite3changegroup_delete(sqlite3_changegroup*); /* ** CAPI3REF: Apply A Changeset To A Database ** -** Apply a changeset to a database. This function attempts to update the -** "main" database attached to handle db with the changes found in the -** changeset passed via the second and third arguments. +** Apply a changeset or patchset to a database. These functions attempt to +** update the "main" database attached to handle db with the changes found in +** the changeset passed via the second and third arguments. ** -** The fourth argument (xFilter) passed to this function is the "filter +** The fourth argument (xFilter) passed to these functions is the "filter ** callback". If it is not NULL, then for each table affected by at least one ** change in the changeset, the filter callback is invoked with ** the table name as the second argument, and a copy of the context pointer -** passed as the sixth argument to this function as the first. If the "filter -** callback" returns zero, then no attempt is made to apply any changes to -** the table. Otherwise, if the return value is non-zero or the xFilter -** argument to this function is NULL, all changes related to the table are -** attempted. +** passed as the sixth argument as the first. If the "filter callback" +** returns zero, then no attempt is made to apply any changes to the table. +** Otherwise, if the return value is non-zero or the xFilter argument to +** is NULL, all changes related to the table are attempted. ** ** For each table that is not excluded by the filter callback, this function ** tests that the target database contains a compatible table. A table is @@ -970,7 +1004,7 @@ ** ** <dl> ** <dt>DELETE Changes<dd> -** For each DELETE change, this function checks if the target database +** For each DELETE change, the function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all non-primary key columns also match the values stored in @@ -1015,7 +1049,7 @@ ** [SQLITE_CHANGESET_REPLACE]. ** ** <dt>UPDATE Changes<dd> -** For each UPDATE change, this function checks if the target database +** For each UPDATE change, the function checks if the target database ** contains a row with the same primary key value (or values) as the ** original row values stored in the changeset. If it does, and the values ** stored in all modified non-primary key columns also match the values @@ -1046,11 +1080,28 @@ ** This can be used to further customize the applications conflict ** resolution strategy. ** -** All changes made by this function are enclosed in a savepoint transaction. +** All changes made by these functions are enclosed in a savepoint transaction. ** If any other error (aside from a constraint failure when attempting to ** write to the target database) occurs, then the savepoint transaction is ** rolled back, restoring the target database to its original state, and an ** SQLite error code returned. +** +** If the output parameters (ppRebase) and (pnRebase) are non-NULL and +** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() +** may set (*ppRebase) to point to a "rebase" that may be used with the +** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) +** is set to the size of the buffer in bytes. It is the responsibility of the +** caller to eventually free any such buffer using sqlite3_free(). The buffer +** is only allocated and populated if one or more conflicts were encountered +** while applying the patchset. See comments surrounding the sqlite3_rebaser +** APIs for further details. +** +** The behavior of sqlite3changeset_apply_v2() and its streaming equivalent +** may be modified by passing a combination of +** [SQLITE_CHANGESETAPPLY_NOSAVEPOINT | supported flags] as the 9th parameter. +** +** Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b> +** and therefore subject to change. */ int sqlite3changeset_apply( sqlite3 *db, /* Apply change to "main" db of this handle */ @@ -1067,6 +1118,41 @@ ), void *pCtx /* First argument passed to xConflict */ ); +int sqlite3changeset_apply_v2( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int nChangeset, /* Size of changeset in bytes */ + void *pChangeset, /* Changeset blob */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, /* OUT: Rebase data */ + int flags /* Combination of SESSION_APPLY_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3changeset_apply_v2 +** +** The following flags may passed via the 9th parameter to +** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]: +** +** <dl> +** <dt>SQLITE_CHANGESETAPPLY_NOSAVEPOINT <dd> +** Usually, the sessions module encloses all operations performed by +** a single call to apply_v2() or apply_v2_strm() in a [SAVEPOINT]. The +** SAVEPOINT is committed if the changeset or patchset is successfully +** applied, or rolled back if an error occurs. Specifying this flag +** causes the sessions module to omit this savepoint. In this case, if the +** caller has an open transaction or savepoint when apply_v2() is called, +** it may revert the partially applied changeset by rolling it back. +*/ +#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -1165,6 +1251,161 @@ #define SQLITE_CHANGESET_ABORT 2 /* +** CAPI3REF: Rebasing changesets +** EXPERIMENTAL +** +** Suppose there is a site hosting a database in state S0. And that +** modifications are made that move that database to state S1 and a +** changeset recorded (the "local" changeset). Then, a changeset based +** on S0 is received from another site (the "remote" changeset) and +** applied to the database. The database is then in state +** (S1+"remote"), where the exact state depends on any conflict +** resolution decisions (OMIT or REPLACE) made while applying "remote". +** Rebasing a changeset is to update it to take those conflict +** resolution decisions into account, so that the same conflicts +** do not have to be resolved elsewhere in the network. +** +** For example, if both the local and remote changesets contain an +** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)": +** +** local: INSERT INTO t1 VALUES(1, 'v1'); +** remote: INSERT INTO t1 VALUES(1, 'v2'); +** +** and the conflict resolution is REPLACE, then the INSERT change is +** removed from the local changeset (it was overridden). Or, if the +** conflict resolution was "OMIT", then the local changeset is modified +** to instead contain: +** +** UPDATE t1 SET b = 'v2' WHERE a=1; +** +** Changes within the local changeset are rebased as follows: +** +** <dl> +** <dt>Local INSERT<dd> +** This may only conflict with a remote INSERT. If the conflict +** resolution was OMIT, then add an UPDATE change to the rebased +** changeset. Or, if the conflict resolution was REPLACE, add +** nothing to the rebased changeset. +** +** <dt>Local DELETE<dd> +** This may conflict with a remote UPDATE or DELETE. In both cases the +** only possible resolution is OMIT. If the remote operation was a +** DELETE, then add no change to the rebased changeset. If the remote +** operation was an UPDATE, then the old.* fields of change are updated +** to reflect the new.* values in the UPDATE. +** +** <dt>Local UPDATE<dd> +** This may conflict with a remote UPDATE or DELETE. If it conflicts +** with a DELETE, and the conflict resolution was OMIT, then the update +** is changed into an INSERT. Any undefined values in the new.* record +** from the update change are filled in using the old.* values from +** the conflicting DELETE. Or, if the conflict resolution was REPLACE, +** the UPDATE change is simply omitted from the rebased changeset. +** +** If conflict is with a remote UPDATE and the resolution is OMIT, then +** the old.* values are rebased using the new.* values in the remote +** change. Or, if the resolution is REPLACE, then the change is copied +** into the rebased changeset with updates to columns also updated by +** the conflicting remote UPDATE removed. If this means no columns would +** be updated, the change is omitted. +** </dl> +** +** A local change may be rebased against multiple remote changes +** simultaneously. If a single key is modified by multiple remote +** changesets, they are combined as follows before the local changeset +** is rebased: +** +** <ul> +** <li> If there has been one or more REPLACE resolutions on a +** key, it is rebased according to a REPLACE. +** +** <li> If there have been no REPLACE resolutions on a key, then +** the local changeset is rebased according to the most recent +** of the OMIT resolutions. +** </ul> +** +** Note that conflict resolutions from multiple remote changesets are +** combined on a per-field basis, not per-row. This means that in the +** case of multiple remote UPDATE operations, some fields of a single +** local change may be rebased for REPLACE while others are rebased for +** OMIT. +** +** In order to rebase a local changeset, the remote changeset must first +** be applied to the local database using sqlite3changeset_apply_v2() and +** the buffer of rebase information captured. Then: +** +** <ol> +** <li> An sqlite3_rebaser object is created by calling +** sqlite3rebaser_create(). +** <li> The new object is configured with the rebase buffer obtained from +** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure(). +** If the local changeset is to be rebased against multiple remote +** changesets, then sqlite3rebaser_configure() should be called +** multiple times, in the same order that the multiple +** sqlite3changeset_apply_v2() calls were made. +** <li> Each local changeset is rebased by calling sqlite3rebaser_rebase(). +** <li> The sqlite3_rebaser object is deleted by calling +** sqlite3rebaser_delete(). +** </ol> +*/ +typedef struct sqlite3_rebaser sqlite3_rebaser; + +/* +** CAPI3REF: Create a changeset rebaser object. +** EXPERIMENTAL +** +** Allocate a new changeset rebaser object. If successful, set (*ppNew) to +** point to the new object and return SQLITE_OK. Otherwise, if an error +** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew) +** to NULL. +*/ +int sqlite3rebaser_create(sqlite3_rebaser **ppNew); + +/* +** CAPI3REF: Configure a changeset rebaser object. +** EXPERIMENTAL +** +** Configure the changeset rebaser object to rebase changesets according +** to the conflict resolutions described by buffer pRebase (size nRebase +** bytes), which must have been obtained from a previous call to +** sqlite3changeset_apply_v2(). +*/ +int sqlite3rebaser_configure( + sqlite3_rebaser*, + int nRebase, const void *pRebase +); + +/* +** CAPI3REF: Rebase a changeset +** EXPERIMENTAL +** +** Argument pIn must point to a buffer containing a changeset nIn bytes +** in size. This function allocates and populates a buffer with a copy +** of the changeset rebased rebased according to the configuration of the +** rebaser object passed as the first argument. If successful, (*ppOut) +** is set to point to the new buffer containing the rebased changset and +** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the +** responsibility of the caller to eventually free the new buffer using +** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) +** are set to zero and an SQLite error code returned. +*/ +int sqlite3rebaser_rebase( + sqlite3_rebaser*, + int nIn, const void *pIn, + int *pnOut, void **ppOut +); + +/* +** CAPI3REF: Delete a changeset rebaser object. +** EXPERIMENTAL +** +** Delete the changeset rebaser object and all associated resources. There +** should be one call to this function for each successful invocation +** of sqlite3rebaser_create(). +*/ +void sqlite3rebaser_delete(sqlite3_rebaser *p); + +/* ** CAPI3REF: Streaming Versions of API functions. ** ** The six streaming API xxx_strm() functions serve similar purposes to the @@ -1173,6 +1414,7 @@ ** <table border=1 style="margin-left:8ex;margin-right:8ex"> ** <tr><th>Streaming function<th>Non-streaming equivalent</th> ** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply] +** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2] ** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat] ** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert] ** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start] @@ -1268,6 +1510,23 @@ ), void *pCtx /* First argument passed to xConflict */ ); +int sqlite3changeset_apply_v2_strm( + sqlite3 *db, /* Apply change to "main" db of this handle */ + int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ + void *pIn, /* First arg for xInput */ + int(*xFilter)( + void *pCtx, /* Copy of sixth arg to _apply() */ + const char *zTab /* Table name */ + ), + int(*xConflict)( + void *pCtx, /* Copy of sixth arg to _apply() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ + ), + void *pCtx, /* First argument passed to xConflict */ + void **ppRebase, int *pnRebase, + int flags +); int sqlite3changeset_concat_strm( int (*xInputA)(void *pIn, void *pData, int *pnData), void *pInA, @@ -1305,6 +1564,13 @@ int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); +int sqlite3rebaser_rebase_strm( + sqlite3_rebaser *pRebaser, + int (*xInput)(void *pIn, void *pData, int *pnData), + void *pIn, + int (*xOutput)(void *pOut, const void *pData, int nData), + void *pOut +); /*
diff --git a/third_party/sqlite/src/ext/session/test_session.c b/third_party/sqlite/src/ext/session/test_session.c index aa248d3..3cd529a 100644 --- a/third_party/sqlite/src/ext/session/test_session.c +++ b/third_party/sqlite/src/ext/session/test_session.c
@@ -14,6 +14,10 @@ # endif #endif +#ifndef SQLITE_AMALGAMATION + typedef unsigned char u8; +#endif + typedef struct TestSession TestSession; struct TestSession { sqlite3_session *pSession; @@ -711,10 +715,8 @@ } -/* -** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT? -*/ -static int SQLITE_TCLAPI test_sqlite3changeset_apply( +static int SQLITE_TCLAPI testSqlite3changesetApply( + int bV2, void * clientData, Tcl_Interp *interp, int objc, @@ -727,18 +729,36 @@ int nChangeset; /* Size of buffer aChangeset in bytes */ TestConflictHandler ctx; TestStreamInput sStr; + void *pRebase = 0; + int nRebase = 0; + int flags = 0; /* Flags for apply_v2() */ memset(&sStr, 0, sizeof(sStr)); sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR); + /* Check for the -nosavepoint flag */ + if( bV2 && objc>1 ){ + const char *z1 = Tcl_GetString(objv[1]); + int n = strlen(z1); + if( n>1 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){ + flags = SQLITE_CHANGESETAPPLY_NOSAVEPOINT; + objc--; + objv++; + } + } + if( objc!=4 && objc!=5 ){ - Tcl_WrongNumArgs(interp, 1, objv, - "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?" - ); + const char *zMsg; + if( bV2 ){ + zMsg = "?-nosavepoint? DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?"; + }else{ + zMsg = "DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?"; + } + Tcl_WrongNumArgs(interp, 1, objv, zMsg); return TCL_ERROR; } if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){ - Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0); + Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[1]), 0); return TCL_ERROR; } db = *(sqlite3 **)info.objClientData; @@ -748,25 +768,69 @@ ctx.interp = interp; if( sStr.nStream==0 ){ - rc = sqlite3changeset_apply(db, nChangeset, pChangeset, - (objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx - ); + if( bV2==0 ){ + rc = sqlite3changeset_apply(db, nChangeset, pChangeset, + (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx + ); + }else{ + rc = sqlite3changeset_apply_v2(db, nChangeset, pChangeset, + (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx, + &pRebase, &nRebase, flags + ); + } }else{ sStr.aData = (unsigned char*)pChangeset; sStr.nData = nChangeset; - rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr, - (objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx - ); + if( bV2==0 ){ + rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr, + (objc==5) ? test_filter_handler : 0, + test_conflict_handler, (void *)&ctx + ); + }else{ + rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr, + (objc==5) ? test_filter_handler : 0, + test_conflict_handler, (void *)&ctx, + &pRebase, &nRebase, flags + ); + } } if( rc!=SQLITE_OK ){ return test_session_error(interp, rc, 0); + }else{ + Tcl_ResetResult(interp); + if( bV2 && pRebase ){ + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase)); + } } - Tcl_ResetResult(interp); + sqlite3_free(pRebase); return TCL_OK; } /* +** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT? +*/ +static int SQLITE_TCLAPI test_sqlite3changeset_apply( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + return testSqlite3changesetApply(0, clientData, interp, objc, objv); +} +/* +** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT? +*/ +static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + return testSqlite3changesetApply(1, clientData, interp, objc, objv); +} + +/* ** sqlite3changeset_apply_replace_all DB CHANGESET */ static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all( @@ -1019,6 +1083,125 @@ return TCL_OK; } +/* +** tclcmd: CMD configure REBASE-BLOB +** tclcmd: CMD rebase CHANGESET +** tclcmd: CMD delete +*/ +static int SQLITE_TCLAPI test_rebaser_cmd( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + struct RebaseSubcmd { + const char *zSub; + int nArg; + const char *zMsg; + int iSub; + } aSub[] = { + { "configure", 1, "REBASE-BLOB" }, /* 0 */ + { "delete", 0, "" }, /* 1 */ + { "rebase", 1, "CHANGESET" }, /* 2 */ + { 0 } + }; + + sqlite3_rebaser *p = (sqlite3_rebaser*)clientData; + int iSub; + int rc; + + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); + return TCL_ERROR; + } + rc = Tcl_GetIndexFromObjStruct(interp, + objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub + ); + if( rc!=TCL_OK ) return rc; + if( objc!=2+aSub[iSub].nArg ){ + Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg); + return TCL_ERROR; + } + + assert( iSub==0 || iSub==1 || iSub==2 ); + assert( rc==SQLITE_OK ); + switch( iSub ){ + case 0: { /* configure */ + int nRebase = 0; + unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase); + rc = sqlite3rebaser_configure(p, nRebase, pRebase); + break; + } + + case 1: /* delete */ + Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); + break; + + default: { /* rebase */ + TestStreamInput sStr; /* Input stream */ + TestSessionsBlob sOut; /* Output blob */ + + memset(&sStr, 0, sizeof(sStr)); + memset(&sOut, 0, sizeof(sOut)); + sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &sStr.nData); + sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR); + + if( sStr.nStream ){ + rc = sqlite3rebaser_rebase_strm(p, + testStreamInput, (void*)&sStr, + testStreamOutput, (void*)&sOut + ); + }else{ + rc = sqlite3rebaser_rebase(p, sStr.nData, sStr.aData, &sOut.n, &sOut.p); + } + + if( rc==SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(sOut.p, sOut.n)); + } + sqlite3_free(sOut.p); + break; + } + } + + if( rc!=SQLITE_OK ){ + return test_session_error(interp, rc, 0); + } + return TCL_OK; +} + +static void SQLITE_TCLAPI test_rebaser_del(void *clientData){ + sqlite3_rebaser *p = (sqlite3_rebaser*)clientData; + sqlite3rebaser_delete(p); +} + +/* +** tclcmd: sqlite3rebaser_create NAME +*/ +static int SQLITE_TCLAPI test_sqlite3rebaser_create( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + sqlite3_rebaser *pNew = 0; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME"); + return SQLITE_ERROR; + } + + rc = sqlite3rebaser_create(&pNew); + if( rc!=SQLITE_OK ){ + return test_session_error(interp, rc, 0); + } + + Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), test_rebaser_cmd, + (ClientData)pNew, test_rebaser_del + ); + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; +} + int TestSession_Init(Tcl_Interp *interp){ struct Cmd { const char *zCmd; @@ -1029,9 +1212,11 @@ { "sqlite3changeset_invert", test_sqlite3changeset_invert }, { "sqlite3changeset_concat", test_sqlite3changeset_concat }, { "sqlite3changeset_apply", test_sqlite3changeset_apply }, + { "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 }, { "sqlite3changeset_apply_replace_all", test_sqlite3changeset_apply_replace_all }, { "sql_exec_changeset", test_sql_exec_changeset }, + { "sqlite3rebaser_create", test_sqlite3rebaser_create }, }; int i;
diff --git a/third_party/sqlite/src/main.mk b/third_party/sqlite/src/main.mk index 7dafe79e..0f0e1f82 100644 --- a/third_party/sqlite/src/main.mk +++ b/third_party/sqlite/src/main.mk
@@ -65,7 +65,7 @@ fts3_write.o fts5.o func.o global.o hash.o \ icu.o insert.o json1.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ - memjournal.o \ + memdb.o memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ @@ -120,6 +120,7 @@ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ + $(TOP)/src/memdb.c \ $(TOP)/src/memjournal.c \ $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ @@ -363,6 +364,7 @@ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/mmapwarm.c \ $(TOP)/ext/misc/nextchar.c \ + $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ @@ -522,6 +524,7 @@ FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 +FUZZCHECK_OPT += -DSQLITE_PRINTF_PRECISION_LIMIT=1000 DBFUZZ_OPT = KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ ST_OPT = -DSQLITE_THREADSAFE=0 @@ -576,6 +579,9 @@ -DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) \ $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c $(TLIBS) $(THREADLIB) +sessionfuzz$(EXE): $(TOP)/test/sessionfuzz.c sqlite3.c sqlite3.h + $(TCC) -o sessionfuzz$(EXE) $(TOP)/test/sessionfuzz.c -lz $(TLIBS) $(THREADLIB) + mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c $(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ $(TLIBS) $(THREADLIB) @@ -897,14 +903,17 @@ queryplantest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/permutations.test queryplanner $(TESTOPTS) -fuzztest: fuzzcheck$(EXE) $(FUZZDATA) +fuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db ./fuzzcheck$(EXE) $(FUZZDATA) + ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db -fastfuzztest: fuzzcheck$(EXE) $(FUZZDATA) +fastfuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db ./fuzzcheck$(EXE) --limit-mem 100M $(FUZZDATA) + ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db -valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) +valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA) + valgrind ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db # The veryquick.test TCL tests. # @@ -1068,6 +1077,7 @@ rm -f mptester mptester.exe rm -f fuzzershell fuzzershell.exe rm -f fuzzcheck fuzzcheck.exe + rm -f sessionfuzz rm -f sqldiff sqldiff.exe rm -f fts5.* fts5parse.* rm -f lsm.h lsm1.c
diff --git a/third_party/sqlite/src/manifest b/third_party/sqlite/src/manifest index e85112ca..0b372620 100644 --- a/third_party/sqlite/src/manifest +++ b/third_party/sqlite/src/manifest
@@ -1,22 +1,22 @@ -C Version\s3.22.0 -D 2018-01-22T18:45:57.681 +C Version\s3.23.1 +D 2018-04-10T17:39:29.721 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea -F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2 +F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc ede26e3fb675e0b3b07627640ce5917154a6ee7f8f2c97424eb5ab5f651cbd56 -F README.md d748f58e3ab0fe0307fb4ae0942b415d93dcc4288756e366cc9e7cf8260c093f -F VERSION 0c10cdfed866fdd2d80434f64f042c3330f1daaed12e54287beb104f04b3faaf +F Makefile.msc bdcad21b027a56a73e54a1121cfb9edd0a35c0abfa53aa12c2f996006ff99960 +F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373 +F VERSION 7169eb6959db9ad1b7004ae3b754ef6e703eb7d8dde3b07d2e63103413eb25fb F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903 -F autoconf/Makefile.am 6cca3f797c649b40c762484ce26491839fec54de72d376d774969e76ed13931f -F autoconf/Makefile.msc 2c50a59319af7da4eaca8c13e3240881b1bc245fd175845a055faab7d03d6e67 +F autoconf/Makefile.am 2c274948734e03c51790ff51468f91db8d570bcca864284d9c6d6e777264cd7e +F autoconf/Makefile.msc 1223d1520e0b833041ad87b377fae61cc3e08d14c5aae4c1a9e36249225bd4e6 F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1 -F autoconf/configure.ac aeeed858e5e54e79052ae44ba774e56595dcb787f23a2155aa98a8aa27327b66 +F autoconf/configure.ac 18fca06f884213be062dd5e07c5297079cc45893d9cd3f522ce426e715033e3d F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873 F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43 @@ -32,7 +32,7 @@ F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 9af547be0e0e1a8fca8553b82599b5a3be1528a3d78deb68cb49d3b611215cb7 x +F configure 41d0e05b0d289c1c981aafe5c4070713c8e70b5a7d3472360764a3fce08a82a8 x F configure.ac d4529ebb26ae046269334f1dac65f2b1d6927c2efe22b2ec24dce24dfe4f83dd F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html 278113807f49d12d04179a93fab92b5b917a08771152ca7949d34e928efa3941 @@ -43,7 +43,7 @@ F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74 F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 -F ext/expert/expert.c 4791c5e064aea81b2b829fa95228b22283380ee370ea88a1e580103b75516ebf +F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test fd21496d8e52c817a7741f467f42b0502c0ac7e07dcdd1d6e15a3e8154ed4e41 F ext/expert/sqlite3expert.c 1dfa561e64dc0f89d56b96e6afda87468c34b43604c2df50c47e3f4362778fb2 F ext/expert/sqlite3expert.h af6354f8ee5c9e025024e63fec3bd640a802afcc3099a44d804752cf0791d811 @@ -96,7 +96,7 @@ F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004 F ext/fts3/fts3_unicode.c 525a3bd9a7564603c5c061b7de55403a565307758a94600e8a2f6b00d1c40d9d F ext/fts3/fts3_unicode2.c cc04fc672bfd42b1e650398cb0bf71f64f9aae032cfe75bbcfe75b9cf966029c -F ext/fts3/fts3_write.c a3f7bf869622d1d0aa66661ba71d88e6f9646d69a2c335f40a0addf25974db47 +F ext/fts3/fts3_write.c b583dede85eb0c3c3026f8d7ccb781ea4e845ae583754fecb2ca425b5907d87d F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73 @@ -111,11 +111,11 @@ F ext/fts5/fts5_aux.c ca666a3bbe07c5a3bbe9fffaea19c935a1efaf337333e28bad7bdd1971ffd093 F ext/fts5/fts5_buffer.c 1dd1ec0446b3acfc2d7d407eb894762a461613e2695273f48e449bfd13e973ff F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857 -F ext/fts5/fts5_expr.c 01048018d21524e2c302b063ff5c3cdcf546e03297215e577205d85b47499deb +F ext/fts5/fts5_expr.c c23a2e4c14c401a147c4a730460e5b37057627bf4be95515ee281cd87f4d277c F ext/fts5/fts5_hash.c 32be400cf761868c9db33efe81a06eb19a17c5402ad477ee9efb51301546dd55 -F ext/fts5/fts5_index.c 5fe14375a29e8a7aa8f3e863babe180a19269206c254c8f47b216821d4ac1e15 -F ext/fts5/fts5_main.c 24868f88ab2a865defbba7a92eebeb726cc991eb092b71b5f5508f180c72605b -F ext/fts5/fts5_storage.c fb5ef3c27073f67ade2e1bea08405f9e43f68f5f3676ed0ab7013bce5ba10be6 +F ext/fts5/fts5_index.c 22b71d0e9e4b3ddd123a39ae27174e0012da2806f91b64087a68584f13f189de +F ext/fts5/fts5_main.c da46761a7e9b582083fcb9f5a3ee50086205fb91f4e68d984a9946e64218e297 +F ext/fts5/fts5_storage.c 4bec8a1b3905978b22a67bca5f4a3cfdb94af234cf51efb36f4f2d733d278634 F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95 F ext/fts5/fts5_test_mi.c 65864ba1e5c34a61d409c4c587e0bbe0466eb4f8f478d85dc42a92caad1338e6 F ext/fts5/fts5_test_tok.c ffd657dd67e7fcdb31bf63fb60b6d867299a581d0f46e97086abacd66c2a9b26 @@ -126,7 +126,7 @@ F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba F ext/fts5/test/fts5_common.tcl b01c584144b5064f30e6c648145a2dd6bc440841 -F ext/fts5/test/fts5aa.test cba3fae6466446980caf1b9f5f26df77f95a999d35db7d932d6e82ae7ba0ede9 +F ext/fts5/test/fts5aa.test 87f4b50e755b52c6192c76ceccf4247d462bb44b52fa17358f273d8ce5d975f0 F ext/fts5/test/fts5ab.test 9205c839332c908aaad2b01ab8670ece8b161e8f2ec8a9fabf18ca9385880bb7 F ext/fts5/test/fts5ac.test a7aa7e1fefc6e1918aa4d3111d5c44a09177168e962c5fd2cca9620de8a7ed6d F ext/fts5/test/fts5ad.test e8cf959dfcd57c8e46d6f5f25665686f3b6627130a9a981371dafdf6482790de @@ -217,7 +217,7 @@ F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093 F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45 F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c -F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 +F ext/icu/README.txt a295e91db742b153e8dce8f7efd31d28ad1eea4df31ef4daa3eedc85be2f5138 F ext/icu/icu.c c2c7592574c08cd1270d909b8fb8797f6ea1f49e931e71dbcc25506b9b224580 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/lsm1/Makefile 98b0a24b45e248283d6bea4b6cb3e58d7b394edd8e96a0ac28c5fa5104813bad @@ -269,31 +269,32 @@ F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87 F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb F ext/misc/appendvfs.c 3777f22ec1057dc4e5fd89f2fbddcc7a29fbeef1ad038c736c54411bb1967af7 -F ext/misc/btreeinfo.c d7fd9a2fe2fa33ba28488e2fce703ebecc759219ea9e0bb3b254784866c0a676 +F ext/misc/btreeinfo.c 78c8c57d325185ccc04b7679e5b020e34a4d9c87453e6b7ac943d0a26cee3256 F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c6005 F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704 -F ext/misc/completion.c 52c3f01523e3e387eb321b4739a89d1fe47cbe6025aa1f2d8d3685e9e365df0f +F ext/misc/completion.c 0d0bd16378415b982e7119baddef52a0d2cc25860c238a9d2832b0cc6a84a16d F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189 F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11 -F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e -F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 -F ext/misc/fileio.c 06bd79dcc43d0887da27ffaadd69b8a698b1bafe203d1d134a3a2964f69368f9 +F ext/misc/dbdump.c 22018e00eb50e9ebf9067c92d4e7162dc5006a3efc4e0c19bc3829825a1043b0 +F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0e3 +F ext/misc/fileio.c 48c7751c78fc4cdd29d8c862fd2f3f98bbfefa2a3cf1ca1496df4bf02eb8cded F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25 F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984 F ext/misc/memvfs.c ab36f49e02ebcdf85a1e08dc4d8599ea8f343e073ac9e0bca18a98b7e1ec9567 F ext/misc/mmapwarm.c 70b618f2d0bde43fae288ad0b7498a629f2b6f61b50a27e06fae3cd23c83af29 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 +F ext/misc/normalize.c 19262ef3ef29d4de2f281b423326865c8916c63d0cb09f1dc98d24d5c1e8ba64 F ext/misc/percentile.c 92699c8cd7d517ff610e6037e56506f8904dae2e F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 540a169cb0d74f15522a8930b0cccdcb37a4fd071d219a5a083a319fc6e8db77 -F ext/misc/scrub.c 1c5bfb8b0cd18b602fcb55755e84abf0023ac2fb -F ext/misc/series.c f3c0dba5c5c749ce1782b53076108f87cf0b71041eb6023f727a9c50681da564 +F ext/misc/scrub.c db9fff56fed322ca587d73727c6021b11ae79ce3f31b389e1d82891d144f22ad +F ext/misc/series.c c7197db304f7009b08d6459a9de02e7f51ad0e1a3fdacbc1ebf5252a9a346959 F ext/misc/sha1.c 0b9e9b855354910d3ca467bf39099d570e73db56 F ext/misc/shathree.c 9e960ba50483214c6a7a4b1517f8d8cef799e9db381195178c3fd3ad207e10c0 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 -F ext/misc/spellfix.c 41cf26c6b89fcaa8798ae10ae64d39c1f1d9d6995152e545bd491c13058b8fac +F ext/misc/spellfix.c 54d650f44f3a69a851814791bd4d304575cdbbf78d96d4f0801b44a8f31a58c5 F ext/misc/sqlar.c 57d5bc45cd5492208e451f697404be88f8612527d64c9d42f96b325b64983d74 F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11 F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512 @@ -303,7 +304,8 @@ F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212 -F ext/misc/zipfile.c d99efb67ecdfcae7e1855984c218c8c33d0d46a833eaa4b5a5c3d7a4f6690ce4 +F ext/misc/zipfile.c c4de8f0ad446ce4a49aae11ff7b771cd7af60d7136c0bcfb53da1475b9075e79 +F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64 F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842 F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee @@ -320,7 +322,8 @@ F ext/rbu/rbuA.test 4e58e46e60d4064248614c43303d71f1b18cc804dd834ce6a913b3861828b28d F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2 F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831 -F ext/rbu/rbu_common.tcl a38e8e2d4a50fd6aaf151633714c1b1d2fae3ead +F ext/rbu/rbu_common.tcl ebb8d81f44dc20e360cff1f34eb2ad0def33128805c5b36afcc44ab338509589 +F ext/rbu/rbucollate.test 86d6fc9b8f59a27b7b5a6e20b5e29816d338a0dbdea8c54bfcc549a0d437f3ea F ext/rbu/rbucrash.test 61470d977a06a0abc2ec35b05d82a1d7d87d10f4ffabad14c1c231edc942ad66 F ext/rbu/rbucrash2.test b2ecbdd7bb72c88bd217c65bd00dafa07f7f2d4d F ext/rbu/rbudiff.test 3e605cf624d00d04d0fb1316a3acec4fbe3b3ac5 @@ -337,27 +340,27 @@ F ext/rbu/rbutemplimit.test cd553a9288d515d0b5f87d277e76fd18c4aa740b761e7880fab11ce986ea18d1 F ext/rbu/rbuvacuum.test ff357e9b556ca7ad4673da0ff7f244def919ff858e0f9f350d3e30fdd83a62a8 F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa -F ext/rbu/sqlite3rbu.c 64bd08c1011456f90564ed167abce3a9c2af421a924b21eb57231e078da04feb +F ext/rbu/sqlite3rbu.c f6e9ca388b5d4680fbf266a4d10a21aec11d6baf48f6d06fd53f6b205fad959f F ext/rbu/sqlite3rbu.h b42bcd4d8357268c6c39ab2a60b29c091e89328fa8cc49c8fac5ab8d007e79b2 -F ext/rbu/test_rbu.c 7073979b9cc80912bb03599ac8d85ab5d3bf03cfacd3463f2dcdd7822997533a +F ext/rbu/test_rbu.c baa23eb28457580673d2175e5f0c29ced0cd320ee819b13ad362398c53b96e90 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c 0dbae18c1b552f58d64f8969e4fb1e7f11930c60a8c2a9a8d50b7f15bdfd54bd F ext/repair/checkindex.c 7d28c01a2e012ac64257d230fc452b2cafb78311a91a343633d01d95220f66f3 F ext/repair/sqlite3_checker.c.in 4a5a3af3f450fe503e5a2985e98516dc2a6b9ad247449e284c1cf140fc91720f -F ext/repair/sqlite3_checker.tcl cc69e7fbc163f94da4a6400609be001543442d9f8f57a797d1eeb7b897585730 +F ext/repair/sqlite3_checker.tcl a9a2caa9660567257c177a91124d8c0dccdfa341e25c51e6da7f1fd9e601eafa F ext/repair/test/README.md 34b2f542cf5be7bffe479242b33ee3492cea30711e447cc4a1a86cb5915f419e F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc78249442da72ff3f8297398a69 F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c d941e44ad901da039caebb9f9fa99d81f2a4fc822e67cafe33fa4f6f789074a0 +F ext/rtree/rtree.c bc61010e978b5b8ae6dbb90274a2fbb5db5ff5e2880b5c6e8abd48eea77264db F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412 -F ext/rtree/rtree1.test 82a353747fcab1083d114b2ac84723dfefdbf86c1a6e1df57bf588c7d4285436 +F ext/rtree/rtree1.test 47e2095bebea6813754fd7afa6a20e2b7b4ebcd5cb7dbcb6932b6c9f86bbf972 F ext/rtree/rtree2.test 5f25b01acd03470067a2d52783b2eb0a50bf836803d4342d20ca39e541220fe2 F ext/rtree/rtree3.test 2cafe8265d1ff28f206fce88d114f208349df482 -F ext/rtree/rtree4.test 67b021858ba4334c8d49b3449476942c2ce0e5ef7123538f2e9dd508ed03a12d -F ext/rtree/rtree5.test 8aaa4bcdc42f718fe165572f5623e4732831aca95a2bc32482d33d4d2cf1325d -F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196 +F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd73452c4b +F ext/rtree/rtree5.test 49c9041d713d54560b315c2c7ef7207ee287eba1b20f8266968a06f2e55d3142 +F ext/rtree/rtree6.test 916a641d2beac01b9880871ff07612d56c1e466190a27c82ab36ffd58be03b9f F ext/rtree/rtree7.test c8fb2e555b128dd0f0bdb520c61380014f497f8a23c40f2e820acc9f9e4fdce5 F ext/rtree/rtree8.test 649f5a37ec656028a4a32674b9b1183104285a7625a09d2a8f52a1cef72c93f2 F ext/rtree/rtree9.test c646f12c8c1c68ef015c6c043d86a0c42488e2e68ed1bb1b0771a7ca246cbabf @@ -367,7 +370,7 @@ F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc F ext/rtree/rtreeE.test e65d3fc625da1800b412fc8785817327d43ccfec5f5973912d8c9e471928caa9 F ext/rtree/rtreeF.test 81ffa7ef51c4e4618d497a57328c265bf576990c7070633b623b23cd450ed331 -F ext/rtree/rtreeG.test fd3af1ca944a0bdb0cbb5455a4905c9f012e2fffcab6b791f07afa0dcbbcae0e +F ext/rtree/rtreeG.test 1b9ca6e3effb48f4161edaa463ddeaa8fca4b2526d084f9cbf5dbe4e0184939c F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl db734b4c5e75fed6acc56d9701f2235345acfdec750b5fc7b587936f5f6bceed F ext/rtree/rtreecheck.test 4d29103d1e16fcbf90135d1c637b833688492b063b2971dfb5dc6ba76555cfee @@ -376,10 +379,10 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a -F ext/session/session1.test 736d7ff178662f0b717c37f46531b84a5ce0210ccb0c4edf629c55dbcbbc3ea1 +F ext/session/session1.test 4532116484f525110eb4cfff7030c59354c0cde9def4d109466b0df2b35ad5cc F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0 F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479 -F ext/session/session4.test 457b02bdc349eb01151e54de014df77abd3c08c8 +F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40 F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169 F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26 F ext/session/session8.test 8e194b3f655d861ca36de5d4de53f702751bab3b @@ -390,26 +393,27 @@ F ext/session/sessionD.test d3617e29aa15c9413aee5286d99587633245d58d2ad28f3f331c822735418a22 F ext/session/sessionE.test 0a616c4ad8fd2c05f23217ebb6212ef80b7fef30f5f086a6633a081f93e84637 F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce -F ext/session/sessionG.test 63f9a744341d670775af29e4f19c1ef09a4810798400f28cd76704803a2e56ff +F ext/session/sessionG.test 3edde849c4071078d92bd682c836186f6e4e5a3fb6bcf3fc1de1a7caa5e4427d F ext/session/sessionH.test 332b60e4c2e0a680105e11936201cabe378216f307e2747803cea56fa7d9ebae -F ext/session/session_common.tcl 7776eda579773113b30c7abfd4545c445228cb73 +F ext/session/session_common.tcl ee925e0d233677e45e395fb1f559b84068ce7baa8aa1034441739d3e87ee249c F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723 F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 -F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 +F ext/session/sessionfault2.test 555a8504de03d59b369ef20209585da5aeb2671dedabc4584e9ffe6269689185 +F ext/session/sessionrebase.test 4e1bcfd26fd8ed8ac571746f56cceeb45184f4d65490ea0d405227cfc8a9cba8 F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc -F ext/session/sqlite3session.c 989466bba4dff0ede8d4c450b1fc65ca222b87e31193eddbf3931b88bf898a57 -F ext/session/sqlite3session.h 01774161cbd328fe3d496323655b9cc142317ff1fb1ae15c1232075ea240e3a4 -F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386 +F ext/session/sqlite3session.c 2d29bbd888599b94b2c8b31ff433675e008273a4d225b336508b18e6187fec1d +F ext/session/sqlite3session.h c01820d5b6e73e86d88008f4d1c1c7dfb83422963018292b864028a0400ceccf +F ext/session/test_session.c dba36c6c0153b22501112d3e8882b5c946cf617c955153b6712bd2f8ba1428c0 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 4d19895cb268021474ade6244119c80b8a3f1d910cb5b13cf5e04f5f37c3d61b +F main.mk 63668484c95454af7fc04a384da27ac556f27368d6d0c345e405e1677c66768f F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -422,81 +426,82 @@ F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594 -F src/analyze.c 6b42e36a5dcc2703a771f2411bd5e99524bd62c7ecde209bb88dfb04c72f046e -F src/attach.c 84c477e856b24c2b9a0983b438a707c0cf4d616cee7a425401d418e58afec24c +F src/analyze.c 71fbbeb7b25417592f54d869fe90c28b48e4cecb9926ef9b06d90fb0aec48941 +F src/attach.c f6f212c43dddba79dfcb723fb9470785f3ff55bde8953cd9d2546f3022070a41 F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73 F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca -F src/btree.c bfc453babec9aa8196ab5db5e588ac5d3f0c398d72faa37296167a84a61c9f2f +F src/btree.c 9eb9531c65346bbfccf5325384b7db1849daf4db6601dcfe21ba5c5b20623b64 F src/btree.h 0866c0a08255142ea0e754aabd211c843cab32045c978a592a43152405ed0c84 -F src/btreeInt.h 55b702efce17e5d1941865464227d3802cfc9c7c832fac81d4c94dced47a71fc -F src/build.c 9f9647454f236cab097f266ae970f899b53c71cadab6756c47e2b2e81392c2a1 +F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96 +F src/build.c 61320fb84034c24313de699f3385c6bfe093c925b4df2931c6eb63d7c94ec62a F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0 +F src/ctime.c bd9da3f1ff21b432564a16ef0b154cff03585dc43742842e99c58907c6cb4bef F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957 F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6 -F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720 +F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91 F src/delete.c 20c8788451dc737a967c87ea53ad43544d617f5b57d32ccce8bd52a0daf9e89b -F src/expr.c 9e06de431c09f144438aa6895ea4d4290fa3c6875bfcc3ba331012ca78deadf0 +F src/expr.c 6a41ceb27924dcfb6dc910a283ce74e136c9c305aba87a5acbfca32f5c49caa7 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331 -F src/func.c bd528d5ed68ce5cbf78a762e3b735fa75009f7197ff07fab07fd771f35ebaa1b -F src/global.c ac3094f1dc59fbeb919aef7cc0cc827a8459d1fb1adb7972ef75bd9e0c10b75b +F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51 +F src/global.c 01506976bd75e5e7b977207a6a05062e2dd0050012f8071be06bbea22ec6d69a F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 14686083cedc198540b15a79586cdd4be2acf6d5fa97627e355f817ab07e9fee +F src/insert.c b9ff71cc2913d1d57698a1e22bf853261a9a642baf62bdf40ddeb3809adb85b5 F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e F src/loadext.c f6e4e416a736369f9e80eba609f0acda97148a8b0453784d670c78d3eed2f302 -F src/main.c 26918d50dd4a61b8f6f210320a522f46b5e7e592335b6aa664ab15b80b7c239b +F src/main.c 1648fc7a9bcfdbfd9a9a04af96ff2796c3164b3f3c7e56ed63a3c51cd11d198d F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944 +F src/memdb.c e94c478a757c4307fd170fe0a7650ef4cf722c59e5a95a8a7896ffedc1679139 F src/memjournal.c 6f3d36a0a8f72f48f6c3c722f04301ac64f2515435fa42924293e46fc7994661 F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81 F src/mutex.c b021263554c8a3995e9d53193b8194b96d1ed28e06c3b532dd7f7d29cf0c7d53 F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85 F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 -F src/mutex_unix.c 27bb6cc49485ee46711a6580ab7b3f1402211d23 -F src/mutex_w32.c a898fa969823b100c0f5fdc57e54c9a1e419ab4d +F src/mutex_unix.c aaf9ebc3f89df28483c52208497a99a02cc3650011422fc9d4c57e4392f7fe58 +F src/mutex_w32.c 7670d770c94bbfe8289bec9d7f1394c5a00a57c37f892aab6b6612d085255235 F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7 -F src/os.c 22d31db3ca5a96a408fbf1ceeaaebcaf64c87024d2ff9fe1cf2ddbec3e75c104 +F src/os.c 1cb0d1d1b3a4267966dee6e292d2b2cdf88e47c0c59cebff27ecafac052dd165 F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 -F src/os_unix.c a82505be158d8ce42b38dcc9b426187d776904c12cdc68dc8925e1dfcc5cb6ce -F src/os_win.c 501dde1ee770f4ffa458bfe1cf376a556de3ab00bb8320d659c5984403991d62 +F src/os_unix.c 2b53b0b8ddc580db096252c721729e5f5f2f355b4fc056f8f3fb328aeb3c9e8a +F src/os_win.c eb03c6d52f893bcd7fdd4c6006674c13c1b5e49543fec98d605201af2997171c F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c 9b9cb4e06c03d43d62480a7a685a012d645fcf3a39e7767ccb505fb41ee083ec -F src/pager.h 581698f2177e8bd4008fe4760898ce20b6133d1df22139b9101b5155f900df7a -F src/parse.y 4e750e1b261ff9f1d0b6b5d40a829c66d691899f48953fde839d8b52d41aa148 -F src/pcache.c 7ae91a4557a43d77d449accbfdc68846e6516f8e2eda46e8bbe4536fb669b201 +F src/pager.c 1bb6a57fa0465296a4d6109a1a64610a0e7adde1f3acf3ef539a9d972908ce8f +F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388 +F src/parse.y 22ca6e5bb34bbf94e4f91bb1cae6fefad7c03c2e0f29fe9b14b4192e8421f234 +F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69 F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 -F src/prepare.c 259f4e7960c47082c9653f3d5f0c294abd68bb9c3aab86de7630700cba1c20fb -F src/printf.c 9506b4b96e59c0467047155f09015750cb2878aeda3d39e5610c1192ddc3c41c +F src/prepare.c b086fea6a1952db88beca31fdd621201ee5e4ce3f02905248cc3035a8174aa89 +F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 -F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730 +F src/resolve.c 66c73fcb7719b8ff0e841b58338f13604ff3e2b50a723f9b8f383595735262f6 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c bebe7cce45d899d2237c76bce059d525abf5b861f2fce92f6b53914a961c01ba -F src/shell.c.in 4e1bcf8c70b8fb97c7cbaca6602e2a291d7fe17eff23a5de003d6fabd87f27d1 -F src/sqlite.h.in 959deaad89679e31d7f68fda668b0c5d1f592fffed7a9c1740fb8ded4e4e754a +F src/select.c dfcd77a9bec9d2bcb221ed93c153cb38cc609faa6404e2dc0ae9491aac110112 +F src/shell.c.in cc960721e56ebc1a78773bb5d2f5608b54275f945cbe49e4afe919d6888062a7 +F src/sqlite.h.in e0be726ea6e4e6571724d39d242472ecd8bd1ba6f84ade88e1641bde98a6d02b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 -F src/sqlite3ext.h 99189e7611eb0bf98f21c7835dc74730a84e2e809c98e1e31c33896dee7a2849 -F src/sqliteInt.h 9c70315598b34810a83e4894455acb18e95cf63ce4e6cbb451ac2d17eabc2544 +F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d +F src/sqliteInt.h a4837c57f9a3e2af100bc59f4be60d16b823f18131f8cef6a6685440f775eebd F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b -F src/status.c 9737ed017279a9e0c5da748701c3c7bf1e8ae0dae459aad20dd64fcff97a7e35 +F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 -F src/tclsqlite.c 1833388c01e3b77f4c712185ee7250b9423ee0981ce6ae7e401e47db0319a696 -F src/test1.c b52f9e7fe62016d357c3266fcfa0793cc1883d3cb2b11dfa39fcba2e70b0305c +F src/tclsqlite.c 916a92de77ec5cbe27818ca194d8cf0c58aa7ad5b87527098f6aa5a6068800ce +F src/test1.c 1ab7cbbb6693e08364c1a9241e2aee17f8c4925e4cc52396be77ae6845a05828 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 @@ -511,7 +516,7 @@ F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857 F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274 -F src/test_config.c cc8a1d44648d9392a14f4ecfc841d027daaf61f952b9f70792edf11373aaa3dd +F src/test_config.c 097c6189803886a1fb26ec37d8bc62b90512cb53ab79a1fb6d35196c1ec42ded F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e F src/test_devsym.c 1960abbb234b97e9b920f07e99503fc04b443f62bbc3c6ff2c2cea2133e3b8a2 @@ -523,7 +528,7 @@ F src/test_intarray.h f3b7672f5d1056eac563c0d6ea8480a660b1475c F src/test_journal.c 619f2aa10e0d7a5f87c0f06825bc61dfce1c6b9c7f3ad990fb13de6c3b8874a3 F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd -F src/test_malloc.c 4f06a805de86be5216a127b3777ca2d5a1ff99d1a9238374ce136a47411be36c +F src/test_malloc.c 5201422e2403e66a7a9c2b7d8df806acd8d2a0429822adb7e932f324e7b5b3c6 F src/test_md5.c 7268e1e8c399d4a5e181b64ac20e1e6f3bc4dd9fc87abac02db145a3d951fa8c F src/test_multiplex.c e054459f7633f3ff8ce1245da724f9a8be189e4e F src/test_multiplex.h 5436d03f2d0501d04f3ed50a75819e190495b635 @@ -545,34 +550,34 @@ F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_windirent.c a895e2c068a06644eef91a7f0a32182445a893b9a0f33d0cdb4283dca2486ac1 -F src/test_windirent.h 8782864172ba5ae52c5c313c70faeadb324ff74de9c3dcc6b56a557dccaa1de6 +F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a90484215 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 5b0c661a85f783d35b9883830736eeb63be4aefc4f6b7d9cd081d48782c041e2 -F src/treeview.c eae35972ff44f67064de2eaf35f04afe94e7aea3271a8b3bcebb3f954880fec3 +F src/treeview.c 14d5d1254702ec96876aa52642cb31548612384134970409fae333b25b39d6bb F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1 -F src/update.c a90a32ffc0100265b0693dbbdbe490756447af181f5ea2c138cce515b08c8795 +F src/update.c 97d4c9514229f540f8c441e124d5af7f93c5b030c9574539d01e99462e273998 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 -F src/util.c ef4a5f904d942e660abade7fbf3e6bdb402dabe9e7c27f3361ecf40b945538b5 -F src/vacuum.c 90839322fd5f00df9617eb21b68beda9b6e2a2937576b0d65985e4aeb1c53739 -F src/vdbe.c ccc1e17a30325068ae4f0292e8601997946886d23acc989c68f2a261a2795c70 +F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157 +F src/vacuum.c 762ee9bbf8733d87d8cd06f58d950e881982e416f8c767334a40ffd341b6bff5 +F src/vdbe.c 066a4e1de2ed83e253adfd2e97a684cf562eaa41d31ee7f3d3e4c8aea4485a55 F src/vdbe.h 134beb7a12a6213c00eba58febaede33447cc4441bc568a0d9c144b33fc3720a -F src/vdbeInt.h c8cfbbc28e37e67a493c3f892fb0596add56a31a00e7537a06049af9ef2f51b0 -F src/vdbeapi.c 02f773681d06e46454b0606339068d4d4490873dc4a7334bc0c6030552bb2c8c +F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110 +F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858 F src/vdbeaux.c 2756ac68ac259c416554100598fc291870063288cd7e1af22847f57b3e130e56 F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191 -F src/vdbemem.c 7548dd5af03d24d534a5dbc41e3bbdf1fab83e9c8856a8d2549ed2ccf33d0e80 +F src/vdbemem.c 414e28d3a7e2a8bee2bb247de115dcbc68e3cbac284d5862d077002f7a93bce1 F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f F src/vdbetrace.c 48e11ebe040c6b41d146abed2602e3d00d621d7ebe4eb29b0a0f1617fd3c2f6c F src/vtab.c 0e4885495172e1bdf54b12cce23b395ac74ef5729031f15e1bc1e3e6b360ed1a F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c 5a3f464edd64596f601683ed321d12e6fd93c5fb9afdfb3653d6ffd0fee9c48f +F src/wal.c aa9cffc7a2bad6b826a86c8562dd4978398720ed41cb8ee7aa9d054eb8b456a0 F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f -F src/where.c caf0b6c9d31f22f0b2c91aba723858de52b5d665aaa89034099015aaf9bb8219 -F src/whereInt.h 82c04c5075308abbac59180c8bad5ecb45b07453981f60a53f3c7dee21e1e971 -F src/wherecode.c af1e79154aaa88cd802d6f2e5b945f67eaca7c958d1525fbf8ee19d5bd7b9020 -F src/whereexpr.c 427ea8e96ec24f2a7814c67b8024ad664a9c7656264c4566c34743cb23186e46 +F src/where.c d6e5f2056e9a60251e79780fc598a5943e88a3c0fa0019d54922e59f99019287 +F src/whereInt.h 2610cb87dd95509995b63decc674c60f2757697a206cfe0c085ee53d9c43cfff +F src/wherecode.c 982b7450c53fb272f61a1d20c93e960260ea4dfe8e2e9bacc190e2a041a1f1a4 +F src/whereexpr.c 53532be687e12f3cd314f1e204cd4fbdac7ad250e918a182b048121e16e828ae F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -586,7 +591,7 @@ F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f -F test/analyze.test 3eb35a4af972f98422e5dc0586501b17d103d321 +F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df F test/analyze3.test 8b3ef8ba6d1096b76c40e0925c0fe51e700d2b779cdda40914580de3f9b9d80f F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213 F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4 @@ -625,7 +630,7 @@ F test/autoindex5.test 96f084a5e6024ea07cace5888df3223f3ea86990 F test/autovacuum.test 0831cd34e14695d297187f7f6519265e3121c5b0a1720e548e86829e796129e9 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 -F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 +F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989 F test/backup2.test 1fd1ad8c5b3d2d5b9c0cce4143a4fc610d51ddc6ae16a7a122973d43e6b50bbd @@ -672,7 +677,7 @@ F test/capi3c.test 7ebed1d8fa2f3190149d556fe8cff5a006be62af437c5c4640db614470126098 F test/capi3d.test 485048dc5cd07bc68011e4917ad035ad6047ab82 F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe -F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 +F test/cast.test 5ceb920718d280b61163500a7d29e0e0a86458b1cbd92d96f962c9d970aa3857 F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef F test/check.test 33a698e8c63613449d85d624a38ef669bf20331daabebe3891c9405dd6df463a F test/close.test 799ea4599d2f5704b0a30f477d17c2c760d8523fa5d0c8be4a7df2a8cad787d8 @@ -725,7 +730,7 @@ F test/crash5.test 05dd3aa9dbb751a22d5cdaf22a9c49b6667aa219 F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba F test/crash7.test 1a194c4900a255258cf94b7fcbfd29536db572df -F test/crash8.test 63cd5aea313222d7a69637cf7174c34d151676cc187d57193b66d4c89dedede3 +F test/crash8.test 64366e459c28dd62edfb7ad87253a409c7533b92d16fcc479a6a8131bdcc3100 F test/crashM.test d95f59046fa749b0d0822edf18a717788c8f318d F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8 @@ -733,13 +738,13 @@ F test/csv01.test 526fc6aefd052badd5a0283f86b4b395c3df76bfe98d96c801f494f5e2c7836c F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3 F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856 -F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b +F test/cursorhint2.test 0078ae1ded4afcf5eb80d06e3a72b6e1c3f1a646aab26eeb583b0a9ec6f0d56e F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373 F test/date2.test 74c234bece1b016e94dd4ef9c8cc7a199a8806c0e2291cab7ba64bace6350b10 F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e F test/dbpage.test dbf50a4d361f9e45a979432c727506065113124478a7d2db12074fa655e65d6c -F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5 -F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab +F test/dbstatus.test c15fa97f743dac7ce996814c84b56317e138895ee15ce27f15b608aa6924c90a +F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d F test/delete.test acc38fca8ee4851467705b1c2cfea64cd26667e5 F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa @@ -768,7 +773,7 @@ F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e F test/e_reindex.test 2bebf7b393e519198b7c654407221cf171a439b8 F test/e_resolve.test a61751c368b109db73df0f20fc75fb47e166b1d8 -F test/e_select.test 16651bb681e83a1a2875ff4a595ed2b4b4dee375 +F test/e_select.test 6fd45fd4a59ec82b6dda7468699dcc0ec1a72538577750b4f90357a62c1d2723 F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10 F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528 @@ -790,7 +795,7 @@ F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac -F test/expr.test 66a2c9ac34f74f036faa4092f5402c7d3162fc93 +F test/expr.test 7cb55e80aeb41d65fec968c08212505123063fea60bdc355d764d747670e9eea F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9 F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79 F test/fallocate.test 07416bd593a116d5893cb244f45a94d5c6fe030561df3bd972e6135f8106e509 @@ -844,7 +849,7 @@ F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654 F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0 -F test/fts3aa.test 39b65c11913d277c91d7426c62cfc1d147d1b4e9a48fecd9e38f60d0b5a5f505 +F test/fts3aa.test f267fcd6aca30fc70b81e5d82b68b34b38f581896020b57ed49e9777c7ebd85f F test/fts3ab.test 7f6cf260ae80dda064023df8e8e503e9a412b91f F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63 F test/fts3ad.test e40570cb6f74f059129ad48bcef3d7cbc20dda49 @@ -894,7 +899,7 @@ F test/fts3prefix.test fa794eaab0bdae466494947b0b153d7844478ab2 F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce F test/fts3query.test f33eb71a1fe1084ea585eeb7ee76b390729f5170 -F test/fts3rank.test e4d2e16a28c98cae95001a75e2b4b05b19b051ffd6aaab15491c5e0595127b9b +F test/fts3rank.test cd99bc83a3c923c8d52afd90d86979cf05fc41849f892faeac3988055ef37b99 F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0 F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e F test/fts3snippet.test 01a4231816e03a0660ae53ba2404fe69012fe0db @@ -916,23 +921,23 @@ F test/fts4merge3.test 8d9ccb4a3d41c4c617a149d6c4b13ad02de797d0 F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b F test/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309 -F test/fts4onepass.test 7319d61a2ed1325fc54afd0c060a0513b462303a +F test/fts4onepass.test d69ddc4ee3415e40b0c5d1d0408488a87614d4f63ba9c44f3e52db541d6b7cc7 F test/fts4opt.test fd6a11684b965e1999564ae763797b7fb9e34c96 F test/fts4unicode.test ceca76422abc251818cb25dabe33d3c3970da5f7c90e1540f190824e6b3a7c95 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef +F test/func.test 09dda479bcfc568f99f3070413e9672a8eeedc1be9c5d819bf55d4788c2583b7 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test d202a7606d23f90988a664e88e268aed1087c11c F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f F test/func5.test cdd224400bc3e48d891827cc913a57051a426fa4 -F test/func6.test a4281c8fcd42b56f7a60f28e8e4d444e8b2256f9e82658b7ab87699f8318f564 +F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1 F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c31 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzcheck.c 2152602232c96d9c790eff3013e1369ce59de3203fa0b75bc613531448454e61 +F test/fuzzcheck.c 5eb86c6ac96833ee622f45bf47e8045999c1b4b10d05e4eb809894a4b39f2f84 F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664 F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973 F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -977,7 +982,7 @@ F test/index8.test bc2e3db70e8e62459aaa1bd7e4a9b39664f8f9d7 F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721 F test/indexedby.test faa585e315e868f09bce0eb39c41d6134649b13d2801638294d3ae616edf1609 -F test/indexexpr1.test ace1ad489adc25325ad298434f13b1a515b36bf5dca9fe2a4b66cdf17aea3fa0 +F test/indexexpr1.test 635261197bcdc19b9b2c59bbfa7227d525c00e9587faddb2d293c44d287ce60e F test/indexexpr2.test 13247bac49143196556eb3f65e97ef301bd3e993f4511558b5db322ddc370ea6 F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 @@ -993,17 +998,18 @@ F test/interrupt2.test e4408ca770a6feafbadb0801e54a0dcd1a8d108d F test/intpkey.test ac71107a49a06492b69b82aafaf225400598d3c8 F test/io.test f95bca1783b01ea7761671560d023360d2dfa4cc -F test/ioerr.test 2a24bd6ed5a8b062e64bfe1f6cf94fb25e92210d +F test/ioerr.test 470fcc78e9cd352d162baf782fe301ea807d764241f58a48fc58109c2dfcdb6b F test/ioerr2.test 2593563599e2cc6b6b4fcf5878b177bdd5d8df26 F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b -F test/join.test 442c462eea85cf065d70a663c626b780a95af6e11585d909bb63b87598afe678 -F test/join2.test 1a0c26399910b015d9f8f95b884e9a079fd2cfdccd65f7b1603846508cae0dc6 +F test/istrue.test d6e659764da5ccc03adcdba18fe77d7917ba5e4abd04ef14bd4e4cf43e024b5b +F test/join.test 2ad9d7fe10e0cc06bc7803c22e5533be11cdadbc592f5f95d789a873b57a5a66 +F test/join2.test f5ea0fd3b0a441c8e439706339dcd17cec63a896a755c04a30bfd442ecce1190 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 -F test/join5.test bc98ea4b4e5003f5b1453701ebb8cd7d1c01a550 +F test/join5.test c6bd62effc37a152bea735f9ef241b19bb967bd4593dc99b20e2fc55ae707e38 F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497 F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4 @@ -1011,12 +1017,12 @@ F test/jrnlmode.test a6693f2bed4541a21e703aaa37bb3e10de154130645952933b82b2dec0a8b539 F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/json101.test d7cdf3e6731d41e0c4bde1c88806abd17f1f478486a1409933c1d8eac9120095 +F test/json101.test 24e97954e3bd6404f3715888c7f8f835e36e19c7ae6513b5d9ab2d381498962d F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb28487bc1 F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0 F test/json104.test 877d5845f6303899b7889ea5dd1bea99076e3100574d5c536082245c5805dcaa F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff -F test/kvtest.c fcb38ffe3db028a3138b4818fc098359c80dc51a0d1278a91c99c554cc1abb92 +F test/kvtest.c 94da54bb66aae7a54e47cf7e4ea4acecc0f217560f79ad3abfcc0361d6d557ba F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 F test/like.test 11cfd7d4ef8625389df9efc46735ff0b0b41d5e62047ef0f3bc24c380d28a7a6 @@ -1037,8 +1043,8 @@ F test/lookaside.test b17c99ae3aef96a8c9fa6f6be33cc75b93d657cb791d3827302b6835b71941f7 F test/main.test 6bbb3999fd461eb8fb335cbab97409a3d7f91bbb8da60635e8be3e4a04a77772 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 -F test/malloc.test 21c213365f2cca95ab2d7dc078dc8525f96065f8 -F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a +F test/malloc.test 18dd1c4188c81ca79cf123527c71b19ee0c31feb9947fdffb0dc6ceb1436816a +F test/malloc3.test 6e88bae6312854a4adb4ecc2a6a5ea8c59b4db778b724ba718e1c43fc8c3c136 F test/malloc4.test 957337613002b7058a85116493a262f679f3a261 F test/malloc5.test f6eb6eca07a4c75f2897bf43a404689b6295bb95ab2e07d4b52eda743f925a27 F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151 @@ -1063,6 +1069,7 @@ F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f F test/memdb.test c1f2a343ad14398d5d6debda6ea33e80d0dafcc7 +F test/memdb1.test 61aa1dbdeea6320791d2ff42a9a6149d5716be674bf06ee0ffa0aad1bf3eb5f8 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test 9e7555a22173b8f1c96c281ce289b338fcba2abe8b157f8798ca195bbf1d347e F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08 @@ -1076,8 +1083,8 @@ F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3 F test/misc5.test 60e1fc758a93cacd19eb2fafcd1d40d150a05047546c7a92389c98047d621901 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 -F test/misc7.test 859894e3192257ce2fc4063b5438b220e352286974b387e485050f0ad1f665d6 -F test/misc8.test ba03aaa08f02d62fbb8d3b2f5595c1b33aa9bbc5 +F test/misc7.test 567e223b6497da2226a0340befaf2d663c91ad57a48aede21a35a984a2882d41 +F test/misc8.test 8fb0f31d7a8aed484d759773ab8ad12ec746a477f4a67394a4af0e677494c3ca F test/misuse.test 9e7f78402005e833af71dcab32d048003869eca5abcaccc985d4f8dc1d86bcc7 F test/mjournal.test 9d86e697dcbc5da2c4e8caba9b176b5765fe65e80c88c278b8c09a917e436795 F test/mmap1.test d2cfc1635171c434dcff0ece2f1c8e0a658807ce @@ -1093,17 +1100,21 @@ F test/mutex1.test ea2cc74d97f077b9e74c84cbd024f14d79a8126f F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/nan.test 437d40e6d0778b050d7750726c0cbd2c9936b81962926e8f8c48ca698f00f4d1 -F test/nockpt.test 9a436a7213ba5ef7a32304998d386d3ea3f76c9d +F test/nockpt.test 8c43b25af63b0bd620cf1b003529e37b6f1dc53bd22690e96a1bd73f78dde53a F test/nolock.test f196cf8b8fbea4e2ca345140a2b3f3b0da45c76e +F test/normalize.test 501630ab49b0b26b65c74124bf03e3374c1b57fa97aae750f84803609141d167 F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161 F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934 -F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62 +F test/notnull.test b6999231221df3534827e45e2005dd7a815fdd5f2c2e1afb9be21ead410816f8 F test/null.test 0dcce4f04284ec66108c503327ad6d224c0752b3 F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1 F test/numindex1.test 20a5450d4b056e48cd5db30e659f13347a099823 F test/offset1.test f06b83657bcf26f9ce805e67450e189e282143b2 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 +F test/optfuzz-db01.c a0c256905c8ac79f9a5de2f374a3d9f757bef0dca2a238dc7c10cc8a38031834 +F test/optfuzz-db01.txt 21f6bdeadc701cf11528276e2a55c70bfcb846ba42df327f979bd9e7b6ce7041 +F test/optfuzz.c 50e330304eb1992e15ddd11f3daaad9bcc0d9aaad09cb2bcc77f9515df2e88b1 F test/orderby1.test 4d22a7c75f6a83fc1f188cc7bb5192285fdf2552 F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04 F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99 @@ -1114,14 +1125,14 @@ F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3 F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f -F test/ossfuzz.c 7f5cc87a0280a5854c1bfa7d5c4d07d34731f08ec34dc9c916aa35ed292b1468 -F test/ossshell.c 296ab63067841bd1b1e97b46a0b2af48ee7f69d50d1a723008bee12dd7122622 +F test/ossfuzz.c c4c4547e2c92ac52f10038b073a03248251a23c1c559728f63a18aeca0e79f03 +F test/ossshell.c f125c5bd16e537a2549aa579b328dd1c59905e7ab1338dfc210e755bb7b69f17 F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f F test/pager1.test f596d3bd53ce96e1d87d44d223d2ae6c8867dd782c425e5eb28b5721fa6aaa97 F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 F test/pager3.test 4e9a83d6ca0838d7c602c9eb93d1357562d9059c1e02ffb138a8271020838370 F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e -F test/pagerfault.test 263c5442c06caf0b9b9e3fe42acdeb11f254dcebe533f69f401aaef9111eaf20 +F test/pagerfault.test 63c5da625562c66345ab4528790327ca63db2f6f9cbae2aba8cb7c51de3d1628 F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8 F test/pageropt.test 84e4cc5cbca285357f7906e99b21be4f2bf5abc0 @@ -1138,7 +1149,7 @@ F test/pragma5.test 824ce6ced5d6b7ec71abe37fc6005ff836fe39d638273dc5192b39864b9ee983 F test/pragmafault.test 275edaf3161771d37de60e5c2b412627ac94cef11739236bec12ed1258b240f8 F test/printf.test b3ff34e73d59124140eaf89f7672e21bc2ca5fcc -F test/printf2.test 9e6db85f81c63f2367c34a9d7db384088bd374ad +F test/printf2.test 30b5dd0b4b992dc5626496846ecce17ff592cacbcb11c3e589f3ac4d7e129dae F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/pushdown.test 5e72c51c5e33253ed639ccee1e01ce62d62b6eee5ca893cd82334e4ee7b1d7fc @@ -1155,7 +1166,7 @@ F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 -F test/releasetest.tcl 6aaa853f7a7bbdc458d4cb42c0425228729b0f3e5769e9b41088c08eee999a49 x +F test/releasetest.tcl 5f15ab8056799e9a6e26a310d49236d2e774d6a30d0ec74601e18d4ce146b79c x F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5 @@ -1163,7 +1174,7 @@ F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d -F test/rowvalue.test 44f3492f415cc9f374e8388a5eb61503eaca5230 +F test/rowvalue.test 32861d6a933ded868035f2ec79aeb993a2a46eb7a6d282ae13415a4c2e369463 F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256 F test/rowvalue4.test 4b556d7de161a0dd8cff095c336e913986398bea @@ -1209,6 +1220,8 @@ F test/selectG.test 089f7d3d7e6db91566f00b036cb353107a2cca6220eb1cb264085a836dae8840 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be +F test/sessionfuzz-data1.db 1f8d5def831f19b1c74571037f0d53a588ea49a6c4ca2a028fc0c27ef896dbcb +F test/sessionfuzz.c b0fcdcf757451957e17396a3af5171f1fdf9b2babc81da9fa35675df46c4729a F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879 F test/shared3.test ab693f9b6e156b8bfb2a0ad94f29fe69602a5d38 @@ -1221,7 +1234,7 @@ F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 9f8b8da05a79b134e252a5e1d8d411245ad83ac7126c262900b9f42b43108ffd +F test/shell1.test e2f7d375ae80eb16590af23d6c40cd0140b6d1f92642c1b71eb94630a144ee08 F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494 F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d @@ -1248,7 +1261,7 @@ F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0 F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5 F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c -F test/sort5.test 30cc17768e0c06ecb048e08efec59c11811fd186 +F test/sort5.test 6b43ae0e2169b5ceed441844492e55ba7f1ae0790528395ddf7888ab3094525d F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66 F test/speed1.test f2974a91d79f58507ada01864c0e323093065452 F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb @@ -1257,11 +1270,12 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa -F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/speedtest1.c a5faf4cbe5769eee4b721b3875cb3f12520a9b99d9026b1063b47c39603375b8 -F test/spellfix.test f9c1f431e2c096c8775fec032952320c0e4700db +F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c +F test/speedtest1.c 20cc4028b0e88392b5a635c2ea5d5e777d569bf7258aead37f8be7a886c38344 +F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 +F test/spellfix4.test 51c7c26514ade169855c66bcf130bd5acfb4d7fd090cc624645ab275ae6a41fb F test/sqldiff1.test 28cd737cf1b0078b1ec1bbf425e674c47785835e F test/sqllimits1.test a74ee2a3740b9f9c2437c246d8fb77354862a142 F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a @@ -1271,7 +1285,7 @@ F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5 F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49 F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f -F test/subquery2.test 438f8a7da1457277b22e4176510f7659b286995f +F test/subquery2.test 8250dfd6a773b04c7a5c37ac63276f62b329157ce171244d0cbe1acc365e3303 F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8 @@ -1289,16 +1303,16 @@ F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 -F test/tclsqlite.test c3d7ac9449634b9f17fd048a3c0212e88a7448be810a9c5bd051acc1ffa00d2f +F test/tclsqlite.test 5337e8890b96dad1ee541b15fbeec32e6bac2fe7fa096f91089057385aadba9b F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08 -F test/tempdb2.test 27e41ed540b2f9b056c2e77e9bddc1b875358507 +F test/tempdb2.test 4749545409c6d7438b435c3f05cdd139cf4145a954a6908d19e3443ffd8724b3 F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900 F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 -F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e +F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc -F test/tester.tcl 3ed81b9e1d9718a8d9603596c8a877793d054294053c4277a3d3897eabab3866 -F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 +F test/tester.tcl 94901a4625d9a2229666dd5c44120ddf7f0fb639470710ef74a4cefc7b039e07 +F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 F test/thread004.test f51dfc3936184aaf73ee85f315224baad272a87f @@ -1455,7 +1469,7 @@ F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4 F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983 -F test/trace3.test 56ab944fddacf628b118cc298503fc45c2e50ab0 +F test/trace3.test 1dff966888773ff1bfea01c080caf15417892b3f998408fe920c4791f7337144 F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 @@ -1553,11 +1567,11 @@ F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6 F test/walprotocol.test a112aba0b79e3adeaa485fed09484b32c654e97df58e454aa8489ac2cd57bf84 F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20 -F test/walro2.test 6c73e8e4b5ccc55f907f4603ba36458b45c085fb6dfb04f30e3c0babbc1c2f41 +F test/walro2.test 0e79dd15cbdb4f482c01ea248373669c732414a726b357d04846a816afafb768 F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f -F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e +F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747 F test/where.test f0c325563acde44f2c4ea6ba348e9e29f7121757 F test/where2.test 478d2170637b9211f593120648858593bf2445a1 F test/where3.test 54cdeb02157acc979de41530b804ae7b09552bf1 @@ -1572,7 +1586,7 @@ F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6 F test/whereD.test 711d4df58d6d4fb9b3f5ce040b818564198be002 F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f -F test/whereF.test d44b58338fe5ddd7286023e9bedb255aa264a6c4d2168b49591b167371c675c7 +F test/whereF.test 3d9412b1199d3e2bed34fcb76b4c48d0bf4df95d27e3f8dd27b6f8b4716d0d89 F test/whereG.test dde4c52a97385a55be6a7cd46be8373f0cf35501 F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2 F test/whereI.test eab5b226bbc344ac70d7dc09b963a064860ae6d7 @@ -1590,8 +1604,9 @@ F test/with1.test ca08e291249a810a2ec9b72ceef5575e07d5925b360fcf6652ae6fe06ac4dced F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab F test/with3.test e71604a0e53cba82bc04c703987cb1d6751ec0b6 +F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205 F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64 -F test/without_rowid1.test 06b7215130882d6a072233820dd364c874c4fd69221e8fc756ec471009192874 +F test/without_rowid1.test 1cb47a1a5ba5b2946f18703fabf9fb2a237b0a8180538793ecbaed834d0df765 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 F test/without_rowid3.test 2724c787a51a5dce09d078453a758117b4b728f1 F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a @@ -1601,11 +1616,13 @@ F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc -F test/zipfile.test cb42e8fa6ba5db4a03ce6baa4401fc6236baf6eb5e62b44f3e463bf6aafd631d +F test/zipfile.test a61f6ba6dbaaf4983849df84a31df140c7ddd1362e2fa9ecd3cdf5cd123b7f18 +F test/zipfile2.test fc2f08d5ec19c18c83289fbed32e378dc5116519972166e57a244da7bf2e5805 +F test/zipfilefault.test 44d4d7a7f7cca7521d569d7f71026b241d65a6b1757aa409c1a168827edbbc2c F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5 F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91 -F tool/addopcodes.tcl 7181c041d495e3f26acc36d15c86923ed722285f9015f017f41a3efdb9a0dab4 +F tool/addopcodes.tcl 0288d5b26b9b35f4cb5affb76eec63f1dfce117bbc2020066708069ef60b86ff F tool/build-all-msvc.bat c12328d06c45fec8baada5949e3d5af54bf8c887 x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/cg_anno.tcl f95b0006c52cf7f0496b506343415b6ee3cdcdd3 x @@ -1619,8 +1636,8 @@ F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f -F tool/lemon.c 7f7735326ca9c3b48327b241063cee52d35d44e20ebe1b3624a81658052a4d39 -F tool/lempar.c da840fc8a6fbac23599a65ff075e6e3d01320417c794ff577088e09f5d74b689 +F tool/lemon.c c1a87d15983f96851b253707985dba8783fbe41ba21ba1b76720e06c8a262206 +F tool/lempar.c 468a155e8729cfbccfe1d85bf60d064f1dab76167a51149ec5c7928a2de63953 F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9 F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862 F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca @@ -1632,14 +1649,14 @@ F tool/mkmsvcmin.tcl 8baf26690b80d861d0ac341b29880eec6ade39e4f11fe690271ded9cb90563a3 F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee -F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e +F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa F tool/mkpragmatab.tcl 2144bc8550a6471a029db262a132d2df4b9e0db61b90398bf64f5b7b3f8d92cd F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae4202626f5 F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb -F tool/mksqlite3c.tcl 1fb69d39166f52d802a70ec37d99bca51d011c8ab30be27bc495be493196ae41 -F tool/mksqlite3h.tcl f92f994d9709aeb9e2b6e6f9fc8b069d2f55202c8e23f453edc44390a25982dc +F tool/mksqlite3c.tcl a03cee30de81a2e67b93e5c659f24113a003677c557daeb008205c8e6d4345d6 +F tool/mksqlite3h.tcl 080873e3856eceb9d289a08a00c4b30f875ea3feadcbece796bd509b1532792c F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5 F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091 @@ -1658,7 +1675,7 @@ F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847 F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe F tool/spaceanal.tcl 4bfd19aad7eb3ce0372ef0255f58035e0bba4ff5e9acfd763a10c6fb365c8dec -F tool/speed-check.sh 9ae425da8819e54e780cf494fc6d8175dfb16e109ae3214a45a5c9bb2b74e2c4 +F tool/speed-check.sh 4ff9b095cf1a7643f0264e7fb7d23f0b12b7cce587a9de315877c378e90eeaf4 F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355 F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff @@ -1700,11 +1717,11 @@ F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 395f8ea790e6e295800fa8927f0585b2419b9521ef4fd591d51d2a48db2a90c4 -R e7cccb4f976b9eb07c1586193cc26ec5 +P 1fdaf2c34431adcac1c7ff29aae0623c4cbaa6a7f38e843c786bd407d8b3e730 +R 4cac5ebce3c2344d130b0b75df1b28c6 T +bgcolor * #d0c0ff T +sym-release * -T +sym-version-3.22.0 * +T +sym-version-3.23.1 * U drh -Z 9c0e858ece53e1ee810a8a479ae4dc82 +Z 96974a1a3d914d4bcb8af6c333b7c418 # Remove this line to create a well-formed manifest.
diff --git a/third_party/sqlite/src/manifest.uuid b/third_party/sqlite/src/manifest.uuid index c5f08c6..6b1d3738 100644 --- a/third_party/sqlite/src/manifest.uuid +++ b/third_party/sqlite/src/manifest.uuid
@@ -1 +1 @@ -0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2171d +4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd148b3b
diff --git a/third_party/sqlite/src/src/analyze.c b/third_party/sqlite/src/src/analyze.c index 6273c5a..179ee4b 100644 --- a/third_party/sqlite/src/src/analyze.c +++ b/third_party/sqlite/src/src/analyze.c
@@ -1015,7 +1015,7 @@ /* Do not gather statistics on views or virtual tables */ return; } - if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){ + if( sqlite3_strlike("sqlite\\_%", pTab->zName, '\\')==0 ){ /* Do not gather statistics on system tables */ return; }
diff --git a/third_party/sqlite/src/src/attach.c b/third_party/sqlite/src/src/attach.c index 73fa2c7..490c941 100644 --- a/third_party/sqlite/src/src/attach.c +++ b/third_party/sqlite/src/src/attach.c
@@ -55,6 +55,10 @@ ** ** If the optional "KEY z" syntax is omitted, an SQL NULL is passed as the ** third argument. +** +** If the db->init.reopenMemdb flags is set, then instead of attaching a +** new database, close the database on db->init.iDb and reopen it as an +** empty MemDB. */ static void attachFunc( sqlite3_context *context, @@ -75,65 +79,85 @@ sqlite3_vfs *pVfs; UNUSED_PARAMETER(NotUsed); - zFile = (const char *)sqlite3_value_text(argv[0]); zName = (const char *)sqlite3_value_text(argv[1]); if( zFile==0 ) zFile = ""; if( zName==0 ) zName = ""; - /* Check for the following errors: - ** - ** * Too many attached databases, - ** * Transaction currently open - ** * Specified database name already being used. - */ - if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ - zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", - db->aLimit[SQLITE_LIMIT_ATTACHED] - ); - goto attach_error; - } - for(i=0; i<db->nDb; i++){ - char *z = db->aDb[i].zDbSName; - assert( z && zName ); - if( sqlite3StrICmp(z, zName)==0 ){ - zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); +#ifdef SQLITE_ENABLE_DESERIALIZE +# define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb) +#else +# define REOPEN_AS_MEMDB(db) (0) +#endif + + if( REOPEN_AS_MEMDB(db) ){ + /* This is not a real ATTACH. Instead, this routine is being called + ** from sqlite3_deserialize() to close database db->init.iDb and + ** reopen it as a MemDB */ + pVfs = sqlite3_vfs_find("memdb"); + if( pVfs==0 ) return; + pNew = &db->aDb[db->init.iDb]; + if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt); + pNew->pBt = 0; + pNew->pSchema = 0; + rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); + }else{ + /* This is a real ATTACH + ** + ** Check for the following errors: + ** + ** * Too many attached databases, + ** * Transaction currently open + ** * Specified database name already being used. + */ + if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){ + zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d", + db->aLimit[SQLITE_LIMIT_ATTACHED] + ); goto attach_error; } - } + for(i=0; i<db->nDb; i++){ + char *z = db->aDb[i].zDbSName; + assert( z && zName ); + if( sqlite3StrICmp(z, zName)==0 ){ + zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName); + goto attach_error; + } + } - /* Allocate the new entry in the db->aDb[] array and initialize the schema - ** hash tables. - */ - if( db->aDb==db->aDbStatic ){ - aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); - if( aNew==0 ) return; - memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); - }else{ - aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); - if( aNew==0 ) return; - } - db->aDb = aNew; - pNew = &db->aDb[db->nDb]; - memset(pNew, 0, sizeof(*pNew)); + /* Allocate the new entry in the db->aDb[] array and initialize the schema + ** hash tables. + */ + if( db->aDb==db->aDbStatic ){ + aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 ); + if( aNew==0 ) return; + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ + aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); + if( aNew==0 ) return; + } + db->aDb = aNew; + pNew = &db->aDb[db->nDb]; + memset(pNew, 0, sizeof(*pNew)); - /* Open the database file. If the btree is successfully opened, use - ** it to obtain the database schema. At this point the schema may - ** or may not be initialized. - */ - flags = db->openFlags; - rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; + /* Open the database file. If the btree is successfully opened, use + ** it to obtain the database schema. At this point the schema may + ** or may not be initialized. + */ + flags = db->openFlags; + rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); + sqlite3_result_error(context, zErr, -1); + sqlite3_free(zErr); + return; + } + assert( pVfs ); + flags |= SQLITE_OPEN_MAIN_DB; + rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); + sqlite3_free( zPath ); + db->nDb++; } - assert( pVfs ); - flags |= SQLITE_OPEN_MAIN_DB; - rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); - sqlite3_free( zPath ); - db->nDb++; db->skipBtreeMutex = 0; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; @@ -160,7 +184,7 @@ sqlite3BtreeLeave(pNew->pBt); } pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; - pNew->zDbSName = sqlite3DbStrDup(db, zName); + if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } @@ -200,13 +224,15 @@ /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and - ** remove the entry from the db->aDb[] array. i.e. put everything back the way - ** we found it. + ** remove the entry from the db->aDb[] array. i.e. put everything back the + ** way we found it. */ if( rc==SQLITE_OK ){ sqlite3BtreeEnterAll(db); + db->init.iDb = 0; rc = sqlite3Init(db, &zErrDyn); sqlite3BtreeLeaveAll(db); + assert( zErrDyn==0 || rc!=SQLITE_OK ); } #ifdef SQLITE_USER_AUTHENTICATION if( rc==SQLITE_OK ){ @@ -218,21 +244,23 @@ } #endif if( rc ){ - int iDb = db->nDb - 1; - assert( iDb>=2 ); - if( db->aDb[iDb].pBt ){ - sqlite3BtreeClose(db->aDb[iDb].pBt); - db->aDb[iDb].pBt = 0; - db->aDb[iDb].pSchema = 0; - } - sqlite3ResetAllSchemasOfConnection(db); - db->nDb = iDb; - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ - sqlite3OomFault(db); - sqlite3DbFree(db, zErrDyn); - zErrDyn = sqlite3MPrintf(db, "out of memory"); - }else if( zErrDyn==0 ){ - zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); + if( !REOPEN_AS_MEMDB(db) ){ + int iDb = db->nDb - 1; + assert( iDb>=2 ); + if( db->aDb[iDb].pBt ){ + sqlite3BtreeClose(db->aDb[iDb].pBt); + db->aDb[iDb].pBt = 0; + db->aDb[iDb].pSchema = 0; + } + sqlite3ResetAllSchemasOfConnection(db); + db->nDb = iDb; + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ + sqlite3OomFault(db); + sqlite3DbFree(db, zErrDyn); + zErrDyn = sqlite3MPrintf(db, "out of memory"); + }else if( zErrDyn==0 ){ + zErrDyn = sqlite3MPrintf(db, "unable to open database: %s", zFile); + } } goto attach_error; } @@ -504,6 +532,14 @@ if( sqlite3FixExpr(pFix, pSelect->pLimit) ){ return 1; } + if( pSelect->pWith ){ + int i; + for(i=0; i<pSelect->pWith->nCte; i++){ + if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){ + return 1; + } + } + } pSelect = pSelect->pPrior; } return 0;
diff --git a/third_party/sqlite/src/src/btree.c b/third_party/sqlite/src/src/btree.c index 8e6907bd..10cd9ad 100644 --- a/third_party/sqlite/src/src/btree.c +++ b/third_party/sqlite/src/src/btree.c
@@ -2231,7 +2231,8 @@ BtShared *pBt = (BtShared*)pArg; assert( pBt->db ); assert( sqlite3_mutex_held(pBt->db->mutex) ); - return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); + return sqlite3InvokeBusyHandler(&pBt->db->busyHandler, + sqlite3PagerFile(pBt->pPager)); } /* @@ -2409,7 +2410,7 @@ } pBt->openFlags = (u8)flags; pBt->db = db; - sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); + sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; @@ -3412,6 +3413,7 @@ } }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && btreeInvokeBusyHandler(pBt) ); + sqlite3PagerResetLockTimeout(pBt->pPager); if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ @@ -4399,7 +4401,7 @@ ** of run-time by skipping the initialization of those elements. */ void sqlite3BtreeCursorZero(BtCursor *p){ - memset(p, 0, offsetof(BtCursor, iPage)); + memset(p, 0, offsetof(BtCursor, BTCURSOR_FIRST_UNINIT)); } /* @@ -4442,11 +4444,19 @@ ** Using this cache reduces the number of calls to btreeParseCell(). */ #ifndef NDEBUG + static int cellInfoEqual(CellInfo *a, CellInfo *b){ + if( a->nKey!=b->nKey ) return 0; + if( a->pPayload!=b->pPayload ) return 0; + if( a->nPayload!=b->nPayload ) return 0; + if( a->nLocal!=b->nLocal ) return 0; + if( a->nSize!=b->nSize ) return 0; + return 1; + } static void assertCellInfo(BtCursor *pCur){ CellInfo info; memset(&info, 0, sizeof(info)); btreeParseCell(pCur->pPage, pCur->ix, &info); - assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 ); + assert( CORRUPT_DB || cellInfoEqual(&info, &pCur->info) ); } #else #define assertCellInfo(x) @@ -4722,14 +4732,15 @@ */ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; - if( nOvfl>pCur->nOvflAlloc ){ + if( pCur->aOverflow==0 + || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) + ){ Pgno *aNew = (Pgno*)sqlite3Realloc( pCur->aOverflow, nOvfl*2*sizeof(Pgno) ); if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ - pCur->nOvflAlloc = nOvfl*2; pCur->aOverflow = aNew; } } @@ -6243,9 +6254,8 @@ } /* -** Free any overflow pages associated with the given Cell. Write the -** local Cell size (the number of bytes on the original page, omitting -** overflow) into *pnSize. +** Free any overflow pages associated with the given Cell. Store +** size information about the cell in pInfo. */ static int clearCell( MemPage *pPage, /* The page that contains the Cell */ @@ -7449,7 +7459,7 @@ } /* Load b.apCell[] with pointers to all cells in pOld. If pOld - ** constains overflow cells, include them in the b.apCell[] array + ** contains overflow cells, include them in the b.apCell[] array ** in the correct spot. ** ** Note that when there are multiple overflow cells, it is always the
diff --git a/third_party/sqlite/src/src/btreeInt.h b/third_party/sqlite/src/src/btreeInt.h index 0694b31a..88f3b43 100644 --- a/third_party/sqlite/src/src/btreeInt.h +++ b/third_party/sqlite/src/src/btreeInt.h
@@ -504,20 +504,20 @@ u8 curFlags; /* zero or more BTCF_* flags defined below */ u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */ u8 hints; /* As configured by CursorSetHints() */ - int nOvflAlloc; /* Allocated size of aOverflow[] array */ - Btree *pBtree; /* The Btree to which this cursor belongs */ - BtShared *pBt; /* The BtShared this cursor points to */ - BtCursor *pNext; /* Forms a linked list of all cursors */ - Pgno *aOverflow; /* Cache of overflow page locations */ - CellInfo info; /* A parse of the cell we are pointing at */ - i64 nKey; /* Size of pKey, or last integer key */ - void *pKey; /* Saved key that was cursor last known position */ - Pgno pgnoRoot; /* The root page of this tree */ int skipNext; /* Prev() is noop if negative. Next() is noop if positive. ** Error code if eState==CURSOR_FAULT */ + Btree *pBtree; /* The Btree to which this cursor belongs */ + Pgno *aOverflow; /* Cache of overflow page locations */ + void *pKey; /* Saved key that was cursor last known position */ /* All fields above are zeroed when the cursor is allocated. See ** sqlite3BtreeCursorZero(). Fields that follow must be manually ** initialized. */ +#define BTCURSOR_FIRST_UNINIT pBt /* Name of first uninitialized field */ + BtShared *pBt; /* The BtShared this cursor points to */ + BtCursor *pNext; /* Forms a linked list of all cursors */ + CellInfo info; /* A parse of the cell we are pointing at */ + i64 nKey; /* Size of pKey, or last integer key */ + Pgno pgnoRoot; /* The root page of this tree */ i8 iPage; /* Index of current page in apPage */ u8 curIntKey; /* Value of apPage[0]->intKey */ u16 ix; /* Current index for apPage[iPage] */ @@ -567,8 +567,8 @@ ** Do nothing else with this cursor. Any attempt to use the cursor ** should return the error code stored in BtCursor.skipNext */ -#define CURSOR_INVALID 0 -#define CURSOR_VALID 1 +#define CURSOR_VALID 0 +#define CURSOR_INVALID 1 #define CURSOR_SKIPNEXT 2 #define CURSOR_REQUIRESEEK 3 #define CURSOR_FAULT 4
diff --git a/third_party/sqlite/src/src/build.c b/third_party/sqlite/src/src/build.c index f8a624c7..912323f 100644 --- a/third_party/sqlite/src/src/build.c +++ b/third_party/sqlite/src/src/build.c
@@ -1118,10 +1118,24 @@ */ void sqlite3AddNotNull(Parse *pParse, int onError){ Table *p; + Column *pCol; p = pParse->pNewTable; if( p==0 || NEVER(p->nCol<1) ) return; - p->aCol[p->nCol-1].notNull = (u8)onError; + pCol = &p->aCol[p->nCol-1]; + pCol->notNull = (u8)onError; p->tabFlags |= TF_HasNotNull; + + /* Set the uniqNotNull flag on any UNIQUE or PK indexes already created + ** on this column. */ + if( pCol->colFlags & COLFLAG_UNIQUE ){ + Index *pIdx; + for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->nKeyCol==1 && pIdx->onError!=OE_None ); + if( pIdx->aiColumn[0]==p->nCol-1 ){ + pIdx->uniqNotNull = 1; + } + } + } } /* @@ -1482,7 +1496,7 @@ Vdbe *v = pParse->pVdbe; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, - db->aDb[iDb].pSchema->schema_cookie+1); + (int)(1+(unsigned)db->aDb[iDb].pSchema->schema_cookie)); } /* @@ -1856,8 +1870,6 @@ p = pParse->pNewTable; if( p==0 ) return; - assert( !db->init.busy || !pSelect ); - /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. ** So do not write to the disk again. Extract the root page number @@ -1868,6 +1880,10 @@ ** table itself. So mark it read-only. */ if( db->init.busy ){ + if( pSelect ){ + sqlite3ErrorMsg(pParse, ""); + return; + } p->tnum = db->init.newTnum; if( p->tnum==1 ) p->tabFlags |= TF_Readonly; } @@ -3085,7 +3101,9 @@ */ if( pList==0 ){ Token prevCol; - sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol-1].zName); + Column *pCol = &pTab->aCol[pTab->nCol-1]; + pCol->colFlags |= COLFLAG_UNIQUE; + sqlite3TokenInit(&prevCol, pCol->zName); pList = sqlite3ExprListAppend(pParse, 0, sqlite3ExprAlloc(db, TK_ID, &prevCol, 0)); if( pList==0 ) goto exit_create_index;
diff --git a/third_party/sqlite/src/src/ctime.c b/third_party/sqlite/src/src/ctime.c index 9430924..191d883 100644 --- a/third_party/sqlite/src/src/ctime.c +++ b/third_party/sqlite/src/src/ctime.c
@@ -188,7 +188,7 @@ "ENABLE_BATCH_ATOMIC_WRITE", #endif #if SQLITE_ENABLE_CEROD - "ENABLE_CEROD", + "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), #endif #if SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA",
diff --git a/third_party/sqlite/src/src/dbstat.c b/third_party/sqlite/src/src/dbstat.c index c577859a..7e7450e 100644 --- a/third_party/sqlite/src/src/dbstat.c +++ b/third_party/sqlite/src/src/dbstat.c
@@ -424,7 +424,7 @@ */ fd = sqlite3PagerFile(pPager); x[0] = pCsr->iPageno; - if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ + if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ pCsr->iOffset = x[0]; pCsr->szPage = (int)x[1]; }
diff --git a/third_party/sqlite/src/src/expr.c b/third_party/sqlite/src/src/expr.c index 14538cec..61060a5b 100644 --- a/third_party/sqlite/src/src/expr.c +++ b/third_party/sqlite/src/src/expr.c
@@ -1733,6 +1733,34 @@ } /* +** If the input expression is an ID with the name "true" or "false" +** then convert it into an TK_TRUEFALSE term. Return non-zero if +** the conversion happened, and zero if the expression is unaltered. +*/ +int sqlite3ExprIdToTrueFalse(Expr *pExpr){ + assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); + if( sqlite3StrICmp(pExpr->u.zToken, "true")==0 + || sqlite3StrICmp(pExpr->u.zToken, "false")==0 + ){ + pExpr->op = TK_TRUEFALSE; + return 1; + } + return 0; +} + +/* +** The argument must be a TK_TRUEFALSE Expr node. Return 1 if it is TRUE +** and 0 if it is FALSE. +*/ +int sqlite3ExprTruthValue(const Expr *pExpr){ + assert( pExpr->op==TK_TRUEFALSE ); + assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0 + || sqlite3StrICmp(pExpr->u.zToken,"false")==0 ); + return pExpr->u.zToken[4]==0; +} + + +/* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The ** Walker.eCode value determines the type of "constant" we are looking @@ -1779,6 +1807,12 @@ return WRC_Abort; } case TK_ID: + /* Convert "true" or "false" in a DEFAULT clause into the + ** appropriate TK_TRUEFALSE operator */ + if( sqlite3ExprIdToTrueFalse(pExpr) ){ + return WRC_Prune; + } + /* Fall thru */ case TK_COLUMN: case TK_AGG_FUNCTION: case TK_AGG_COLUMN: @@ -3543,6 +3577,10 @@ codeInteger(pParse, pExpr, 0, target); return target; } + case TK_TRUEFALSE: { + sqlite3VdbeAddOp2(v, OP_Integer, sqlite3ExprTruthValue(pExpr), target); + return target; + } #ifndef SQLITE_OMIT_FLOATING_POINT case TK_FLOAT: { assert( !ExprHasProperty(pExpr, EP_IntValue) ); @@ -3698,6 +3736,18 @@ sqlite3VdbeAddOp2(v, op, r1, inReg); break; } + case TK_TRUTH: { + int isTrue; /* IS TRUE or IS NOT TRUE */ + int bNormal; /* IS TRUE or IS FALSE */ + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + testcase( regFree1==0 ); + isTrue = sqlite3ExprTruthValue(pExpr->pRight); + bNormal = pExpr->op2==TK_IS; + testcase( isTrue && bNormal); + testcase( !isTrue && bNormal); + sqlite3VdbeAddOp4Int(v, OP_IsTrue, r1, inReg, !isTrue, isTrue ^ bNormal); + break; + } case TK_ISNULL: case TK_NOTNULL: { int addr; @@ -4473,6 +4523,23 @@ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); break; } + case TK_TRUTH: { + int isNot; /* IS NOT TRUE or IS NOT FALSE */ + int isTrue; /* IS TRUE or IS NOT TRUE */ + testcase( jumpIfNull==0 ); + isNot = pExpr->op2==TK_ISNOT; + isTrue = sqlite3ExprTruthValue(pExpr->pRight); + testcase( isTrue && isNot ); + testcase( !isTrue && isNot ); + if( isTrue ^ isNot ){ + sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, + isNot ? SQLITE_JUMPIFNULL : 0); + }else{ + sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, + isNot ? SQLITE_JUMPIFNULL : 0); + } + break; + } case TK_IS: case TK_ISNOT: testcase( op==TK_IS ); @@ -4627,6 +4694,26 @@ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); break; } + case TK_TRUTH: { + int isNot; /* IS NOT TRUE or IS NOT FALSE */ + int isTrue; /* IS TRUE or IS NOT TRUE */ + testcase( jumpIfNull==0 ); + isNot = pExpr->op2==TK_ISNOT; + isTrue = sqlite3ExprTruthValue(pExpr->pRight); + testcase( isTrue && isNot ); + testcase( !isTrue && isNot ); + if( isTrue ^ isNot ){ + /* IS TRUE and IS NOT FALSE */ + sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, + isNot ? 0 : SQLITE_JUMPIFNULL); + + }else{ + /* IS FALSE and IS NOT TRUE */ + sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, + isNot ? 0 : SQLITE_JUMPIFNULL); + } + break; + } case TK_IS: case TK_ISNOT: testcase( pExpr->op==TK_IS ); @@ -4915,6 +5002,105 @@ } /* +** This is the Expr node callback for sqlite3ExprImpliesNotNullRow(). +** If the expression node requires that the table at pWalker->iCur +** have a non-NULL column, then set pWalker->eCode to 1 and abort. +*/ +static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + /* This routine is only called for WHERE clause expressions and so it + ** cannot have any TK_AGG_COLUMN entries because those are only found + ** in HAVING clauses. We can get a TK_AGG_FUNCTION in a WHERE clause, + ** but that is an illegal construct and the query will be rejected at + ** a later stage of processing, so the TK_AGG_FUNCTION case does not + ** need to be considered here. */ + assert( pExpr->op!=TK_AGG_COLUMN ); + testcase( pExpr->op==TK_AGG_FUNCTION ); + + if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune; + switch( pExpr->op ){ + case TK_ISNOT: + case TK_NOT: + case TK_ISNULL: + case TK_IS: + case TK_OR: + case TK_CASE: + case TK_IN: + case TK_FUNCTION: + testcase( pExpr->op==TK_ISNOT ); + testcase( pExpr->op==TK_NOT ); + testcase( pExpr->op==TK_ISNULL ); + testcase( pExpr->op==TK_IS ); + testcase( pExpr->op==TK_OR ); + testcase( pExpr->op==TK_CASE ); + testcase( pExpr->op==TK_IN ); + testcase( pExpr->op==TK_FUNCTION ); + return WRC_Prune; + case TK_COLUMN: + if( pWalker->u.iCur==pExpr->iTable ){ + pWalker->eCode = 1; + return WRC_Abort; + } + return WRC_Prune; + + /* Virtual tables are allowed to use constraints like x=NULL. So + ** a term of the form x=y does not prove that y is not null if x + ** is the column of a virtual table */ + case TK_EQ: + case TK_NE: + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + testcase( pExpr->op==TK_EQ ); + testcase( pExpr->op==TK_NE ); + testcase( pExpr->op==TK_LT ); + testcase( pExpr->op==TK_LE ); + testcase( pExpr->op==TK_GT ); + testcase( pExpr->op==TK_GE ); + if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->pTab)) + || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->pTab)) + ){ + return WRC_Prune; + } + default: + return WRC_Continue; + } +} + +/* +** Return true (non-zero) if expression p can only be true if at least +** one column of table iTab is non-null. In other words, return true +** if expression p will always be NULL or false if every column of iTab +** is NULL. +** +** False negatives are acceptable. In other words, it is ok to return +** zero even if expression p will never be true of every column of iTab +** is NULL. A false negative is merely a missed optimization opportunity. +** +** False positives are not allowed, however. A false positive may result +** in an incorrect answer. +** +** Terms of p that are marked with EP_FromJoin (and hence that come from +** the ON or USING clauses of LEFT JOINS) are excluded from the analysis. +** +** This routine is used to check if a LEFT JOIN can be converted into +** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE +** clause requires that some column of the right table of the LEFT JOIN +** be non-NULL, then the LEFT JOIN can be safely converted into an +** ordinary join. +*/ +int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ + Walker w; + w.xExprCallback = impliesNotNullRow; + w.xSelectCallback = 0; + w.xSelectCallback2 = 0; + w.eCode = 0; + w.u.iCur = iTab; + sqlite3WalkExpr(&w, p); + return w.eCode; +} + +/* ** An instance of the following structure is used by the tree walker ** to determine if an expression can be evaluated by reference to the ** index only, without having to do a search for the corresponding
diff --git a/third_party/sqlite/src/src/func.c b/third_party/sqlite/src/src/func.c index 87e2b12..3e84a0f 100644 --- a/third_party/sqlite/src/src/func.c +++ b/third_party/sqlite/src/src/func.c
@@ -35,6 +35,8 @@ ** iteration of the aggregate loop. */ static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){ + assert( context->isError<=0 ); + context->isError = -1; context->skipFlag = 1; } @@ -101,8 +103,6 @@ int argc, sqlite3_value **argv ){ - int len; - assert( argc==1 ); UNUSED_PARAMETER(argc); switch( sqlite3_value_type(argv[0]) ){ @@ -114,13 +114,17 @@ } case SQLITE_TEXT: { const unsigned char *z = sqlite3_value_text(argv[0]); + const unsigned char *z0; + unsigned char c; if( z==0 ) return; - len = 0; - while( *z ){ - len++; - SQLITE_SKIP_UTF8(z); + z0 = z; + while( (c = *z)!=0 ){ + z++; + if( c>=0xc0 ){ + while( (*z & 0xc0)==0x80 ){ z++; z0++; } + } } - sqlite3_result_int(context, len); + sqlite3_result_int(context, (int)(z-z0)); break; } default: { @@ -1195,6 +1199,8 @@ i64 nOut; /* Maximum size of zOut */ int loopLimit; /* Last zStr[] that might match zPattern[] */ int i, j; /* Loop counters */ + unsigned cntExpand; /* Number zOut expansions */ + sqlite3 *db = sqlite3_context_db_handle(context); assert( argc==3 ); UNUSED_PARAMETER(argc); @@ -1226,33 +1232,40 @@ return; } loopLimit = nStr - nPattern; + cntExpand = 0; for(i=j=0; i<=loopLimit; i++){ if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){ zOut[j++] = zStr[i]; }else{ - u8 *zOld; - sqlite3 *db = sqlite3_context_db_handle(context); - nOut += nRep - nPattern; - testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); - testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - sqlite3_free(zOut); - return; - } - zOld = zOut; - zOut = sqlite3_realloc64(zOut, (int)nOut); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - sqlite3_free(zOld); - return; + if( nRep>nPattern ){ + nOut += nRep - nPattern; + testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); + testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); + if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + sqlite3_result_error_toobig(context); + sqlite3_free(zOut); + return; + } + cntExpand++; + if( (cntExpand&(cntExpand-1))==0 ){ + /* Grow the size of the output buffer only on substitutions + ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ + u8 *zOld; + zOld = zOut; + zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1)); + if( zOut==0 ){ + sqlite3_result_error_nomem(context); + sqlite3_free(zOld); + return; + } + } } memcpy(&zOut[j], zRep, nRep); j += nRep; i += nPattern-1; } } - assert( j+nStr-i+1==nOut ); + assert( j+nStr-i+1<=nOut ); memcpy(&zOut[j], &zStr[i], nStr-i); j += nStr - i; assert( j<=nOut );
diff --git a/third_party/sqlite/src/src/global.c b/third_party/sqlite/src/src/global.c index 395ca06..18c5370 100644 --- a/third_party/sqlite/src/src/global.c +++ b/third_party/sqlite/src/src/global.c
@@ -258,6 +258,13 @@ { "1", 1 } }; +#ifdef VDBE_PROFILE +/* +** The following performance counter can be used in place of +** sqlite3Hwtime() for profiling. This is a no-op on standard builds. +*/ +sqlite3_uint64 sqlite3NProfileCnt = 0; +#endif /* ** The value of the "pending" byte must be 0x40000000 (1 byte past the
diff --git a/third_party/sqlite/src/src/insert.c b/third_party/sqlite/src/src/insert.c index e1dea27..86f397f 100644 --- a/third_party/sqlite/src/src/insert.c +++ b/third_party/sqlite/src/src/insert.c
@@ -210,11 +210,12 @@ ** first use of table pTab. On 2nd and subsequent uses, the original ** AutoincInfo structure is used. ** -** Three memory locations are allocated: +** Four consecutive registers are allocated: ** -** (1) Register to hold the name of the pTab table. -** (2) Register to hold the maximum ROWID of pTab. -** (3) Register to hold the rowid in sqlite_sequence of pTab +** (1) The name of the pTab table. +** (2) The maximum ROWID of pTab. +** (3) The rowid in sqlite_sequence of pTab +** (4) The original value of the max ROWID in pTab, or NULL if none ** ** The 2nd register is the one that is returned. That is all the ** insert routine needs to know about. @@ -242,7 +243,7 @@ pInfo->iDb = iDb; pToplevel->nMem++; /* Register to hold name of table */ pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ - pToplevel->nMem++; /* Rowid in sqlite_sequence */ + pToplevel->nMem +=2; /* Rowid in sqlite_sequence + orig max val */ } memId = pInfo->regCtr; } @@ -270,15 +271,17 @@ static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList autoInc[] = { /* 0 */ {OP_Null, 0, 0, 0}, - /* 1 */ {OP_Rewind, 0, 9, 0}, + /* 1 */ {OP_Rewind, 0, 10, 0}, /* 2 */ {OP_Column, 0, 0, 0}, - /* 3 */ {OP_Ne, 0, 7, 0}, + /* 3 */ {OP_Ne, 0, 9, 0}, /* 4 */ {OP_Rowid, 0, 0, 0}, /* 5 */ {OP_Column, 0, 1, 0}, - /* 6 */ {OP_Goto, 0, 9, 0}, - /* 7 */ {OP_Next, 0, 2, 0}, - /* 8 */ {OP_Integer, 0, 0, 0}, - /* 9 */ {OP_Close, 0, 0, 0} + /* 6 */ {OP_AddImm, 0, 0, 0}, + /* 7 */ {OP_Copy, 0, 0, 0}, + /* 8 */ {OP_Goto, 0, 11, 0}, + /* 9 */ {OP_Next, 0, 2, 0}, + /* 10 */ {OP_Integer, 0, 0, 0}, + /* 11 */ {OP_Close, 0, 0, 0} }; VdbeOp *aOp; pDb = &db->aDb[p->iDb]; @@ -289,14 +292,17 @@ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn); if( aOp==0 ) break; aOp[0].p2 = memId; - aOp[0].p3 = memId+1; + aOp[0].p3 = memId+2; aOp[2].p3 = memId; aOp[3].p1 = memId-1; aOp[3].p3 = memId; aOp[3].p5 = SQLITE_JUMPIFNULL; aOp[4].p2 = memId+1; aOp[5].p3 = memId; - aOp[8].p2 = memId; + aOp[6].p1 = memId; + aOp[7].p2 = memId+2; + aOp[7].p1 = memId; + aOp[10].p2 = memId; } } @@ -343,6 +349,8 @@ iRec = sqlite3GetTempReg(pParse); assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); + sqlite3VdbeAddOp3(v, OP_Le, memId+2, sqlite3VdbeCurrentAddr(v)+7, memId); + VdbeCoverage(v); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn); if( aOp==0 ) break;
diff --git a/third_party/sqlite/src/src/main.c b/third_party/sqlite/src/src/main.c index 5a4ebfa..e1ea7632 100644 --- a/third_party/sqlite/src/src/main.c +++ b/third_party/sqlite/src/src/main.c
@@ -239,6 +239,11 @@ sqlite3GlobalConfig.isPCacheInit = 1; rc = sqlite3OsInit(); } +#ifdef SQLITE_ENABLE_DESERIALIZE + if( rc==SQLITE_OK ){ + rc = sqlite3MemdbInit(); + } +#endif if( rc==SQLITE_OK ){ sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); @@ -271,7 +276,7 @@ #ifndef NDEBUG #ifndef SQLITE_OMIT_FLOATING_POINT /* This section of code's only "output" is via assert() statements. */ - if ( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ u64 x = (((u64)1)<<63)-1; double y; assert(sizeof(x)==8); @@ -1438,6 +1443,8 @@ /* SQLITE_FORMAT */ 0, /* SQLITE_RANGE */ "column index out of range", /* SQLITE_NOTADB */ "file is not a database", + /* SQLITE_NOTICE */ "notification message", + /* SQLITE_WARNING */ "warning message", }; const char *zErr = "unknown error"; switch( rc ){ @@ -1445,6 +1452,14 @@ zErr = "abort due to ROLLBACK"; break; } + case SQLITE_ROW: { + zErr = "another row available"; + break; + } + case SQLITE_DONE: { + zErr = "no more rows available"; + break; + } default: { rc &= 0xff; if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){ @@ -1461,21 +1476,40 @@ ** again until a timeout value is reached. The timeout value is ** an integer number of milliseconds passed in as the first ** argument. +** +** Return non-zero to retry the lock. Return zero to stop trying +** and cause SQLite to return SQLITE_BUSY. */ static int sqliteDefaultBusyCallback( - void *ptr, /* Database connection */ - int count /* Number of times table has been busy */ + void *ptr, /* Database connection */ + int count, /* Number of times table has been busy */ + sqlite3_file *pFile /* The file on which the lock occurred */ ){ #if SQLITE_OS_WIN || HAVE_USLEEP + /* This case is for systems that have support for sleeping for fractions of + ** a second. Examples: All windows systems, unix systems with usleep() */ static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; # define NDELAY ArraySize(delays) sqlite3 *db = (sqlite3 *)ptr; - int timeout = db->busyTimeout; + int tmout = db->busyTimeout; int delay, prior; +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){ + if( count ){ + tmout = 0; + sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout); + return 0; + }else{ + return 1; + } + } +#else + UNUSED_PARAMETER(pFile); +#endif assert( count>=0 ); if( count < NDELAY ){ delay = delays[count]; @@ -1484,16 +1518,19 @@ delay = delays[NDELAY-1]; prior = totals[NDELAY-1] + delay*(count-(NDELAY-1)); } - if( prior + delay > timeout ){ - delay = timeout - prior; + if( prior + delay > tmout ){ + delay = tmout - prior; if( delay<=0 ) return 0; } sqlite3OsSleep(db->pVfs, delay*1000); return 1; #else + /* This case for unix systems that lack usleep() support. Sleeping + ** must be done in increments of whole seconds */ sqlite3 *db = (sqlite3 *)ptr; - int timeout = ((sqlite3 *)ptr)->busyTimeout; - if( (count+1)*1000 > timeout ){ + int tmout = ((sqlite3 *)ptr)->busyTimeout; + UNUSED_PARAMETER(pFile); + if( (count+1)*1000 > tmout ){ return 0; } sqlite3OsSleep(db->pVfs, 1000000); @@ -1504,14 +1541,25 @@ /* ** Invoke the given busy handler. ** -** This routine is called when an operation failed with a lock. +** This routine is called when an operation failed to acquire a +** lock on VFS file pFile. +** ** If this routine returns non-zero, the lock is retried. If it ** returns 0, the operation aborts with an SQLITE_BUSY error. */ -int sqlite3InvokeBusyHandler(BusyHandler *p){ +int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){ int rc; - if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0; - rc = p->xFunc(p->pArg, p->nBusy); + if( p->xBusyHandler==0 || p->nBusy<0 ) return 0; + if( p->bExtraFileArg ){ + /* Add an extra parameter with the pFile pointer to the end of the + ** callback argument list */ + int (*xTra)(void*,int,sqlite3_file*); + xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler; + rc = xTra(p->pBusyArg, p->nBusy, pFile); + }else{ + /* Legacy style busy handler callback */ + rc = p->xBusyHandler(p->pBusyArg, p->nBusy); + } if( rc==0 ){ p->nBusy = -1; }else{ @@ -1533,9 +1581,10 @@ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif sqlite3_mutex_enter(db->mutex); - db->busyHandler.xFunc = xBusy; - db->busyHandler.pArg = pArg; + db->busyHandler.xBusyHandler = xBusy; + db->busyHandler.pBusyArg = pArg; db->busyHandler.nBusy = 0; + db->busyHandler.bExtraFileArg = 0; db->busyTimeout = 0; sqlite3_mutex_leave(db->mutex); return SQLITE_OK; @@ -1583,8 +1632,10 @@ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif if( ms>0 ){ - sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db); + sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, + (void*)db); db->busyTimeout = ms; + db->busyHandler.bExtraFileArg = 1; }else{ sqlite3_busy_handler(db, 0, 0); } @@ -3585,10 +3636,8 @@ }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); rc = SQLITE_OK; - }else if( fd->pMethods ){ - rc = sqlite3OsFileControl(fd, op, pArg); }else{ - rc = SQLITE_NOTFOUND; + rc = sqlite3OsFileControl(fd, op, pArg); } sqlite3BtreeLeave(pBtree); }
diff --git a/third_party/sqlite/src/src/memdb.c b/third_party/sqlite/src/src/memdb.c new file mode 100644 index 0000000..2edc78bb --- /dev/null +++ b/third_party/sqlite/src/src/memdb.c
@@ -0,0 +1,589 @@ +/* +** 2016-09-07 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file implements an in-memory VFS. A database is held as a contiguous +** block of memory. +** +** This file also implements interface sqlite3_serialize() and +** sqlite3_deserialize(). +*/ +#ifdef SQLITE_ENABLE_DESERIALIZE +#include "sqliteInt.h" + +/* +** Forward declaration of objects used by this utility +*/ +typedef struct sqlite3_vfs MemVfs; +typedef struct MemFile MemFile; + +/* Access to a lower-level VFS that (might) implement dynamic loading, +** access to randomness, etc. +*/ +#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) + +/* An open file */ +struct MemFile { + sqlite3_file base; /* IO methods */ + sqlite3_int64 sz; /* Size of the file */ + sqlite3_int64 szMax; /* Space allocated to aData */ + unsigned char *aData; /* content of the file */ + int nMmap; /* Number of memory mapped pages */ + unsigned mFlags; /* Flags */ + int eLock; /* Most recent lock against this file */ +}; + +/* +** Methods for MemFile +*/ +static int memdbClose(sqlite3_file*); +static int memdbRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int memdbWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); +static int memdbTruncate(sqlite3_file*, sqlite3_int64 size); +static int memdbSync(sqlite3_file*, int flags); +static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int memdbLock(sqlite3_file*, int); +/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */ +static int memdbFileControl(sqlite3_file*, int op, void *pArg); +/* static int memdbSectorSize(sqlite3_file*); // not used */ +static int memdbDeviceCharacteristics(sqlite3_file*); +static int memdbFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); +static int memdbUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); + +/* +** Methods for MemVfs +*/ +static int memdbOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +/* static int memdbDelete(sqlite3_vfs*, const char *zName, int syncDir); */ +static int memdbAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int memdbFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); +static void *memdbDlOpen(sqlite3_vfs*, const char *zFilename); +static void memdbDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); +static void memdbDlClose(sqlite3_vfs*, void*); +static int memdbRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int memdbSleep(sqlite3_vfs*, int microseconds); +/* static int memdbCurrentTime(sqlite3_vfs*, double*); */ +static int memdbGetLastError(sqlite3_vfs*, int, char *); +static int memdbCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); + +static sqlite3_vfs memdb_vfs = { + 2, /* iVersion */ + 0, /* szOsFile (set when registered) */ + 1024, /* mxPathname */ + 0, /* pNext */ + "memdb", /* zName */ + 0, /* pAppData (set when registered) */ + memdbOpen, /* xOpen */ + 0, /* memdbDelete, */ /* xDelete */ + memdbAccess, /* xAccess */ + memdbFullPathname, /* xFullPathname */ + memdbDlOpen, /* xDlOpen */ + memdbDlError, /* xDlError */ + memdbDlSym, /* xDlSym */ + memdbDlClose, /* xDlClose */ + memdbRandomness, /* xRandomness */ + memdbSleep, /* xSleep */ + 0, /* memdbCurrentTime, */ /* xCurrentTime */ + memdbGetLastError, /* xGetLastError */ + memdbCurrentTimeInt64 /* xCurrentTimeInt64 */ +}; + +static const sqlite3_io_methods memdb_io_methods = { + 3, /* iVersion */ + memdbClose, /* xClose */ + memdbRead, /* xRead */ + memdbWrite, /* xWrite */ + memdbTruncate, /* xTruncate */ + memdbSync, /* xSync */ + memdbFileSize, /* xFileSize */ + memdbLock, /* xLock */ + memdbLock, /* xUnlock - same as xLock in this case */ + 0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */ + memdbFileControl, /* xFileControl */ + 0, /* memdbSectorSize,*/ /* xSectorSize */ + memdbDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + memdbFetch, /* xFetch */ + memdbUnfetch /* xUnfetch */ +}; + + + +/* +** Close an memdb-file. +** +** The pData pointer is owned by the application, so there is nothing +** to free. +*/ +static int memdbClose(sqlite3_file *pFile){ + MemFile *p = (MemFile *)pFile; + if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData); + return SQLITE_OK; +} + +/* +** Read data from an memdb-file. +*/ +static int memdbRead( + sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + MemFile *p = (MemFile *)pFile; + if( iOfst+iAmt>p->sz ){ + memset(zBuf, 0, iAmt); + if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst); + return SQLITE_IOERR_SHORT_READ; + } + memcpy(zBuf, p->aData+iOfst, iAmt); + return SQLITE_OK; +} + +/* +** Try to enlarge the memory allocation to hold at least sz bytes +*/ +static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ + unsigned char *pNew; + if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ + return SQLITE_FULL; + } + pNew = sqlite3_realloc64(p->aData, newSz); + if( pNew==0 ) return SQLITE_NOMEM; + p->aData = pNew; + p->szMax = newSz; + return SQLITE_OK; +} + +/* +** Write data to an memdb-file. +*/ +static int memdbWrite( + sqlite3_file *pFile, + const void *z, + int iAmt, + sqlite_int64 iOfst +){ + MemFile *p = (MemFile *)pFile; + if( iOfst+iAmt>p->sz ){ + int rc; + if( iOfst+iAmt>p->szMax + && (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK + ){ + return rc; + } + if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz); + p->sz = iOfst+iAmt; + } + memcpy(p->aData+iOfst, z, iAmt); + return SQLITE_OK; +} + +/* +** Truncate an memdb-file. +** +** In rollback mode (which is always the case for memdb, as it does not +** support WAL mode) the truncate() method is only used to reduce +** the size of a file, never to increase the size. +*/ +static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){ + MemFile *p = (MemFile *)pFile; + if( NEVER(size>p->sz) ) return SQLITE_FULL; + p->sz = size; + return SQLITE_OK; +} + +/* +** Sync an memdb-file. +*/ +static int memdbSync(sqlite3_file *pFile, int flags){ + return SQLITE_OK; +} + +/* +** Return the current file-size of an memdb-file. +*/ +static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + MemFile *p = (MemFile *)pFile; + *pSize = p->sz; + return SQLITE_OK; +} + +/* +** Lock an memdb-file. +*/ +static int memdbLock(sqlite3_file *pFile, int eLock){ + MemFile *p = (MemFile *)pFile; + p->eLock = eLock; + return SQLITE_OK; +} + +#if 0 /* Never used because memdbAccess() always returns false */ +/* +** Check if another file-handle holds a RESERVED lock on an memdb-file. +*/ +static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + *pResOut = 0; + return SQLITE_OK; +} +#endif + +/* +** File control method. For custom operations on an memdb-file. +*/ +static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ + MemFile *p = (MemFile *)pFile; + int rc = SQLITE_NOTFOUND; + if( op==SQLITE_FCNTL_VFSNAME ){ + *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); + rc = SQLITE_OK; + } + return rc; +} + +#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */ +/* +** Return the sector-size in bytes for an memdb-file. +*/ +static int memdbSectorSize(sqlite3_file *pFile){ + return 1024; +} +#endif + +/* +** Return the device characteristic flags supported by an memdb-file. +*/ +static int memdbDeviceCharacteristics(sqlite3_file *pFile){ + return SQLITE_IOCAP_ATOMIC | + SQLITE_IOCAP_POWERSAFE_OVERWRITE | + SQLITE_IOCAP_SAFE_APPEND | + SQLITE_IOCAP_SEQUENTIAL; +} + +/* Fetch a page of a memory-mapped file */ +static int memdbFetch( + sqlite3_file *pFile, + sqlite3_int64 iOfst, + int iAmt, + void **pp +){ + MemFile *p = (MemFile *)pFile; + p->nMmap++; + *pp = (void*)(p->aData + iOfst); + return SQLITE_OK; +} + +/* Release a memory-mapped page */ +static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ + MemFile *p = (MemFile *)pFile; + p->nMmap--; + return SQLITE_OK; +} + +/* +** Open an mem file handle. +*/ +static int memdbOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + MemFile *p = (MemFile*)pFile; + if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ + return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); + } + memset(p, 0, sizeof(*p)); + p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; + assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ + *pOutFlags = flags | SQLITE_OPEN_MEMORY; + p->base.pMethods = &memdb_io_methods; + return SQLITE_OK; +} + +#if 0 /* Only used to delete rollback journals, master journals, and WAL + ** files, none of which exist in memdb. So this routine is never used */ +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int memdbDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + return SQLITE_IOERR_DELETE; +} +#endif + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +** +** With memdb, no files ever exist on disk. So always return false. +*/ +static int memdbAccess( + sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + *pResOut = 0; + return SQLITE_OK; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (INST_MAX_PATHNAME+1) bytes. +*/ +static int memdbFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + sqlite3_snprintf(nOut, zOut, "%s", zPath); + return SQLITE_OK; +} + +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *memdbDlOpen(sqlite3_vfs *pVfs, const char *zPath){ + return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); +} + +/* +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. +*/ +static void memdbDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); +} + +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ + return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); +} + +/* +** Close the dynamic library handle pHandle. +*/ +static void memdbDlClose(sqlite3_vfs *pVfs, void *pHandle){ + ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); +} + +/* +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. +*/ +static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); +} + +/* +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. +*/ +static int memdbSleep(sqlite3_vfs *pVfs, int nMicro){ + return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); +} + +#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */ +/* +** Return the current time as a Julian Day number in *pTimeOut. +*/ +static int memdbCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); +} +#endif + +static int memdbGetLastError(sqlite3_vfs *pVfs, int a, char *b){ + return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); +} +static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ + return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p); +} + +/* +** Translate a database connection pointer and schema name into a +** MemFile pointer. +*/ +static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){ + MemFile *p = 0; + int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p); + if( rc ) return 0; + if( p->base.pMethods!=&memdb_io_methods ) return 0; + return p; +} + +/* +** Return the serialization of a database +*/ +unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which database within the connection */ + sqlite3_int64 *piSize, /* Write size here, if not NULL */ + unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */ +){ + MemFile *p; + int iDb; + Btree *pBt; + sqlite3_int64 sz; + int szPage = 0; + sqlite3_stmt *pStmt = 0; + unsigned char *pOut; + char *zSql; + int rc; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + p = memdbFromDbSchema(db, zSchema); + iDb = sqlite3FindDbName(db, zSchema); + if( piSize ) *piSize = -1; + if( iDb<0 ) return 0; + if( p ){ + if( piSize ) *piSize = p->sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = p->aData; + }else{ + pOut = sqlite3_malloc64( p->sz ); + if( pOut ) memcpy(pOut, p->aData, p->sz); + } + return pOut; + } + pBt = db->aDb[iDb].pBt; + if( pBt==0 ) return 0; + szPage = sqlite3BtreeGetPageSize(pBt); + zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema); + rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM; + sqlite3_free(zSql); + if( rc ) return 0; + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + pOut = 0; + }else{ + sz = sqlite3_column_int64(pStmt, 0)*szPage; + if( piSize ) *piSize = sz; + if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ + pOut = 0; + }else{ + pOut = sqlite3_malloc64( sz ); + if( pOut ){ + int nPage = sqlite3_column_int(pStmt, 0); + Pager *pPager = sqlite3BtreePager(pBt); + int pgno; + for(pgno=1; pgno<=nPage; pgno++){ + DbPage *pPage = 0; + unsigned char *pTo = pOut + szPage*(sqlite3_int64)(pgno-1); + rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0); + if( rc==SQLITE_OK ){ + memcpy(pTo, sqlite3PagerGetData(pPage), szPage); + }else{ + memset(pTo, 0, szPage); + } + sqlite3PagerUnref(pPage); + } + } + } + } + sqlite3_finalize(pStmt); + return pOut; +} + +/* Convert zSchema to a MemDB and initialize its content. +*/ +int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ +){ + MemFile *p; + char *zSql; + sqlite3_stmt *pStmt = 0; + int rc; + int iDb; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } + if( szDb<0 ) return SQLITE_MISUSE_BKPT; + if( szBuf<0 ) return SQLITE_MISUSE_BKPT; +#endif + + sqlite3_mutex_enter(db->mutex); + if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; + iDb = sqlite3FindDbName(db, zSchema); + if( iDb<0 ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc ) goto end_deserialize; + db->init.iDb = (u8)iDb; + db->init.reopenMemdb = 1; + rc = sqlite3_step(pStmt); + db->init.reopenMemdb = 0; + if( rc!=SQLITE_DONE ){ + rc = SQLITE_ERROR; + goto end_deserialize; + } + p = memdbFromDbSchema(db, zSchema); + if( p==0 ){ + rc = SQLITE_ERROR; + }else{ + p->aData = pData; + p->sz = szDb; + p->szMax = szBuf; + p->mFlags = mFlags; + rc = SQLITE_OK; + } + +end_deserialize: + sqlite3_finalize(pStmt); + sqlite3_mutex_leave(db->mutex); + return rc; +} + +/* +** This routine is called when the extension is loaded. +** Register the new VFS. +*/ +int sqlite3MemdbInit(void){ + sqlite3_vfs *pLower = sqlite3_vfs_find(0); + int sz = pLower->szOsFile; + memdb_vfs.pAppData = pLower; + /* In all known configurations of SQLite, the size of a default + ** sqlite3_file is greater than the size of a memdb sqlite3_file. + ** Should that ever change, remove the following NEVER() */ + if( NEVER(sz<sizeof(MemFile)) ) sz = sizeof(MemFile); + memdb_vfs.szOsFile = sz; + return sqlite3_vfs_register(&memdb_vfs, 0); +} +#endif /* SQLITE_ENABLE_DESERIALIZE */
diff --git a/third_party/sqlite/src/src/mutex_w32.c b/third_party/sqlite/src/src/mutex_w32.c index 9da93cf..8a8ae28 100644 --- a/third_party/sqlite/src/src/mutex_w32.c +++ b/third_party/sqlite/src/src/mutex_w32.c
@@ -40,7 +40,7 @@ #ifdef SQLITE_DEBUG volatile int nRef; /* Number of enterances */ volatile DWORD owner; /* Thread holding this mutex */ - volatile int trace; /* True to trace changes */ + volatile LONG trace; /* True to trace changes */ #endif }; @@ -52,10 +52,10 @@ #define SQLITE_W32_MUTEX_INITIALIZER { 0 } #ifdef SQLITE_DEBUG -#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \ +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \ 0L, (DWORD)0, 0 } #else -#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 } +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id } #endif #ifdef SQLITE_DEBUG @@ -98,18 +98,18 @@ ** Initialize and deinitialize the mutex subsystem. */ static sqlite3_mutex winMutex_staticMutexes[] = { - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER, - SQLITE3_MUTEX_INITIALIZER + SQLITE3_MUTEX_INITIALIZER(2), + SQLITE3_MUTEX_INITIALIZER(3), + SQLITE3_MUTEX_INITIALIZER(4), + SQLITE3_MUTEX_INITIALIZER(5), + SQLITE3_MUTEX_INITIALIZER(6), + SQLITE3_MUTEX_INITIALIZER(7), + SQLITE3_MUTEX_INITIALIZER(8), + SQLITE3_MUTEX_INITIALIZER(9), + SQLITE3_MUTEX_INITIALIZER(10), + SQLITE3_MUTEX_INITIALIZER(11), + SQLITE3_MUTEX_INITIALIZER(12), + SQLITE3_MUTEX_INITIALIZER(13) }; static int winMutex_isInit = 0; @@ -239,15 +239,15 @@ } #endif p = &winMutex_staticMutexes[iType-2]; - p->id = iType; #ifdef SQLITE_DEBUG #ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC - p->trace = 1; + InterlockedCompareExchange(&p->trace, 1, 0); #endif #endif break; } } + assert( p==0 || p->id==iType ); return p; }
diff --git a/third_party/sqlite/src/src/os.c b/third_party/sqlite/src/src/os.c index 26c80650..997f971 100644 --- a/third_party/sqlite/src/src/os.c +++ b/third_party/sqlite/src/src/os.c
@@ -125,8 +125,11 @@ ** routine has no return value since the return value would be meaningless. */ int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ + if( id->pMethods==0 ) return SQLITE_NOTFOUND; #ifdef SQLITE_TEST - if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO + && op!=SQLITE_FCNTL_LOCK_TIMEOUT + ){ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite ** is using a regular VFS, it is called after the corresponding ** transaction has been committed. Injecting a fault at this point @@ -143,7 +146,7 @@ return id->pMethods->xFileControl(id, op, pArg); } void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ - (void)id->pMethods->xFileControl(id, op, pArg); + if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg); } int sqlite3OsSectorSize(sqlite3_file *id){
diff --git a/third_party/sqlite/src/src/os_unix.c b/third_party/sqlite/src/src/os_unix.c index e2ae7e55..31085d6d 100644 --- a/third_party/sqlite/src/src/os_unix.c +++ b/third_party/sqlite/src/src/os_unix.c
@@ -229,6 +229,9 @@ #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + unsigned iBusyTimeout; /* Wait this many millisec on locks */ +#endif #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID */ #endif @@ -468,7 +471,11 @@ #endif #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) +#if defined(HAVE_FCHOWN) { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 }, +#else + { "geteuid", (sqlite3_syscall_ptr)0, 0 }, +#endif #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 @@ -696,15 +703,16 @@ ** assert( unixMutexHeld() ); ** unixEnterLeave() */ +static sqlite3_mutex *unixBigLock = 0; static void unixEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_enter(unixBigLock); } static void unixLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_leave(unixBigLock); } #ifdef SQLITE_DEBUG static int unixMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + return sqlite3_mutex_held(unixBigLock); } #endif @@ -1467,6 +1475,43 @@ } /* +** Set a posix-advisory-lock. +** +** There are two versions of this routine. If compiled with +** SQLITE_ENABLE_SETLK_TIMEOUT then the routine has an extra parameter +** which is a pointer to a unixFile. If the unixFile->iBusyTimeout +** value is set, then it is the number of milliseconds to wait before +** failing the lock. The iBusyTimeout value is always reset back to +** zero on each call. +** +** If SQLITE_ENABLE_SETLK_TIMEOUT is not defined, then do a non-blocking +** attempt to set the lock. +*/ +#ifndef SQLITE_ENABLE_SETLK_TIMEOUT +# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x) +#else +static int osSetPosixAdvisoryLock( + int h, /* The file descriptor on which to take the lock */ + struct flock *pLock, /* The description of the lock */ + unixFile *pFile /* Structure holding timeout value */ +){ + int rc = osFcntl(h,F_SETLK,pLock); + while( rc<0 && pFile->iBusyTimeout>0 ){ + /* On systems that support some kind of blocking file lock with a timeout, + ** make appropriate changes here to invoke that blocking file lock. On + ** generic posix, however, there is no such API. So we simply try the + ** lock once every millisecond until either the timeout expires, or until + ** the lock is obtained. */ + usleep(1000); + rc = osFcntl(h,F_SETLK,pLock); + pFile->iBusyTimeout--; + } + return rc; +} +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ + + +/* ** Attempt to set a system-lock on the file pFile. The lock is ** described by pLock. ** @@ -1498,7 +1543,7 @@ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; lock.l_type = F_WRLCK; - rc = osFcntl(pFile->h, F_SETLK, &lock); + rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile); if( rc<0 ) return rc; pInode->bProcessLock = 1; pInode->nLock++; @@ -1506,7 +1551,7 @@ rc = 0; } }else{ - rc = osFcntl(pFile->h, F_SETLK, pLock); + rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); } return rc; } @@ -3866,6 +3911,12 @@ *(int*)pArg = fileHasMoved(pFile); return SQLITE_OK; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + case SQLITE_FCNTL_LOCK_TIMEOUT: { + pFile->iBusyTimeout = *(int*)pArg; + return SQLITE_OK; + } +#endif #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; @@ -4181,13 +4232,11 @@ if( pShmNode->h>=0 ){ /* Initialize the locking parameters */ - memset(&f, 0, sizeof(f)); f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; - - rc = osFcntl(pShmNode->h, F_SETLK, &f); + rc = osSetPosixAdvisoryLock(pShmNode->h, &f, pFile); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; } @@ -7775,6 +7824,7 @@ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); } + unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); return SQLITE_OK; } @@ -7786,6 +7836,7 @@ ** This routine is a no-op for unix. */ int sqlite3_os_end(void){ + unixBigLock = 0; return SQLITE_OK; }
diff --git a/third_party/sqlite/src/src/os_win.c b/third_party/sqlite/src/src/os_win.c index 3b82da0..a69ca7b 100644 --- a/third_party/sqlite/src/src/os_win.c +++ b/third_party/sqlite/src/src/os_win.c
@@ -3631,15 +3631,16 @@ ** assert( winShmMutexHeld() ); ** winShmLeaveMutex() */ +static sqlite3_mutex *winBigLock = 0; static void winShmEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_enter(winBigLock); } static void winShmLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + sqlite3_mutex_leave(winBigLock); } #ifndef NDEBUG static int winShmMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); + return sqlite3_mutex_held(winBigLock); } #endif @@ -6062,6 +6063,10 @@ sqlite3_vfs_register(&winLongPathNolockVfs, 0); #endif +#ifndef SQLITE_OMIT_WAL + winBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); +#endif + return SQLITE_OK; } @@ -6072,6 +6077,11 @@ sleepObj = NULL; } #endif + +#ifndef SQLITE_OMIT_WAL + winBigLock = 0; +#endif + return SQLITE_OK; }
diff --git a/third_party/sqlite/src/src/pager.c b/third_party/sqlite/src/src/pager.c index c5ebc74..593d461 100644 --- a/third_party/sqlite/src/src/pager.c +++ b/third_party/sqlite/src/src/pager.c
@@ -699,7 +699,7 @@ char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ - int aStat[3]; /* Total cache hits, misses and writes */ + int aStat[4]; /* Total cache hits, misses, writes, spills */ #ifdef SQLITE_TEST int nRead; /* Database pages read */ #endif @@ -727,6 +727,7 @@ #define PAGER_STAT_HIT 0 #define PAGER_STAT_MISS 1 #define PAGER_STAT_WRITE 2 +#define PAGER_STAT_SPILL 3 /* ** The following global variables hold counters used for @@ -1213,7 +1214,7 @@ #endif #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( dc&SQLITE_IOCAP_BATCH_ATOMIC ){ + if( pPager->dbSize>0 && (dc&SQLITE_IOCAP_BATCH_ATOMIC) ){ return -1; } #endif @@ -2129,7 +2130,7 @@ rc = pager_truncate(pPager, pPager->dbSize); } - if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){ + if( rc==SQLITE_OK && bCommit ){ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; } @@ -2948,9 +2949,7 @@ ** assertion that the transaction counter was modified. */ #ifdef SQLITE_DEBUG - if( pPager->fd->pMethods ){ - sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); - } + sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0); #endif /* If this playback is happening automatically as a result of an IO or @@ -3703,20 +3702,18 @@ ** retried. If it returns zero, then the SQLITE_BUSY error is ** returned to the caller of the pager API function. */ -void sqlite3PagerSetBusyhandler( +void sqlite3PagerSetBusyHandler( Pager *pPager, /* Pager object */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ ){ + void **ap; pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; - - if( isOpen(pPager->fd) ){ - void **ap = (void **)&pPager->xBusyHandler; - assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); - assert( ap[1]==pBusyHandlerArg ); - sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); - } + ap = (void **)&pPager->xBusyHandler; + assert( ((int(*)(void *))(ap[0]))==xBusyHandler ); + assert( ap[1]==pBusyHandlerArg ); + sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap); } /* @@ -4102,6 +4099,30 @@ } } +/* Verify that the database file has not be deleted or renamed out from +** under the pager. Return SQLITE_OK if the database is still where it ought +** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error +** code from sqlite3OsAccess()) if the database has gone missing. +*/ +static int databaseIsUnmoved(Pager *pPager){ + int bHasMoved = 0; + int rc; + + if( pPager->tempFile ) return SQLITE_OK; + if( pPager->dbSize==0 ) return SQLITE_OK; + assert( pPager->zFilename && pPager->zFilename[0] ); + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); + if( rc==SQLITE_NOTFOUND ){ + /* If the HAS_MOVED file-control is unimplemented, assume that the file + ** has not been moved. That is the historical behavior of SQLite: prior to + ** version 3.8.3, it never checked */ + rc = SQLITE_OK; + }else if( rc==SQLITE_OK && bHasMoved ){ + rc = SQLITE_READONLY_DBMOVED; + } + return rc; +} + /* ** Shutdown the page cache. Free all memory and close all files. @@ -4118,8 +4139,7 @@ ** to the caller. */ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ - u8 *pTmp = (u8 *)pPager->pTmpSpace; - + u8 *pTmp = (u8*)pPager->pTmpSpace; assert( db || pagerUseWal(pPager)==0 ); assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); @@ -4128,11 +4148,17 @@ /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL - assert( db || pPager->pWal==0 ); - sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize, - (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp) - ); - pPager->pWal = 0; + { + u8 *a = 0; + assert( db || pPager->pWal==0 ); + if( db && 0==(db->flags & SQLITE_NoCkptOnClose) + && SQLITE_OK==databaseIsUnmoved(pPager) + ){ + a = pTmp; + } + sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); + pPager->pWal = 0; + } #endif pager_reset(pPager); if( MEMDB ){ @@ -4589,6 +4615,7 @@ return SQLITE_OK; } + pPager->aStat[PAGER_STAT_SPILL]++; pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* Write a single frame for this page to the log. */ @@ -4694,6 +4721,11 @@ int rc = SQLITE_OK; /* Return code */ int tempFile = 0; /* True for temp files (incl. in-memory files) */ int memDb = 0; /* True if this is an in-memory file */ +#ifdef SQLITE_ENABLE_DESERIALIZE + int memJM = 0; /* Memory journal mode */ +#else +# define memJM 0 +#endif int readOnly = 0; /* True if this is a read-only file */ int journalFileSize; /* Bytes to allocate for each journal fd */ char *zPathname = 0; /* Full path to database file */ @@ -4821,7 +4853,10 @@ int fout = 0; /* VFS flags returned by xOpen() */ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); assert( !memDb ); - readOnly = (fout&SQLITE_OPEN_READONLY); +#ifdef SQLITE_ENABLE_DESERIALIZE + memJM = (fout&SQLITE_OPEN_MEMORY)!=0; +#endif + readOnly = (fout&SQLITE_OPEN_READONLY)!=0; /* If the file was successfully opened for read/write access, ** choose a default page size in case we have to create the @@ -4952,7 +4987,7 @@ setSectorSize(pPager); if( !useJournal ){ pPager->journalMode = PAGER_JOURNALMODE_OFF; - }else if( memDb ){ + }else if( memDb || memJM ){ pPager->journalMode = PAGER_JOURNALMODE_MEMORY; } /* pPager->xBusyHandler = 0; */ @@ -4967,30 +5002,6 @@ } -/* Verify that the database file has not be deleted or renamed out from -** under the pager. Return SQLITE_OK if the database is still were it ought -** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error -** code from sqlite3OsAccess()) if the database has gone missing. -*/ -static int databaseIsUnmoved(Pager *pPager){ - int bHasMoved = 0; - int rc; - - if( pPager->tempFile ) return SQLITE_OK; - if( pPager->dbSize==0 ) return SQLITE_OK; - assert( pPager->zFilename && pPager->zFilename[0] ); - rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); - if( rc==SQLITE_NOTFOUND ){ - /* If the HAS_MOVED file-control is unimplemented, assume that the file - ** has not been moved. That is the historical behavior of SQLite: prior to - ** version 3.8.3, it never checked */ - rc = SQLITE_OK; - }else if( rc==SQLITE_OK && bHasMoved ){ - rc = SQLITE_READONLY_DBMOVED; - } - return rc; -} - /* ** This function is called after transitioning from PAGER_UNLOCK to @@ -5678,6 +5689,7 @@ assert( pPg->pgno==1 ); assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ pPager = pPg->pPager; + sqlite3PagerResetLockTimeout(pPager); sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } @@ -6273,12 +6285,9 @@ */ int sqlite3PagerSync(Pager *pPager, const char *zMaster){ int rc = SQLITE_OK; - - if( isOpen(pPager->fd) ){ - void *pArg = (void*)zMaster; - rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - } + void *pArg = (void*)zMaster; + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; if( rc==SQLITE_OK && !pPager->noSync ){ assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); @@ -6499,8 +6508,9 @@ if( bBatch ){ if( rc==SQLITE_OK ){ rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); - }else{ - sqlite3OsFileControl(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); + } + if( rc!=SQLITE_OK ){ + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0); } } @@ -6724,8 +6734,12 @@ #endif /* -** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or -** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the +** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE, +** or _WRITE+1. The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation +** of SQLITE_DBSTATUS_CACHE_SPILL. The _SPILL case is not contiguous because +** it was added later. +** +** Before returning, *pnVal is incremented by the ** current cache hit or miss count, according to the value of eStat. If the ** reset parameter is non-zero, the cache hit or miss count is zeroed before ** returning. @@ -6735,15 +6749,18 @@ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT || eStat==SQLITE_DBSTATUS_CACHE_MISS || eStat==SQLITE_DBSTATUS_CACHE_WRITE + || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1 ); assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS ); assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE ); - assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 ); + assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 + && PAGER_STAT_WRITE==2 && PAGER_STAT_SPILL==3 ); - *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT]; + eStat -= SQLITE_DBSTATUS_CACHE_HIT; + *pnVal += pPager->aStat[eStat]; if( reset ){ - pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0; + pPager->aStat[eStat] = 0; } } @@ -6947,6 +6964,16 @@ return pPager->fd; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +/* +** Reset the lock timeout for pager. +*/ +void sqlite3PagerResetLockTimeout(Pager *pPager){ + int x = 0; + sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x); +} +#endif + /* ** Return the file handle for the journal file (if it exists). ** This will be either the rollback journal or the WAL file. @@ -7407,6 +7434,7 @@ pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, pnLog, pnCkpt ); + sqlite3PagerResetLockTimeout(pPager); } return rc; }
diff --git a/third_party/sqlite/src/src/pager.h b/third_party/sqlite/src/src/pager.h index 01ea900b..570f1cb 100644 --- a/third_party/sqlite/src/src/pager.h +++ b/third_party/sqlite/src/src/pager.h
@@ -126,7 +126,7 @@ int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ -void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); +void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); #ifdef SQLITE_HAS_CODEC void sqlite3PagerAlignReserve(Pager*,Pager*); @@ -212,6 +212,11 @@ void sqlite3PagerCacheStat(Pager *, int, int, int *); void sqlite3PagerClearCache(Pager*); int sqlite3SectorSize(sqlite3_file *); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +void sqlite3PagerResetLockTimeout(Pager *pPager); +#else +# define sqlite3PagerResetLockTimeout(X) +#endif /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno);
diff --git a/third_party/sqlite/src/src/parse.y b/third_party/sqlite/src/src/parse.y index f1d5e82..6791ad12 100644 --- a/third_party/sqlite/src/src/parse.y +++ b/third_party/sqlite/src/src/parse.y
@@ -313,6 +313,10 @@ } ccons ::= DEFAULT scanpt id(X). { Expr *p = tokenExpr(pParse, TK_STRING, X); + if( p ){ + sqlite3ExprIdToTrueFalse(p); + testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) ); + } sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n); } @@ -460,7 +464,7 @@ } } -select(A) ::= with(W) selectnowith(X). { +select(A) ::= WITH wqlist(W) selectnowith(X). { Select *p = X; if( p ){ p->pWith = W; @@ -468,7 +472,24 @@ }else{ sqlite3WithDelete(pParse->db, W); } - A = p; /*A-overwrites-W*/ + A = p; +} +select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). { + Select *p = X; + if( p ){ + p->pWith = W; + parserDoubleLinkSelect(pParse, p); + }else{ + sqlite3WithDelete(pParse->db, W); + } + A = p; +} +select(A) ::= selectnowith(X). { + Select *p = X; + if( p ){ + parserDoubleLinkSelect(pParse, p); + } + A = p; /*A-overwrites-X*/ } selectnowith(A) ::= oneselect(A). @@ -519,8 +540,7 @@ if( A!=0 ){ const char *z = s.z+6; int i; - sqlite3_snprintf(sizeof(A->zSelName), A->zSelName, "#%d", - ++pParse->nSelect); + sqlite3_snprintf(sizeof(A->zSelName), A->zSelName,"#%d",++pParse->nSelect); while( z[0]==' ' ) z++; if( z[0]=='/' && z[1]=='*' ){ z += 2; @@ -663,7 +683,9 @@ %type fullname {SrcList*} %destructor fullname {sqlite3SrcListDelete(pParse->db, $$);} -fullname(A) ::= nm(X) dbnm(Y). +fullname(A) ::= nm(X). + {A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/} +fullname(A) ::= nm(X) DOT nm(Y). {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/} %type joinop {int} @@ -759,16 +781,14 @@ /////////////////////////// The DELETE statement ///////////////////////////// // %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W) +cmd ::= with DELETE FROM fullname(X) indexed_opt(I) where_opt(W) orderby_opt(O) limit_opt(L). { - sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3DeleteFrom(pParse,X,W,O,L); } %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). { - sqlite3WithPush(pParse, C, 1); +cmd ::= with DELETE FROM fullname(X) indexed_opt(I) where_opt(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3DeleteFrom(pParse,X,W,0,0); } @@ -783,18 +803,16 @@ ////////////////////////// The UPDATE command //////////////////////////////// // %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) +cmd ::= with UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) orderby_opt(O) limit_opt(L). { - sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); sqlite3Update(pParse,X,Y,W,R,O,L); } %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) +cmd ::= with UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). { - sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); sqlite3Update(pParse,X,Y,W,R,0,0); @@ -821,13 +839,11 @@ ////////////////////////// The INSERT command ///////////////////////////////// // -cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). { - sqlite3WithPush(pParse, W, 1); +cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). { sqlite3Insert(pParse, X, S, F, R); } -cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES. +cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES. { - sqlite3WithPush(pParse, W, 1); sqlite3Insert(pParse, X, 0, F, R); } @@ -1485,15 +1501,13 @@ //////////////////////// COMMON TABLE EXPRESSIONS //////////////////////////// -%type with {With*} %type wqlist {With*} -%destructor with {sqlite3WithDelete(pParse->db, $$);} %destructor wqlist {sqlite3WithDelete(pParse->db, $$);} -with(A) ::= . {A = 0;} +with ::= . %ifndef SQLITE_OMIT_CTE -with(A) ::= WITH wqlist(W). { A = W; } -with(A) ::= WITH RECURSIVE wqlist(W). { A = W; } +with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); } +with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); } wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. { A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/
diff --git a/third_party/sqlite/src/src/pcache.c b/third_party/sqlite/src/src/pcache.c index d25af12..d5e8711 100644 --- a/third_party/sqlite/src/src/pcache.c +++ b/third_party/sqlite/src/src/pcache.c
@@ -431,7 +431,7 @@ sqlite3_log(SQLITE_FULL, "spill page %d making room for %d - cache used: %d/%d", pPg->pgno, pgno, - sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), + sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache), numberOfCachePages(pCache)); #endif pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
diff --git a/third_party/sqlite/src/src/prepare.c b/third_party/sqlite/src/src/prepare.c index 3370c18..33135a10 100644 --- a/third_party/sqlite/src/src/prepare.c +++ b/third_party/sqlite/src/src/prepare.c
@@ -29,7 +29,7 @@ char *z; if( zObj==0 ) zObj = "?"; z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj); - if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); + if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); sqlite3DbFree(db, *pData->pzErrMsg); *pData->pzErrMsg = z; }
diff --git a/third_party/sqlite/src/src/printf.c b/third_party/sqlite/src/src/printf.c index a614b7ea..112613f 100644 --- a/third_party/sqlite/src/src/printf.c +++ b/third_party/sqlite/src/src/printf.c
@@ -206,6 +206,11 @@ PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ + /* pAccum never starts out with an empty buffer that was obtained from + ** malloc(). This precondition is required by the mprintf("%z...") + ** optimization. */ + assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); + bufpt = 0; if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){ pArgList = va_arg(ap, PrintfArguments*); @@ -624,9 +629,38 @@ case etCHARX: if( bArgList ){ bufpt = getTextArg(pArgList); - c = bufpt ? bufpt[0] : 0; + length = 1; + if( bufpt ){ + buf[0] = c = *(bufpt++); + if( (c&0xc0)==0xc0 ){ + while( length<4 && (bufpt[0]&0xc0)==0x80 ){ + buf[length++] = *(bufpt++); + } + } + }else{ + buf[0] = 0; + } }else{ - c = va_arg(ap,int); + unsigned int ch = va_arg(ap,unsigned int); + if( ch<0x00080 ){ + buf[0] = ch & 0xff; + length = 1; + }else if( ch<0x00800 ){ + buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); + buf[1] = 0x80 + (u8)(ch & 0x3f); + length = 2; + }else if( ch<0x10000 ){ + buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); + buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); + buf[2] = 0x80 + (u8)(ch & 0x3f); + length = 3; + }else{ + buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); + buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); + buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); + buf[3] = 0x80 + (u8)(ch & 0x3f); + length = 4; + } } if( precision>1 ){ width -= precision-1; @@ -634,12 +668,13 @@ sqlite3AppendChar(pAccum, width-1, ' '); width = 0; } - sqlite3AppendChar(pAccum, precision-1, c); + while( precision-- > 1 ){ + sqlite3StrAccumAppend(pAccum, buf, length); + } } - length = 1; - buf[0] = c; bufpt = buf; - break; + flag_altform2 = 1; + goto adjust_width_for_utf8; case etSTRING: case etDYNSTRING: if( bArgList ){ @@ -651,17 +686,45 @@ if( bufpt==0 ){ bufpt = ""; }else if( xtype==etDYNSTRING ){ + if( pAccum->nChar==0 && pAccum->mxAlloc && width==0 && precision<0 ){ + /* Special optimization for sqlite3_mprintf("%z..."): + ** Extend an existing memory allocation rather than creating + ** a new one. */ + assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 ); + pAccum->zText = bufpt; + pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt); + pAccum->nChar = 0x7fffffff & (int)strlen(bufpt); + pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED; + length = 0; + break; + } zExtra = bufpt; } if( precision>=0 ){ - for(length=0; length<precision && bufpt[length]; length++){} + if( flag_altform2 ){ + /* Set length to the number of bytes needed in order to display + ** precision characters */ + unsigned char *z = (unsigned char*)bufpt; + while( precision-- > 0 && z[0] ){ + SQLITE_SKIP_UTF8(z); + } + length = (int)(z - (unsigned char*)bufpt); + }else{ + for(length=0; length<precision && bufpt[length]; length++){} + } }else{ length = 0x7fffffff & (int)strlen(bufpt); } + adjust_width_for_utf8: + if( flag_altform2 && width>0 ){ + /* Adjust width to account for extra bytes in UTF-8 characters */ + int ii = length - 1; + while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; + } break; - case etSQLESCAPE: /* Escape ' characters */ - case etSQLESCAPE2: /* Escape ' and enclose in '...' */ - case etSQLESCAPE3: { /* Escape " characters */ + case etSQLESCAPE: /* %q: Escape ' characters */ + case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ + case etSQLESCAPE3: { /* %w: Escape " characters */ int i, j, k, n, isnull; int needQuote; char ch; @@ -675,9 +738,17 @@ } isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); + /* For %q, %Q, and %w, the precision is the number of byte (or + ** characters if the ! flags is present) to use from the input. + ** Because of the extra quoting characters inserted, the number + ** of output characters may be larger than the precision. + */ k = precision; for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){ if( ch==q ) n++; + if( flag_altform2 && (ch&0xc0)==0xc0 ){ + while( (escarg[i+1]&0xc0)==0x80 ){ i++; } + } } needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 3; @@ -700,10 +771,7 @@ if( needQuote ) bufpt[j++] = q; bufpt[j] = 0; length = j; - /* The precision in %q and %Q means how many input characters to - ** consume, not the length of the output... - ** if( precision>=0 && precision<length ) length = precision; */ - break; + goto adjust_width_for_utf8; } case etTOKEN: { Token *pToken; @@ -742,7 +810,10 @@ /* ** The text of the conversion is pointed to by "bufpt" and is ** "length" characters long. The field width is "width". Do - ** the output. + ** the output. Both length and width are in bytes, not characters, + ** at this point. If the "!" flag was present on string conversions + ** indicating that width and precision should be expressed in characters, + ** then the values have been translated prior to reaching this point. */ width -= length; if( width>0 ){
diff --git a/third_party/sqlite/src/src/recover.h b/third_party/sqlite/src/src/recover.h index 5bf50c8..49b0d9e8 100644 --- a/third_party/sqlite/src/src/recover.h +++ b/third_party/sqlite/src/src/recover.h
@@ -16,7 +16,7 @@ ** selected users to enable it (currently sql/recovery.cc). */ SQLITE_API -int chrome_sqlite3_recoverVtableInit(sqlite3* db); +int chrome_sqlite3_recoverVtableInit(sqlite3 *db); #ifdef __cplusplus } /* End of the 'extern "C"' block */
diff --git a/third_party/sqlite/src/src/resolve.c b/third_party/sqlite/src/src/resolve.c index 29db9fa..d26a00e7d 100644 --- a/third_party/sqlite/src/src/resolve.c +++ b/third_party/sqlite/src/src/resolve.c
@@ -431,10 +431,16 @@ ** Because no reference was made to outer contexts, the pNC->nRef ** fields are not changed in any context. */ - if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){ - pExpr->op = TK_STRING; - pExpr->pTab = 0; - return WRC_Prune; + if( cnt==0 && zTab==0 ){ + assert( pExpr->op==TK_ID ); + if( ExprHasProperty(pExpr,EP_DblQuoted) ){ + pExpr->op = TK_STRING; + pExpr->pTab = 0; + return WRC_Prune; + } + if( sqlite3ExprIdToTrueFalse(pExpr) ){ + return WRC_Prune; + } } /* @@ -783,15 +789,30 @@ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr); break; } + case TK_IS: + case TK_ISNOT: { + Expr *pRight; + assert( !ExprHasProperty(pExpr, EP_Reduced) ); + /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", + ** and "x IS NOT FALSE". */ + if( (pRight = pExpr->pRight)->op==TK_ID ){ + int rc = resolveExprStep(pWalker, pRight); + if( rc==WRC_Abort ) return WRC_Abort; + if( pRight->op==TK_TRUEFALSE ){ + pExpr->op2 = pExpr->op; + pExpr->op = TK_TRUTH; + return WRC_Continue; + } + } + /* Fall thru */ + } case TK_BETWEEN: case TK_EQ: case TK_NE: case TK_LT: case TK_LE: case TK_GT: - case TK_GE: - case TK_IS: - case TK_ISNOT: { + case TK_GE: { int nLeft, nRight; if( pParse->db->mallocFailed ) break; assert( pExpr->pLeft!=0 );
diff --git a/third_party/sqlite/src/src/select.c b/third_party/sqlite/src/src/select.c index 140779b..381f40d 100644 --- a/third_party/sqlite/src/src/select.c +++ b/third_party/sqlite/src/src/select.c
@@ -21,8 +21,7 @@ /***/ int sqlite3SelectTrace = 0; # define SELECTTRACE(K,P,S,X) \ if(sqlite3SelectTrace&(K)) \ - sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\ - (S)->zSelName,(S)),\ + sqlite3DebugPrintf("%s/%p: ",(S)->zSelName,(S)),\ sqlite3DebugPrintf X #else # define SELECTTRACE(K,P,S,X) @@ -383,6 +382,29 @@ } } +/* Undo the work of setJoinExpr(). In the expression tree p, convert every +** term that is marked with EP_FromJoin and iRightJoinTable==iTable into +** an ordinary term that omits the EP_FromJoin mark. +** +** This happens when a LEFT JOIN is simplified into an ordinary JOIN. +*/ +static void unsetJoinExpr(Expr *p, int iTable){ + while( p ){ + if( ExprHasProperty(p, EP_FromJoin) + && (iTable<0 || p->iRightJoinTable==iTable) ){ + ExprClearProperty(p, EP_FromJoin); + } + if( p->op==TK_FUNCTION && p->x.pList ){ + int i; + for(i=0; i<p->x.pList->nExpr; i++){ + unsetJoinExpr(p->x.pList->a[i].pExpr, iTable); + } + } + unsetJoinExpr(p->pLeft, iTable); + p = p->pRight; + } +} + /* ** This routine processes the join information for a SELECT statement. ** ON and USING clauses are converted into extra terms of the WHERE clause. @@ -1266,12 +1288,15 @@ iSortTab = iTab; bSeq = 1; } - for(i=0, iCol=nKey+bSeq; i<nSortData; i++){ + for(i=0, iCol=nKey+bSeq-1; i<nSortData; i++){ + if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++; + } + for(i=nSortData-1; i>=0; i--){ int iRead; if( aOutEx[i].u.x.iOrderByCol ){ iRead = aOutEx[i].u.x.iOrderByCol-1; }else{ - iRead = iCol++; + iRead = iCol--; } sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i); VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan)); @@ -3748,7 +3773,6 @@ pOrderBy->a[i].u.x.iOrderByCol = 0; } assert( pParent->pOrderBy==0 ); - assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } @@ -3832,12 +3856,22 @@ ** (3) The inner query has a LIMIT clause (since the changes to the WHERE ** close would change the meaning of the LIMIT). ** -** (4) The inner query is the right operand of a LEFT JOIN. (The caller -** enforces this restriction since this routine does not have enough -** information to know.) +** (4) The inner query is the right operand of a LEFT JOIN and the +** expression to be pushed down does not come from the ON clause +** on that LEFT JOIN. ** ** (5) The WHERE clause expression originates in the ON or USING clause -** of a LEFT JOIN. +** of a LEFT JOIN where iCursor is not the right-hand table of that +** left join. An example: +** +** SELECT * +** FROM (SELECT 1 AS a1 UNION ALL SELECT 2) AS aa +** JOIN (SELECT 1 AS b2 UNION ALL SELECT 2) AS bb ON (a1=b2) +** LEFT JOIN (SELECT 8 AS c3 UNION ALL SELECT 9) AS cc ON (b2=2); +** +** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9). +** But if the (b2=2) term were to be pushed down into the bb subquery, +** then the (1,1,NULL) row would be suppressed. ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. @@ -3846,7 +3880,8 @@ Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ Expr *pWhere, /* The WHERE clause of the outer query */ - int iCursor /* Cursor number of the subquery */ + int iCursor, /* Cursor number of the subquery */ + int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */ ){ Expr *pNew; int nChng = 0; @@ -3870,15 +3905,25 @@ return 0; /* restriction (3) */ } while( pWhere->op==TK_AND ){ - nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, iCursor); + nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, + iCursor, isLeftJoin); pWhere = pWhere->pLeft; } - if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction (5) */ + if( isLeftJoin + && (ExprHasProperty(pWhere,EP_FromJoin)==0 + || pWhere->iRightJoinTable!=iCursor) + ){ + return 0; /* restriction (4) */ + } + if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){ + return 0; /* restriction (5) */ + } if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ nChng++; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); + unsetJoinExpr(pNew, -1); x.pParse = pParse; x.iTable = iCursor; x.iNewTable = iCursor; @@ -4333,9 +4378,7 @@ } pTabList = p->pSrc; pEList = p->pEList; - if( OK_IF_ALWAYS_TRUE(p->pWith) ){ - sqlite3WithPush(pParse, p->pWith, 0); - } + sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -4911,14 +4954,6 @@ #endif /* -** Context object for havingToWhereExprCb(). -*/ -struct HavingToWhereCtx { - Expr **ppWhere; - ExprList *pGroupBy; -}; - -/* ** sqlite3WalkExpr() callback used by havingToWhere(). ** ** If the node passed to the callback is a TK_AND node, return @@ -4931,15 +4966,16 @@ */ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ if( pExpr->op!=TK_AND ){ - struct HavingToWhereCtx *p = pWalker->u.pHavingCtx; - if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, p->pGroupBy) ){ + Select *pS = pWalker->u.pSelect; + if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){ sqlite3 *db = pWalker->pParse->db; Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0); if( pNew ){ - Expr *pWhere = *(p->ppWhere); + Expr *pWhere = pS->pWhere; SWAP(Expr, *pNew, *pExpr); pNew = sqlite3ExprAnd(db, pWhere, pNew); - *(p->ppWhere) = pNew; + pS->pWhere = pNew; + pWalker->eCode = 1; } } return WRC_Prune; @@ -4962,23 +4998,19 @@ ** entirely of constants and expressions that are also GROUP BY terms that ** use the "BINARY" collation sequence. */ -static void havingToWhere( - Parse *pParse, - ExprList *pGroupBy, - Expr *pHaving, - Expr **ppWhere -){ - struct HavingToWhereCtx sCtx; +static void havingToWhere(Parse *pParse, Select *p){ Walker sWalker; - - sCtx.ppWhere = ppWhere; - sCtx.pGroupBy = pGroupBy; - memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.xExprCallback = havingToWhereExprCb; - sWalker.u.pHavingCtx = &sCtx; - sqlite3WalkExpr(&sWalker, pHaving); + sWalker.u.pSelect = p; + sqlite3WalkExpr(&sWalker, p->pHaving); +#if SELECTTRACE_ENABLED + if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){ + SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); + sqlite3TreeViewSelect(0, p, 0); + } +#endif } /* @@ -5139,7 +5171,6 @@ if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; memset(&sAggInfo, 0, sizeof(sAggInfo)); #if SELECTTRACE_ENABLED - pParse->nSelectIndent++; SELECTTRACE(1,pParse,p, ("begin processing:\n")); if( sqlite3SelectTrace & 0x100 ){ sqlite3TreeViewSelect(0, p, 0); @@ -5185,13 +5216,29 @@ generateColumnNames(pParse, p); } - /* Try to flatten subqueries in the FROM clause up into the main query + /* Try to various optimizations (flattening subqueries, and strength + ** reduction of join operators) in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; Table *pTab = pItem->pTab; + + /* Convert LEFT JOIN into JOIN if there are terms of the right table + ** of the LEFT JOIN used in the WHERE clause. + */ + if( (pItem->fg.jointype & JT_LEFT)!=0 + && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor) + && OptimizationEnabled(db, SQLITE_SimplifyJoin) + ){ + SELECTTRACE(0x100,pParse,p, + ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); + pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); + unsetJoinExpr(p->pWhere, pItem->iCursor); + } + + /* No futher action if this term of the FROM clause is no a subquery */ if( pSub==0 ) continue; /* Catch mismatch in the declared columns of a view and the number of @@ -5260,7 +5307,6 @@ explainSetInteger(pParse->iSelectId, iRestoreSelectId); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p,("end compound-select processing\n")); - pParse->nSelectIndent--; #endif return rc; } @@ -5333,8 +5379,9 @@ /* Make copies of constant WHERE-clause terms in the outer query down ** inside the subquery. This can help the subquery to run more efficiently. */ - if( (pItem->fg.jointype & JT_OUTER)==0 - && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor) + if( OptimizationEnabled(db, SQLITE_PushDown) + && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, + (pItem->fg.jointype & JT_OUTER)!=0) ){ #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x100 ){ @@ -5342,6 +5389,8 @@ sqlite3TreeViewSelect(0, p, 0); } #endif + }else{ + SELECTTRACE(0x100,pParse,p,("Push-down not possible\n")); } zSavedAuthContext = pParse->zAuthContext; @@ -5544,6 +5593,7 @@ wctrlFlags |= p->selFlags & SF_FixedLimit; /* Begin the database scan. */ + SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, p->pEList, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; @@ -5645,7 +5695,9 @@ if( pHaving ){ if( pGroupBy ){ assert( pWhere==p->pWhere ); - havingToWhere(pParse, pGroupBy, pHaving, &p->pWhere); + assert( pHaving==p->pHaving ); + assert( pGroupBy==p->pGroupBy ); + havingToWhere(pParse, p); pWhere = p->pWhere; } sqlite3ExprAnalyzeAggregates(&sNC, pHaving); @@ -5732,6 +5784,7 @@ ** in the right order to begin with. */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); + SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 ); @@ -5987,6 +6040,7 @@ assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 ); assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 ); + SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, 0, minMaxFlag, 0); if( pWInfo==0 ){ @@ -6042,7 +6096,6 @@ sqlite3DbFree(db, sAggInfo.aFunc); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p,("end processing\n")); - pParse->nSelectIndent--; #endif return rc; }
diff --git a/third_party/sqlite/src/src/shell.c.in b/third_party/sqlite/src/src/shell.c.in index 8c61856..18751fc7 100644 --- a/third_party/sqlite/src/src/shell.c.in +++ b/third_party/sqlite/src/src/shell.c.in
@@ -132,6 +132,9 @@ # ifndef access # define access(f,m) _access((f),(m)) # endif +# ifndef unlink +# define unlink _unlink +# endif # undef popen # define popen _popen # undef pclose @@ -1069,6 +1072,7 @@ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ +#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */ /* ** These are the allowed shellFlgs values @@ -1175,6 +1179,7 @@ ** ** Also throw an error if the EDITOR program returns a non-zero exit code. */ +#ifndef SQLITE_NOHAVE_SYSTEM static void editFunc( sqlite3_context *context, int argc, @@ -1272,9 +1277,10 @@ goto edit_func_end; } if( bBin ){ - sqlite3_result_blob(context, p, sz, sqlite3_free); + sqlite3_result_blob64(context, p, sz, sqlite3_free); }else{ - sqlite3_result_text(context, (const char*)p, sz, sqlite3_free); + sqlite3_result_text64(context, (const char*)p, sz, + sqlite3_free, SQLITE_UTF8); } p = 0; @@ -1284,6 +1290,7 @@ sqlite3_free(zTempFile); sqlite3_free(p); } +#endif /* SQLITE_NOHAVE_SYSTEM */ /* ** Save or restore the current output mode @@ -2269,29 +2276,54 @@ ){ int iCur; int iHiwtr; + FILE *out; + if( pArg==0 || pArg->out==0 ) return 0; + out = pArg->out; - if( pArg && pArg->out ){ - displayStatLine(pArg, "Memory Used:", - "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); - displayStatLine(pArg, "Number of Outstanding Allocations:", - "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); - if( pArg->shellFlgs & SHFLG_Pagecache ){ - displayStatLine(pArg, "Number of Pcache Pages Used:", - "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); - } - displayStatLine(pArg, "Number of Pcache Overflow Bytes:", - "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine(pArg, "Largest Allocation:", - "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine(pArg, "Largest Pcache Allocation:", - "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); -#ifdef YYTRACKMAXSTACKDEPTH - displayStatLine(pArg, "Deepest Parser Stack:", - "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); + if( pArg->pStmt && (pArg->statsOn & 2) ){ + int nCol, i, x; + sqlite3_stmt *pStmt = pArg->pStmt; + char z[100]; + nCol = sqlite3_column_count(pStmt); + raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol); + for(i=0; i<nCol; i++){ + sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); +#ifndef SQLITE_OMIT_DECLTYPE + sqlite3_snprintf(30, z+x, "declared type:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); #endif +#ifdef SQLITE_ENABLE_COLUMN_METADATA + sqlite3_snprintf(30, z+x, "database name:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_database_name(pStmt,i)); + sqlite3_snprintf(30, z+x, "table name:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); + sqlite3_snprintf(30, z+x, "origin name:"); + utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i)); +#endif + } } - if( pArg && pArg->out && db ){ + displayStatLine(pArg, "Memory Used:", + "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); + displayStatLine(pArg, "Number of Outstanding Allocations:", + "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); + if( pArg->shellFlgs & SHFLG_Pagecache ){ + displayStatLine(pArg, "Number of Pcache Pages Used:", + "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); + } + displayStatLine(pArg, "Number of Pcache Overflow Bytes:", + "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); + displayStatLine(pArg, "Largest Allocation:", + "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); + displayStatLine(pArg, "Largest Pcache Allocation:", + "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); +#ifdef YYTRACKMAXSTACKDEPTH + displayStatLine(pArg, "Deepest Parser Stack:", + "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); +#endif + + if( db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, @@ -2326,6 +2358,9 @@ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); raw_printf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; + sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); + raw_printf(pArg->out, "Page cache spills: %d\n", iCur); + iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); @@ -2335,7 +2370,7 @@ iCur); } - if( pArg && pArg->out && db && pArg->pStmt ){ + if( pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); @@ -2345,6 +2380,12 @@ raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE, bReset); + raw_printf(pArg->out, "Reprepare operations: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); + raw_printf(pArg->out, "Number of times run: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); + raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ @@ -2560,8 +2601,7 @@ */ static void exec_prepared_stmt( ShellState *pArg, /* Pointer to ShellState */ - sqlite3_stmt *pStmt, /* Statment to run */ - int (*xCallback)(void*,int,char**,char**,int*) /* Callback function */ + sqlite3_stmt *pStmt /* Statment to run */ ){ int rc; @@ -2571,54 +2611,47 @@ rc = sqlite3_step(pStmt); /* if we have a result set... */ if( SQLITE_ROW == rc ){ - /* if we have a callback... */ - if( xCallback ){ - /* allocate space for col name ptr, value ptr, and type */ - int nCol = sqlite3_column_count(pStmt); - void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); - if( !pData ){ - rc = SQLITE_NOMEM; - }else{ - char **azCols = (char **)pData; /* Names of result columns */ - char **azVals = &azCols[nCol]; /* Results */ - int *aiTypes = (int *)&azVals[nCol]; /* Result types */ - int i, x; - assert(sizeof(int) <= sizeof(char *)); - /* save off ptrs to column names */ - for(i=0; i<nCol; i++){ - azCols[i] = (char *)sqlite3_column_name(pStmt, i); - } - do{ - /* extract the data and data types */ - for(i=0; i<nCol; i++){ - aiTypes[i] = x = sqlite3_column_type(pStmt, i); - if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ - azVals[i] = ""; - }else{ - azVals[i] = (char*)sqlite3_column_text(pStmt, i); - } - if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ - rc = SQLITE_NOMEM; - break; /* from for */ - } - } /* end for */ - - /* if data and types extracted successfully... */ - if( SQLITE_ROW == rc ){ - /* call the supplied callback with the result row data */ - if( xCallback(pArg, nCol, azVals, azCols, aiTypes) ){ - rc = SQLITE_ABORT; - }else{ - rc = sqlite3_step(pStmt); - } - } - } while( SQLITE_ROW == rc ); - sqlite3_free(pData); - } + /* allocate space for col name ptr, value ptr, and type */ + int nCol = sqlite3_column_count(pStmt); + void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1); + if( !pData ){ + rc = SQLITE_NOMEM; }else{ + char **azCols = (char **)pData; /* Names of result columns */ + char **azVals = &azCols[nCol]; /* Results */ + int *aiTypes = (int *)&azVals[nCol]; /* Result types */ + int i, x; + assert(sizeof(int) <= sizeof(char *)); + /* save off ptrs to column names */ + for(i=0; i<nCol; i++){ + azCols[i] = (char *)sqlite3_column_name(pStmt, i); + } do{ - rc = sqlite3_step(pStmt); - } while( rc == SQLITE_ROW ); + /* extract the data and data types */ + for(i=0; i<nCol; i++){ + aiTypes[i] = x = sqlite3_column_type(pStmt, i); + if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ + azVals[i] = ""; + }else{ + azVals[i] = (char*)sqlite3_column_text(pStmt, i); + } + if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ + rc = SQLITE_NOMEM; + break; /* from for */ + } + } /* end for */ + + /* if data and types extracted successfully... */ + if( SQLITE_ROW == rc ){ + /* call the supplied callback with the result row data */ + if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){ + rc = SQLITE_ABORT; + }else{ + rc = sqlite3_step(pStmt); + } + } + } while( SQLITE_ROW == rc ); + sqlite3_free(pData); } } } @@ -2764,17 +2797,15 @@ ** and callback data argument. */ static int shell_exec( - sqlite3 *db, /* An open database */ - const char *zSql, /* SQL to be evaluated */ - int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ - /* (not the same as sqlite3_exec) */ ShellState *pArg, /* Pointer to ShellState */ + const char *zSql, /* SQL to be evaluated */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ int rc2; const char *zLeftover; /* Tail of unprocessed SQL */ + sqlite3 *db = pArg->db; if( pzErrMsg ){ *pzErrMsg = NULL; @@ -2845,13 +2876,18 @@ if( rc==SQLITE_OK ){ pArg->cMode = MODE_Explain; explain_data_prepare(pArg, pExplain); - exec_prepared_stmt(pArg, pExplain, xCallback); + exec_prepared_stmt(pArg, pExplain); explain_data_delete(pArg); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); } - sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, triggerEQP, 0); + if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ + sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); + /* Reprepare pStmt before reactiving trace modes */ + sqlite3_finalize(pStmt); + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + } restore_debug_trace_modes(); } @@ -2871,7 +2907,7 @@ } } - exec_prepared_stmt(pArg, pStmt, xCallback); + exec_prepared_stmt(pArg, pStmt); explain_data_delete(pArg); /* print usage stats if stats on */ @@ -3134,11 +3170,11 @@ savedMode = p->mode; p->zDestTable = sTable.z; p->mode = p->cMode = MODE_Insert; - rc = shell_exec(p->db, sSelect.z, shell_callback, p, 0); + rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); - shell_exec(p->db, sSelect.z, shell_callback, p, 0); + shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); } p->zDestTable = savedDestTable; @@ -3255,6 +3291,7 @@ " on the output.\n" ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE\n" " The --new option starts with an empty file\n" + " Other options: --readonly --append --zip\n" ".output ?FILE? Send output to FILE or stdout\n" ".print STRING... Print literal STRING\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" @@ -3272,10 +3309,14 @@ ".session CMD ... Create or control sessions\n" #endif ".sha3sum ?OPTIONS...? Compute a SHA3 hash of database content\n" +#ifndef SQLITE_NOHAVE_SYSTEM ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" +#endif ".show Show the current values for various settings\n" ".stats ?on|off? Show stats or turn stats on or off\n" +#ifndef SQLITE_NOHAVE_SYSTEM ".system CMD ARGS... Run CMD ARGS... in a system shell\n" +#endif ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" @@ -3404,13 +3445,21 @@ /* ** Try to deduce the type of file for zName based on its content. Return ** one of the SHELL_OPEN_* constants. +** +** If the file does not exist or is empty but its name looks like a ZIP +** archive and the dfltZip flag is true, then assume it is a ZIP archive. +** Otherwise, assume an ordinary database regardless of the filename if +** the type cannot be determined from content. */ -static int deduceDatabaseType(const char *zName){ +static int deduceDatabaseType(const char *zName, int dfltZip){ FILE *f = fopen(zName, "rb"); size_t n; int rc = SHELL_OPEN_UNSPEC; char zBuf[100]; - if( f==0 ) return SHELL_OPEN_NORMAL; + if( f==0 ){ + if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ) return SHELL_OPEN_ZIPFILE; + return SHELL_OPEN_NORMAL; + } fseek(f, -25, SEEK_END); n = fread(zBuf, 25, 1, f); if( n==1 && memcmp(zBuf, "Start-Of-SQLite3-", 17)==0 ){ @@ -3421,6 +3470,8 @@ if( n==1 && zBuf[0]==0x50 && zBuf[1]==0x4b && zBuf[2]==0x05 && zBuf[3]==0x06 ){ rc = SHELL_OPEN_ZIPFILE; + }else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ + return SHELL_OPEN_ZIPFILE; } } fclose(f); @@ -3435,7 +3486,7 @@ if( p->db==0 ){ sqlite3_initialize(); if( p->openMode==SHELL_OPEN_UNSPEC && access(p->zDbFilename,0)==0 ){ - p->openMode = deduceDatabaseType(p->zDbFilename); + p->openMode = (u8)deduceDatabaseType(p->zDbFilename, 0); } switch( p->openMode ){ case SHELL_OPEN_APPENDVFS: { @@ -3447,6 +3498,10 @@ sqlite3_open(":memory:", &p->db); break; } + case SHELL_OPEN_READONLY: { + sqlite3_open_v2(p->zDbFilename, &p->db, SQLITE_OPEN_READONLY, 0); + break; + } case SHELL_OPEN_UNSPEC: case SHELL_OPEN_NORMAL: { sqlite3_open(p->zDbFilename, &p->db); @@ -3476,10 +3531,12 @@ shellModuleSchema, 0, 0); sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, shellPutsFunc, 0, 0); +#ifndef SQLITE_NOHAVE_SYSTEM sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, editFunc, 0, 0); sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0, editFunc, 0, 0); +#endif if( p->openMode==SHELL_OPEN_ZIPFILE ){ char *zSql = sqlite3_mprintf( "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename); @@ -3676,7 +3733,6 @@ return f; } -#if !defined(SQLITE_UNTESTABLE) #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) /* ** A routine for handling output from sqlite3_trace(). @@ -3699,7 +3755,6 @@ return 0; } #endif -#endif /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is @@ -4094,6 +4149,7 @@ #endif }else{ output_file_close(p->out); +#ifndef SQLITE_NOHAVE_SYSTEM if( p->doXdgOpen ){ const char *zXdgOpenCmd = #if defined(_WIN32) @@ -4112,6 +4168,7 @@ outputModePop(p); p->doXdgOpen = 0; } +#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ } p->outfile[0] = 0; p->out = stdout; @@ -5222,8 +5279,8 @@ " data BLOB -- compressed content\n" ")"; const char *zDrop = "DROP TABLE IF EXISTS sqlar"; - const char *zInsertFmt = - "REPLACE INTO sqlar(name,mode,mtime,sz,data)\n" + const char *zInsertFmt[2] = { + "REPLACE INTO %s(name,mode,mtime,sz,data)\n" " SELECT\n" " %s,\n" " mode,\n" @@ -5232,30 +5289,70 @@ " WHEN '-' THEN length(data)\n" " WHEN 'd' THEN 0\n" " ELSE -1 END,\n" - " CASE WHEN lsmode(mode) LIKE 'd%%' THEN NULL else data END\n" + " sqlar_compress(data)\n" " FROM fsdir(%Q,%Q)\n" - " WHERE lsmode(mode) NOT LIKE '?%%';"; + " WHERE lsmode(mode) NOT LIKE '?%%';", + "REPLACE INTO %s(name,mode,mtime,data)\n" + " SELECT\n" + " %s,\n" + " mode,\n" + " mtime,\n" + " data\n" + " FROM fsdir(%Q,%Q)\n" + " WHERE lsmode(mode) NOT LIKE '?%%';" + }; int i; /* For iterating through azFile[] */ int rc; /* Return code */ + const char *zTab = 0; /* SQL table into which to insert */ + char *zSql; + char zTemp[50]; + arExecSql(pAr, "PRAGMA page_size=512"); rc = arExecSql(pAr, "SAVEPOINT ar;"); if( rc!=SQLITE_OK ) return rc; - if( bUpdate==0 ){ - rc = arExecSql(pAr, zDrop); - if( rc!=SQLITE_OK ) return rc; + zTemp[0] = 0; + if( pAr->bZip ){ + /* Initialize the zipfile virtual table, if necessary */ + if( pAr->zFile ){ + sqlite3_uint64 r; + sqlite3_randomness(sizeof(r),&r); + sqlite3_snprintf(sizeof(zTemp),zTemp,"zip%016llx",r); + zTab = zTemp; + zSql = sqlite3_mprintf( + "CREATE VIRTUAL TABLE temp.%s USING zipfile(%Q)", + zTab, pAr->zFile + ); + rc = arExecSql(pAr, zSql); + sqlite3_free(zSql); + }else{ + zTab = "zip"; + } + }else{ + /* Initialize the table for an SQLAR */ + zTab = "sqlar"; + if( bUpdate==0 ){ + rc = arExecSql(pAr, zDrop); + if( rc!=SQLITE_OK ) goto end_ar_transaction; + } + rc = arExecSql(pAr, zCreate); } - rc = arExecSql(pAr, zCreate); for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ - char *zSql = sqlite3_mprintf(zInsertFmt, + char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab, pAr->bVerbose ? "shell_putsnl(name)" : "name", pAr->azArg[i], pAr->zDir); - rc = arExecSql(pAr, zSql); - sqlite3_free(zSql); + rc = arExecSql(pAr, zSql2); + sqlite3_free(zSql2); } +end_ar_transaction: if( rc!=SQLITE_OK ){ arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;"); }else{ rc = arExecSql(pAr, "RELEASE ar;"); + if( pAr->bZip && pAr->zFile ){ + zSql = sqlite3_mprintf("DROP TABLE %s", zTemp); + arExecSql(pAr, zSql); + sqlite3_free(zSql); + } } return rc; } @@ -5277,20 +5374,17 @@ cmd.p = pState; cmd.db = pState->db; if( cmd.zFile ){ - eDbType = deduceDatabaseType(cmd.zFile); + eDbType = deduceDatabaseType(cmd.zFile, 1); }else{ eDbType = pState->openMode; } if( eDbType==SHELL_OPEN_ZIPFILE ){ - if( cmd.zFile==0 ){ - cmd.zSrcTable = sqlite3_mprintf("zip"); - }else{ - cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); - } - if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){ - utf8_printf(stderr, "zip archives are read-only\n"); - rc = SQLITE_ERROR; - goto end_ar_command; + if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){ + if( cmd.zFile==0 ){ + cmd.zSrcTable = sqlite3_mprintf("zip"); + }else{ + cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); + } } cmd.bZip = 1; }else if( cmd.zFile ){ @@ -5315,14 +5409,12 @@ goto end_ar_command; } sqlite3_fileio_init(cmd.db, 0, 0); -#ifdef SQLITE_HAVE_ZLIB sqlite3_sqlar_init(cmd.db, 0, 0); -#endif sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, shellPutsFunc, 0, 0); } - if( cmd.zSrcTable==0 ){ + if( cmd.zSrcTable==0 && cmd.bZip==0 ){ if( cmd.eCmd!=AR_CMD_CREATE && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ @@ -5715,7 +5807,7 @@ }else if( strcmp(azArg[1],"trigger")==0 ){ p->autoEQP = AUTOEQP_trigger; }else{ - p->autoEQP = booleanValue(azArg[1]); + p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ raw_printf(stderr, "Usage: .eqp off|on|trigger|full\n"); @@ -5802,14 +5894,11 @@ callback, &data, &zErrMsg); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; - shell_exec(p->db, "SELECT * FROM sqlite_stat1", - shell_callback, &data,&zErrMsg); + shell_exec(p, "SELECT * FROM sqlite_stat1", &zErrMsg); data.zDestTable = "sqlite_stat3"; - shell_exec(p->db, "SELECT * FROM sqlite_stat3", - shell_callback, &data,&zErrMsg); + shell_exec(p, "SELECT * FROM sqlite_stat3", &zErrMsg); data.zDestTable = "sqlite_stat4"; - shell_exec(p->db, "SELECT * FROM sqlite_stat4", - shell_callback, &data, &zErrMsg); + shell_exec(p, "SELECT * FROM sqlite_stat4", &zErrMsg); raw_printf(p->out, "ANALYZE sqlite_master;\n"); } }else @@ -6290,12 +6379,14 @@ const char *z = azArg[iName]; if( optionMatch(z,"new") ){ newFlag = 1; -#ifdef SQLITE_HAVE_ZIP +#ifdef SQLITE_HAVE_ZLIB }else if( optionMatch(z, "zip") ){ p->openMode = SHELL_OPEN_ZIPFILE; #endif }else if( optionMatch(z, "append") ){ p->openMode = SHELL_OPEN_APPENDVFS; + }else if( optionMatch(z, "readonly") ){ + p->openMode = SHELL_OPEN_READONLY; }else if( z[0]=='-' ){ utf8_printf(stderr, "unknown option: %s\n", z); rc = 1; @@ -6352,6 +6443,7 @@ } output_reset(p); if( zFile[0]=='-' && zFile[1]=='-' ) zFile++; +#ifndef SQLITE_NOHAVE_SYSTEM if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){ p->doXdgOpen = 1; outputModePush(p); @@ -6366,6 +6458,7 @@ } zFile = p->zTempFile; } +#endif /* SQLITE_NOHAVE_SYSTEM */ if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); @@ -6485,10 +6578,9 @@ sqlite3_close(pSrc); }else - if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ - p->scanstatsOn = booleanValue(azArg[1]); + p->scanstatsOn = (u8)booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); #endif @@ -6527,8 +6619,8 @@ } } if( zName!=0 ){ - int isMaster = sqlite3_strlike(zName, "sqlite_master", 0)==0; - if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master",0)==0 ){ + int isMaster = sqlite3_strlike(zName, "sqlite_master", '\\')==0; + if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = sqlite3_mprintf( "CREATE TABLE %s (\n" @@ -6588,13 +6680,18 @@ appendText(&sSelect, ") WHERE ", 0); if( zName ){ char *zQarg = sqlite3_mprintf("%Q", zName); + int bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 || + strchr(zName, '[') != 0; if( strchr(zName, '.') ){ appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); }else{ appendText(&sSelect, "lower(tbl_name)", 0); } - appendText(&sSelect, strchr(zName, '*') ? " GLOB " : " LIKE ", 0); + appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0); appendText(&sSelect, zQarg, 0); + if( !bGlob ){ + appendText(&sSelect, " ESCAPE '\\' ", 0); + } appendText(&sSelect, " AND ", 0); sqlite3_free(zQarg); } @@ -7009,7 +7106,7 @@ }else{ zLike = z; bSeparate = 1; - if( sqlite3_strlike("sqlite_%", zLike, 0)==0 ) bSchema = 1; + if( sqlite3_strlike("sqlite\\_%", zLike, '\\')==0 ) bSchema = 1; } } if( bSchema ){ @@ -7076,11 +7173,12 @@ if( bDebug ){ utf8_printf(p->out, "%s\n", zSql); }else{ - shell_exec(p->db, zSql, shell_callback, p, 0); + shell_exec(p, zSql, 0); } sqlite3_free(zSql); }else +#ifndef SQLITE_NOHAVE_SYSTEM if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ @@ -7100,6 +7198,7 @@ sqlite3_free(zCmd); if( x ) raw_printf(stderr, "System command returns %d\n", x); }else +#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ if( c=='s' && strncmp(azArg[0], "show", n)==0 ){ static const char *azBool[] = { "off", "on", "trigger", "full"}; @@ -7139,7 +7238,7 @@ if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){ if( nArg==2 ){ - p->statsOn = booleanValue(azArg[1]); + p->statsOn = (u8)booleanValue(azArg[1]); }else if( nArg==1 ){ display_stats(p->db, p, 0); }else{ @@ -7680,6 +7779,16 @@ } /* +** We need a default sqlite3_complete() implementation to use in case +** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes +** any arbitrary text is a complete SQL statement. This is not very +** user-friendly, but it does seem to work. +*/ +#ifdef SQLITE_OMIT_COMPLETE +int sqlite3_complete(const char *zSql){ return 1; } +#endif + +/* ** Return true if zSql is a complete SQL statement. Return false if it ** ends in the middle of a string literal or C-style comment. */ @@ -7703,7 +7812,7 @@ open_db(p, 0); if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); BEGIN_TIMER; - rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); + rc = shell_exec(p, zSql, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; @@ -7935,6 +8044,10 @@ ** Show available command line options */ static const char zOptions[] = +#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) + " -A ARGS... run \".archive ARGS\" and exit\n" +#endif + " -append append the database to the end of the file\n" " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" @@ -7961,6 +8074,7 @@ " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" + " -readonly open the database read-only\n" " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" @@ -7968,6 +8082,9 @@ #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif +#ifdef SQLITE_HAVE_ZLIB + " -zip open the file as a ZIP Archive\n" +#endif ; static void usage(int showDetail){ utf8_printf(stderr, @@ -8070,21 +8187,39 @@ } #endif main_init(&data); + + /* On Windows, we must translate command-line arguments into UTF-8. + ** The SQLite memory allocator subsystem has to be enabled in order to + ** do this. But we want to run an sqlite3_shutdown() afterwards so that + ** subsequent sqlite3_config() calls will work. So copy all results into + ** memory that does not come from the SQLite memory allocator. + */ #if !SQLITE_SHELL_IS_UTF8 sqlite3_initialize(); - argv = sqlite3_malloc64(sizeof(argv[0])*argc); + argv = malloc(sizeof(argv[0])*argc); if( argv==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } for(i=0; i<argc; i++){ - argv[i] = sqlite3_win32_unicode_to_utf8(wargv[i]); + char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); + int n; + if( z==0 ){ + raw_printf(stderr, "out of memory\n"); + exit(1); + } + n = (int)strlen(z); + argv[i] = malloc( n+1 ); if( argv[i]==0 ){ raw_printf(stderr, "out of memory\n"); exit(1); } + memcpy(argv[i], z, n+1); + sqlite3_free(z); } + sqlite3_shutdown(); #endif + assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; @@ -8214,12 +8349,20 @@ utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]); exit(1); } -#ifdef SQLITE_HAVE_ZIP +#ifdef SQLITE_HAVE_ZLIB }else if( strcmp(z,"-zip")==0 ){ data.openMode = SHELL_OPEN_ZIPFILE; #endif }else if( strcmp(z,"-append")==0 ){ data.openMode = SHELL_OPEN_APPENDVFS; + }else if( strcmp(z,"-readonly")==0 ){ + data.openMode = SHELL_OPEN_READONLY; +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) + }else if( strncmp(z, "-A",2)==0 ){ + /* All remaining command-line arguments are passed to the ".archive" + ** command, so ignore them */ + break; +#endif } } if( data.zDbFilename==0 ){ @@ -8273,12 +8416,14 @@ }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); -#ifdef SQLITE_HAVE_ZIP +#ifdef SQLITE_HAVE_ZLIB }else if( strcmp(z,"-zip")==0 ){ data.openMode = SHELL_OPEN_ZIPFILE; #endif }else if( strcmp(z,"-append")==0 ){ data.openMode = SHELL_OPEN_APPENDVFS; + }else if( strcmp(z,"-readonly")==0 ){ + data.openMode = SHELL_OPEN_READONLY; }else if( strcmp(z,"-ascii")==0 ){ data.mode = MODE_Ascii; sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, @@ -8356,7 +8501,7 @@ if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); - rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg); + rc = shell_exec(&data, z, &zErrMsg); if( zErrMsg!=0 ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; @@ -8365,6 +8510,23 @@ if( bail_on_error ) return rc; } } +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) + }else if( strncmp(z, "-A", 2)==0 ){ + if( nCmd>0 ){ + utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands" + " with \"%s\"\n", z); + return 1; + } + open_db(&data, 0); + if( z[2] ){ + argv[i] = &z[2]; + arDotCommand(&data, argv+(i-1), argc-(i-1)); + }else{ + arDotCommand(&data, argv+i, argc-i); + } + readStdin = 0; + break; +#endif }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); raw_printf(stderr,"Use -help for a list of options.\n"); @@ -8384,7 +8546,7 @@ if( rc ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); - rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg); + rc = shell_exec(&data, azCmd[i], &zErrMsg); if( zErrMsg!=0 ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); return rc!=0 ? rc : 1; @@ -8447,8 +8609,8 @@ data.doXdgOpen = 0; clearTempFile(&data); #if !SQLITE_SHELL_IS_UTF8 - for(i=0; i<argc; i++) sqlite3_free(argv[i]); - sqlite3_free(argv); + for(i=0; i<argc; i++) free(argv[i]); + free(argv); #endif return rc; }
diff --git a/third_party/sqlite/src/src/sqlite.h.in b/third_party/sqlite/src/src/sqlite.h.in index 4f1b4a6..d35b7d5 100644 --- a/third_party/sqlite/src/src/sqlite.h.in +++ b/third_party/sqlite/src/src/sqlite.h.in
@@ -1064,6 +1064,12 @@ ** so that all subsequent write operations are independent. ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. +** +** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] +** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain +** a file lock using the xLock or xShmLock methods of the VFS to wait +** for up to M milliseconds before failing, where M is the single +** unsigned integer parameter. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -1098,6 +1104,7 @@ #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 +#define SQLITE_FCNTL_LOCK_TIMEOUT 34 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -2054,11 +2061,13 @@ ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to ** override this behaviour. The first parameter passed to this operation -** is an integer - non-zero to disable checkpoints-on-close, or zero (the -** default) to enable them. The second parameter is a pointer to an integer +** is an integer - positive to disable checkpoints-on-close, or zero (the +** default) to enable them, and negative to leave the setting unchanged. +** The second parameter is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. ** </dd> +** ** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt> ** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, @@ -2068,13 +2077,20 @@ ** slower. But the QPSG has the advantage of more predictable behavior. With ** the QPSG active, SQLite will always use the same query plan in the field as ** was used during testing in the lab. +** The first argument to this setting is an integer which is 0 to disable +** the QPSG, positive to enable QPSG, or negative to leave the setting +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether the QPSG is disabled or enabled +** following this call. ** </dd> +** ** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt> ** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not ** include output for any operations performed by trigger programs. This ** option is used to set or clear (the default) a flag that governs this ** behavior. The first parameter passed to this operation is an integer - -** non-zero to enable output for trigger programs, or zero to disable it. +** positive to enable output for trigger programs, or zero to disable it, +** or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which is written ** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if ** it is not disabled, 1 if it is. @@ -2496,16 +2512,16 @@ ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. -** These routines understand most of the common K&R formatting options, -** plus some additional non-standard formats, detailed below. -** Note that some of the more obscure formatting options from recent -** C-library standards are omitted from this implementation. +** These routines understand most of the common formatting options from +** the standard library printf() +** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]). +** See the [built-in printf()] documentation for details. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their -** results into memory obtained from [sqlite3_malloc()]. +** results into memory obtained from [sqlite3_malloc64()]. ** The strings returned by these two routines should be ** released by [sqlite3_free()]. ^Both routines return a -** NULL pointer if [sqlite3_malloc()] is unable to allocate enough +** NULL pointer if [sqlite3_malloc64()] is unable to allocate enough ** memory to hold the resulting string. ** ** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from @@ -2529,71 +2545,7 @@ ** ** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). ** -** These routines all implement some additional formatting -** options that are useful for constructing SQL statements. -** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", "%w" and "%z" options. -** -** ^(The %q option works like %s in that it substitutes a nul-terminated -** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal.)^ By doubling each '\'' -** character it escapes that character and allows it to be inserted into -** the string. -** -** For example, assume the string variable zText contains text as follows: -** -** <blockquote><pre> -** char *zText = "It's a happy day!"; -** </pre></blockquote> -** -** One can use this text in an SQL statement as follows: -** -** <blockquote><pre> -** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText); -** sqlite3_exec(db, zSQL, 0, 0, 0); -** sqlite3_free(zSQL); -** </pre></blockquote> -** -** Because the %q format string is used, the '\'' character in zText -** is escaped and the SQL generated is as follows: -** -** <blockquote><pre> -** INSERT INTO table1 VALUES('It''s a happy day!') -** </pre></blockquote> -** -** This is correct. Had we used %s instead of %q, the generated SQL -** would have looked like this: -** -** <blockquote><pre> -** INSERT INTO table1 VALUES('It's a happy day!'); -** </pre></blockquote> -** -** This second example is an SQL syntax error. As a general rule you should -** always use %q instead of %s when inserting text into a string literal. -** -** ^(The %Q option works like %q except it also adds single quotes around -** the outside of the total string. Additionally, if the parameter in the -** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes).)^ So, for example, one could say: -** -** <blockquote><pre> -** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText); -** sqlite3_exec(db, zSQL, 0, 0, 0); -** sqlite3_free(zSQL); -** </pre></blockquote> -** -** The code above will render a correct SQL statement in the zSQL -** variable even if the zText variable is a NULL pointer. -** -** ^(The "%w" formatting option is like "%q" except that it expects to -** be contained within double-quotes instead of single quotes, and it -** escapes the double-quote character instead of the single-quote -** character.)^ The "%w" formatting option is intended for safely inserting -** table and column names into a constructed SQL statement. -** -** ^(The "%z" formatting option works like "%s" but with the -** addition that after the string has been read and copied into -** the result, [sqlite3_free()] is called on the input string.)^ +** See also: [built-in printf()], [printf() SQL function] */ char *sqlite3_mprintf(const char*,...); char *sqlite3_vmprintf(const char*, va_list); @@ -3659,13 +3611,13 @@ ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** </li> +** </ol> ** ** <p>^sqlite3_prepare_v3() differs from sqlite3_prepare_v2() only in having ** the extra prepFlags parameter, which is a bit array consisting of zero or ** more of the [SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_*] flags. ^The ** sqlite3_prepare_v2() interface works exactly the same as ** sqlite3_prepare_v3() with a zero prepFlags parameter. -** </ol> */ int sqlite3_prepare( sqlite3 *db, /* Database handle */ @@ -7294,6 +7246,15 @@ ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** </dd> ** +** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> +** <dd>This parameter returns the number of dirty cache entries that have +** been written to disk in the middle of a transaction due to the page +** cache overflowing. Transactions are more efficient if they are written +** to disk all at once. When pages spill mid-transaction, that introduces +** additional overhead. This parameter can be used help identify +** inefficiencies that can be resolve by increasing the cache size. +** </dd> +** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been @@ -7313,7 +7274,8 @@ #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 -#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_CACHE_SPILL 12 +#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */ /* @@ -8817,6 +8779,128 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* +** CAPI3REF: Serialize a database +** +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory +** that is a serialization of the S database on [database connection] D. +** If P is not a NULL pointer, then the size of the database in bytes +** is written into *P. +** +** For an ordinary on-disk database file, the serialization is just a +** copy of the disk file. For an in-memory database or a "TEMP" database, +** the serialization is the same sequence of bytes which would be written +** to disk if that database where backed up to disk. +** +** The usual case is that sqlite3_serialize() copies the serialization of +** the database into memory obtained from [sqlite3_malloc64()] and returns +** a pointer to that memory. The caller is responsible for freeing the +** returned value to avoid a memory leak. However, if the F argument +** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations +** are made, and the sqlite3_serialize() function will return a pointer +** to the contiguous memory representation of the database that SQLite +** is currently using for that database, or NULL if the no such contiguous +** memory representation of the database exists. A contiguous memory +** representation of the database will usually only exist if there has +** been a prior call to [sqlite3_deserialize(D,S,...)] with the same +** values of D and S. +** The size of the database is written into *P even if the +** SQLITE_SERIALIZE_NOCOPY bit is set but no contigious copy +** of the database exists. +** +** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the +** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory +** allocation error occurs. +** +** This interface is only available if SQLite is compiled with the +** [SQLITE_ENABLE_DESERIALIZE] option. +*/ +unsigned char *sqlite3_serialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to serialize. ex: "main", "temp", ... */ + sqlite3_int64 *piSize, /* Write size of the DB here, if not NULL */ + unsigned int mFlags /* Zero or more SQLITE_SERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_serialize +** +** Zero or more of the following constants can be OR-ed together for +** the F argument to [sqlite3_serialize(D,S,P,F)]. +** +** SQLITE_SERIALIZE_NOCOPY means that [sqlite3_serialize()] will return +** a pointer to contiguous in-memory database that it is currently using, +** without making a copy of the database. If SQLite is not currently using +** a contiguous in-memory database, then this option causes +** [sqlite3_serialize()] to return a NULL pointer. SQLite will only be +** using a contiguous in-memory database if it has been initialized by a +** prior call to [sqlite3_deserialize()]. +*/ +#define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ + +/* +** CAPI3REF: Deserialize a database +** +** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the +** [database connection] D to disconnect from database S and then +** reopen S as an in-memory database based on the serialization contained +** in P. The serialized database P is N bytes in size. M is the size of +** the buffer P, which might be larger than N. If M is larger than N, and +** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is +** permitted to add content to the in-memory database as long as the total +** size does not exceed M bytes. +** +** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will +** invoke sqlite3_free() on the serialization buffer when the database +** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then +** SQLite will try to increase the buffer size using sqlite3_realloc64() +** if writes on the database cause it to grow larger than M bytes. +** +** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the +** database is currently in a read transaction or is involved in a backup +** operation. +** +** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the +** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then +** [sqlite3_free()] is invoked on argument P prior to returning. +** +** This interface is only available if SQLite is compiled with the +** [SQLITE_ENABLE_DESERIALIZE] option. +*/ +int sqlite3_deserialize( + sqlite3 *db, /* The database connection */ + const char *zSchema, /* Which DB to reopen with the deserialization */ + unsigned char *pData, /* The serialized database content */ + sqlite3_int64 szDb, /* Number bytes in the deserialization */ + sqlite3_int64 szBuf, /* Total size of buffer pData[] */ + unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ +); + +/* +** CAPI3REF: Flags for sqlite3_deserialize() +** +** The following are allowed values for 6th argument (the F argument) to +** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. +** +** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization +** in the P argument is held in memory obtained from [sqlite3_malloc64()] +** and that SQLite should take ownership of this memory and automatically +** free it when it has finished using it. Without this flag, the caller +** is resposible for freeing any dynamically allocated memory. +** +** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to +** grow the size of the database using calls to [sqlite3_realloc64()]. This +** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used. +** Without this flag, the deserialized database cannot increase in size beyond +** the number of bytes specified by the M parameter. +** +** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database +** should be treated as read-only. +*/ +#define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ +#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ +#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */
diff --git a/third_party/sqlite/src/src/sqlite3ext.h b/third_party/sqlite/src/src/sqlite3ext.h index a515874..2c7f1b5 100644 --- a/third_party/sqlite/src/src/sqlite3ext.h +++ b/third_party/sqlite/src/src/sqlite3ext.h
@@ -563,8 +563,8 @@ #define sqlite3_value_pointer sqlite3_api->value_pointer /* Version 3.22.0 and later */ #define sqlite3_vtab_nochange sqlite3_api->vtab_nochange -#define sqlite3_value_nochange sqltie3_api->value_nochange -#define sqlite3_vtab_collation sqltie3_api->vtab_collation +#define sqlite3_value_nochange sqlite3_api->value_nochange +#define sqlite3_vtab_collation sqlite3_api->vtab_collation #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/third_party/sqlite/src/src/sqliteInt.h b/third_party/sqlite/src/src/sqliteInt.h index f422794..a05fee4 100644 --- a/third_party/sqlite/src/src/sqliteInt.h +++ b/third_party/sqlite/src/src/sqliteInt.h
@@ -961,9 +961,10 @@ */ typedef struct BusyHandler BusyHandler; struct BusyHandler { - int (*xFunc)(void *,int); /* The busy callback */ - void *pArg; /* First arg to busy callback */ - int nBusy; /* Incremented with each busy call */ + int (*xBusyHandler)(void *,int); /* The busy callback */ + void *pBusyArg; /* First arg to busy callback */ + int nBusy; /* Incremented with each busy call */ + u8 bExtraFileArg; /* Include sqlite3_file as callback arg */ }; /* @@ -1365,8 +1366,9 @@ int newTnum; /* Rootpage of table being initialized */ u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ - u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ - u8 imposterTable; /* Building an imposter table */ + unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ + unsigned imposterTable : 1; /* Building an imposter table */ + unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ @@ -1531,6 +1533,8 @@ #define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */ #define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */ /* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */ +#define SQLITE_PushDown 0x1000 /* The push-down optimization */ +#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */ #define SQLITE_AllOpts 0xffff /* All optimizations */ /* @@ -1754,6 +1758,7 @@ #define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ #define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ #define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */ +#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */ /* ** A "Collating Sequence" is defined by an instance of the following @@ -2991,7 +2996,6 @@ int nMaxArg; /* Max args passed to user function by sub-program */ #if SELECTTRACE_ENABLED int nSelect; /* Number of SELECT statements seen */ - int nSelectIndent; /* How far to indent SELECTTRACE() output */ #endif #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ @@ -3355,9 +3359,9 @@ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ int *aiCol; /* array of column indexes */ struct IdxCover *pIdxCover; /* Check for index coverage */ - struct IdxExprTrans *pIdxTrans; /* Convert indexed expr to column */ + struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */ ExprList *pGroupBy; /* GROUP BY clause */ - struct HavingToWhereCtx *pHavingCtx; /* HAVING to WHERE clause ctx */ + Select *pSelect; /* HAVING to WHERE clause ctx */ } u; }; @@ -3821,6 +3825,7 @@ int sqlite3ExprCompareSkip(Expr*, Expr*, int); int sqlite3ExprListCompare(ExprList*, ExprList*, int); int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); +int sqlite3ExprImpliesNonNullRow(Expr*,int); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); @@ -3838,6 +3843,8 @@ void sqlite3Savepoint(Parse*, int, Token*); void sqlite3CloseSavepoints(sqlite3 *); void sqlite3LeaveMutexAndCloseZombie(sqlite3*); +int sqlite3ExprIdToTrueFalse(Expr*); +int sqlite3ExprTruthValue(const Expr*); int sqlite3ExprIsConstant(Expr*); int sqlite3ExprIsConstantNotJoin(Expr*); int sqlite3ExprIsConstantOrFunction(Expr*, u8); @@ -4020,6 +4027,10 @@ const char *sqlite3ErrName(int); #endif +#ifdef SQLITE_ENABLE_DESERIALIZE +int sqlite3MemdbInit(void); +#endif + const char *sqlite3ErrStr(int); int sqlite3ReadSchema(Parse *pParse); CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); @@ -4068,6 +4079,9 @@ extern int sqlite3PendingByte; #endif #endif +#ifdef VDBE_PROFILE +extern sqlite3_uint64 sqlite3NProfileCnt; +#endif void sqlite3RootPageMoved(sqlite3*, int, int, int); void sqlite3Reindex(Parse*, Token*, Token*); void sqlite3AlterFunctions(void); @@ -4090,7 +4104,7 @@ CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); char sqlite3AffinityType(const char*, u8*); void sqlite3Analyze(Parse*, Token*, Token*); -int sqlite3InvokeBusyHandler(BusyHandler*); +int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*); int sqlite3FindDb(sqlite3*, Token*); int sqlite3FindDbName(sqlite3 *, const char *); int sqlite3AnalysisLoad(sqlite3*,int iDB);
diff --git a/third_party/sqlite/src/src/status.c b/third_party/sqlite/src/src/status.c index f859dd4d..e2b3a74 100644 --- a/third_party/sqlite/src/src/status.c +++ b/third_party/sqlite/src/src/status.c
@@ -337,6 +337,9 @@ ** pagers the database handle is connected to. *pHighwater is always set ** to zero. */ + case SQLITE_DBSTATUS_CACHE_SPILL: + op = SQLITE_DBSTATUS_CACHE_WRITE+1; + /* Fall through into the next case */ case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{
diff --git a/third_party/sqlite/src/src/tclsqlite.c b/third_party/sqlite/src/src/tclsqlite.c index 99b1808..dcab142 100644 --- a/third_party/sqlite/src/src/tclsqlite.c +++ b/third_party/sqlite/src/src/tclsqlite.c
@@ -64,7 +64,9 @@ # define GETPID getpid #elif !defined(_WIN32_WCE) # ifndef SQLITE_AMALGAMATION -# define WIN32_LEAN_AND_MEAN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif # include <windows.h> # endif # define GETPID (int)GetCurrentProcessId @@ -646,7 +648,7 @@ } case SQLITE_TRACE_PROFILE: { sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; - sqlite3_int64 ns = (sqlite3_int64)xd; + sqlite3_int64 ns = *(sqlite3_int64*)xd; pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); Tcl_IncrRefCount(pCmd); @@ -1846,35 +1848,35 @@ int choice; int rc = TCL_OK; static const char *DB_strs[] = { - "authorizer", "backup", "busy", - "cache", "changes", "close", - "collate", "collation_needed", "commit_hook", - "complete", "copy", "enable_load_extension", - "errorcode", "eval", "exists", - "function", "incrblob", "interrupt", - "last_insert_rowid", "nullvalue", "onecolumn", - "preupdate", "profile", "progress", - "rekey", "restore", "rollback_hook", - "status", "timeout", "total_changes", - "trace", "trace_v2", "transaction", - "unlock_notify", "update_hook", "version", - "wal_hook", + "authorizer", "backup", "busy", + "cache", "changes", "close", + "collate", "collation_needed", "commit_hook", + "complete", "copy", "deserialize", + "enable_load_extension", "errorcode", "eval", + "exists", "function", "incrblob", + "interrupt", "last_insert_rowid", "nullvalue", + "onecolumn", "preupdate", "profile", + "progress", "rekey", "restore", + "rollback_hook", "serialize", "status", + "timeout", "total_changes", "trace", + "trace_v2", "transaction", "unlock_notify", + "update_hook", "version", "wal_hook", 0 }; enum DB_enum { - DB_AUTHORIZER, DB_BACKUP, DB_BUSY, - DB_CACHE, DB_CHANGES, DB_CLOSE, - DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, - DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION, - DB_ERRORCODE, DB_EVAL, DB_EXISTS, - DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT, - DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN, - DB_PREUPDATE, DB_PROFILE, DB_PROGRESS, - DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK, - DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, - DB_TRACE, DB_TRACE_V2, DB_TRANSACTION, - DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION, - DB_WAL_HOOK, + DB_AUTHORIZER, DB_BACKUP, DB_BUSY, + DB_CACHE, DB_CHANGES, DB_CLOSE, + DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, + DB_COMPLETE, DB_COPY, DB_DESERIALIZE, + DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL, + DB_EXISTS, DB_FUNCTION, DB_INCRBLOB, + DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE, + DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE, + DB_PROGRESS, DB_REKEY, DB_RESTORE, + DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS, + DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, + DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY, + DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -2413,6 +2415,53 @@ } /* + ** $db deserialize ?DATABASE? VALUE + ** + ** Reopen DATABASE (default "main") using the content in $VALUE + */ + case DB_DESERIALIZE: { +#ifndef SQLITE_ENABLE_DESERIALIZE + Tcl_AppendResult(interp, "MEMDB not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + const char *zSchema; + Tcl_Obj *pValue; + unsigned char *pBA; + unsigned char *pData; + int len, xrc; + + if( objc==3 ){ + zSchema = 0; + pValue = objv[2]; + }else if( objc==4 ){ + zSchema = Tcl_GetString(objv[2]); + pValue = objv[3]; + }else{ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? VALUE"); + rc = TCL_ERROR; + break; + } + pBA = Tcl_GetByteArrayFromObj(pValue, &len); + pData = sqlite3_malloc64( len ); + if( pData==0 && len>0 ){ + Tcl_AppendResult(interp, "out of memory", (char*)0); + rc = TCL_ERROR; + }else{ + if( len>0 ) memcpy(pData, pBA, len); + xrc = sqlite3_deserialize(pDb->db, zSchema, pData, len, len, + SQLITE_DESERIALIZE_FREEONCLOSE | + SQLITE_DESERIALIZE_RESIZEABLE); + if( xrc ){ + Tcl_AppendResult(interp, "unable to set MEMDB content", (char*)0); + rc = TCL_ERROR; + } + } +#endif + break; + } + + /* ** $db enable_load_extension BOOLEAN ** ** Turn the extension loading feature on or off. It if off by @@ -2888,6 +2937,39 @@ } /* + ** $db serialize ?DATABASE? + ** + ** Return a serialization of a database. + */ + case DB_SERIALIZE: { +#ifndef SQLITE_ENABLE_DESERIALIZE + Tcl_AppendResult(interp, "MEMDB not available in this build", + (char*)0); + rc = TCL_ERROR; +#else + const char *zSchema = objc>=3 ? Tcl_GetString(objv[2]) : "main"; + sqlite3_int64 sz = 0; + unsigned char *pData; + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE?"); + rc = TCL_ERROR; + }else{ + int needFree; + pData = sqlite3_serialize(pDb->db, zSchema, &sz, SQLITE_SERIALIZE_NOCOPY); + if( pData ){ + needFree = 0; + }else{ + pData = sqlite3_serialize(pDb->db, zSchema, &sz, 0); + needFree = 1; + } + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pData,sz)); + if( needFree ) sqlite3_free(pData); + } +#endif + break; + } + + /* ** $db status (step|sort|autoindex|vmstep) ** ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or @@ -3348,6 +3430,24 @@ #endif /* SQLITE_TCL_NRE */ /* +** Issue the usage message when the "sqlite3" command arguments are +** incorrect. +*/ +static int sqliteCmdUsage( + Tcl_Interp *interp, + Tcl_Obj *const*objv +){ + Tcl_WrongNumArgs(interp, 1, objv, + "HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" + " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) + " ?-key CODECKEY?" +#endif + ); + return TCL_ERROR; +} + +/* ** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN? ** ?-create BOOLEAN? ?-nomutex BOOLEAN? ** @@ -3372,7 +3472,7 @@ const char *zArg; char *zErrMsg; int i; - const char *zFile; + const char *zFile = 0; const char *zVfs = 0; int flags; Tcl_DString translatedFilename; @@ -3383,7 +3483,7 @@ int rc; /* In normal use, each TCL interpreter runs in a single thread. So - ** by default, we can turn of mutexing on SQLite database connections. + ** by default, we can turn off mutexing on SQLite database connections. ** However, for testing purposes it is useful to have mutexes turned ** on. So, by default, mutexes default off. But if compiled with ** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on. @@ -3412,18 +3512,26 @@ #endif return TCL_OK; } + if( zArg[0]=='-' ) return sqliteCmdUsage(interp, objv); } - for(i=3; i+1<objc; i+=2){ + for(i=2; i<objc; i++){ zArg = Tcl_GetString(objv[i]); + if( zArg[0]!='-' ){ + if( zFile!=0 ) return sqliteCmdUsage(interp, objv); + zFile = zArg; + continue; + } + if( i==objc-1 ) return sqliteCmdUsage(interp, objv); + i++; if( strcmp(zArg,"-key")==0 ){ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) - pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey); + pKey = Tcl_GetByteArrayFromObj(objv[i], &nKey); #endif }else if( strcmp(zArg, "-vfs")==0 ){ - zVfs = Tcl_GetString(objv[i+1]); + zVfs = Tcl_GetString(objv[i]); }else if( strcmp(zArg, "-readonly")==0 ){ int b; - if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR; + if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR; if( b ){ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); flags |= SQLITE_OPEN_READONLY; @@ -3433,7 +3541,7 @@ } }else if( strcmp(zArg, "-create")==0 ){ int b; - if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR; + if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR; if( b && (flags & SQLITE_OPEN_READONLY)==0 ){ flags |= SQLITE_OPEN_CREATE; }else{ @@ -3441,7 +3549,7 @@ } }else if( strcmp(zArg, "-nomutex")==0 ){ int b; - if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR; + if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR; if( b ){ flags |= SQLITE_OPEN_NOMUTEX; flags &= ~SQLITE_OPEN_FULLMUTEX; @@ -3450,7 +3558,7 @@ } }else if( strcmp(zArg, "-fullmutex")==0 ){ int b; - if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR; + if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR; if( b ){ flags |= SQLITE_OPEN_FULLMUTEX; flags &= ~SQLITE_OPEN_NOMUTEX; @@ -3459,7 +3567,7 @@ } }else if( strcmp(zArg, "-uri")==0 ){ int b; - if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR; + if( Tcl_GetBooleanFromObj(interp, objv[i], &b) ) return TCL_ERROR; if( b ){ flags |= SQLITE_OPEN_URI; }else{ @@ -3470,20 +3578,10 @@ return TCL_ERROR; } } - if( objc<3 || (objc&1)!=1 ){ - Tcl_WrongNumArgs(interp, 1, objv, - "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" - " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" -#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) - " ?-key CODECKEY?" -#endif - ); - return TCL_ERROR; - } zErrMsg = 0; p = (SqliteDb*)Tcl_Alloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); - zFile = Tcl_GetStringFromObj(objv[2], 0); + if( zFile==0 ) zFile = ""; zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); rc = sqlite3_open_v2(zFile, &p->db, flags, zVfs); Tcl_DStringFree(&translatedFilename);
diff --git a/third_party/sqlite/src/src/test1.c b/third_party/sqlite/src/src/test1.c index 4e846e4d..9aa064e 100644 --- a/third_party/sqlite/src/src/test1.c +++ b/third_party/sqlite/src/src/test1.c
@@ -4560,6 +4560,35 @@ } /* +** Usage: sqlite3_normalize SQL +** +** Return the normalized value for an SQL statement. +*/ +static int SQLITE_TCLAPI test_normalize( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + char *zSql; + char *zNorm; + extern char *sqlite3_normalize(const char*); + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SQL"); + return TCL_ERROR; + } + + zSql = (char*)Tcl_GetString(objv[1]); + zNorm = sqlite3_normalize(zSql); + if( zNorm ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(zNorm, -1)); + sqlite3_free(zNorm); + } + return TCL_OK; +} + +/* ** Usage: sqlite3_step STMT ** ** Advance the statement to the next row. @@ -7547,6 +7576,7 @@ { "sqlite3_open16", test_open16 ,0 }, { "sqlite3_open_v2", test_open_v2 ,0 }, { "sqlite3_complete16", test_complete16 ,0 }, + { "sqlite3_normalize", test_normalize ,0 }, { "sqlite3_prepare", test_prepare ,0 }, { "sqlite3_prepare16", test_prepare16 ,0 },
diff --git a/third_party/sqlite/src/src/test_config.c b/third_party/sqlite/src/src/test_config.c index f1a832d8..0c03d1a 100644 --- a/third_party/sqlite/src/src/test_config.c +++ b/third_party/sqlite/src/src/test_config.c
@@ -148,6 +148,12 @@ Tcl_SetVar2(interp, "sqlite_options", "hiddencolumns", "0", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_ENABLE_DESERIALIZE + Tcl_SetVar2(interp, "sqlite_options", "deserialize", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "deserialize", "0", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_ENABLE_MEMSYS3 Tcl_SetVar2(interp, "sqlite_options", "mem3", "1", TCL_GLOBAL_ONLY); #else @@ -501,6 +507,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY); +#ifdef SQLITE_ENABLE_NULL_TRIM + Tcl_SetVar2(interp, "sqlite_options", "null_trim", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "null_trim", "0", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_OR_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "or_opt", "0", TCL_GLOBAL_ONLY); #else
diff --git a/third_party/sqlite/src/src/test_malloc.c b/third_party/sqlite/src/src/test_malloc.c index 54a61df..b7965bee 100644 --- a/third_party/sqlite/src/src/test_malloc.c +++ b/third_party/sqlite/src/src/test_malloc.c
@@ -1383,6 +1383,7 @@ { "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE }, { "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS }, { "CACHE_USED_SHARED", SQLITE_DBSTATUS_CACHE_USED_SHARED }, + { "CACHE_SPILL", SQLITE_DBSTATUS_CACHE_SPILL }, }; Tcl_Obj *pResult; if( objc!=4 ){
diff --git a/third_party/sqlite/src/src/test_windirent.h b/third_party/sqlite/src/src/test_windirent.h index d71b49f..ada53225 100644 --- a/third_party/sqlite/src/src/test_windirent.h +++ b/third_party/sqlite/src/src/test_windirent.h
@@ -20,7 +20,10 @@ ** We need several data types from the Windows SDK header. */ +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif + #include "windows.h" /*
diff --git a/third_party/sqlite/src/src/treeview.c b/third_party/sqlite/src/src/treeview.c index e8d5a01..11122f5 100644 --- a/third_party/sqlite/src/src/treeview.c +++ b/third_party/sqlite/src/src/treeview.c
@@ -137,11 +137,21 @@ sqlite3TreeViewPush(pView, 1); } do{ +#if SELECTTRACE_ENABLED + sqlite3TreeViewLine(pView, + "SELECT%s%s (%s/%p) selFlags=0x%x nSelectRow=%d", + ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), + p->zSelName, p, p->selFlags, + (int)p->nSelectRow + ); +#else sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d", ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags, (int)p->nSelectRow ); +#endif if( cnt++ ) sqlite3TreeViewPop(pView); if( p->pPrior ){ n = 1000; @@ -292,6 +302,11 @@ sqlite3TreeViewLine(pView,"NULL"); break; } + case TK_TRUEFALSE: { + sqlite3TreeViewLine(pView, + sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE"); + break; + } #ifndef SQLITE_OMIT_BLOB_LITERAL case TK_BLOB: { sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); @@ -348,6 +363,19 @@ case TK_ISNULL: zUniOp = "ISNULL"; break; case TK_NOTNULL: zUniOp = "NOTNULL"; break; + case TK_TRUTH: { + int x; + const char *azOp[] = { + "IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE" + }; + assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); + assert( pExpr->pRight ); + assert( pExpr->pRight->op==TK_TRUEFALSE ); + x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight); + zUniOp = azOp[x]; + break; + } + case TK_SPAN: { sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
diff --git a/third_party/sqlite/src/src/update.c b/third_party/sqlite/src/src/update.c index 7b8ae5cd..bffc66a 100644 --- a/third_party/sqlite/src/src/update.c +++ b/third_party/sqlite/src/src/update.c
@@ -396,7 +396,7 @@ regKey = ++pParse->nMem; iEph = pParse->nTab++; - sqlite3VdbeAddOp2(v, OP_Null, 0, iPk); + sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1); addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); sqlite3VdbeSetP4KeyInfo(pParse, pPk); }
diff --git a/third_party/sqlite/src/src/util.c b/third_party/sqlite/src/src/util.c index 25a5d7d..4f700d4 100644 --- a/third_party/sqlite/src/src/util.c +++ b/third_party/sqlite/src/src/util.c
@@ -595,7 +595,7 @@ ** Returns: ** ** 0 Successful transformation. Fits in a 64-bit signed integer. -** 1 Excess text after the integer value +** 1 Excess non-space text after the integer value ** 2 Integer too large for a 64-bit signed integer or is malformed ** 3 Special case of 9223372036854775808 ** @@ -638,47 +638,57 @@ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ u = u*10 + c - '0'; } + testcase( i==18*incr ); + testcase( i==19*incr ); + testcase( i==20*incr ); if( u>LARGEST_INT64 ){ + /* This test and assignment is needed only to suppress UB warnings + ** from clang and -fsanitize=undefined. This test and assignment make + ** the code a little larger and slower, and no harm comes from omitting + ** them, but we must appaise the undefined-behavior pharisees. */ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; }else if( neg ){ *pNum = -(i64)u; }else{ *pNum = (i64)u; } - testcase( i==18 ); - testcase( i==19 ); - testcase( i==20 ); - if( &zNum[i]<zEnd /* Extra bytes at the end */ - || (i==0 && zStart==zNum) /* No digits */ + rc = 0; + if( (i==0 && zStart==zNum) /* No digits */ || nonNum /* UTF16 with high-order bytes non-zero */ ){ rc = 1; - }else{ - rc = 0; + }else if( &zNum[i]<zEnd ){ /* Extra bytes at the end */ + int jj = i; + do{ + if( !sqlite3Isspace(zNum[jj]) ){ + rc = 1; /* Extra non-space text after the integer */ + break; + } + jj += incr; + }while( &zNum[jj]<zEnd ); } - if( i>19*incr ){ /* Too many digits */ - /* zNum is empty or contains non-numeric text or is longer - ** than 19 digits (thus guaranteeing that it is too large) */ - return 2; - }else if( i<19*incr ){ + if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ assert( u<=LARGEST_INT64 ); return rc; }else{ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ - c = compare2pow63(zNum, incr); + c = i>19*incr ? 1 : compare2pow63(zNum, incr); if( c<0 ){ /* zNum is less than 9223372036854775808 so it fits */ assert( u<=LARGEST_INT64 ); return rc; - }else if( c>0 ){ - /* zNum is greater than 9223372036854775808 so it overflows */ - return 2; }else{ - /* zNum is exactly 9223372036854775808. Fits if negative. The - ** special case 2 overflow if positive */ - assert( u-1==LARGEST_INT64 ); - return neg ? rc : 3; + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; + if( c>0 ){ + /* zNum is greater than 9223372036854775808 so it overflows */ + return 2; + }else{ + /* zNum is exactly 9223372036854775808. Fits if negative. The + ** special case 2 overflow if positive */ + assert( u-1==LARGEST_INT64 ); + return neg ? rc : 3; + } } } }
diff --git a/third_party/sqlite/src/src/vacuum.c b/third_party/sqlite/src/src/vacuum.c index 42e247f..496164d 100644 --- a/third_party/sqlite/src/src/vacuum.c +++ b/third_party/sqlite/src/src/vacuum.c
@@ -39,8 +39,8 @@ while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0); assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 ); - if( zSubSql ){ - assert( zSubSql[0]!='S' ); + assert( sqlite3_strnicmp(zSubSql,"SELECT",6)!=0 || CORRUPT_DB ); + if( zSubSql && zSubSql[0]!='S' ){ rc = execSql(db, pzErrMsg, zSubSql); if( rc!=SQLITE_OK ) break; }
diff --git a/third_party/sqlite/src/src/vdbe.c b/third_party/sqlite/src/src/vdbe.c index 5427c48..bbf495a 100644 --- a/third_party/sqlite/src/src/vdbe.c +++ b/third_party/sqlite/src/src/vdbe.c
@@ -264,6 +264,11 @@ pRec->flags |= MEM_Real; if( bTryForInt ) sqlite3VdbeIntegerAffinity(pRec); } + /* TEXT->NUMERIC is many->one. Hence, it is important to invalidate the + ** string representation after computing a numeric equivalent, because the + ** string representation might not be the canonical representation for the + ** numeric value. Ticket [343634942dd54ab57b7024] 2018-01-31. */ + pRec->flags &= ~MEM_Str; } /* @@ -643,7 +648,7 @@ assert( pOp>=aOp && pOp<&aOp[p->nOp]); #ifdef VDBE_PROFILE - start = sqlite3Hwtime(); + start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); #endif nVmStep++; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS @@ -2167,18 +2172,8 @@ int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ){ - v1 = 2; - }else{ - v1 = sqlite3VdbeIntValue(pIn1)!=0; - } - pIn2 = &aMem[pOp->p2]; - if( pIn2->flags & MEM_Null ){ - v2 = 2; - }else{ - v2 = sqlite3VdbeIntValue(pIn2)!=0; - } + v1 = sqlite3VdbeBooleanValue(&aMem[pOp->p1], 2); + v2 = sqlite3VdbeBooleanValue(&aMem[pOp->p2], 2); if( pOp->opcode==OP_And ){ static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; v1 = and_logic[v1*3+v2]; @@ -2196,6 +2191,35 @@ break; } +/* Opcode: IsTrue P1 P2 P3 P4 * +** Synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 +** +** This opcode implements the IS TRUE, IS FALSE, IS NOT TRUE, and +** IS NOT FALSE operators. +** +** Interpret the value in register P1 as a boolean value. Store that +** boolean (a 0 or 1) in register P2. Or if the value in register P1 is +** NULL, then the P3 is stored in register P2. Invert the answer if P4 +** is 1. +** +** The logic is summarized like this: +** +** <ul> +** <li> If P3==0 and P4==0 then r[P2] := r[P1] IS TRUE +** <li> If P3==1 and P4==1 then r[P2] := r[P1] IS FALSE +** <li> If P3==0 and P4==1 then r[P2] := r[P1] IS NOT TRUE +** <li> If P3==1 and P4==0 then r[P2] := r[P1] IS NOT FALSE +** </ul> +*/ +case OP_IsTrue: { /* in1, out2 */ + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p4.i==0 || pOp->p4.i==1 ); + assert( pOp->p3==0 || pOp->p3==1 ); + sqlite3VdbeMemSetInt64(&aMem[pOp->p2], + sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3) ^ pOp->p4.i); + break; +} + /* Opcode: Not P1 P2 * * * ** Synopsis: r[P2]= !r[P1] ** @@ -2206,10 +2230,10 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; - sqlite3VdbeMemSetNull(pOut); if( (pIn1->flags & MEM_Null)==0 ){ - pOut->flags = MEM_Int; - pOut->u.i = !sqlite3VdbeIntValue(pIn1); + sqlite3VdbeMemSetInt64(pOut, !sqlite3VdbeBooleanValue(pIn1,0)); + }else{ + sqlite3VdbeMemSetNull(pOut); } break; } @@ -2276,30 +2300,25 @@ ** is considered true if it is numeric and non-zero. If the value ** in P1 is NULL then take the jump if and only if P3 is non-zero. */ +case OP_If: { /* jump, in1 */ + int c; + c = sqlite3VdbeBooleanValue(&aMem[pOp->p1], pOp->p3); + VdbeBranchTaken(c!=0, 2); + if( c ) goto jump_to_p2; + break; +} + /* Opcode: IfNot P1 P2 P3 * * ** ** Jump to P2 if the value in register P1 is False. The value ** is considered false if it has a numeric value of zero. If the value ** in P1 is NULL then take the jump if and only if P3 is non-zero. */ -case OP_If: /* jump, in1 */ case OP_IfNot: { /* jump, in1 */ int c; - pIn1 = &aMem[pOp->p1]; - if( pIn1->flags & MEM_Null ){ - c = pOp->p3; - }else{ -#ifdef SQLITE_OMIT_FLOATING_POINT - c = sqlite3VdbeIntValue(pIn1)!=0; -#else - c = sqlite3VdbeRealValue(pIn1)!=0.0; -#endif - if( pOp->opcode==OP_IfNot ) c = !c; - } + c = !sqlite3VdbeBooleanValue(&aMem[pOp->p1], !pOp->p3); VdbeBranchTaken(c!=0, 2); - if( c ){ - goto jump_to_p2; - } + if( c ) goto jump_to_p2; break; } @@ -2360,7 +2379,7 @@ ** P2 is the column number for the argument to the sqlite_offset() function. ** This opcode does not use P2 itself, but the P2 value is used by the ** code generator. The P1, P2, and P3 operands to this opcode are the -** as as for OP_Column. +** same as for OP_Column. ** ** This opcode is only available if SQLite is compiled with the ** -DSQLITE_ENABLE_OFFSET_SQL_FUNC option. @@ -4268,6 +4287,10 @@ pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; + if( !pC->isTable ){ + rc = SQLITE_CORRUPT_BKPT; + goto abort_due_to_error; + } assert( pC!=0 ); assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->uc.pCursor!=0 ); @@ -6204,12 +6227,17 @@ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); - pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*)); + pCtx = sqlite3DbMallocRawNN(db, n*sizeof(sqlite3_value*) + + (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlite3_value*))); if( pCtx==0 ) goto no_mem; pCtx->pMem = 0; + pCtx->pOut = (Mem*)&(pCtx->argv[n]); + sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); pCtx->pFunc = pOp->p4.pFunc; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; + pCtx->skipFlag = 0; + pCtx->isError = 0; pCtx->argc = n; pOp->p4type = P4_FUNCCTX; pOp->p4.pCtx = pCtx; @@ -6220,7 +6248,6 @@ int i; sqlite3_context *pCtx; Mem *pMem; - Mem t; assert( pOp->p4type==P4_FUNCCTX ); pCtx = pOp->p4.pCtx; @@ -6243,26 +6270,28 @@ #endif pMem->n++; - sqlite3VdbeMemInit(&t, db, MEM_Null); - pCtx->pOut = &t; - pCtx->fErrorOrAux = 0; - pCtx->skipFlag = 0; + assert( pCtx->pOut->flags==MEM_Null ); + assert( pCtx->isError==0 ); + assert( pCtx->skipFlag==0 ); (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */ - if( pCtx->fErrorOrAux ){ - if( pCtx->isError ){ - sqlite3VdbeError(p, "%s", sqlite3_value_text(&t)); + if( pCtx->isError ){ + if( pCtx->isError>0 ){ + sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut)); rc = pCtx->isError; } - sqlite3VdbeMemRelease(&t); + if( pCtx->skipFlag ){ + assert( pOp[-1].opcode==OP_CollSeq ); + i = pOp[-1].p1; + if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1); + pCtx->skipFlag = 0; + } + sqlite3VdbeMemRelease(pCtx->pOut); + pCtx->pOut->flags = MEM_Null; + pCtx->isError = 0; if( rc ) goto abort_due_to_error; - }else{ - assert( t.flags==MEM_Null ); } - if( pCtx->skipFlag ){ - assert( pOp[-1].opcode==OP_CollSeq ); - i = pOp[-1].p1; - if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1); - } + assert( pCtx->pOut->flags==MEM_Null ); + assert( pCtx->skipFlag==0 ); break; } @@ -6749,7 +6778,8 @@ } rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); sqlite3VtabImportErrmsg(p, pVtab); - if( sContext.isError ){ + if( sContext.isError>0 ){ + sqlite3VdbeError(p, "%s", sqlite3_value_text(pDest)); rc = sContext.isError; } sqlite3VdbeChangeEncoding(pDest, encoding); @@ -7014,6 +7044,7 @@ pCtx->pFunc = pOp->p4.pFunc; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; + pCtx->isError = 0; pCtx->argc = n; pOp->p4type = P4_FUNCCTX; pOp->p4.pCtx = pCtx; @@ -7048,16 +7079,17 @@ } #endif MemSetTypeFlag(pOut, MEM_Null); - pCtx->fErrorOrAux = 0; + assert( pCtx->isError==0 ); (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */ /* If the function returned an error, throw an exception */ - if( pCtx->fErrorOrAux ){ - if( pCtx->isError ){ + if( pCtx->isError ){ + if( pCtx->isError>0 ){ sqlite3VdbeError(p, "%s", sqlite3_value_text(pOut)); rc = pCtx->isError; } sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1); + pCtx->isError = 0; if( rc ) goto abort_due_to_error; } @@ -7099,8 +7131,10 @@ */ case OP_Trace: case OP_Init: { /* jump */ - char *zTrace; int i; +#ifndef SQLITE_OMIT_TRACE + char *zTrace; +#endif /* If the P4 argument is not NULL, then it must be an SQL comment string. ** The "--" string is broken up to prevent false-positives with srcck1.c. @@ -7217,7 +7251,7 @@ #ifdef VDBE_PROFILE { - u64 endTime = sqlite3Hwtime(); + u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); if( endTime>start ) pOrigOp->cycles += endTime - start; pOrigOp->cnt++; }
diff --git a/third_party/sqlite/src/src/vdbeInt.h b/third_party/sqlite/src/src/vdbeInt.h index ec97679..6ce2e863 100644 --- a/third_party/sqlite/src/src/vdbeInt.h +++ b/third_party/sqlite/src/src/vdbeInt.h
@@ -224,8 +224,6 @@ ** If the MEM_Null flag is set, then the value is an SQL NULL value. ** For a pointer type created using sqlite3_bind_pointer() or ** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. -** If both MEM_Null and MEM_Zero are set, that means that the value is -** an unchanging column value from VColumn. ** ** If the MEM_Str flag is set then Mem.z points at a string representation. ** Usually this is encoded in the same unicode encoding as the main @@ -319,7 +317,6 @@ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ u8 skipFlag; /* Skip accumulator loading if true */ - u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */ u8 argc; /* Number of arguments */ sqlite3_value *argv[1]; /* Argument set */ }; @@ -489,6 +486,7 @@ i64 sqlite3VdbeIntValue(Mem*); int sqlite3VdbeMemIntegerify(Mem*); double sqlite3VdbeRealValue(Mem*); +int sqlite3VdbeBooleanValue(Mem*, int ifNull); void sqlite3VdbeIntegerAffinity(Mem*); int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*);
diff --git a/third_party/sqlite/src/src/vdbeapi.c b/third_party/sqlite/src/src/vdbeapi.c index 0adb6ef..80115542 100644 --- a/third_party/sqlite/src/src/vdbeapi.c +++ b/third_party/sqlite/src/src/vdbeapi.c
@@ -372,14 +372,12 @@ void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; - pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; - pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif @@ -485,8 +483,7 @@ return SQLITE_OK; } void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ - pCtx->isError = errCode; - pCtx->fErrorOrAux = 1; + pCtx->isError = errCode ? errCode : -1; #ifdef SQLITE_DEBUG if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; #endif @@ -500,7 +497,6 @@ void sqlite3_result_error_toobig(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_TOOBIG; - pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, SQLITE_UTF8, SQLITE_STATIC); } @@ -510,7 +506,6 @@ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM_BKPT; - pCtx->fErrorOrAux = 1; sqlite3OomFault(pCtx->pOut->db); } @@ -917,10 +912,7 @@ pAuxData->iAuxArg = iArg; pAuxData->pNextAux = pVdbe->pAuxData; pVdbe->pAuxData = pAuxData; - if( pCtx->fErrorOrAux==0 ){ - pCtx->isError = 0; - pCtx->fErrorOrAux = 1; - } + if( pCtx->isError==0 ) pCtx->isError = -1; }else if( pAuxData->xDeleteAux ){ pAuxData->xDeleteAux(pAuxData->pAux); } @@ -1676,7 +1668,9 @@ Vdbe *pVdbe = (Vdbe*)pStmt; u32 v; #ifdef SQLITE_ENABLE_API_ARMOR - if( !pStmt ){ + if( !pStmt + || (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter))) + ){ (void)SQLITE_MISUSE_BKPT; return 0; }
diff --git a/third_party/sqlite/src/src/vdbemem.c b/third_party/sqlite/src/src/vdbemem.c index 1dca6fe..5c6dda1 100644 --- a/third_party/sqlite/src/src/vdbemem.c +++ b/third_party/sqlite/src/src/vdbemem.c
@@ -93,6 +93,51 @@ } #endif +#ifdef SQLITE_DEBUG +/* +** Check that string value of pMem agrees with its integer or real value. +** +** A single int or real value always converts to the same strings. But +** many different strings can be converted into the same int or real. +** If a table contains a numeric value and an index is based on the +** corresponding string value, then it is important that the string be +** derived from the numeric value, not the other way around, to ensure +** that the index and table are consistent. See ticket +** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for +** an example. +** +** This routine looks at pMem to verify that if it has both a numeric +** representation and a string representation then the string rep has +** been derived from the numeric and not the other way around. It returns +** true if everything is ok and false if there is a problem. +** +** This routine is for use inside of assert() statements only. +*/ +int sqlite3VdbeMemConsistentDualRep(Mem *p){ + char zBuf[100]; + char *z; + int i, j, incr; + if( (p->flags & MEM_Str)==0 ) return 1; + if( (p->flags & (MEM_Int|MEM_Real))==0 ) return 1; + if( p->flags & MEM_Int ){ + sqlite3_snprintf(sizeof(zBuf),zBuf,"%lld",p->u.i); + }else{ + sqlite3_snprintf(sizeof(zBuf),zBuf,"%!.15g",p->u.r); + } + z = p->z; + i = j = 0; + incr = 1; + if( p->enc!=SQLITE_UTF8 ){ + incr = 2; + if( p->enc==SQLITE_UTF16BE ) z++; + } + while( zBuf[j] ){ + if( zBuf[j++]!=z[i] ) return 0; + i += incr; + } + return 1; +} +#endif /* SQLITE_DEBUG */ /* ** If pMem is an object with a valid string representation, this routine @@ -528,6 +573,16 @@ } /* +** Return 1 if pMem represents true, and return 0 if pMem represents false. +** Return the value ifNull if pMem is NULL. +*/ +int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){ + if( pMem->flags & MEM_Int ) return pMem->u.i!=0; + if( pMem->flags & MEM_Null ) return ifNull; + return sqlite3VdbeRealValue(pMem)!=0.0; +} + +/* ** The MEM structure is already a MEM_Real. Try to also make it a ** MEM_Int if we can. */ @@ -582,6 +637,18 @@ return SQLITE_OK; } +/* Compare a floating point value to an integer. Return true if the two +** values are the same within the precision of the floating point value. +** +** For some versions of GCC on 32-bit machines, if you do the more obvious +** comparison of "r1==(double)i" you sometimes get an answer of false even +** though the r1 and (double)i values are bit-for-bit the same. +*/ +static int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){ + double r2 = (double)i; + return memcmp(&r1, &r2, sizeof(r1))==0; +} + /* ** Convert pMem so that it has types MEM_Real or MEM_Int or both. ** Invalidate any prior representations. @@ -601,7 +668,7 @@ }else{ i64 i = pMem->u.i; sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( rc==1 && pMem->u.r==(double)i ){ + if( rc==1 && sqlite3RealSameAsInt(pMem->u.r, i) ){ pMem->u.i = i; MemSetTypeFlag(pMem, MEM_Int); }else{ @@ -1084,6 +1151,7 @@ assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 || pVal->db->mallocFailed ); if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){ + assert( sqlite3VdbeMemConsistentDualRep(pVal) ); return pVal->z; }else{ return 0; @@ -1106,6 +1174,7 @@ assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); assert( (pVal->flags & MEM_RowSet)==0 ); if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){ + assert( sqlite3VdbeMemConsistentDualRep(pVal) ); return pVal->z; } if( pVal->flags&MEM_Null ){ @@ -1415,6 +1484,11 @@ rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); } #endif + else if( op==TK_TRUEFALSE ){ + pVal = valueNew(db, pCtx); + pVal->flags = MEM_Int; + pVal->u.i = pExpr->u.zToken[4]==0; + } *ppVal = pVal; return rc;
diff --git a/third_party/sqlite/src/src/wal.c b/third_party/sqlite/src/src/wal.c index a516aa77..b97dab1 100644 --- a/third_party/sqlite/src/src/wal.c +++ b/third_party/sqlite/src/src/wal.c
@@ -554,7 +554,11 @@ ** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, ** then an SQLite error code is returned and *ppPage is set to 0. */ -static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ +static SQLITE_NOINLINE int walIndexPageRealloc( + Wal *pWal, /* The WAL context */ + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ +){ int rc = SQLITE_OK; /* Enlarge the pWal->apWiData[] array if required */ @@ -573,21 +577,20 @@ } /* Request a pointer to the required page from the VFS */ - if( pWal->apWiData[iPage]==0 ){ - if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ - pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); - if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; - }else{ - rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, - pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] - ); - assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); - testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); - if( (rc&0xff)==SQLITE_READONLY ){ - pWal->readOnly |= WAL_SHM_RDONLY; - if( rc==SQLITE_READONLY ){ - rc = SQLITE_OK; - } + assert( pWal->apWiData[iPage]==0 ); + if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ + pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); + if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; + }else{ + rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, + pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] + ); + assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); + testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); + if( (rc&0xff)==SQLITE_READONLY ){ + pWal->readOnly |= WAL_SHM_RDONLY; + if( rc==SQLITE_READONLY ){ + rc = SQLITE_OK; } } } @@ -596,6 +599,16 @@ assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); return rc; } +static int walIndexPage( + Wal *pWal, /* The WAL context */ + int iPage, /* The page we seek */ + volatile u32 **ppPage /* Write the page pointer here */ +){ + if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){ + return walIndexPageRealloc(pWal, iPage, ppPage); + } + return SQLITE_OK; +} /* ** Return a pointer to the WalCkptInfo structure in the wal-index. @@ -1572,8 +1585,9 @@ /* ** Construct a WalInterator object that can be used to loop over all -** pages in the WAL in ascending order. The caller must hold the checkpoint -** lock. +** pages in the WAL following frame nBackfill in ascending order. Frames +** nBackfill or earlier may be included - excluding them is an optimization +** only. The caller must hold the checkpoint lock. ** ** On success, make *pp point to the newly allocated WalInterator object ** return SQLITE_OK. Otherwise, return an error code. If this routine @@ -1582,7 +1596,7 @@ ** The calling routine should invoke walIteratorFree() to destroy the ** WalIterator object when it has finished with it. */ -static int walIteratorInit(Wal *pWal, WalIterator **pp){ +static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ WalIterator *p; /* Return value */ int nSegment; /* Number of segments to merge */ u32 iLast; /* Last frame in log */ @@ -1619,7 +1633,7 @@ rc = SQLITE_NOMEM_BKPT; } - for(i=0; rc==SQLITE_OK && i<nSegment; i++){ + for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){ volatile ht_slot *aHash; u32 iZero; volatile u32 *aPgno; @@ -1653,6 +1667,7 @@ if( rc!=SQLITE_OK ){ walIteratorFree(p); + p = 0; } *pp = p; return rc; @@ -1775,13 +1790,6 @@ pInfo = walCkptInfo(pWal); if( pInfo->nBackfill<pWal->hdr.mxFrame ){ - /* Allocate the iterator */ - rc = walIteratorInit(pWal, &pIter); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pIter ); - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); @@ -1818,7 +1826,13 @@ } } - if( pInfo->nBackfill<mxSafeFrame + /* Allocate the iterator */ + if( pInfo->nBackfill<mxSafeFrame ){ + rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter); + assert( rc==SQLITE_OK || pIter==0 ); + } + + if( pIter && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK ){ i64 nSize; /* Current size of database file */ @@ -2868,7 +2882,7 @@ ** table after the current read-transaction had started. */ iMinHash = walFramePage(pWal->minFrame); - for(iHash=walFramePage(iLast); iHash>=iMinHash && iRead==0; iHash--){ + for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){ volatile ht_slot *aHash; /* Pointer to hash table */ volatile u32 *aPgno; /* Pointer to array of page numbers */ u32 iZero; /* Frame number corresponding to aPgno[0] */ @@ -2891,6 +2905,7 @@ return SQLITE_CORRUPT_BKPT; } } + if( iRead ) break; } #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
diff --git a/third_party/sqlite/src/src/where.c b/third_party/sqlite/src/src/where.c index 2ed7439..117849d 100644 --- a/third_party/sqlite/src/src/where.c +++ b/third_party/sqlite/src/src/where.c
@@ -2373,8 +2373,8 @@ pNew = pBuilder->pNew; if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; - WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n", - pProbe->zName, pNew->u.btree.nEq)); + WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n", + pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq)); assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); @@ -2481,10 +2481,12 @@ if( iCol==XN_ROWID || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) ){ - if( iCol>=0 && pProbe->uniqNotNull==0 ){ - pNew->wsFlags |= WHERE_UNQ_WANTED; - }else{ + if( iCol==XN_ROWID || pProbe->uniqNotNull + || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) + ){ pNew->wsFlags |= WHERE_ONEROW; + }else{ + pNew->wsFlags |= WHERE_UNQ_WANTED; } } }else if( eOp & WO_ISNULL ){ @@ -2658,8 +2660,8 @@ pNew->wsFlags = saved_wsFlags; } - WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n", - pProbe->zName, saved_nEq, rc)); + WHERETRACE(0x800, ("END %s.addBtreeIdx(%s), nEq=%d, rc=%d\n", + pProbe->pTable->zName, pProbe->zName, saved_nEq, rc)); return rc; } @@ -3097,9 +3099,9 @@ || pNew->aLTerm[iTerm]!=0 || pIdxCons->usable==0 ){ - rc = SQLITE_ERROR; sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); - return rc; + testcase( pIdxInfo->needToFreeIdxStr ); + return SQLITE_ERROR; } testcase( iTerm==nConstraint-1 ); testcase( j==0 ); @@ -3127,6 +3129,15 @@ pNew->u.vtab.omitMask &= ~mNoOmit; pNew->nLTerm = mxTerm+1; + for(i=0; i<=mxTerm; i++){ + if( pNew->aLTerm[i]==0 ){ + /* The non-zero argvIdx values must be contiguous. Raise an + ** error if they are not */ + sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); + testcase( pIdxInfo->needToFreeIdxStr ); + return SQLITE_ERROR; + } + } assert( pNew->nLTerm<=pNew->nLSlot ); pNew->u.vtab.idxNum = pIdxInfo->idxNum; pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; @@ -3242,6 +3253,7 @@ } /* First call xBestIndex() with all constraints usable. */ + WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); @@ -3317,6 +3329,7 @@ if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); sqlite3DbFreeNN(pParse->db, p); + WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -4631,6 +4644,7 @@ */ for(ii=0; ii<sWLB.pWC->nTerm; ii++){ WhereTerm *pT = &sWLB.pWC->a[ii]; + if( pT->wtFlags & TERM_VIRTUAL ) continue; if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); pT->wtFlags |= TERM_CODED;
diff --git a/third_party/sqlite/src/src/whereInt.h b/third_party/sqlite/src/src/whereInt.h index 03b74bb3..6862d6e 100644 --- a/third_party/sqlite/src/src/whereInt.h +++ b/third_party/sqlite/src/src/whereInt.h
@@ -19,7 +19,7 @@ ** Trace output macros */ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) -/***/ int sqlite3WhereTrace; +/***/ extern int sqlite3WhereTrace; #endif #if defined(SQLITE_DEBUG) \ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
diff --git a/third_party/sqlite/src/src/wherecode.c b/third_party/sqlite/src/src/wherecode.c index 1b96185..b58bba13 100644 --- a/third_party/sqlite/src/src/wherecode.c +++ b/third_party/sqlite/src/src/wherecode.c
@@ -1395,7 +1395,15 @@ if( sqlite3ExprIsVector(pX->pRight) ){ r1 = rTemp = sqlite3GetTempReg(pParse); codeExprOrVector(pParse, pX->pRight, r1, 1); - op = aMoveOp[(pX->op - TK_GT) | 0x0001]; + testcase( pX->op==TK_GT ); + testcase( pX->op==TK_GE ); + testcase( pX->op==TK_LT ); + testcase( pX->op==TK_LE ); + op = aMoveOp[((pX->op - TK_GT - 1) & 0x3) | 0x1]; + assert( pX->op!=TK_GT || op==OP_SeekGE ); + assert( pX->op!=TK_GE || op==OP_SeekGE ); + assert( pX->op!=TK_LT || op==OP_SeekLE ); + assert( pX->op!=TK_LE || op==OP_SeekLE ); }else{ r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp); disableTerm(pLevel, pStart); @@ -2119,7 +2127,7 @@ continue; } - if( pTerm->wtFlags & TERM_LIKECOND ){ + if( (pTerm->wtFlags & TERM_LIKECOND)!=0 ){ /* If the TERM_LIKECOND flag is set, that means that the range search ** is sufficient to guarantee that the LIKE operator is true, so we ** can skip the call to the like(A,B) function. But this only works @@ -2129,8 +2137,9 @@ continue; #else u32 x = pLevel->iLikeRepCntr; - assert( x>0 ); - skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If, (int)(x>>1)); + if( x>0 ){ + skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1)); + } VdbeCoverage(v); #endif } @@ -2170,6 +2179,12 @@ WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; + if( (pAlt->eOperator & WO_IN) + && (pAlt->pExpr->flags & EP_xIsSelect) + && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) + ){ + continue; + } testcase( pAlt->eOperator & WO_EQ ); testcase( pAlt->eOperator & WO_IS ); testcase( pAlt->eOperator & WO_IN );
diff --git a/third_party/sqlite/src/src/whereexpr.c b/third_party/sqlite/src/src/whereexpr.c index 1937f4f..6e2d9da 100644 --- a/third_party/sqlite/src/src/whereexpr.c +++ b/third_party/sqlite/src/src/whereexpr.c
@@ -876,6 +876,9 @@ for(i=0; i<pSrc->nSrc; i++){ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn); + if( pSrc->a[i].fg.isTabFunc ){ + mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); + } } } pS = pS->pPrior; @@ -1288,7 +1291,7 @@ exprAnalyze(pSrc, pWC, idxNew); } pTerm = &pWC->a[idxTerm]; - pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */ + pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */ pTerm->eOperator = 0; }
diff --git a/third_party/sqlite/src/test/analyze.test b/third_party/sqlite/src/test/analyze.test index 697c575..e2332830 100644 --- a/third_party/sqlite/src/test/analyze.test +++ b/third_party/sqlite/src/test/analyze.test
@@ -349,7 +349,7 @@ # This test corrupts the database file so it must be the last test # in the series. # -do_test analyze-99.1 { +do_test analyze-5.99 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense' WHERE name='sqlite_stat1'; @@ -361,4 +361,20 @@ } } {1 {malformed database schema (sqlite_stat1)}} +# Verify that tables whose names begin with "sqlite" but not +# "sqlite_" are analyzed. +# +db close +sqlite3 db :memory: +do_execsql_test analyze-6.1 { + CREATE TABLE sqliteDemo(a); + INSERT INTO sqliteDemo(a) VALUES(1),(2),(3),(4),(5); + CREATE TABLE SQLiteDemo2(a INTEGER PRIMARY KEY AUTOINCREMENT); + INSERT INTO SQLiteDemo2 SELECT * FROM sqliteDemo; + CREATE TABLE t1(b); + INSERT INTO t1(b) SELECT a FROM sqliteDemo; + ANALYZE; + SELECT tbl FROM sqlite_stat1 WHERE idx IS NULL ORDER BY tbl; +} {SQLiteDemo2 sqliteDemo t1} + finish_test
diff --git a/third_party/sqlite/src/test/avtrans.test b/third_party/sqlite/src/test/avtrans.test index edf53ec9..5425f88 100644 --- a/third_party/sqlite/src/test/avtrans.test +++ b/third_party/sqlite/src/test/avtrans.test
@@ -22,7 +22,7 @@ # Create several tables to work with. # do_test avtrans-1.0 { - execsql { PRAGMA auto_vacuum=ON } + execsql { PRAGMA auto_vacuum=full } wal_set_journal_mode execsql { CREATE TABLE one(a int PRIMARY KEY, b text); @@ -32,6 +32,7 @@ SELECT b FROM one ORDER BY a; } } {one two three} +do_test avtrans-1.0.1 { execsql { PRAGMA auto_vacuum } } 1 do_test avtrans-1.1 { execsql { CREATE TABLE two(a int PRIMARY KEY, b text);
diff --git a/third_party/sqlite/src/test/cast.test b/third_party/sqlite/src/test/cast.test index 80f1ca7..f7de569 100644 --- a/third_party/sqlite/src/test/cast.test +++ b/third_party/sqlite/src/test/cast.test
@@ -343,4 +343,49 @@ } } {0 abc 0.0 abc} +# Added 2018-01-26 +# +# EVIDENCE-OF: R-48741-32454 If the prefix integer is greater than +# +9223372036854775807 then the result of the cast is exactly +# +9223372036854775807. +do_execsql_test cast-5.1 { + SELECT CAST('9223372036854775808' AS integer); + SELECT CAST(' +000009223372036854775808' AS integer); + SELECT CAST('12345678901234567890123' AS INTEGER); +} {9223372036854775807 9223372036854775807 9223372036854775807} + +# EVIDENCE-OF: R-06028-16857 Similarly, if the prefix integer is less +# than -9223372036854775808 then the result of the cast is exactly +# -9223372036854775808. +do_execsql_test cast-5.2 { + SELECT CAST('-9223372036854775808' AS integer); + SELECT CAST('-9223372036854775809' AS integer); + SELECT CAST('-12345678901234567890123' AS INTEGER); +} {-9223372036854775808 -9223372036854775808 -9223372036854775808} + +# EVIDENCE-OF: R-33990-33527 When casting to INTEGER, if the text looks +# like a floating point value with an exponent, the exponent will be +# ignored because it is no part of the integer prefix. +# EVIDENCE-OF: R-24225-46995 For example, "(CAST '123e+5' AS INTEGER)" +# results in 123, not in 12300000. +do_execsql_test case-5.3 { + SELECT CAST('123e+5' AS INTEGER); + SELECT CAST('123e+5' AS NUMERIC); +} {123 12300000.0} + + +# The following does not have anything to do with the CAST operator, +# but it does deal with affinity transformations. +# +do_execsql_test case-6.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a NUMERIC); + INSERT INTO t1 VALUES + ('9000000000000000001'), + ('9000000000000000001 '), + (' 9000000000000000001'), + (' 9000000000000000001 '); + SELECT * FROM t1; +} {9000000000000000001 9000000000000000001 9000000000000000001 9000000000000000001} + finish_test
diff --git a/third_party/sqlite/src/test/crash8.test b/third_party/sqlite/src/test/crash8.test index c8efa07..08de1459 100644 --- a/third_party/sqlite/src/test/crash8.test +++ b/third_party/sqlite/src/test/crash8.test
@@ -352,7 +352,11 @@ # Since the following tests (crash8-5.*) rely upon being able # to copy a file while open, they will not work on Windows. # -if {$::tcl_platform(platform)=="unix"} { +# They also depend on being able to copy the journal file, which +# is not created on F2FS file-systems that support atomic +# write. So do not run these tests in that case either. +# +if {$::tcl_platform(platform)=="unix" && [atomic_batch_write test.db]==0 } { for {set i 1} {$i < 10} {incr i} { catch { db close } forcedelete test.db test.db-journal
diff --git a/third_party/sqlite/src/test/cursorhint2.test b/third_party/sqlite/src/test/cursorhint2.test index 383cab10..679bb53 100644 --- a/third_party/sqlite/src/test/cursorhint2.test +++ b/third_party/sqlite/src/test/cursorhint2.test
@@ -136,42 +136,47 @@ x2 {EQ(c0,r[2])} } -do_extract_hints_test 2.6 { - SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT NULL) -} { - x2 {EQ(c0,r[2])} -} - -do_extract_hints_test 2.7 { - SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT +NULL) -} { - x2 {EQ(c0,r[2])} -} - -do_extract_hints_test 2.8 { - SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS NOT +NULL -} { - x2 {EQ(c0,r[2])} -} - -do_extract_hints_test 2.9 { - SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE CASE b WHEN 0 THEN 0 ELSE 1 END; -} { - x2 {EQ(c0,r[2])} -} - -do_extract_hints_test 2.10 { - SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32 -} { - x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))} -} - -ifcapable !icu { - # This test only works using the built-in LIKE, not the ICU LIKE extension. - do_extract_hints_test 2.11 { - SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b LIKE 'abc%' +if {0} { + # These tests no longer work due to the LEFT-JOIN strength reduction + # optimization + do_extract_hints_test 2.6 { + SELECT * FROM x1 CROSS JOIN x2 ON (a=x) WHERE 0 = (b IS NOT NULL) } { - x2 {AND(expr,EQ(c0,r[2]))} + x2 {EQ(c0,r[2])} + } + + do_extract_hints_test 2.7 { + SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT +NULL) + } { + x2 {EQ(c0,r[2])} + } + + do_extract_hints_test 2.8 { + SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS NOT +NULL + } { + x2 {EQ(c0,r[2])} + } + + do_extract_hints_test 2.9 { + SELECT * FROM x1 LEFT JOIN x2 ON (a=x) + WHERE CASE b WHEN 0 THEN 0 ELSE 1 END; + } { + x2 {EQ(c0,r[2])} + } + + do_extract_hints_test 2.10 { + SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32 + } { + x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))} + } + + ifcapable !icu { + # This test only works using the built-in LIKE, not the ICU LIKE extension. + do_extract_hints_test 2.11 { + SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b LIKE 'abc%' + } { + x2 {AND(expr,EQ(c0,r[2]))} + } } }
diff --git a/third_party/sqlite/src/test/dbstatus.test b/third_party/sqlite/src/test/dbstatus.test index 630686a..f6b5cab8 100644 --- a/third_party/sqlite/src/test/dbstatus.test +++ b/third_party/sqlite/src/test/dbstatus.test
@@ -415,4 +415,43 @@ } } +#------------------------------------------------------------------------- +# Test that passing an out-of-range value to sqlite3_stmt_status does +# not cause a crash. +reset_db +do_execsql_test 5.0 { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); +} + +do_test 5.1 { + set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy] + sqlite3_step $::stmt + sqlite3_step $::stmt + sqlite3_step $::stmt + sqlite3_reset $::stmt +} {SQLITE_OK} + +ifcapable api_armor { + do_test 5.2 { sqlite3_stmt_status $::stmt -1 0 } 0 +} +do_test 5.3 { sqlite3_stmt_status $::stmt 0 0 } 0 +do_test 5.4 { + expr [sqlite3_stmt_status $::stmt 99 0]>0 +} 1 +foreach {tn id res} { + 1 SQLITE_STMTSTATUS_MEMUSED 1 + 2 SQLITE_STMTSTATUS_FULLSCAN_STEP 1 + 3 SQLITE_STMTSTATUS_SORT 0 + 4 SQLITE_STMTSTATUS_AUTOINDEX 0 + 5 SQLITE_STMTSTATUS_VM_STEP 1 + 6 SQLITE_STMTSTATUS_REPREPARE 0 + 7 SQLITE_STMTSTATUS_RUN 1 +} { +if {$tn==2} breakpoint + do_test 5.5.$tn { expr [sqlite3_stmt_status $::stmt $id 0]>0 } $res +} + +sqlite3_finalize $::stmt finish_test
diff --git a/third_party/sqlite/src/test/dbstatus2.test b/third_party/sqlite/src/test/dbstatus2.test index 47ef362..1aa9a3ee 100644 --- a/third_party/sqlite/src/test/dbstatus2.test +++ b/third_party/sqlite/src/test/dbstatus2.test
@@ -37,6 +37,10 @@ sqlite3_db_status $db CACHE_WRITE $reset } +proc db_spill {db {reset 0}} { + sqlite3_db_status $db CACHE_SPILL $reset +} + do_test 1.1 { db close sqlite3 db test.db @@ -99,4 +103,13 @@ do_test 2.8 { db_write db 1 } {0 4 0} do_test 2.9 { db_write db 0 } {0 0 0} +do_test 3.0 { db_spill db 1 } {0 0 0} +do_test 3.1 { db_spill db 0 } {0 0 0} +do_execsql_test 3.2 { + PRAGMA journal_mode=DELETE; + PRAGMA cache_size=3; + UPDATE t1 SET b=randomblob(1000); +} {delete} +do_test 3.3 { db_spill db 0 } {0 8 0} + finish_test
diff --git a/third_party/sqlite/src/test/e_select.test b/third_party/sqlite/src/test/e_select.test index d9c799a7..e38839ea 100644 --- a/third_party/sqlite/src/test/e_select.test +++ b/third_party/sqlite/src/test/e_select.test
@@ -748,7 +748,7 @@ SELECT k FROM x1 LEFT JOIN x2 USING(k) } {1 2 3 4 5 6} do_execsql_test e_select-3.2.1b { - SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k + SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k ORDER BY +k } {1 3 5} do_execsql_test e_select-3.2.2 { SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k IS NULL
diff --git a/third_party/sqlite/src/test/expr.test b/third_party/sqlite/src/test/expr.test index 9c77a53d..1a2181b 100644 --- a/third_party/sqlite/src/test/expr.test +++ b/third_party/sqlite/src/test/expr.test
@@ -977,6 +977,63 @@ SELECT '' <= ""; } {1} +# 2018-02-26. Ticket https://www.sqlite.org/src/tktview/36fae083b450e3af85 +# +do_execsql_test expr-14.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(0),(1),(NULL),(0.5),('1x'),('0x'); + SELECT count(*) FROM t1 + WHERE (x OR (8==9)) != (CASE WHEN x THEN 1 ELSE 0 END); +} {0} +do_execsql_test expr-14.2 { + SELECT count(*) FROM t1 + WHERE (x OR (8==9)) != (NOT NOT x); +} {0} +do_execsql_test expr-14.3 { + SELECT sum(NOT x) FROM t1 + WHERE x +} {0} +do_execsql_test expr-14.4 { + SELECT sum(CASE WHEN x THEN 0 ELSE 1 END) FROM t1 + WHERE x +} {0} +foreach {tn val} [list 1 NaN 2 -NaN 3 NaN0 4 -NaN0 5 Inf 6 -Inf] { + do_execsql_test expr-15.$tn.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(0),(1),(NULL),(0.5),('1x'),('0x'); + } + + do_test expr-15.$tn.2 { + set ::STMT [sqlite3_prepare db "INSERT INTO t1 VALUES(?)" -1 TAIL] + sqlite3_bind_double $::STMT 1 $val + sqlite3_step $::STMT + sqlite3_reset $::STMT + sqlite3_finalize $::STMT + } {SQLITE_OK} + + do_execsql_test expr-15.$tn.3 { + SELECT count(*) FROM t1 + WHERE (x OR (8==9)) != (CASE WHEN x THEN 1 ELSE 0 END); + } {0} + + do_execsql_test expr-15.$tn.4 { + SELECT count(*) FROM t1 + WHERE (x OR (8==9)) != (NOT NOT x); + } {0} + + do_execsql_test expr-15.$tn.5 { + SELECT sum(NOT x) FROM t1 + WHERE x + } {0} + + do_execsql_test expr-15.$tn.6 { + SELECT sum(CASE WHEN x THEN 0 ELSE 1 END) FROM t1 + WHERE x + } {0} +} + finish_test
diff --git a/third_party/sqlite/src/test/fts3aa.test b/third_party/sqlite/src/test/fts3aa.test index 10ec273..d5f96d81 100644 --- a/third_party/sqlite/src/test/fts3aa.test +++ b/third_party/sqlite/src/test/fts3aa.test
@@ -250,4 +250,5 @@ CREATE VIRTUAL TABLE t10 USING fts3(<, b, c); } +expand_all_sql db finish_test
diff --git a/third_party/sqlite/src/test/fts3rank.test b/third_party/sqlite/src/test/fts3rank.test index 93b8c45..48524813 100644 --- a/third_party/sqlite/src/test/fts3rank.test +++ b/third_party/sqlite/src/test/fts3rank.test
@@ -14,7 +14,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -set testprefix fts3expr5 +set testprefix fts3rank # If SQLITE_ENABLE_FTS3 is defined, omit this file. ifcapable !fts3 { @@ -56,9 +56,14 @@ SELECT * FROM t1 ORDER BY rank(x'0000000000000000') DESC, rowid } {0 {{one two} one {one two} three {one two} two}} -do_catchsql_test 1.5 { - SELECT * FROM t1 ORDER BY rank(x'0100000001000000') DESC, rowid -} {1 {invalid matchinfo blob passed to function rank()}} +if {$tcl_platform(byteOrder)=="littleEndian"} { + do_catchsql_test 1.5le { + SELECT * FROM t1 ORDER BY rank(x'0100000001000000') DESC, rowid + } {1 {invalid matchinfo blob passed to function rank()}} +} else { + do_catchsql_test 1.5be { + SELECT * FROM t1 ORDER BY rank(x'0000000100000001') DESC, rowid + } {1 {invalid matchinfo blob passed to function rank()}} +} finish_test -
diff --git a/third_party/sqlite/src/test/fts4onepass.test b/third_party/sqlite/src/test/fts4onepass.test index 4962fe7f..1c66ae19 100644 --- a/third_party/sqlite/src/test/fts4onepass.test +++ b/third_party/sqlite/src/test/fts4onepass.test
@@ -143,4 +143,18 @@ eval $tcl2 } +do_execsql_test 4.0 { + CREATE VIRTUAL TABLE zt USING fts4(a, b); + INSERT INTO zt(rowid, a, b) VALUES(1, 'unus duo', NULL); + INSERT INTO zt(rowid, a, b) VALUES(2, NULL, NULL); + + BEGIN; + UPDATE zt SET b='septum' WHERE rowid = 1; + UPDATE zt SET b='octo' WHERE rowid = 1; + COMMIT; + + SELECT count(*) FROM zt_segdir; +} {3} + + finish_test
diff --git a/third_party/sqlite/src/test/func.test b/third_party/sqlite/src/test/func.test index abebc3cc..4521146 100644 --- a/third_party/sqlite/src/test/func.test +++ b/third_party/sqlite/src/test/func.test
@@ -507,6 +507,17 @@ execsql {SELECT hex(replace('aabcdefg','a','aaa'))} } {616161616161626364656667} } +do_execsql_test func-9.14 { + WITH RECURSIVE c(x) AS ( + VALUES(1) + UNION ALL + SELECT x+1 FROM c WHERE x<1040 + ) + SELECT + count(*), + sum(length(replace(printf('abc%.*cxyz',x,'m'),'m','nnnn'))-(6+x*4)) + FROM c; +} {1040 0} # Use the "sqlite_register_test_function" TCL command which is part of # the text fixture in order to verify correct operation of some of
diff --git a/third_party/sqlite/src/test/func6.test b/third_party/sqlite/src/test/func6.test index 2bfcb3d..b5d8bd1 100644 --- a/third_party/sqlite/src/test/func6.test +++ b/third_party/sqlite/src/test/func6.test
@@ -22,6 +22,11 @@ return } +set bNullTrim 0 +ifcapable null_trim { + set bNullTrim 1 +} + do_execsql_test func6-100 { PRAGMA page_size=4096; PRAGMA auto_vacuum=NONE; @@ -33,54 +38,137 @@ CREATE TABLE t2(x TEXT PRIMARY KEY, y) WITHOUT ROWID; INSERT INTO t2(x,y) SELECT a, b FROM t1; } + +# Load the contents of $file from disk and return it encoded as a hex +# string. +proc loadhex {file} { + set fd [open $file] + fconfigure $fd -translation binary -encoding binary + set data [read $fd] + close $fd + binary encode hex $data +} + +# Each argument is either an integer between 0 and 65535, a text value, or +# an empty string representing an SQL NULL. This command builds an SQLite +# record containing the values passed as arguments and returns it encoded +# as a hex string. +proc hexrecord {args} { + set hdr "" + set body "" + + if {$::bNullTrim} { + while {[llength $args] && [lindex $args end]=={}} { + set args [lrange $args 0 end-1] + } + } + + foreach x $args { + if {$x==""} { + append hdr 00 + } elseif {[string is integer $x]==0} { + set n [string length $x] + append hdr [format %02x [expr $n*2 + 13]] + append body [binary encode hex $x] + } elseif {$x == 0} { + append hdr 08 + } elseif {$x == 1} { + append hdr 09 + } elseif {$x <= 127} { + append hdr 01 + append body [format %02x $x] + } else { + append hdr 02 + append body [format %04x $x] + } + } + set res [format %02x [expr 1 + [string length $hdr]/2]] + append res $hdr + append res $body +} + +# Argument $off is an offset into the database image encoded as a hex string +# in argument $hexdb. This command returns 0 if the offset contains the hex +# $hexrec, or throws an exception otherwise. +# +proc offset_contains_record {off hexdb hexrec} { + set n [string length $hexrec] + set off [expr $off*2] + if { [string compare $hexrec [string range $hexdb $off [expr $off+$n-1]]] } { + error "record not found!" + } + return 0 +} + +# This command is the implementation of SQL function "offrec()". The first +# argument to this is an offset value. The remaining values are used to +# formulate an SQLite record. If database file test.db does not contain +# an equivalent record at the specified offset, an exception is thrown. +# Otherwise, 0 is returned. +# +proc offrec {args} { + set offset [lindex $args 0] + set rec [hexrecord {*}[lrange $args 1 end]] + offset_contains_record $offset $::F $rec +} +set F [loadhex test.db] +db func offrec offrec + +# Test the sanity of the tests. +if {$bNullTrim} { + set offset 8180 +} else { + set offset 8179 +} +do_execsql_test func6-105 { + SELECT sqlite_offset(d) FROM t1 ORDER BY rowid LIMIT 1; +} $offset +do_test func6-106 { + set r [hexrecord abc001 1 999 {}] + offset_contains_record $offset $F $r +} 0 + +set z100 [string trim [string repeat "0 " 100]] + +# Test offsets within table b-tree t1. do_execsql_test func6-110 { - SELECT a, sqlite_offset(d)/4096 + 1, - sqlite_offset(d)%4096 FROM t1 - ORDER BY rowid LIMIT 2; -} {abc001 2 4084 abc002 2 4069} + SELECT offrec(sqlite_offset(d), a, b, c, d) FROM t1 ORDER BY rowid +} $z100 + do_execsql_test func6-120 { SELECT a, typeof(sqlite_offset(+a)) FROM t1 ORDER BY rowid LIMIT 2; } {abc001 null abc002 null} + +# Test offsets within index b-tree t1a. do_execsql_test func6-130 { - SELECT a, sqlite_offset(a)/4096+1, - sqlite_offset(a)%4096 - FROM t1 - ORDER BY a LIMIT 2; -} {abc001 3 4087 abc002 3 4076} + SELECT offrec(sqlite_offset(a), a, rowid) FROM t1 ORDER BY a +} $z100 + +# Test offsets within table b-tree t1 with a temp b-tree ORDER BY. do_execsql_test func6-140 { - SELECT a, sqlite_offset(d)/4096+1, - sqlite_offset(d)%4096 - FROM t1 - ORDER BY a LIMIT 2; -} {abc001 2 4084 abc002 2 4069} + SELECT offrec(sqlite_offset(d), a, b, c, d) FROM t1 ORDER BY a +} $z100 + +# Test offsets from both index t1a and table t1 in the same query. do_execsql_test func6-150 { - SELECT a, - sqlite_offset(a)/4096+1, - sqlite_offset(a)%4096, - sqlite_offset(d)/4096+1, - sqlite_offset(d)%4096 - FROM t1 - ORDER BY a LIMIT 2; -} {abc001 3 4087 2 4084 abc002 3 4076 2 4069} + SELECT offrec(sqlite_offset(a), a, rowid), + offrec(sqlite_offset(d), a, b, c, d) + FROM t1 ORDER BY a +} [concat $z100 $z100] + +# Test offsets from both index t1bc and table t1 in the same query. do_execsql_test func6-160 { - SELECT b, - sqlite_offset(b)/4096+1, - sqlite_offset(b)%4096, - sqlite_offset(c)/4096+1, - sqlite_offset(c)%4096, - sqlite_offset(d)/4096+1, - sqlite_offset(d)%4096 - FROM t1 - ORDER BY b LIMIT 2; -} {1 4 4090 4 4090 2 4084 2 4 4081 4 4081 2 4069} + SELECT offrec(sqlite_offset(b), b, c, rowid), + offrec(sqlite_offset(c), b, c, rowid), + offrec(sqlite_offset(d), a, b, c, d) + FROM t1 + ORDER BY b +} [concat $z100 $z100 $z100] - +# Test offsets in WITHOUT ROWID table t2. do_execsql_test func6-200 { - SELECT y, sqlite_offset(y)/4096+1, - sqlite_offset(y)%4096 - FROM t2 - ORDER BY x LIMIT 2; -} {1 5 4087 2 5 4076} + SELECT offrec( sqlite_offset(y), x, y ) FROM t2 ORDER BY x +} $z100 finish_test
diff --git a/third_party/sqlite/src/test/fuzzcheck.c b/third_party/sqlite/src/test/fuzzcheck.c index be42c44d8a..10f0489b 100644 --- a/third_party/sqlite/src/test/fuzzcheck.c +++ b/third_party/sqlite/src/test/fuzzcheck.c
@@ -81,7 +81,13 @@ #ifdef SQLITE_OSS_FUZZ # include <stddef.h> -# include <stdint.h> +# if !defined(_MSC_VER) +# include <stdint.h> +# endif +#endif + +#if defined(_MSC_VER) +typedef unsigned char uint8_t; #endif /*
diff --git a/third_party/sqlite/src/test/indexexpr1.test b/third_party/sqlite/src/test/indexexpr1.test index 7718961..648b09d 100644 --- a/third_party/sqlite/src/test/indexexpr1.test +++ b/third_party/sqlite/src/test/indexexpr1.test
@@ -425,4 +425,25 @@ REPLACE INTO t1 SELECT a, randomblob(a) FROM t1 } {} +# 2018-01-31 https://www.sqlite.org/src/tktview/343634942dd54ab57b702411 +# When an index on an expression depends on the string representation of +# a numeric table column, trouble can arise since there are multiple +# string that can map to the same numeric value. (Ex: 123, 0123, 000123). +# +do_execsql_test indexexpr-1600 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1 (a INTEGER, b); + CREATE INDEX idx1 ON t1 (lower(a)); + INSERT INTO t1 VALUES('0001234',3); + PRAGMA integrity_check; +} {ok} +do_execsql_test indexexpr-1610 { + INSERT INTO t1 VALUES('1234',0),('001234',2),('01234',1); + SELECT b FROM t1 WHERE lower(a)='1234' ORDER BY +b; +} {0 1 2 3} +do_execsql_test indexexpr-1620 { + SELECT b FROM t1 WHERE lower(a)='01234' ORDER BY +b; +} {} + + finish_test
diff --git a/third_party/sqlite/src/test/ioerr.test b/third_party/sqlite/src/test/ioerr.test index 0e1c845f..5d731676 100644 --- a/third_party/sqlite/src/test/ioerr.test +++ b/third_party/sqlite/src/test/ioerr.test
@@ -172,7 +172,7 @@ # These tests can't be run on windows because the windows version of # SQLite holds a mandatory exclusive lock on journal files it has open. # -if {$tcl_platform(platform)!="windows"} { +if {$tcl_platform(platform)!="windows" && ![atomic_batch_write test.db]} { do_ioerr_test ioerr-7 -tclprep { db close sqlite3 db2 test2.db @@ -211,7 +211,7 @@ # For test coverage: Cause an IO error whilst reading the master-journal # name from a journal file. -if {$tcl_platform(platform)=="unix"} { +if {$tcl_platform(platform)=="unix" && [atomic_batch_write test.db]==0} { do_ioerr_test ioerr-9 -ckrefcount true -tclprep { execsql { CREATE TABLE t1(a,b,c);
diff --git a/third_party/sqlite/src/test/istrue.test b/third_party/sqlite/src/test/istrue.test new file mode 100644 index 0000000..ee52b09 --- /dev/null +++ b/third_party/sqlite/src/test/istrue.test
@@ -0,0 +1,146 @@ +# 2018-02-26 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing expressions of the form +# +# x IS TRUE +# x IS FALSE +# x IS NOT TRUE +# x IS NOT FALSE +# +# Tests are also included for the use of TRUE and FALSE as +# literal values. + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test istrue-100 { + CREATE TABLE t1(x INTEGER PRIMARY KEY, y BOOLEAN); + INSERT INTO t1 VALUES(1, true),(2, false),(3, null); + SELECT x FROM t1 WHERE y IS TRUE; +} {1} +do_execsql_test istrue-110 { + SELECT x FROM t1 WHERE y IS FALSE; +} {2} +do_execsql_test istrue-120 { + SELECT x FROM t1 WHERE y IS NULL; +} {3} +do_execsql_test istrue-130 { + SELECT x FROM t1 WHERE y IS NOT TRUE; +} {2 3} +do_execsql_test istrue-140 { + SELECT x FROM t1 WHERE y IS NOT FALSE; +} {1 3} +do_execsql_test istrue-150 { + SELECT x FROM t1 WHERE y IS NOT NULL; +} {1 2} +unset -nocomplain X +set X 9 +do_execsql_test istrue-160 { + SELECT x FROM t1 WHERE y IS TRUE OR (8==$X) +} {1} +do_execsql_test istrue-170 { + SELECT x FROM t1 WHERE y IS FALSE OR (8==$X) +} {2} +do_execsql_test istrue-180 { + SELECT x FROM t1 WHERE y IS NULL OR (8==$X); +} {3} +do_execsql_test istrue-190 { + SELECT x FROM t1 WHERE y IS NOT TRUE OR (8==$X); +} {2 3} +do_execsql_test istrue-200 { + SELECT x FROM t1 WHERE y IS NOT FALSE OR (8==$X); +} {1 3} +do_execsql_test istrue-210 { + SELECT x FROM t1 WHERE y IS NOT NULL OR (8==$X); +} {1 2} + +do_execsql_test istrue-300 { + SELECT x, + y IS TRUE, y IS FALSE, y is NULL, + y IS NOT TRUE, y IS NOT FALSE, y IS NOT NULL, '|' + FROM t1 ORDER BY x; +} {1 1 0 0 0 1 1 | 2 0 1 0 1 0 1 | 3 0 0 1 1 1 0 |} + +do_execsql_test istrue-400 { + SELECT x FROM t1 WHERE true; +} {1 2 3} +do_execsql_test istrue-410 { + SELECT x FROM t1 WHERE false; +} {} + +do_execsql_test istrue-500 { + CREATE TABLE t2( + a INTEGER PRIMARY KEY, + b BOOLEAN DEFAULT true, + c BOOLEAN DEFAULT(true), + d BOOLEAN DEFAULT false, + e BOOLEAN DEFAULT(false) + ); + INSERT INTO t2 DEFAULT VALUES; + SELECT * FROM t2; +} {1 1 1 0 0} +do_execsql_test istrue-510 { + DROP TABLE t2; + CREATE TABLE t2( + a INTEGER PRIMARY KEY, + b BOOLEAN DEFAULT(not true), + c BOOLEAN DEFAULT(not false) + ); + INSERT INTO t2(a) VALUES(99); + SELECT * FROM t2; +} {99 0 1} +do_execsql_test istrue-520 { + DROP TABLE t2; + CREATE TABLE t2( + a INTEGER PRIMARY KEY, + b BOOLEAN CHECK(b IS TRUE), + c BOOLEAN CHECK(c IS FALSE), + d BOOLEAN CHECK(d IS NOT TRUE), + e BOOLEAN CHECK(e IS NOT FALSE) + ); + INSERT INTO t2 VALUES(1,true,false,null,null); + SELECT * FROM t2; +} {1 1 0 {} {}} +do_catchsql_test istrue-521 { + INSERT INTO t2 VALUES(2,false,false,null,null); +} {1 {CHECK constraint failed: t2}} +do_catchsql_test istrue-522 { + INSERT INTO t2 VALUES(2,true,true,null,null); +} {1 {CHECK constraint failed: t2}} +do_catchsql_test istrue-523 { + INSERT INTO t2 VALUES(2,true,false,true,null); +} {1 {CHECK constraint failed: t2}} +do_catchsql_test istrue-524 { + INSERT INTO t2 VALUES(2,true,false,null,false); +} {1 {CHECK constraint failed: t2}} + +foreach {tn val} [list 1 NaN 2 -NaN 3 NaN0 4 -NaN0 5 Inf 6 -Inf] { + do_execsql_test istrue-600.$tn.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x); + } + do_test istrue-600.$tn.2 { + set ::STMT [sqlite3_prepare db "INSERT INTO t1 VALUES(?)" -1 TAIL] + sqlite3_bind_double $::STMT 1 $val + sqlite3_step $::STMT + sqlite3_reset $::STMT + sqlite3_finalize $::STMT + } {SQLITE_OK} + do_execsql_test istrue-600.$tn.3 { + SELECT x IS TRUE FROM t1; + } [expr {$tn in [list 5 6] ? {1} : {0}}] + do_execsql_test istrue-600.$tn.4 { + SELECT x IS FALSE FROM t1; + } {0} +} + +finish_test
diff --git a/third_party/sqlite/src/test/join.test b/third_party/sqlite/src/test/join.test index a040967..1951028e 100644 --- a/third_party/sqlite/src/test/join.test +++ b/third_party/sqlite/src/test/join.test
@@ -780,4 +780,68 @@ LEFT JOIN t3 AS x3 ON x2.id=x3.c3; } {456 {} {}} +# 2018-03-24. +# E.Pasma discovered that the LEFT JOIN strength reduction optimization +# was misbehaving. The problem turned out to be that the +# sqlite3ExprImpliesNotNull() routine was saying that CASE expressions +# like +# +# CASE WHEN true THEN true ELSE x=0 END +# +# could never be true if x is NULL. The following test cases verify +# that this error has been resolved. +# +db close +sqlite3 db :memory: +do_execsql_test join-15.100 { + CREATE TABLE t1(a INT, b INT); + INSERT INTO t1 VALUES(1,2),(3,4); + CREATE TABLE t2(x INT, y INT); + SELECT *, 'x' + FROM t1 LEFT JOIN t2 + WHERE CASE WHEN FALSE THEN a=x ELSE 1 END; +} {1 2 {} {} x 3 4 {} {} x} +do_execsql_test join-15.105 { + SELECT *, 'x' + FROM t1 LEFT JOIN t2 + WHERE a IN (1,3,x,y); +} {1 2 {} {} x 3 4 {} {} x} +do_execsql_test join-15.106 { + SELECT *, 'x' + FROM t1 LEFT JOIN t2 + WHERE NOT ( 'x'='y' AND t2.y=1 ); +} {1 2 {} {} x 3 4 {} {} x} +do_execsql_test join-15.107 { + SELECT *, 'x' + FROM t1 LEFT JOIN t2 + WHERE t2.y IS NOT 'abc' +} {1 2 {} {} x 3 4 {} {} x} +do_execsql_test join-15.110 { + DROP TABLE t1; + DROP TABLE t2; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER); + INSERT INTO t1(a,b) VALUES(1,0),(11,1),(12,1),(13,1),(121,12); + CREATE INDEX t1b ON t1(b); + CREATE TABLE t2(x INTEGER PRIMARY KEY); + INSERT INTO t2(x) VALUES(0),(1); + SELECT a1, a2, a3, a4, a5 + FROM (SELECT a AS a1 FROM t1 WHERE b=0) + JOIN (SELECT x AS x1 FROM t2) + LEFT JOIN (SELECT a AS a2, b AS b2 FROM t1) + ON x1 IS TRUE AND b2=a1 + JOIN (SELECT x AS x2 FROM t2) + ON x2<=CASE WHEN x1 THEN CASE WHEN a2 THEN 1 ELSE -1 END ELSE 0 END + LEFT JOIN (SELECT a AS a3, b AS b3 FROM t1) + ON x2 IS TRUE AND b3=a2 + JOIN (SELECT x AS x3 FROM t2) + ON x3<=CASE WHEN x2 THEN CASE WHEN a3 THEN 1 ELSE -1 END ELSE 0 END + LEFT JOIN (SELECT a AS a4, b AS b4 FROM t1) + ON x3 IS TRUE AND b4=a3 + JOIN (SELECT x AS x4 FROM t2) + ON x4<=CASE WHEN x3 THEN CASE WHEN a4 THEN 1 ELSE -1 END ELSE 0 END + LEFT JOIN (SELECT a AS a5, b AS b5 FROM t1) + ON x4 IS TRUE AND b5=a4 + ORDER BY a1, a2, a3, a4, a5; +} {1 {} {} {} {} 1 11 {} {} {} 1 12 {} {} {} 1 12 121 {} {} 1 13 {} {} {}} + finish_test
diff --git a/third_party/sqlite/src/test/join2.test b/third_party/sqlite/src/test/join2.test index 37b1a95..40915a2 100644 --- a/third_party/sqlite/src/test/join2.test +++ b/third_party/sqlite/src/test/join2.test
@@ -86,7 +86,7 @@ } do_catchsql_test 2.1 { - SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=c); + SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=coalesce(c,1)); } {1 {ON clause references tables to its right}} do_catchsql_test 2.2 { SELECT * FROM aa JOIN cc ON (a=b) JOIN bb ON (b=c); @@ -155,20 +155,65 @@ SELECT v1, v3 FROM c1 LEFT JOIN c2 LEFT JOIN c3 ON (c3.k=v1+1); } {2 v3 2 v3 1112 {} 1112 {}} -do_eqp_test 4.2.1 { +do_eqp_test 4.1.5 { SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v2); } { 0 0 0 {SCAN TABLE c1} 0 1 1 {SEARCH TABLE c2 USING INTEGER PRIMARY KEY (rowid=?)} 0 2 2 {SEARCH TABLE c3 USING INTEGER PRIMARY KEY (rowid=?)} } -do_eqp_test 4.2.2 { +do_eqp_test 4.1.6 { SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v1+1); } { 0 0 0 {SCAN TABLE c1} 0 1 2 {SEARCH TABLE c3 USING INTEGER PRIMARY KEY (rowid=?)} } +do_execsql_test 4.2.0 { + DROP TABLE c1; + DROP TABLE c2; + DROP TABLE c3; + CREATE TABLE c1(k UNIQUE, v1); + CREATE TABLE c2(k UNIQUE, v2); + CREATE TABLE c3(k UNIQUE, v3); + + INSERT INTO c1 VALUES(1, 2); + INSERT INTO c2 VALUES(2, 3); + INSERT INTO c3 VALUES(3, 'v3'); + + INSERT INTO c1 VALUES(111, 1112); + INSERT INTO c2 VALUES(112, 1113); + INSERT INTO c3 VALUES(113, 'v1113'); +} +do_execsql_test 4.2.1 { + SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v2); +} {2 v3 1112 {}} +do_execsql_test 4.2.2 { + SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v1+1); +} {2 v3 1112 {}} + +do_execsql_test 4.2.3 { + SELECT DISTINCT v1, v3 FROM c1 LEFT JOIN c2 LEFT JOIN c3 ON (c3.k=v1+1); +} {2 v3 1112 {}} + +do_execsql_test 4.2.4 { + SELECT v1, v3 FROM c1 LEFT JOIN c2 LEFT JOIN c3 ON (c3.k=v1+1); +} {2 v3 2 v3 1112 {} 1112 {}} + +do_eqp_test 4.2.5 { + SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v2); +} { + 0 0 0 {SCAN TABLE c1} + 0 1 1 {SEARCH TABLE c2 USING INDEX sqlite_autoindex_c2_1 (k=?)} + 0 2 2 {SEARCH TABLE c3 USING INDEX sqlite_autoindex_c3_1 (k=?)} +} +do_eqp_test 4.2.6 { + SELECT v1, v3 FROM c1 LEFT JOIN c2 ON (c2.k=v1) LEFT JOIN c3 ON (c3.k=v1+1); +} { + 0 0 0 {SCAN TABLE c1} + 0 1 2 {SEARCH TABLE c3 USING INDEX sqlite_autoindex_c3_1 (k=?)} +} + # 2017-11-23 (Thanksgiving day) # OSSFuzz found an assertion fault in the new LEFT JOIN eliminator code. # @@ -192,4 +237,46 @@ LEFT JOIN t2 AS c ON (a.x=c.x); } {1 {} 2 {} 3 {} 4 {} 5 {} 6 {} 7 {} 8 {} 9 {} 10 10} +do_execsql_test 5.0 { + CREATE TABLE s1 (a INTEGER PRIMARY KEY); + CREATE TABLE s2 (a INTEGER PRIMARY KEY); + CREATE TABLE s3 (a INTEGER); + CREATE UNIQUE INDEX ndx on s3(a); +} +do_eqp_test 5.1 { + SELECT s1.a FROM s1 left join s2 using (a); +} { + 0 0 0 {SCAN TABLE s1} +} +do_eqp_test 5.2 { + SELECT s1.a FROM s1 left join s3 using (a); +} { + 0 0 0 {SCAN TABLE s1} +} + +do_execsql_test 6.0 { + CREATE TABLE u1(a INTEGER PRIMARY KEY, b, c); + CREATE TABLE u2(a INTEGER PRIMARY KEY, b, c); + CREATE INDEX u1ab ON u1(b, c); +} +do_eqp_test 6.1 { + SELECT u2.* FROM u2 LEFT JOIN u1 ON( u1.a=u2.a AND u1.b=u2.b AND u1.c=u2.c ); +} { + 0 0 0 {SCAN TABLE u2} +} + +db close +sqlite3 db :memory: +do_execsql_test 7.0 { + CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2),(3,4),(5,6); + CREATE TABLE t2(c,d); INSERT INTO t2 VALUES(2,4),(3,6); + CREATE TABLE t3(x); INSERT INTO t3 VALUES(9); + CREATE VIEW test AS + SELECT *, 'x' + FROM t1 LEFT JOIN (SELECT * FROM t2, t3) ON (c=b AND x=9) + WHERE c IS NULL; + SELECT * FROM test; +} {3 4 {} {} {} x 5 6 {} {} {} x} + + finish_test
diff --git a/third_party/sqlite/src/test/join5.test b/third_party/sqlite/src/test/join5.test index 352ffd4..c2bf1e3f 100644 --- a/third_party/sqlite/src/test/join5.test +++ b/third_party/sqlite/src/test/join5.test
@@ -164,7 +164,7 @@ # Ticket https://www.sqlite.org/src/tktview/c2a19d81652f40568c770c43 on # 2015-08-20. LEFT JOIN and the push-down optimization. # -do_execsql_test join6-4.1 { +do_execsql_test join5-4.1 { SELECT * FROM ( SELECT 'apple' fruit @@ -178,7 +178,7 @@ SELECT 1 isyellow ) c ON b.fruit='banana'; } {apple apple {} banana banana 1} -do_execsql_test join6-4.2 { +do_execsql_test join5-4.2 { SELECT * FROM (SELECT 'apple' fruit UNION ALL SELECT 'banana') LEFT JOIN (SELECT 1) ON fruit='banana';
diff --git a/third_party/sqlite/src/test/json101.test b/third_party/sqlite/src/test/json101.test index cc66aa1c..709863e 100644 --- a/third_party/sqlite/src/test/json101.test +++ b/third_party/sqlite/src/test/json101.test
@@ -759,7 +759,30 @@ FROM t12; } {0} - +# 2018-01-26 +# ticket https://www.sqlite.org/src/tktview/80177f0c226ff54f6ddd41 +# Make sure the query planner knows about the arguments to table-valued functions. +# +do_execsql_test json-13.100 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(id, json); + INSERT INTO t1(id,json) VALUES(1,'{"items":[3,5]}'); + CREATE TABLE t2(id, json); + INSERT INTO t2(id,json) VALUES(2,'{"value":2}'); + INSERT INTO t2(id,json) VALUES(3,'{"value":3}'); + INSERT INTO t2(id,json) VALUES(4,'{"value":4}'); + INSERT INTO t2(id,json) VALUES(5,'{"value":5}'); + INSERT INTO t2(id,json) VALUES(6,'{"value":6}'); + SELECT * FROM t1 CROSS JOIN t2 + WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z + WHERE Z.value==t2.id); +} {1 {{"items":[3,5]}} 3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}}} +do_execsql_test json-13.110 { + SELECT * FROM t2 CROSS JOIN t1 + WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z + WHERE Z.value==t2.id); +} {3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}} 1 {{"items":[3,5]}}} finish_test
diff --git a/third_party/sqlite/src/test/kvtest.c b/third_party/sqlite/src/test/kvtest.c index 54150f4..71ac1487 100644 --- a/third_party/sqlite/src/test/kvtest.c +++ b/third_party/sqlite/src/test/kvtest.c
@@ -132,7 +132,9 @@ # define access _access #endif -#include <stdint.h> +#if !defined(_MSC_VER) +# include <stdint.h> +#endif /* ** The following macros are used to cast pointers to integers and @@ -557,10 +559,10 @@ iKey/10000, (iKey/100)%100, iKey%100); } out = fopen(zFN, "wb"); - nWrote = fwrite(pData, 1, nData, out); + nWrote = fwrite(pData, 1, (size_t)nData, out); fclose(out); printf("\r%s ", zTail); fflush(stdout); - if( nWrote!=nData ){ + if( nWrote!=(size_t)nData ){ fatalError("Wrote only %d of %d bytes to %s\n", (int)nWrote, nData, zFN); }
diff --git a/third_party/sqlite/src/test/malloc.test b/third_party/sqlite/src/test/malloc.test index 316267d..a74b2c94 100644 --- a/third_party/sqlite/src/test/malloc.test +++ b/third_party/sqlite/src/test/malloc.test
@@ -329,7 +329,7 @@ } } -if {$tcl_platform(platform)!="windows"} { +if {$tcl_platform(platform)!="windows" && [atomic_batch_write test.db]==0} { do_malloc_test 14 -tclprep { catch {db close} sqlite3 db2 test2.db
diff --git a/third_party/sqlite/src/test/malloc3.test b/third_party/sqlite/src/test/malloc3.test index 66e58b5..e0debb8b 100644 --- a/third_party/sqlite/src/test/malloc3.test +++ b/third_party/sqlite/src/test/malloc3.test
@@ -27,6 +27,17 @@ return } +# Do not run these tests if F2FS batch writes are supported. In this case, +# it is possible for a single DML statement in an implicit transaction +# to fail with SQLITE_NOMEM, but for the transaction to still end up +# committed to disk. Which confuses the tests in this module. +# +if {[atomic_batch_write test.db]} { + puts "Skipping malloc3 tests: atomic-batch support" + finish_test + return +} + # Do not run these tests with an in-memory journal. #
diff --git a/third_party/sqlite/src/test/memdb1.test b/third_party/sqlite/src/test/memdb1.test new file mode 100644 index 0000000..771d5e42 --- /dev/null +++ b/third_party/sqlite/src/test/memdb1.test
@@ -0,0 +1,163 @@ +# 2018-01-02 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is the "memdb" VFS +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix memdb1 +do_not_use_codec + +ifcapable !deserialize { + finish_test + return +} + +# Create a MEMDB and populate it with some dummy data. +# Then extract the database into the $::db1 variable. +# Verify that the size of $::db1 is the same as the size of +# the database. +# +unset -nocomplain db1 +unset -nocomplain sz1 +unset -nocomplain pgsz +do_test 100 { + db eval { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2); + } + set ::pgsz [db one {PRAGMA page_size}] + set ::sz1 [expr {$::pgsz*[db one {PRAGMA page_count}]}] + set ::db1 [db serialize] + expr {[string length $::db1]==$::sz1} +} 1 +set fd [open db1.db wb] +puts -nonewline $fd $db1 +close $fd + +# Create a new MEMDB and initialize it to the content of $::db1 +# Verify that the content is the same. +# +db close +sqlite3 db +db deserialize $db1 +do_execsql_test 110 { + SELECT * FROM t1; +} {1 2} + +# What happens when we try to VACUUM a MEMDB database? +# +do_execsql_test 120 { + PRAGMA auto_vacuum = off; + VACUUM; +} {} +do_execsql_test 130 { + CREATE TABLE t2(x, y); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) + INSERT INTO t2(x, y) SELECT x, randomblob(1000) FROM c; + DROP TABLE t2; + PRAGMA page_count; +} {116} +do_execsql_test 140 { + VACUUM; + PRAGMA page_count; +} {2} + +# Build a largish on-disk database and serialize it. Verify that the +# serialization works. +# +db close +forcedelete test.db +sqlite3 db test.db +do_execsql_test 200 { + CREATE TABLE t3(x, y); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<400) + INSERT INTO t3(x, y) SELECT x, randomblob(1000) FROM c; + PRAGMA quick_check; +} {ok} +set fd [open test.db rb] +unset -nocomplain direct +set direct [read $fd] +close $fd +do_test 210 { + string length [db serialize] +} [string length $direct] +do_test 220 { + db eval {ATTACH ':memory:' AS aux1} + db deserialize aux1 $::direct + db eval { + SELECT x, y FROM main.t3 EXCEPT SELECT x, y FROM aux1.t3; + } +} {} +unset -nocomplain direct + +# Do the same with a :memory: database. +# +db close +sqlite3 db :memory: +do_execsql_test 300 { + CREATE TABLE t3(x, y); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<400) + INSERT INTO t3(x, y) SELECT x, randomblob(1000) FROM c; + PRAGMA quick_check; +} {ok} +do_test 310 { + db eval {ATTACH ':memory:' AS aux1} + db deserialize aux1 [db serialize main] + db eval { + SELECT x, y FROM main.t3 EXCEPT SELECT x, y FROM aux1.t3; + } +} {} + +# Deserialize an empty database +# +db close +sqlite3 db +db deserialize {} +do_execsql_test 400 { + PRAGMA integrity_check; +} {ok} +do_execsql_test 410 { + CREATE TABLE t4(a,b); + INSERT INTO t4 VALUES('hello','world!'); + PRAGMA integrity_check; + SELECT * FROM t4; +} {ok hello world!} + +# Deserialize something that is not a database. +# +db close +sqlite3 db +do_test 500 { + set rc [catch {db deserialize not-a-database} msg] + lappend rc $msg +} {0 {}} +do_catchsql_test 510 { + PRAGMA integrity_check; +} {1 {file is not a database}} + +# Abuse the serialize and deserialize commands. Make sure errors are caught. +# +do_test 600 { + set rc [catch {db deserialize} msg] + lappend rc $msg +} {1 {wrong # args: should be "db deserialize ?DATABASE? VALUE"}} +do_test 610 { + set rc [catch {db deserialize a b c} msg] + lappend rc $msg +} {1 {wrong # args: should be "db deserialize ?DATABASE? VALUE"}} +do_test 620 { + set rc [catch {db serialize a b} msg] + lappend rc $msg +} {1 {wrong # args: should be "db serialize ?DATABASE?"}} + +finish_test
diff --git a/third_party/sqlite/src/test/misc7.test b/third_party/sqlite/src/test/misc7.test index b69b8f1..e4790ff 100644 --- a/third_party/sqlite/src/test/misc7.test +++ b/third_party/sqlite/src/test/misc7.test
@@ -43,15 +43,17 @@ # Try to open a file with a directory where its journal file should be. # -do_test misc7-5 { - delete_file mydir - file mkdir mydir-journal - sqlite3 db2 ./mydir - catchsql { - CREATE TABLE abc(a, b, c); - } db2 -} {1 {unable to open database file}} -db2 close +if {[atomic_batch_write test.db]==0} { + do_test misc7-5 { + delete_file mydir + file mkdir mydir-journal + sqlite3 db2 ./mydir + catchsql { + CREATE TABLE abc(a, b, c); + } db2 + } {1 {unable to open database file}} + db2 close +} #-------------------------------------------------------------------- # The following tests, misc7-6.* test the libraries behaviour when @@ -522,7 +524,9 @@ catch { db close } forcedelete test.db -if {$::tcl_platform(platform)=="unix"} { +if {$::tcl_platform(platform)=="unix" + && [atomic_batch_write test.db]==0 +} { reset_db do_execsql_test 23.0 { CREATE TABLE t1(x, y);
diff --git a/third_party/sqlite/src/test/misc8.test b/third_party/sqlite/src/test/misc8.test index dcc6d21e2..f7d17928 100644 --- a/third_party/sqlite/src/test/misc8.test +++ b/third_party/sqlite/src/test/misc8.test
@@ -57,6 +57,10 @@ ORDER BY rowid; } {1 {abort due to ROLLBACK}} +do_catchsql_test misc8-1.8 { + PRAGMA empty_result_callbacks = 1; + SELECT eval('SELECT * FROM t1 WHERE 1 = 0;'); +} {0 {{}}} reset_db
diff --git a/third_party/sqlite/src/test/nockpt.test b/third_party/sqlite/src/test/nockpt.test index bd3953f..8e6afef 100644 --- a/third_party/sqlite/src/test/nockpt.test +++ b/third_party/sqlite/src/test/nockpt.test
@@ -61,6 +61,87 @@ do_execsql_test 1.14 { PRAGMA main.journal_mode = delete } {delete} do_test 1.15 { file exists test.db-wal } {0} +if {$::tcl_platform(platform)!="windows"} { +#------------------------------------------------------------------------- +# Test an unusual scenario: +# +# 1. A wal mode db is opened and written. Then sqlite3_close_v2() used +# to close the db handle while there is still an unfinalized +# statement (so the db handle stays open). +# +# 2. The db, wal and *-shm files are deleted from the file system. +# +# 3. Another connection creates a new wal mode db at the same file-system +# location as the previous one. +# +# 4. The statement left unfinalized in (1) is finalized. +# +# The test is to ensure that the connection left open in step (1) does +# not try to delete the wal file from the file-system as part of step +# 4. +# +reset_db +db close + +# Open a connection on a wal database. Write to it a bit. Then prepare +# a statement and call sqlite3_close_v2() (so that the statement handle +# holds the db connection open). +# +set ::db1 [sqlite3_open_v2 test.db SQLITE_OPEN_READWRITE ""] +do_test 2.0 { + lindex [ + sqlite3_exec $::db1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x PRIMARY KEY, y UNIQUE, z); + INSERT INTO t1 VALUES(1, 2, 3); + PRAGMA wal_checkpoint; + }] 0 +} {0} +set ::stmt [sqlite3_prepare $::db1 "SELECT * FROM t1" -1 dummy] +sqlite3_close_v2 $::db1 + +# Delete the database, wal and shm files. +# +forcedelete test.db test.db-wal test.db-shm + +# Open and populate a new database file at the same file-system location +# as the one just deleted. Contrive a partial checkpoint on it. +# +sqlite3 db test.db +sqlite3 db2 test.db +do_execsql_test 2.1 { + PRAGMA auto_vacuum=OFF; + PRAGMA journal_mode = wal; + CREATE TABLE y1(a PRIMARY KEY, b UNIQUE, c); + INSERT INTO y1 VALUES('a', 'b', 'c'); + INSERT INTO y1 VALUES('d', 'e', 'f'); +} {wal} +do_execsql_test -db db2 2.2 { + BEGIN; + SELECT * FROM y1; +} {a b c d e f} +do_execsql_test 2.3 { + UPDATE y1 SET c='g' WHERE a='d'; + PRAGMA wal_checkpoint; +} {0 11 10} +do_execsql_test -db db2 2.4 { + COMMIT +} + +# Finalize the statement handle, causing the first connection to be +# closed. Test that this has not corrupted the database file by +# deleting the new wal file from the file-system. If it has, this +# test should fail with an IO or corruption error. +# +do_test 2.5 { + sqlite3_finalize $::stmt + sqlite3 db3 test.db + execsql { + PRAGMA integrity_check; + SELECT * FROM y1; + } db3 +} {ok a b c d e g} +} finish_test
diff --git a/third_party/sqlite/src/test/normalize.test b/third_party/sqlite/src/test/normalize.test new file mode 100644 index 0000000..8932650 --- /dev/null +++ b/third_party/sqlite/src/test/normalize.test
@@ -0,0 +1,72 @@ +# 2018-01-08 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Tests for the sqlite3_normalize() extension function. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix normalize + +foreach {tnum sql norm} { + 100 + {SELECT * FROM t1 WHERE a IN (1) AND b=51.42} + {select*from t1 where a in(?,?,?)and b=?;} + + 110 + {SELECT a, b+15, c FROM t1 WHERE d NOT IN (SELECT x FROM t2);} + {select a,b+?,c from t1 where d not in(select x from t2);} + + 120 + { SELECT NULL, b FROM t1 -- comment text + WHERE d IN (WITH t(a) AS (VALUES(5)) /* CTE */ + SELECT a FROM t) + OR e='hello'; + } + {select?,b from t1 where d in(with t(a)as(values(?))select a from t)or e=?;} + + 121 + {/*Initial comment*/ + -- another comment line + SELECT NULL /* comment */ , b FROM t1 -- comment text + WHERE d IN (WITH t(a) AS (VALUES(5)) /* CTE */ + SELECT a FROM t) + OR e='hello'; + } + {select?,b from t1 where d in(with t(a)as(values(?))select a from t)or e=?;} + + 130 + {/* Query containing parameters */ + SELECT x,$::abc(15),y,@abc,z,?99,w FROM t1 /* Trailing comment */} + {select x,?,y,?,z,?,w from t1;} + + 140 + {/* Long list on the RHS of IN */ + SELECT 15 IN (1,2,3,(SELECT * FROM t1),'xyz',x'abcd',22*(x+5),null);} + {select?in(?,?,?);} + + 150 + {SELECT x'abc'; -- illegal token} + {} + + 160 + {SELECT a,NULL,b FROM t1 WHERE c IS NOT NULL or D is null or e=5} + {select a,?,b from t1 where c is not null or d is null or e=?;} + + 170 + {/* IN list exactly 5 bytes long */ + SELECT * FROM t1 WHERE x IN (1,2,3);} + {select*from t1 where x in(?,?,?);} +} { + do_test $tnum [list sqlite3_normalize $sql] $norm +} + +finish_test
diff --git a/third_party/sqlite/src/test/notnull.test b/third_party/sqlite/src/test/notnull.test index 23fd33d..32d95ea 100644 --- a/third_party/sqlite/src/test/notnull.test +++ b/third_party/sqlite/src/test/notnull.test
@@ -561,4 +561,49 @@ execsql { SELECT * FROM t1 } } {1 2} +#------------------------------------------------------------------------- +# Check that UNIQUE NOT NULL indexes are always recognized as such. +# +proc uses_op_next {sql} { + db eval "EXPLAIN $sql" a { + if {$a(opcode)=="Next"} { return 1 } + } + return 0 +} + +proc do_uses_op_next_test {tn sql res} { + uplevel [list do_test $tn [list uses_op_next $sql] $res] +} + +reset_db +do_execsql_test notnull-6.0 { + CREATE TABLE t1(a UNIQUE); + CREATE TABLE t2(a NOT NULL UNIQUE); + CREATE TABLE t3(a UNIQUE NOT NULL); + CREATE TABLE t4(a NOT NULL); + CREATE UNIQUE INDEX t4a ON t4(a); + + CREATE TABLE t5(a PRIMARY KEY); + CREATE TABLE t6(a PRIMARY KEY NOT NULL); + CREATE TABLE t7(a NOT NULL PRIMARY KEY); + CREATE TABLE t8(a PRIMARY KEY) WITHOUT ROWID; + + CREATE TABLE t9(a PRIMARY KEY UNIQUE NOT NULL); + CREATE TABLE t10(a UNIQUE PRIMARY KEY NOT NULL); +} + +do_uses_op_next_test notnull-6.1 "SELECT * FROM t1 WHERE a IS ?" 1 +do_uses_op_next_test notnull-6.2 "SELECT * FROM t2 WHERE a IS ?" 0 +do_uses_op_next_test notnull-6.3 "SELECT * FROM t3 WHERE a IS ?" 0 +do_uses_op_next_test notnull-6.4 "SELECT * FROM t4 WHERE a IS ?" 0 + +do_uses_op_next_test notnull-6.5 "SELECT * FROM t5 WHERE a IS ?" 1 +do_uses_op_next_test notnull-6.6 "SELECT * FROM t6 WHERE a IS ?" 0 +do_uses_op_next_test notnull-6.7 "SELECT * FROM t7 WHERE a IS ?" 0 +do_uses_op_next_test notnull-6.8 "SELECT * FROM t8 WHERE a IS ?" 0 + +do_uses_op_next_test notnull-6.9 "SELECT * FROM t8 WHERE a IS ?" 0 +do_uses_op_next_test notnull-6.10 "SELECT * FROM t8 WHERE a IS ?" 0 + finish_test +
diff --git a/third_party/sqlite/src/test/optfuzz-db01.c b/third_party/sqlite/src/test/optfuzz-db01.c new file mode 100644 index 0000000..1cd3867 --- /dev/null +++ b/third_party/sqlite/src/test/optfuzz-db01.c
@@ -0,0 +1,948 @@ +/* content of file testdb01.db */ +unsigned char data001[] = { + 83, 81, 76,105,116,101, 32,102,111,114,109, 97,116, 32, 51, 0, 2, 0, 1, + 1, 0, 64, 32, 32, 0, 0, 0, 2, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 31, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 46, 32,152, 5, 0, 0, 0, 7, 1,221, 0, 0, 0, 0, 35, 1,251, + 1,246, 1,241, 1,236, 1,231, 1,226, 1,221, 84, 4, 7, 23, 17, 17, 1, + 129, 19,116, 97, 98,108,101,116, 52,116, 52, 5, 67, 82, 69, 65, 84, 69, 32, + 84, 65, 66, 76, 69, 32,116, 52, 40, 97, 32, 73, 78, 84, 32, 85, 78, 73, 81, + 85, 69, 32, 78, 79, 84, 32, 78, 85, 76, 76, 44, 32, 98, 32, 73, 78, 84, 32, + 85, 78, 73, 81, 85, 69, 32, 78, 79, 84, 32, 78, 85, 76, 76, 44, 99, 44,100, + 44,101, 41, 35, 6, 6, 23, 55, 17, 1, 0,105,110,100,101,120,115,113,108, + 105,116,101, 95, 97,117,116,111,105,110,100,101,120, 95,116, 52, 95, 50,116, + 52, 7, 35, 5, 6, 23, 55, 17, 1, 0,105,110,100,101,120,115,113,108,105, + 116,101, 95, 97,117,116,111,105,110,100,101,120, 95,116, 52, 95, 49,116, 52, + 6, 42, 3, 6, 23, 17, 17, 1, 65,116, 97, 98,108,101,116, 51,116, 51, 4, + 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 51, 40, 97, 44, 98, + 44, 99, 44,100, 44,101, 41, 95, 2, 7, 23, 17, 17, 1,129, 41,116, 97, 98, + 108,101,116, 50,116, 50, 3, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, + 32,116, 50, 40, 97, 32, 73, 78, 84, 44, 32, 98, 32, 73, 78, 84, 44, 32, 99, + 32, 73, 78, 84, 44,100, 32, 73, 78, 84, 44,101, 32, 73, 78, 84, 44, 80, 82, + 73, 77, 65, 82, 89, 32, 75, 69, 89, 40, 98, 44, 97, 41, 41, 87, 73, 84, 72, + 79, 85, 84, 32, 82, 79, 87, 73, 68, 83, 1, 7, 23, 17, 17, 1,129, 17,116, + 97, 98,108,101,116, 49,116, 49, 2, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, + 76, 69, 32,116, 49, 40, 97, 32, 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, + 77, 65, 0, 0, 0, 34, 32, 0, 0, 0, 33, 29, 0, 0, 0, 32, 26, 0, 0, + 0, 31, 23, 0, 0, 0, 30, 19, 0, 0, 0, 11, 14, 0, 0, 0, 9, 7, 5, + 0, 0, 0, 1, 1,251, 0, 0, 0, 0, 16, 1,251, 1,195, 1,180, 1,166, + 1,151, 1,136, 1,121, 1,105, 1, 91, 1, 76, 1, 61, 1, 46, 1, 29, 1, + 14, 0,252, 0,238, 0,224, 0,209, 0,194, 0,177, 0,157, 0,143, 0,128, + 0,110, 0, 94, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 28, 6, 0, 1, 1, 1, 23, 17, 67, 31,119,111,114,107,115, 14, 27, + 6, 0, 1, 1, 1, 23, 22, 71, 3, 97,110,103,101,108, 16, 26, 6, 0, 1, + 1, 1, 27, 40, 98, 17,109,111,114,110,105,110,103, 13, 25, 6, 0, 1, 1, + 1, 21, 10, 7, 19,103,111,110,101, 12, 24, 6, 0, 1, 1, 9, 21, 43, 46, + 119, 97,121,115, 18, 23, 6, 0, 1, 1, 1, 31, 6, 37, 31,115, 97, 99,114, + 105,102,105, 99,101, 15, 22, 6, 0, 1, 1, 1, 25, 45, 71, 28,116,104,111, + 117,103,104, 13, 21, 6, 0, 1, 1, 1, 21, 22, 92, 18,115,111,109,101, 13, + 20, 6, 0, 9, 1, 1, 23, 2, 45, 97, 98,111,118,101, 12, 19, 6, 0, 1, + 1, 8, 21, 4, 58,119, 97,121,115, 12, 18, 6, 0, 1, 1, 1, 19, 44, 19, + 43,119, 97,114, 16, 17, 6, 0, 1, 1, 1, 27, 29, 74, 36, 98,101,116,119, + 101,101,110, 13, 16, 6, 0, 1, 1, 1, 21, 44, 52, 19,112,111,111,114, 15, + 15, 6, 0, 1, 1, 1, 25, 6, 3, 11,116,101,109,112,108,101, 13, 14, 6, + 0, 1, 1, 1, 21, 35, 48, 27,100,105,101,100, 13, 13, 6, 0, 1, 1, 1, + 21, 4, 21, 39,100,111,116,104, 13, 12, 6, 0, 1, 1, 1, 21, 4, 38, 36, + 115,101,110,100, 12, 11, 6, 0, 1, 1, 1, 19, 13, 48, 22,115,105,120, 14, + 10, 6, 0, 1, 1, 1, 23, 41, 89, 14,115,101,114,118,101, 13, 9, 6, 0, + 8, 1, 1, 23, 16, 50, 98,101,103, 97,116, 13, 8, 6, 0, 1, 1, 1, 21, + 42, 49, 34,115,101,110,100, 13, 7, 6, 0, 1, 1, 1, 21, 21, 91, 38,110, + 101, 97,114, 12, 6, 6, 0, 1, 1, 1, 19, 2, 37, 11, 99, 97,110, 13, 5, + 6, 0, 1, 1, 1, 21, 25, 27, 28,103,111,110,101, 13, 4, 6, 0, 1, 1, + 1, 21, 41, 32, 35,110,101, 97,114, 14, 3, 6, 0, 1, 1, 1, 23, 32, 24, + 26,115,101,114,118,101, 13, 2, 6, 0, 1, 1, 1, 21, 45, 14, 39,115, 97, + 118,101, 13, 1, 6, 0, 1, 1, 1, 21, 40, 68, 0, 0, 0, 15, 28, 2, 0, + 0, 0, 1, 1,238, 0, 0, 0, 0, 22, 1,238, 1,197, 1,181, 1,166, 1, + 151, 1,137, 1,121, 1,104, 1, 84, 1, 73, 1, 59, 1, 41, 1, 26, 1, 11, + 0,253, 0,238, 0,223, 0,207, 0,191, 0,175, 0,159, 0,144, 0,129, 0, + 113, 0, 97, 0, 82, 0, 68, 0, 0, 13, 6, 1, 1, 1, 1, 19, 26, 34, 15, + 20, 97,114,107, 14, 6, 1, 1, 1, 1, 21, 25, 5, 27, 28,103,111,110,101, + 15, 6, 1, 1, 1, 1, 23, 22, 47, 16, 40, 97,110,103,101,114, 15, 6, 1, + 1, 1, 1, 23, 22, 27, 71, 3, 97,110,103,101,108, 14, 6, 1, 1, 1, 1, + 21, 22, 21, 92, 18,115,111,109,101, 14, 6, 1, 1, 1, 1, 21, 21, 7, 91, + 38,110,101, 97,114, 15, 6, 1, 1, 1, 1, 23, 20, 42, 18, 5, 98,101,103, + 97,116, 15, 6, 1, 1, 1, 1, 23, 17, 37, 66, 18,100,119,101,108,116, 15, + 6, 1, 1, 1, 1, 23, 17, 28, 67, 31,119,111,114,107,115, 15, 6, 1, 1, + 1, 8, 25, 16, 32, 7,112,108, 97, 99,101,115, 14, 6, 1, 1, 1, 1, 21, + 16, 30, 81, 25,119, 97,108,107, 14, 6, 1, 1, 1, 1, 21, 14, 40, 30, 26, + 115,101,110,100, 13, 6, 1, 1, 1, 1, 19, 13, 11, 48, 22,115,105,120, 14, + 6, 1, 1, 1, 1, 21, 10, 38, 97, 34,115,104,101,119, 14, 6, 1, 1, 1, + 1, 21, 10, 25, 7, 19,103,111,110,101, 17, 6, 1, 1, 1, 1, 27, 9, 50, + 92, 29,116,104,101,114,101,105,110, 13, 6, 1, 1, 1, 1, 19, 9, 49, 51, + 38,111,105,108, 10, 6, 1, 1, 1, 1, 0, 7, 33, 72, 31, 19, 6, 1, 1, + 1, 1, 31, 6, 23, 37, 31,115, 97, 99,114,105,102,105, 99,101, 16, 6, 1, + 1, 1, 1, 25, 6, 15, 3, 11,116,101,109,112,108,101, 15, 6, 1, 1, 1, + 1, 23, 5, 43, 23, 41, 98,101,103, 97,116, 13, 6, 1, 1, 1, 8, 21, 4, + 19, 58,119, 97,121,115, 14, 6, 1, 1, 1, 1, 21, 4, 13, 21, 39,100,111, + 116,104, 14, 6, 1, 1, 1, 1, 21, 4, 12, 38, 36,115,101,110,100, 15, 6, + 1, 1, 1, 1, 23, 3, 39, 21, 45, 98,101,103, 97,116, 13, 6, 1, 1, 1, + 1, 19, 2, 6, 37, 11, 99, 97,110, 14, 6, 9, 1, 1, 1, 23, 20, 2, 45, + 97, 98,111,118,101, 14, 6, 8, 1, 1, 1, 23, 36, 52, 17, 99,104, 0, 0, + 0, 21, 13, 6, 1, 1, 1, 1, 19, 26, 34, 15, 20, 97,114,107, 13, 0, 0, + 0, 35, 0, 92, 0, 1,244, 1,232, 1,216, 1,204, 1,186, 1,171, 1,160, + 1,149, 1,138, 1,128, 1,117, 1,106, 1, 92, 1, 76, 1, 65, 1, 49, 1, + 32, 1, 21, 1, 10, 0,255, 0,241, 0,225, 0,214, 0,203, 0,192, 0,180, + 0,168, 0,156, 0,144, 0,132, 0,124, 0,116, 0,108, 0,100, 0, 92, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 35, 6, 0, 0, 0, + 0, 0, 6, 34, 6, 0, 0, 0, 0, 0, 6, 33, 6, 0, 0, 0, 0, 0, 6, + 32, 6, 0, 0, 0, 0, 0, 6, 31, 6, 0, 0, 0, 0, 0, 10, 30, 6, 1, + 1, 1, 1, 0, 48, 37, 93, 7, 10, 29, 6, 1, 1, 1, 1, 0, 28, 17, 67, + 31, 10, 28, 6, 1, 1, 1, 1, 0, 22, 45, 71, 28, 10, 27, 6, 1, 1, 1, + 1, 0, 12, 4, 38, 36, 10, 26, 6, 1, 1, 1, 1, 0, 49, 9, 51, 38, 9, + 25, 6, 1, 1, 1, 0, 0, 17, 29, 74, 9, 24, 6, 1, 1, 1, 0, 0, 47, + 22, 16, 9, 23, 6, 1, 1, 1, 0, 0, 32, 16, 7, 14, 22, 6, 1, 1, 1, + 0, 23, 42, 20, 18, 98,101,103, 97,116, 12, 21, 6, 1, 1, 1, 0, 19, 34, + 26, 15, 97,114,107, 9, 20, 6, 1, 1, 0, 1, 0, 49, 9, 38, 9, 19, 6, + 1, 1, 0, 1, 0, 44, 48, 9, 9, 18, 6, 1, 1, 0, 1, 0, 21, 22, 18, + 15, 17, 6, 1, 1, 0, 1, 25, 35, 38, 22, 99,117, 98,105,116,115, 14, 16, + 6, 1, 1, 0, 1, 23, 37, 17, 18,100,119,101,108,116, 9, 15, 6, 1, 0, + 1, 1, 0, 49, 51, 38, 14, 14, 6, 1, 0, 1, 1, 23, 10, 89, 14,115,101, + 114,118,101, 12, 13, 6, 9, 0, 1, 1, 21, 68, 32,100,111,116,104, 9, 12, + 6, 1, 0, 1, 1, 0, 47, 16, 40, 9, 11, 6, 1, 0, 1, 1, 0, 25, 7, + 19, 8, 10, 6, 0, 1, 1, 8, 0, 16, 7, 9, 9, 6, 0, 1, 1, 1, 0, + 16, 81, 25, 9, 8, 6, 0, 1, 1, 1, 0, 7, 72, 31, 9, 7, 6, 0, 1, + 1, 1, 0, 6, 37, 31, 13, 6, 6, 0, 1, 1, 1, 21, 21, 91, 38,110,101, + 97,114, 16, 5, 6, 1, 1, 1, 1, 25, 15, 6, 3, 11,116,101,109,112,108, + 101, 10, 4, 6, 1, 1, 1, 1, 0, 21, 22, 92, 18, 14, 3, 6, 1, 1, 1, + 1, 21, 4, 41, 32, 35,110,101, 97,114, 10, 2, 6, 1, 1, 1, 1, 0, 46, + 28, 88, 22, 10, 1, 6, 1, 1, 1, 1, 0, 17, 29, 74, 36, 13, 0, 0, 0, + 15, 1, 71, 0, 1,243, 1,230, 1,217, 1,204, 1,191, 1,179, 1,167, 1, + 155, 1,143, 1,131, 1,119, 1,107, 1, 95, 1, 83, 1, 71, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 15, 6, 1, 1, 1, 1, 0, 48, 37, 93, 7, 10, 14, 6, 1, 1, 1, 1, + 0, 22, 45, 71, 28, 10, 13, 6, 1, 1, 1, 1, 0, 12, 4, 38, 36, 10, 12, + 6, 1, 1, 1, 0, 1, 32, 16, 7, 79, 10, 11, 6, 1, 1, 1, 0, 1, 42, + 20, 18, 19, 10, 10, 6, 1, 1, 1, 0, 1, 34, 26, 15, 13, 10, 9, 6, 1, + 1, 0, 1, 1, 49, 9, 38, 97, 10, 8, 6, 1, 1, 0, 1, 1, 44, 48, 9, + 90, 10, 7, 6, 1, 1, 0, 1, 1, 35, 38, 22, 33, 10, 6, 6, 1, 1, 0, + 1, 1, 37, 17, 18, 18, 11, 5, 6, 1, 1, 1, 1, 1, 15, 6, 3, 11, 43, + 11, 4, 6, 1, 1, 1, 1, 1, 21, 22, 92, 18, 62, 11, 3, 6, 1, 1, 1, + 1, 1, 4, 41, 32, 35, 36, 11, 2, 6, 1, 1, 1, 1, 1, 46, 28, 88, 22, + 77, 11, 1, 6, 1, 1, 1, 1, 1, 17, 29, 74, 36, 61, 10, 0, 0, 0, 15, + 1,167, 0, 1,250, 1,244, 1,238, 1,233, 1,227, 1,221, 1,215, 1,209, + 1,203, 1,197, 1,191, 1,185, 1,179, 1,173, 1,167, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 3, 1, 1, 49, 9, 5, 3, 1, 1, 48, 15, 5, 3, 1, 1, 46, 2, 5, + 3, 1, 1, 44, 8, 5, 3, 1, 1, 42, 11, 5, 3, 1, 1, 37, 6, 5, 3, + 1, 1, 35, 7, 5, 3, 1, 1, 34, 10, 5, 3, 1, 1, 32, 12, 5, 3, 1, + 1, 22, 14, 5, 3, 1, 1, 21, 4, 4, 3, 1, 9, 17, 5, 3, 1, 1, 15, + 5, 5, 3, 1, 1, 12, 13, 5, 3, 1, 1, 4, 3, 10, 0, 0, 0, 15, 1, + 167, 0, 1,250, 1,244, 1,238, 1,232, 1,226, 1,220, 1,214, 1,208, 1, + 202, 1,197, 1,191, 1,185, 1,179, 1,173, 1,167, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 3, 1, 1, 48, 8, 5, 3, 1, 1, 45, 14, 5, 3, 1, 1, 41, 3, 5, 3, + 1, 1, 38, 7, 5, 3, 1, 1, 37, 15, 4, 3, 1, 9, 29, 5, 3, 1, 1, + 28, 2, 5, 3, 1, 1, 26, 10, 5, 3, 1, 1, 22, 4, 5, 3, 1, 1, 20, + 11, 5, 3, 1, 1, 17, 6, 5, 3, 1, 1, 16, 12, 5, 3, 1, 1, 9, 9, + 5, 3, 1, 1, 6, 5, 5, 3, 1, 1, 4, 13, 5, 0, 0, 0, 2, 1,246, + 0, 0, 0, 0, 27, 1,251, 1,246, 1,168, 1,148, 1,130, 1,107, 1, 86, + 1, 65, 1, 44, 1, 27, 1, 14, 0,250, 0,224, 0,205, 0,184, 0,165, 0, + 145, 0,123, 0,106, 0, 86, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 23, 6, 0, 23, 1, 1, 21,107,110,111,119,110, 52, 19,112, + 111,111,114, 18, 22, 6, 0, 23, 1, 1, 23, 97, 98,111,118,101, 24, 26,115, + 101,114,118,101, 15, 21, 6, 0, 19, 1, 1, 21,119, 97,114, 52, 19,112,111, + 111,114, 20, 20, 6, 0, 27, 1, 8, 25,110,111,116,104,105,110,103, 7,112, + 108, 97, 99,101,115, 18, 19, 6, 0, 23, 1, 1, 23, 98,101,103, 97,116, 90, + 27,116,114,117,116,104, 17, 18, 6, 0, 23, 1, 1, 21,100,119,101,108,116, + 21, 39,100,111,116,104, 19, 17, 6, 0, 27, 1, 1, 21,109,111,114,110,105, + 110,103, 52, 19,112,111,111,114, 17, 16, 6, 0, 21, 1, 1, 23,115,104,101, + 119, 90, 27,116,114,117,116,104, 24, 15, 6, 0, 27, 1, 1, 31,116,104,101, + 114,101,105,110, 37, 31,115, 97, 99,114,105,102,105, 99,101, 18, 14, 6, 0, + 23, 1, 8, 25,115,109,111,116,101, 7,112,108, 97, 99,101,115, 11, 13, 6, + 0, 19, 1, 1, 0, 97,114,107, 72, 31, 15, 12, 6, 0, 21, 1, 8, 21,119, + 105,110,101, 58,119, 97,121,115, 19, 11, 6, 0, 21, 1, 1, 27,115,111,109, + 101, 98, 17,109,111,114,110,105,110,103, 19, 10, 6, 0, 27, 1, 1, 21, 98, + 101,116,119,101,101,110, 92, 18,115,111,109,101, 19, 9, 6, 0, 21, 1, 1, + 27,115, 97,118,101, 74, 36, 98,101,116,119,101,101,110, 21, 8, 6, 0, 25, + 1, 1, 27,116,104,111,117,103,104, 98, 17,109,111,114,110,105,110,103, 16, + 7, 6, 0, 21, 1, 1, 21,115,101,110,100, 49, 34,115,101,110,100, 18, 6, + 6, 0, 25, 1, 1, 21,119,105,115,100,111,109, 38, 36,115,101,110,100, 16, + 5, 6, 0, 23, 1, 9, 21, 97,110,103,101,114, 46,119, 97,121,115, 14, 4, + 6, 0, 19, 1, 1, 19, 99, 97,110, 19, 43,119, 97,114, 16, 3, 6, 0, 23, + 1, 1, 19,111,102,102,101,114, 48, 22,115,105,120, 16, 2, 6, 0, 23, 1, + 8, 21,119,111,114,107,115, 58,119, 97,121,115, 16, 1, 6, 0, 23, 1, 1, + 19, 0, 0, 0, 26, 45, 0, 0, 0, 25, 23, 13, 0, 0, 0, 7, 0, 48, 0, + 1,171, 1, 74, 1, 30, 0,126, 0,249, 0,212, 0, 48, 0, 81, 0, 0, 84, + 4, 7, 23, 17, 17, 1,129, 19,116, 97, 98,108,101,116, 52,116, 52, 5, 67, + 82, 69, 76, 7, 7, 23, 17, 17, 1,129, 3,116, 97, 98,108,101,116, 53,116, + 53, 8, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 53, 40, 97, + 32, 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, + 89, 44, 32, 98, 32, 84, 69, 88, 84, 32, 85, 78, 73, 81, 85, 69, 44, 99, 44, + 100, 44,101, 41, 84, 4, 7, 23, 17, 17, 1,129, 19,116, 97, 98,108,101,116, + 52,116, 52, 5, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 52, + 40, 97, 32, 73, 78, 84, 32, 85, 78, 73, 81, 85, 69, 32, 78, 79, 84, 32, 78, + 85, 76, 76, 44, 32, 98, 32, 73, 78, 84, 32, 85, 78, 73, 81, 85, 69, 32, 78, + 79, 84, 32, 78, 85, 76, 76, 44, 99, 44,100, 44,101, 41, 35, 6, 6, 23, 55, + 17, 1, 0,105,110,100,101,120,115,113,108,105,116,101, 95, 97,117,116,111, + 105,110,100,101,120, 95,116, 52, 95, 50,116, 52, 7, 35, 5, 6, 23, 55, 17, + 1, 0,105,110,100,101,120,115,113,108,105,116,101, 95, 97,117,116,111,105, + 110,100,101,120, 95,116, 52, 95, 49,116, 52, 6, 42, 3, 6, 23, 17, 17, 1, + 65,116, 97, 98,108,101,116, 51,116, 51, 4, 67, 82, 69, 65, 84, 69, 32, 84, + 65, 66, 76, 69, 32,116, 51, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 95, + 2, 7, 23, 17, 17, 1,129, 41,116, 97, 98,108,101,116, 50,116, 50, 3, 67, + 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 50, 40, 97, 32, 73, 78, + 84, 44, 32, 98, 32, 73, 78, 84, 44, 32, 99, 32, 73, 78, 84, 44,100, 32, 73, + 78, 84, 44,101, 32, 73, 78, 84, 44, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, + 89, 40, 98, 44, 97, 41, 41, 87, 73, 84, 72, 79, 85, 84, 32, 82, 79, 87, 73, + 68, 83, 1, 7, 23, 17, 17, 1,129, 17,116, 97, 98,108,101,116, 49,116, 49, + 2, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 49, 40, 97, 32, + 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, 89, + 44, 32, 98, 32, 73, 78, 84, 44, 32, 99, 32, 73, 78, 84, 44, 32,100, 32, 73, + 78, 84, 44, 32,101, 32, 73, 78, 84, 41, 2, 0, 0, 0, 1, 1,243, 0, 0, + 0, 0, 29, 1,243, 1,218, 1,209, 1,199, 1,187, 1,179, 1,169, 1,158, + 1,145, 1,136, 1,127, 1,117, 1,107, 1, 98, 1, 82, 1, 72, 1, 63, 1, + 51, 1, 42, 1, 30, 1, 20, 1, 12, 1, 3, 0,248, 0,239, 0,225, 0,216, + 0,207, 0,197, 0,188, 0,180, 0,170, 0,161, 0,152, 0,141, 0,129, 0, + 118, 0,106, 0, 97, 0, 0, 0, 0, 0, 0, 0, 8, 3, 21, 1,116,114,101, + 101, 49, 11, 3, 27, 1,116,104,121,115,101,108,102, 27, 10, 3, 25, 1,116, + 104,111,117,103,104, 8, 11, 3, 27, 1,116,104,101,114,101,105,110, 15, 10, + 3, 25, 1,116,101,109,112,108,101, 43, 8, 3, 21, 1,116,101,108,108, 25, + 8, 3, 21, 1,115,111,109,101, 11, 9, 3, 23, 1,115,109,111,116,101, 14, + 7, 3, 19, 1,115,105,120, 48, 8, 3, 21, 1,115,104,101,119, 16, 9, 3, + 23, 1,115,101,114,118,101, 37, 8, 3, 21, 1,115,101,110,100, 7, 8, 3, + 21, 1,115, 97,118,101, 9, 13, 3, 31, 1,115, 97, 99,114,105,102,105, 99, + 101, 24, 8, 3, 21, 1,112,111,111,114, 40, 10, 3, 25, 1,112,108, 97, 99, + 101,115, 28, 8, 3, 21, 1,112, 97,114,116, 30, 7, 3, 19, 1,111,105,108, + 46, 9, 3, 23, 1,111,102,102,101,114, 3, 11, 3, 27, 1,110,111,116,104, + 105,110,103, 20, 8, 3, 21, 1,110,101, 97,114, 36, 11, 3, 27, 1,109,111, + 114,110,105,110,103, 17, 8, 3, 21, 1,108,111,110,103, 35, 9, 3, 23, 1, + 107,110,111,119,110, 23, 15, 3, 35, 1,105,110,104, 97, 98,105,116, 97,110, + 116,115, 45, 8, 3, 21, 1,103,111,110,101, 32, 9, 3, 23, 1,102,114,117, + 105,116, 38, 9, 3, 23, 1,100,119,101,108,116, 18, 8, 3, 21, 1,100,111, + 116,104, 39, 8, 3, 21, 1,100,105,101,100, 47, 12, 3, 29, 1,100,101,112, + 97,114,116,101,100, 26, 10, 3, 25, 1, 99,117, 98,105,116,115, 33, 9, 3, + 23, 1, 99,104,105,108,100, 42, 7, 3, 19, 1, 99, 97,110, 4, 11, 3, 27, + 1, 98,101,116,119,101,101,110, 10, 9, 3, 23, 1, 98,101,103, 97,116, 19, + 8, 3, 21, 1, 98,101, 97,114, 29, 7, 3, 19, 1, 97,114,107, 13, 9, 3, + 23, 1, 97,110,103,101,114, 5, 9, 3, 23, 1, 97,110,103, 0, 0, 0, 28, + 8, 3, 21, 1,116,114,101,101, 49, 13, 1,104, 0, 7, 0, 24, 0, 1, 67, + 1, 13, 0,225, 0,177, 0,109, 1,171, 0, 24, 0, 0, 83, 14, 7, 21, 19, + 19, 8,129, 17,118,105,101,119,118, 50, 48,118, 50, 48, 67, 82, 69, 65, 84, + 69, 32, 86, 73, 69, 87, 32,118, 50, 48, 40, 97, 44, 98, 44, 99, 44,100, 44, + 101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44, + 100, 44,101, 32, 70, 82, 79, 77, 32,116, 50, 32, 87, 72, 69, 82, 69, 32, 97, + 60, 62, 50, 53, 66, 12, 6, 21, 19, 19, 8,113,118,105,101,119,118, 48, 48, + 118, 48, 48, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 48, 48, 40, + 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, + 84, 32, 49, 44, 49, 44, 49, 44, 49, 44, 39,111,110,101, 39, 46, 11, 6, 23, + 21, 17, 1, 69,105,110,100,101,120,116, 50,101,100,116, 50, 14, 67, 82, 69, + 65, 84, 69, 32, 73, 78, 68, 69, 88, 32,116, 50,101,100, 32, 79, 78, 32,116, + 50, 40,101, 44,100, 41, 42, 10, 6, 23, 19, 17, 1, 63,105,110,100,101,120, + 116, 49,101,116, 49, 13, 67, 82, 69, 65, 84, 69, 32, 73, 78, 68, 69, 88, 32, + 116, 49,101, 32, 79, 78, 32,116, 49, 40,101, 41, 52, 9, 6, 23, 21, 17, 1, + 81,105,110,100,101,120,116, 51,120, 49,116, 51, 12, 67, 82, 69, 65, 84, 69, + 32, 73, 78, 68, 69, 88, 32,116, 51,120, 49, 32, 79, 78, 32,116, 51, 40, 97, + 44, 98, 44, 99, 44,100, 44,101, 41, 35, 8, 6, 23, 55, 17, 1, 0,105,110, + 100,101,120,115,113,108,105,116,101, 95, 97,117,116,111,105,110,100,101,120, + 95,116, 53, 95, 49,116, 53, 10, 0, 0, 0, 67, 17, 17, 1,129, 3,116, 97, + 98,108,101,116, 53,116, 53, 8, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, + 69, 32,116, 53, 40, 97, 32, 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, 77, + 65, 82, 89, 32, 75, 69, 89, 44, 32, 98, 32, 84, 69, 88, 84, 32, 85, 78, 83, + 13, 7, 21, 19, 19, 8,129, 17,118,105,101,119,118, 49, 48,118, 49, 48, 67, + 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 49, 48, 40, 97, 44, 98, 44, + 99, 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, + 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 32, 87, 72, 69, + 82, 69, 32, 97, 60, 62, 50, 53, 2, 0, 0, 0, 1, 1,240, 0, 0, 0, 0, + 24, 1,240, 1,220, 1,211, 1,199, 1,187, 1,176, 1,164, 1,148, 1,133, + 1,116, 1, 99, 1, 86, 1, 67, 1, 55, 1, 43, 1, 31, 1, 18, 1, 5, 0, + 249, 0,236, 0,224, 0,209, 0,191, 0,174, 0,157, 0,145, 0,132, 0,120, + 0,108, 0, 95, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 7, 1, 0, + 1, 1, 0, 1, 49, 51, 38, 15, 12, 7, 1, 1, 1, 1, 0, 1, 48, 37, 93, + 7, 30, 11, 7, 1, 1, 1, 0, 0, 1, 47, 22, 16, 24, 11, 7, 1, 0, 1, + 1, 0, 1, 47, 16, 40, 12, 12, 7, 1, 1, 1, 1, 0, 1, 46, 28, 88, 22, + 2, 11, 7, 1, 1, 0, 1, 0, 1, 44, 48, 9, 19, 16, 7, 1, 1, 1, 0, + 23, 1, 42, 20, 18, 98,101,103, 97,116, 22, 16, 7, 1, 1, 0, 1, 23, 1, + 37, 17, 18,100,119,101,108,116, 16, 17, 7, 1, 1, 0, 1, 25, 1, 35, 38, + 22, 99,117, 98,105,116,115, 17, 14, 7, 1, 1, 1, 0, 19, 1, 34, 26, 15, + 97,114,107, 21, 11, 7, 1, 1, 1, 0, 0, 1, 32, 16, 7, 23, 12, 7, 1, + 1, 1, 1, 0, 1, 28, 17, 67, 31, 29, 11, 7, 1, 0, 1, 1, 0, 1, 25, + 7, 19, 11, 12, 7, 1, 1, 1, 1, 0, 1, 22, 45, 71, 28, 28, 12, 7, 1, + 1, 1, 1, 0, 1, 21, 22, 92, 18, 4, 11, 7, 1, 1, 0, 1, 0, 1, 21, + 22, 18, 18, 11, 7, 1, 1, 1, 1, 0, 9, 17, 29, 74, 36, 11, 7, 1, 1, + 1, 0, 0, 1, 17, 29, 74, 25, 18, 7, 1, 1, 1, 1, 25, 1, 15, 6, 3, + 11,116,101,109,112,108,101, 5, 12, 7, 1, 1, 1, 1, 0, 1, 12, 4, 38, + 36, 27, 16, 7, 1, 0, 1, 1, 23, 1, 10, 89, 14,115,101,114,118,101, 14, + 16, 7, 1, 1, 1, 1, 21, 1, 4, 41, 32, 35,110,101, 97,114, 3, 14, 7, + 9, 0, 1, 1, 21, 1, 68, 32,100,111,116,104, 13, 15, 7, 0, 1, 1, 1, + 21, 1, 21, 91, 38,110,101, 97,114, 6, 11, 7, 0, 1, 1, 1, 0, 1, 16, + 81, 25, 9, 10, 7, 0, 1, 1, 8, 0, 1, 16, 7, 10, 11, 7, 0, 1, 1, + 1, 0, 1, 7, 72, 31, 8, 11, 7, 0, 1, 1, 1, 0, 1, 6, 37, 31, 7, + 8, 7, 0, 0, 0, 0, 0, 1, 35, 8, 7, 0, 0, 0, 0, 0, 1, 34, 8, + 7, 0, 0, 0, 0, 0, 1, 33, 8, 7, 0, 0, 0, 23, 11, 7, 1, 0, 1, + 1, 0, 1, 49, 51, 38, 15, 2, 0, 0, 0, 1, 1,241, 0, 0, 0, 0, 18, + 1,241, 1,221, 1,211, 1,203, 1,193, 1,183, 1,173, 1,163, 1,151, 1, + 143, 1,133, 1,122, 1,109, 1,100, 1, 92, 1, 83, 1, 74, 1, 64, 1, 55, + 1, 46, 1, 34, 1, 22, 1, 13, 1, 4, 0,252, 0,241, 0,232, 0,218, 0, + 209, 0,200, 0,191, 0,182, 0,173, 0,163, 0,153, 0,144, 0,136, 0,127, + 0,116, 0,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 3, + 25, 1,116,101,109,112,108,101, 48, 10, 3, 25, 1,116,101,109,112,108,101, + 15, 8, 3, 21, 1,115,111,109,101, 21, 7, 3, 19, 1,115,105,120, 11, 8, + 3, 21, 1,115,104,101,119, 38, 9, 3, 23, 1,115,101,114,118,101, 10, 9, + 3, 23, 1,115,101,114,118,101, 3, 8, 3, 21, 1,115,101,110,100, 40, 8, + 3, 21, 1,115,101,110,100, 29, 8, 3, 21, 1,115,101,110,100, 12, 8, 3, + 21, 1,115,101,110,100, 8, 8, 3, 21, 1,115, 97,118,101, 2, 13, 3, 31, + 1,115, 97, 99,114,105,102,105, 99,101, 23, 8, 3, 21, 1,112,111,111,114, + 16, 10, 3, 25, 1,112,108, 97, 99,101,115, 32, 7, 3, 19, 1,111,105,108, + 49, 8, 3, 21, 1,110,101, 97,114, 7, 8, 3, 21, 1,110,101, 97,114, 4, + 11, 3, 27, 1,109,111,114,110,105,110,103, 41, 11, 3, 27, 1,109,111,114, + 110,105,110,103, 26, 8, 3, 21, 1,103,111,110,101, 25, 8, 3, 21, 1,103, + 111,110,101, 5, 9, 3, 23, 1,100,119,101,108,116, 37, 8, 3, 21, 1,100, + 111,116,104, 44, 8, 3, 21, 1,100,111,116,104, 13, 7, 3, 21, 9,100,111, + 116,104, 8, 3, 21, 1,100,105,101,100, 14, 12, 3, 29, 1,100,101,112, 97, + 114,116,101,100, 46, 10, 3, 25, 1, 99,117, 98,105,116,115, 35, 9, 3, 23, + 1, 99,104,105,108,100, 36, 7, 3, 19, 1, 99, 97,110, 6, 11, 3, 27, 1, + 98,101,116,119,101,101,110, 17, 9, 3, 23, 1, 98,101,103, 97,116, 43, 9, + 3, 23, 1, 98,101,103, 97,116, 42, 9, 3, 23, 1, 98,101,103, 97,116, 39, + 9, 3, 23, 1, 98,101,103, 97,116, 9, 7, 3, 19, 1, 97,114,107, 34, 9, + 3, 23, 1, 97,110,103,101,114, 47, 9, 3, 23, 1, 97,110,103,101,108, 27, + 9, 3, 23, 1, 97, 98,111,118,101, 45, 0, 0, 0, 17, 10, 3, 25, 1,116, + 101,109,112,108,101, 48, 2, 0, 0, 0, 1, 1,239, 0, 0, 0, 0, 20, 1, + 239, 1,206, 1,192, 1,180, 1,166, 1,152, 1,138, 1,125, 1,109, 1, 97, + 1, 84, 1, 69, 1, 52, 1, 39, 1, 26, 1, 14, 1, 1, 0,243, 0,230, 0, + 217, 0,201, 0,185, 0,172, 0,159, 0,147, 0,133, 0,120, 0,102, 0, 89, + 0, 76, 0, 0, 0, 0, 12, 5, 21, 1, 1, 1,115,101,110,100, 26, 14, 40, + 12, 5, 21, 1, 1, 1,115, 97,118,101, 39, 45, 2, 17, 5, 31, 1, 1, 1, + 115, 97, 99,114,105,102,105, 99,101, 31, 6, 23, 12, 5, 21, 1, 1, 1,112, + 111,111,114, 19, 44, 16, 13, 5, 25, 8, 1, 1,112,108, 97, 99,101,115, 16, + 32, 11, 5, 19, 1, 1, 1,111,105,108, 38, 9, 49, 12, 5, 21, 1, 1, 1, + 110,101, 97,114, 38, 21, 7, 12, 5, 21, 1, 1, 1,110,101, 97,114, 35, 41, + 4, 15, 5, 27, 1, 1, 1,109,111,114,110,105,110,103, 17, 40, 26, 15, 5, + 27, 1, 1, 1,109,111,114,110,105,110,103, 13, 46, 41, 12, 5, 21, 1, 1, + 1,103,111,110,101, 28, 25, 5, 12, 5, 21, 1, 1, 1,103,111,110,101, 19, + 10, 25, 13, 5, 23, 1, 1, 1,100,119,101,108,116, 18, 17, 37, 12, 5, 21, + 1, 1, 1,100,111,116,104, 39, 4, 13, 11, 5, 21, 1, 1, 9,100,111,116, + 104, 32, 40, 12, 5, 21, 1, 1, 1,100,111,116,104, 9, 48, 44, 12, 5, 21, + 1, 1, 1,100,105,101,100, 27, 35, 14, 16, 5, 29, 1, 1, 1,100,101,112, + 97,114,116,101,100, 22, 28, 46, 14, 5, 25, 1, 1, 1, 99,117, 98,105,116, + 115, 22, 38, 35, 12, 5, 23, 1, 8, 1, 99,104,105,108,100, 17, 36, 11, 5, + 19, 1, 1, 1, 99, 97,110, 11, 2, 6, 15, 5, 27, 1, 1, 1, 98,101,116, + 119,101,101,110, 36, 29, 17, 12, 5, 23, 1, 8, 1, 98,101,103, 97,116, 50, + 9, 13, 5, 23, 1, 1, 1, 98,101,103, 97,116, 45, 3, 39, 13, 5, 23, 1, + 1, 1, 98,101,103, 97,116, 41, 5, 43, 13, 5, 23, 1, 1, 1, 98,101,103, + 97,116, 5, 20, 42, 11, 5, 19, 1, 1, 1, 97,114,107, 20, 26, 34, 13, 5, + 23, 1, 1, 1, 97,110,103,101,114, 40, 22, 47, 13, 5, 23, 1, 1, 1, 97, + 110,103,101,108, 3, 22, 27, 12, 5, 23, 1, 9, 1, 97, 98,111,118,101, 45, + 20, 13, 5, 23, 1, 1, 1, 0, 0, 0, 19, 12, 5, 21, 1, 1, 1,115,101, + 110,100, 26, 14, 40, 13, 0, 0, 0, 28, 0, 78, 0, 1,241, 1,226, 1,210, + 1,195, 1,180, 1,166, 1,151, 1,136, 1,121, 1,105, 1, 91, 1, 76, 1, + 61, 1, 46, 1, 29, 1, 14, 0,252, 0,238, 0,224, 0,209, 0,194, 0,177, + 0,157, 0,143, 0,128, 0,110, 0, 94, 0, 78, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 14, 28, 6, 0, 1, 1, 1, 23, 17, 67, 31,119, + 111,114,107,115, 14, 27, 6, 0, 1, 1, 1, 23, 22, 71, 3, 97,110,103,101, + 108, 16, 26, 6, 0, 1, 1, 1, 27, 40, 98, 17,109,111,114,110,105,110,103, + 13, 25, 6, 0, 1, 1, 1, 21, 10, 7, 19,103,111,110,101, 12, 24, 6, 0, + 1, 1, 9, 21, 43, 46,119, 97,121,115, 18, 23, 6, 0, 1, 1, 1, 31, 6, + 37, 31,115, 97, 99,114,105,102,105, 99,101, 15, 22, 6, 0, 1, 1, 1, 25, + 45, 71, 28,116,104,111,117,103,104, 13, 21, 6, 0, 1, 1, 1, 21, 22, 92, + 18,115,111,109,101, 13, 20, 6, 0, 9, 1, 1, 23, 2, 45, 97, 98,111,118, + 101, 12, 19, 6, 0, 1, 1, 8, 21, 4, 58,119, 97,121,115, 12, 18, 6, 0, + 1, 1, 1, 19, 44, 19, 43,119, 97,114, 16, 17, 6, 0, 1, 1, 1, 27, 29, + 74, 36, 98,101,116,119,101,101,110, 13, 16, 6, 0, 1, 1, 1, 21, 44, 52, + 19,112,111,111,114, 15, 15, 6, 0, 1, 1, 1, 25, 6, 3, 11,116,101,109, + 112,108,101, 13, 14, 6, 0, 1, 1, 1, 21, 35, 48, 27,100,105,101,100, 13, + 13, 6, 0, 1, 1, 1, 21, 4, 21, 39,100,111,116,104, 13, 12, 6, 0, 1, + 1, 1, 21, 4, 38, 36,115,101,110,100, 12, 11, 6, 0, 1, 1, 1, 19, 13, + 48, 22,115,105,120, 14, 10, 6, 0, 1, 1, 1, 23, 41, 89, 14,115,101,114, + 118,101, 13, 9, 6, 0, 8, 1, 1, 23, 16, 50, 98,101,103, 97,116, 13, 8, + 6, 0, 1, 1, 1, 21, 42, 49, 34,115,101,110,100, 13, 7, 6, 0, 1, 1, + 1, 21, 21, 91, 38,110,101, 97,114, 12, 6, 6, 0, 1, 1, 1, 19, 2, 37, + 11, 99, 97,110, 13, 5, 6, 0, 1, 1, 1, 21, 25, 27, 28,103,111,110,101, + 13, 4, 6, 0, 1, 1, 1, 21, 41, 32, 35,110,101, 97,114, 14, 3, 6, 0, + 1, 1, 1, 23, 32, 24, 26,115,101,114,118,101, 13, 2, 6, 0, 1, 1, 1, + 21, 45, 14, 39,115, 97,118,101, 13, 1, 6, 0, 1, 1, 1, 21, 40, 68, 32, + 100,111,116,104, 13, 0, 0, 0, 22, 0,166, 0, 1,241, 1,226, 1,210, 1, + 194, 1,183, 1,169, 1,152, 1,137, 1,121, 1,106, 1, 90, 1, 75, 1, 57, + 1, 41, 1, 25, 1, 10, 0,250, 0,231, 0,215, 0,198, 0,184, 0,166, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 50, 6, 0, 1, 1, 1, 27, 9, 92, 29,116,104,101,114,101,105,110, 12, 49, + 6, 0, 1, 1, 1, 19, 9, 51, 38,111,105,108, 15, 48, 6, 0, 1, 1, 1, + 25, 37, 93, 7,116,101,109,112,108,101, 14, 47, 6, 0, 1, 1, 1, 23, 22, + 16, 40, 97,110,103,101,114, 17, 46, 6, 0, 1, 1, 1, 29, 28, 88, 22,100, + 101,112, 97,114,116,101,100, 14, 45, 6, 0, 1, 1, 1, 23, 47, 54, 12, 97, + 98,111,118,101, 13, 44, 6, 0, 1, 1, 1, 21, 48, 15, 9,100,111,116,104, + 14, 43, 6, 0, 1, 1, 1, 23, 5, 23, 41, 98,101,103, 97,116, 14, 42, 6, + 0, 1, 1, 1, 23, 20, 18, 5, 98,101,103, 97,116, 16, 41, 6, 0, 1, 1, + 1, 27, 46, 92, 13,109,111,114,110,105,110,103, 13, 40, 6, 0, 1, 1, 1, + 21, 14, 30, 26,115,101,110,100, 14, 39, 6, 0, 1, 1, 1, 23, 3, 21, 45, + 98,101,103, 97,116, 13, 38, 6, 0, 1, 1, 1, 21, 10, 97, 34,115,104,101, + 119, 14, 37, 6, 0, 1, 1, 1, 23, 17, 66, 18,100,119,101,108,116, 13, 36, + 6, 0, 8, 1, 1, 23, 52, 17, 99,104,105,108,100, 15, 35, 6, 0, 1, 1, + 1, 25, 38, 34, 22, 99,117, 98,105,116,115, 12, 34, 6, 0, 1, 1, 1, 19, + 26, 15, 20, 97,114,107, 9, 33, 6, 0, 1, 1, 1, 0, 7, 72, 31, 14, 32, + 6, 0, 1, 1, 8, 25, 16, 7,112,108, 97, 99,101,115, 14, 31, 6, 0, 1, + 1, 1, 23, 39, 90, 27,116,114,117,116,104, 13, 30, 6, 0, 1, 1, 1, 21, + 16, 81, 25,119, 97,108,107, 13, 29, 6, 0, 1, 1, 1, 21, 34, 62, 27,115, + 101,110,100, 10, 0, 0, 0, 41, 0,116, 0, 1,251, 1,241, 1,231, 1,221, + 1,211, 1,203, 1,193, 1,183, 1,173, 1,163, 1,151, 1,143, 1,133, 1, + 122, 1,109, 1,100, 1, 92, 1, 83, 1, 74, 1, 64, 1, 55, 1, 46, 1, 34, + 1, 22, 1, 13, 1, 4, 0,252, 0,241, 0,232, 0,218, 0,209, 0,200, 0, + 191, 0,182, 0,173, 0,163, 0,153, 0,144, 0,136, 0,127, 0,116, 0,105, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,116,101, + 109,112,108,101, 48, 10, 3, 25, 1,116,101,109,112,108,101, 15, 8, 3, 21, + 1,115,111,109,101, 21, 7, 3, 19, 1,115,105,120, 11, 8, 3, 21, 1,115, + 104,101,119, 38, 9, 3, 23, 1,115,101,114,118,101, 10, 9, 3, 23, 1,115, + 101,114,118,101, 3, 8, 3, 21, 1,115,101,110,100, 40, 8, 3, 21, 1,115, + 101,110,100, 29, 8, 3, 21, 1,115,101,110,100, 12, 8, 3, 21, 1,115,101, + 110,100, 8, 8, 3, 21, 1,115, 97,118,101, 2, 13, 3, 31, 1,115, 97, 99, + 114,105,102,105, 99,101, 23, 8, 3, 21, 1,112,111,111,114, 16, 10, 3, 25, + 1,112,108, 97, 99,101,115, 32, 7, 3, 19, 1,111,105,108, 49, 8, 3, 21, + 1,110,101, 97,114, 7, 8, 3, 21, 1,110,101, 97,114, 4, 11, 3, 27, 1, + 109,111,114,110,105,110,103, 41, 11, 3, 27, 1,109,111,114,110,105,110,103, + 26, 8, 3, 21, 1,103,111,110,101, 25, 8, 3, 21, 1,103,111,110,101, 5, + 9, 3, 23, 1,100,119,101,108,116, 37, 8, 3, 21, 1,100,111,116,104, 44, + 8, 3, 21, 1,100,111,116,104, 13, 7, 3, 21, 9,100,111,116,104, 8, 3, + 21, 1,100,105,101,100, 14, 12, 3, 29, 1,100,101,112, 97,114,116,101,100, + 46, 10, 3, 25, 1, 99,117, 98,105,116,115, 35, 9, 3, 23, 1, 99,104,105, + 108,100, 36, 7, 3, 19, 1, 99, 97,110, 6, 11, 3, 27, 1, 98,101,116,119, + 101,101,110, 17, 9, 3, 23, 1, 98,101,103, 97,116, 43, 9, 3, 23, 1, 98, + 101,103, 97,116, 42, 9, 3, 23, 1, 98,101,103, 97,116, 39, 9, 3, 23, 1, + 98,101,103, 97,116, 9, 7, 3, 19, 1, 97,114,107, 34, 9, 3, 23, 1, 97, + 110,103,101,114, 47, 9, 3, 23, 1, 97,110,103,101,108, 27, 9, 3, 23, 1, + 97, 98,111,118,101, 45, 9, 3, 23, 1, 97, 98,111,118,101, 20, 4, 3, 0, + 1, 33, 10, 0, 0, 0, 8, 1,178, 0, 1,244, 1,233, 1,223, 1,214, 1, + 206, 1,197, 1,188, 1,178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 3, 23, 1,119,111,114,107,115, 28, 8, 3, 21, 1,119, 97,121,115, 24, 8, + 3, 21, 1,119, 97,121,115, 19, 7, 3, 19, 1,119, 97,114, 18, 8, 3, 21, + 1,119, 97,108,107, 30, 9, 3, 23, 1,116,114,117,116,104, 31, 10, 3, 25, + 1,116,104,111,117,103,104, 22, 11, 3, 27, 1,116,104,101,114,101,105,110, + 50, 10, 0, 0, 0, 31, 0, 89, 0, 1,247, 1,233, 1,220, 1,206, 1,192, + 1,180, 1,166, 1,152, 1,138, 1,125, 1,109, 1, 97, 1, 84, 1, 69, 1, + 52, 1, 39, 1, 26, 1, 14, 1, 1, 0,243, 0,230, 0,217, 0,201, 0,185, + 0,172, 0,159, 0,147, 0,133, 0,120, 0,102, 0, 89, 0, 76, 0, 0, 0, + 0, 0, 0, 0, 13, 1, 1,115,101,110,100, 26, 14, 40, 12, 5, 21, 1, 1, + 1,115, 97,118,101, 39, 45, 2, 17, 5, 31, 1, 1, 1,115, 97, 99,114,105, + 102,105, 99,101, 31, 6, 23, 12, 5, 21, 1, 1, 1,112,111,111,114, 19, 44, + 16, 13, 5, 25, 8, 1, 1,112,108, 97, 99,101,115, 16, 32, 11, 5, 19, 1, + 1, 1,111,105,108, 38, 9, 49, 12, 5, 21, 1, 1, 1,110,101, 97,114, 38, + 21, 7, 12, 5, 21, 1, 1, 1,110,101, 97,114, 35, 41, 4, 15, 5, 27, 1, + 1, 1,109,111,114,110,105,110,103, 17, 40, 26, 15, 5, 27, 1, 1, 1,109, + 111,114,110,105,110,103, 13, 46, 41, 12, 5, 21, 1, 1, 1,103,111,110,101, + 28, 25, 5, 12, 5, 21, 1, 1, 1,103,111,110,101, 19, 10, 25, 13, 5, 23, + 1, 1, 1,100,119,101,108,116, 18, 17, 37, 12, 5, 21, 1, 1, 1,100,111, + 116,104, 39, 4, 13, 11, 5, 21, 1, 1, 9,100,111,116,104, 32, 40, 12, 5, + 21, 1, 1, 1,100,111,116,104, 9, 48, 44, 12, 5, 21, 1, 1, 1,100,105, + 101,100, 27, 35, 14, 16, 5, 29, 1, 1, 1,100,101,112, 97,114,116,101,100, + 22, 28, 46, 14, 5, 25, 1, 1, 1, 99,117, 98,105,116,115, 22, 38, 35, 12, + 5, 23, 1, 8, 1, 99,104,105,108,100, 17, 36, 11, 5, 19, 1, 1, 1, 99, + 97,110, 11, 2, 6, 15, 5, 27, 1, 1, 1, 98,101,116,119,101,101,110, 36, + 29, 17, 12, 5, 23, 1, 8, 1, 98,101,103, 97,116, 50, 9, 13, 5, 23, 1, + 1, 1, 98,101,103, 97,116, 45, 3, 39, 13, 5, 23, 1, 1, 1, 98,101,103, + 97,116, 41, 5, 43, 13, 5, 23, 1, 1, 1, 98,101,103, 97,116, 5, 20, 42, + 11, 5, 19, 1, 1, 1, 97,114,107, 20, 26, 34, 13, 5, 23, 1, 1, 1, 97, + 110,103,101,114, 40, 22, 47, 13, 5, 23, 1, 1, 1, 97,110,103,101,108, 3, + 22, 27, 12, 5, 23, 1, 9, 1, 97, 98,111,118,101, 45, 20, 13, 5, 23, 1, + 1, 1, 97, 98,111,118,101, 12, 47, 45, 8, 5, 0, 1, 1, 1, 31, 7, 33, + 10, 0, 0, 0, 18, 1, 13, 0, 1,243, 1,230, 1,217, 1,203, 1,189, 1, + 176, 1,164, 1,151, 1,136, 1,121, 1,105, 1, 90, 1, 76, 1, 63, 1, 51, + 1, 39, 1, 27, 1, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 13, 5, 23, 1, 1, 1,119,111,114,107,115, 31, 17, 28, 11, 5, + 21, 9, 1, 1,119, 97,121,115, 43, 24, 11, 5, 21, 8, 1, 1,119, 97,121, + 115, 4, 19, 11, 5, 19, 1, 1, 1,119, 97,114, 43, 44, 18, 12, 5, 21, 1, + 1, 1,119, 97,108,107, 25, 16, 30, 13, 5, 23, 1, 1, 1,116,114,117,116, + 104, 27, 39, 31, 14, 5, 25, 1, 1, 1,116,104,111,117,103,104, 28, 45, 22, + 15, 5, 27, 1, 1, 1,116,104,101,114,101,105,110, 29, 9, 50, 14, 5, 25, + 1, 1, 1,116,101,109,112,108,101, 11, 6, 15, 14, 5, 25, 1, 1, 1,116, + 101,109,112,108,101, 7, 37, 48, 12, 5, 21, 1, 1, 1,115,111,109,101, 18, + 22, 21, 11, 5, 19, 1, 1, 1,115,105,120, 22, 13, 11, 12, 5, 21, 1, 1, + 1,115,104,101,119, 34, 10, 38, 13, 5, 23, 1, 1, 1,115,101,114,118,101, + 26, 32, 3, 13, 5, 23, 1, 1, 1,115,101,114,118,101, 14, 41, 10, 12, 5, + 21, 1, 1, 1,115,101,110,100, 36, 4, 12, 12, 5, 21, 1, 1, 1,115,101, + 110,100, 34, 42, 8, 12, 5, 21, 1, 1, 1,115,101,110,100, 27, 34, 29, 10, + 0, 0, 0, 28, 0, 82, 0, 1,241, 1,226, 1,211, 1,197, 1,181, 1,166, + 1,151, 1,137, 1,121, 1,104, 1, 84, 1, 73, 1, 59, 1, 41, 1, 26, 1, + 11, 0,253, 0,238, 0,223, 0,207, 0,191, 0,175, 0,159, 0,144, 0,129, + 0,113, 0, 97, 0, 82, 0, 68, 0, 0, 0, 0, 0, 14, 1, 1, 19, 26, 34, + 15, 20, 97,114,107, 14, 6, 1, 1, 1, 1, 21, 25, 5, 27, 28,103,111,110, + 101, 15, 6, 1, 1, 1, 1, 23, 22, 47, 16, 40, 97,110,103,101,114, 15, 6, + 1, 1, 1, 1, 23, 22, 27, 71, 3, 97,110,103,101,108, 14, 6, 1, 1, 1, + 1, 21, 22, 21, 92, 18,115,111,109,101, 14, 6, 1, 1, 1, 1, 21, 21, 7, + 91, 38,110,101, 97,114, 15, 6, 1, 1, 1, 1, 23, 20, 42, 18, 5, 98,101, + 103, 97,116, 15, 6, 1, 1, 1, 1, 23, 17, 37, 66, 18,100,119,101,108,116, + 15, 6, 1, 1, 1, 1, 23, 17, 28, 67, 31,119,111,114,107,115, 15, 6, 1, + 1, 1, 8, 25, 16, 32, 7,112,108, 97, 99,101,115, 14, 6, 1, 1, 1, 1, + 21, 16, 30, 81, 25,119, 97,108,107, 14, 6, 1, 1, 1, 1, 21, 14, 40, 30, + 26,115,101,110,100, 13, 6, 1, 1, 1, 1, 19, 13, 11, 48, 22,115,105,120, + 14, 6, 1, 1, 1, 1, 21, 10, 38, 97, 34,115,104,101,119, 14, 6, 1, 1, + 1, 1, 21, 10, 25, 7, 19,103,111,110,101, 17, 6, 1, 1, 1, 1, 27, 9, + 50, 92, 29,116,104,101,114,101,105,110, 13, 6, 1, 1, 1, 1, 19, 9, 49, + 51, 38,111,105,108, 10, 6, 1, 1, 1, 1, 0, 7, 33, 72, 31, 19, 6, 1, + 1, 1, 1, 31, 6, 23, 37, 31,115, 97, 99,114,105,102,105, 99,101, 16, 6, + 1, 1, 1, 1, 25, 6, 15, 3, 11,116,101,109,112,108,101, 15, 6, 1, 1, + 1, 1, 23, 5, 43, 23, 41, 98,101,103, 97,116, 13, 6, 1, 1, 1, 8, 21, + 4, 19, 58,119, 97,121,115, 14, 6, 1, 1, 1, 1, 21, 4, 13, 21, 39,100, + 111,116,104, 14, 6, 1, 1, 1, 1, 21, 4, 12, 38, 36,115,101,110,100, 15, + 6, 1, 1, 1, 1, 23, 3, 39, 21, 45, 98,101,103, 97,116, 13, 6, 1, 1, + 1, 1, 19, 2, 6, 37, 11, 99, 97,110, 14, 6, 9, 1, 1, 1, 23, 20, 2, + 45, 97, 98,111,118,101, 14, 6, 8, 1, 1, 1, 23, 36, 52, 17, 99,104,105, + 108,100, 14, 6, 8, 1, 1, 1, 23, 9, 16, 50, 98,101,103, 97,116, 10, 0, + 0, 0, 21, 0,177, 0, 1,237, 1,219, 1,203, 1,188, 1,173, 1,156, 1, + 139, 1,123, 1,109, 1, 91, 1, 76, 1, 60, 1, 45, 1, 31, 1, 16, 1, 2, + 0,243, 0,226, 0,208, 0,192, 0,177, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 14, 6, 1, 1, 1, 1, 21, 48, 44, 15, 9,100,111,116,104, + 15, 6, 1, 1, 1, 1, 23, 47, 45, 54, 12, 97, 98,111,118,101, 17, 6, 1, + 1, 1, 1, 27, 46, 41, 92, 13,109,111,114,110,105,110,103, 16, 6, 1, 1, + 1, 1, 25, 45, 22, 71, 28,116,104,111,117,103,104, 14, 6, 1, 1, 1, 1, + 21, 45, 2, 14, 39,115, 97,118,101, 13, 6, 1, 1, 1, 1, 19, 44, 18, 19, + 43,119, 97,114, 14, 6, 1, 1, 1, 1, 21, 44, 16, 52, 19,112,111,111,114, + 13, 6, 1, 1, 1, 9, 21, 43, 24, 46,119, 97,121,115, 14, 6, 1, 1, 1, + 1, 21, 42, 8, 49, 34,115,101,110,100, 15, 6, 1, 1, 1, 1, 23, 41, 10, + 89, 14,115,101,114,118,101, 14, 6, 1, 1, 1, 1, 21, 41, 4, 32, 35,110, + 101, 97,114, 17, 6, 1, 1, 1, 1, 27, 40, 26, 98, 17,109,111,114,110,105, + 110,103, 13, 6, 1, 9, 1, 1, 21, 40, 68, 32,100,111,116,104, 15, 6, 1, + 1, 1, 1, 23, 39, 31, 90, 27,116,114,117,116,104, 16, 6, 1, 1, 1, 1, + 25, 38, 35, 34, 22, 99,117, 98,105,116,115, 16, 6, 1, 1, 1, 1, 25, 37, + 48, 93, 7,116,101,109,112,108,101, 14, 6, 1, 1, 1, 1, 21, 35, 14, 48, + 27,100,105,101,100, 14, 6, 1, 1, 1, 1, 21, 34, 29, 62, 27,115,101,110, + 100, 15, 6, 1, 1, 1, 1, 23, 32, 3, 24, 26,115,101,114,118,101, 17, 6, + 1, 1, 1, 1, 27, 29, 17, 74, 36, 98,101,116,119,101,101,110, 18, 6, 1, + 1, 1, 1, 29, 28, 46, 88, 22,100,101,112, 97,114,116,101,100, 10, 0, 0, + 0, 32, 0, 95, 0, 1,247, 1,238, 1,229, 1,220, 1,211, 1,199, 1,187, + 1,176, 1,164, 1,148, 1,133, 1,116, 1, 99, 1, 86, 1, 67, 1, 55, 1, + 43, 1, 31, 1, 18, 1, 5, 0,249, 0,236, 0,224, 0,209, 0,191, 0,174, + 0,157, 0,145, 0,132, 0,120, 0,108, 0, 95, 0, 83, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 1, 1, 0, 1, 49, 51, 38, 15, 12, 7, 1, + 1, 1, 1, 0, 1, 48, 37, 93, 7, 30, 11, 7, 1, 1, 1, 0, 0, 1, 47, + 22, 16, 24, 11, 7, 1, 0, 1, 1, 0, 1, 47, 16, 40, 12, 12, 7, 1, 1, + 1, 1, 0, 1, 46, 28, 88, 22, 2, 11, 7, 1, 1, 0, 1, 0, 1, 44, 48, + 9, 19, 16, 7, 1, 1, 1, 0, 23, 1, 42, 20, 18, 98,101,103, 97,116, 22, + 16, 7, 1, 1, 0, 1, 23, 1, 37, 17, 18,100,119,101,108,116, 16, 17, 7, + 1, 1, 0, 1, 25, 1, 35, 38, 22, 99,117, 98,105,116,115, 17, 14, 7, 1, + 1, 1, 0, 19, 1, 34, 26, 15, 97,114,107, 21, 11, 7, 1, 1, 1, 0, 0, + 1, 32, 16, 7, 23, 12, 7, 1, 1, 1, 1, 0, 1, 28, 17, 67, 31, 29, 11, + 7, 1, 0, 1, 1, 0, 1, 25, 7, 19, 11, 12, 7, 1, 1, 1, 1, 0, 1, + 22, 45, 71, 28, 28, 12, 7, 1, 1, 1, 1, 0, 1, 21, 22, 92, 18, 4, 11, + 7, 1, 1, 0, 1, 0, 1, 21, 22, 18, 18, 11, 7, 1, 1, 1, 1, 0, 9, + 17, 29, 74, 36, 11, 7, 1, 1, 1, 0, 0, 1, 17, 29, 74, 25, 18, 7, 1, + 1, 1, 1, 25, 1, 15, 6, 3, 11,116,101,109,112,108,101, 5, 12, 7, 1, + 1, 1, 1, 0, 1, 12, 4, 38, 36, 27, 16, 7, 1, 0, 1, 1, 23, 1, 10, + 89, 14,115,101,114,118,101, 14, 16, 7, 1, 1, 1, 1, 21, 1, 4, 41, 32, + 35,110,101, 97,114, 3, 14, 7, 9, 0, 1, 1, 21, 1, 68, 32,100,111,116, + 104, 13, 15, 7, 0, 1, 1, 1, 21, 1, 21, 91, 38,110,101, 97,114, 6, 11, + 7, 0, 1, 1, 1, 0, 1, 16, 81, 25, 9, 10, 7, 0, 1, 1, 8, 0, 1, + 16, 7, 10, 11, 7, 0, 1, 1, 1, 0, 1, 7, 72, 31, 8, 11, 7, 0, 1, + 1, 1, 0, 1, 6, 37, 31, 7, 8, 7, 0, 0, 0, 0, 0, 1, 35, 8, 7, + 0, 0, 0, 0, 0, 1, 34, 8, 7, 0, 0, 0, 0, 0, 1, 33, 8, 7, 0, + 0, 0, 0, 0, 1, 32, 8, 7, 0, 0, 0, 0, 0, 1, 31, 10, 0, 0, 0, + 2, 1,231, 0, 1,244, 1,231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 7, 1, 1, 1, 1, 0, 1, 49, 9, 51, + 38, 26, 11, 7, 1, 1, 0, 1, 0, 1, 49, 9, 38, 20, 13, 0, 0, 0, 23, + 0, 67, 0, 1,238, 1,220, 1,202, 1,186, 1,168, 1,148, 1,130, 1,107, + 1, 86, 1, 65, 1, 44, 1, 27, 1, 14, 0,250, 0,224, 0,205, 0,184, 0, + 165, 0,145, 0,123, 0,106, 0, 86, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 23, 6, 0, 23, 1, 1, 21,107,110,111,119,110, 52, + 19,112,111,111,114, 18, 22, 6, 0, 23, 1, 1, 23, 97, 98,111,118,101, 24, + 26,115,101,114,118,101, 15, 21, 6, 0, 19, 1, 1, 21,119, 97,114, 52, 19, + 112,111,111,114, 20, 20, 6, 0, 27, 1, 8, 25,110,111,116,104,105,110,103, + 7,112,108, 97, 99,101,115, 18, 19, 6, 0, 23, 1, 1, 23, 98,101,103, 97, + 116, 90, 27,116,114,117,116,104, 17, 18, 6, 0, 23, 1, 1, 21,100,119,101, + 108,116, 21, 39,100,111,116,104, 19, 17, 6, 0, 27, 1, 1, 21,109,111,114, + 110,105,110,103, 52, 19,112,111,111,114, 17, 16, 6, 0, 21, 1, 1, 23,115, + 104,101,119, 90, 27,116,114,117,116,104, 24, 15, 6, 0, 27, 1, 1, 31,116, + 104,101,114,101,105,110, 37, 31,115, 97, 99,114,105,102,105, 99,101, 18, 14, + 6, 0, 23, 1, 8, 25,115,109,111,116,101, 7,112,108, 97, 99,101,115, 11, + 13, 6, 0, 19, 1, 1, 0, 97,114,107, 72, 31, 15, 12, 6, 0, 21, 1, 8, + 21,119,105,110,101, 58,119, 97,121,115, 19, 11, 6, 0, 21, 1, 1, 27,115, + 111,109,101, 98, 17,109,111,114,110,105,110,103, 19, 10, 6, 0, 27, 1, 1, + 21, 98,101,116,119,101,101,110, 92, 18,115,111,109,101, 19, 9, 6, 0, 21, + 1, 1, 27,115, 97,118,101, 74, 36, 98,101,116,119,101,101,110, 21, 8, 6, + 0, 25, 1, 1, 27,116,104,111,117,103,104, 98, 17,109,111,114,110,105,110, + 103, 16, 7, 6, 0, 21, 1, 1, 21,115,101,110,100, 49, 34,115,101,110,100, + 18, 6, 6, 0, 25, 1, 1, 21,119,105,115,100,111,109, 38, 36,115,101,110, + 100, 16, 5, 6, 0, 23, 1, 9, 21, 97,110,103,101,114, 46,119, 97,121,115, + 14, 4, 6, 0, 19, 1, 1, 19, 99, 97,110, 19, 43,119, 97,114, 16, 3, 6, + 0, 23, 1, 1, 19,111,102,102,101,114, 48, 22,115,105,120, 16, 2, 6, 0, + 23, 1, 8, 21,119,111,114,107,115, 58,119, 97,121,115, 16, 1, 6, 0, 23, + 1, 1, 19,116,114,117,116,104, 37, 11, 99, 97,110, 13, 0, 0, 0, 22, 0, + 64, 0, 1,230, 1,213, 1,191, 1,169, 1,148, 1,130, 1,108, 1, 89, 1, + 70, 1, 51, 1, 34, 1, 16, 0,253, 0,233, 0,214, 0,194, 0,174, 0,151, + 0,132, 0,109, 0, 90, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 45, 6, 0, 35, 1, 1, 23,105,110,104, 97, 98,105,116, 97,110,116, + 115, 23, 41, 98,101,103, 97,116, 17, 44, 6, 0, 23, 1, 1, 21, 97,110,103, + 101,108, 48, 27,100,105,101,100, 21, 43, 6, 0, 25, 1, 1, 27,116,101,109, + 112,108,101, 74, 36, 98,101,116,119,101,101,110, 17, 42, 6, 0, 23, 1, 1, + 21, 99,104,105,108,100, 81, 25,119, 97,108,107, 21, 41, 6, 0, 21, 1, 1, + 31,119, 97,121,115, 37, 31,115, 97, 99,114,105,102,105, 99,101, 18, 40, 6, + 0, 21, 1, 1, 25,112,111,111,114, 93, 7,116,101,109,112,108,101, 18, 39, + 6, 0, 21, 1, 1, 25,100,111,116,104, 3, 11,116,101,109,112,108,101, 17, + 38, 6, 0, 23, 1, 1, 21,102,114,117,105,116, 62, 27,115,101,110,100, 18, + 37, 6, 0, 23, 1, 1, 23,115,101,114,118,101, 90, 27,116,114,117,116,104, + 17, 36, 6, 0, 21, 1, 1, 23,110,101, 97,114, 90, 27,116,114,117,116,104, + 16, 35, 6, 0, 21, 1, 1, 21,108,111,110,103, 14, 39,115, 97,118,101, 15, + 34, 6, 0, 21, 1, 1, 19,119, 97,108,107, 15, 20, 97,114,107, 17, 33, 6, + 0, 25, 1, 9, 21, 99,117, 98,105,116,115, 46,119, 97,121,115, 17, 32, 6, + 0, 21, 1, 1, 23,103,111,110,101, 23, 41, 98,101,103, 97,116, 17, 31, 6, + 0, 23, 1, 1, 21,119,104,105,108,101, 49, 34,115,101,110,100, 20, 30, 6, + 0, 21, 1, 1, 29,112, 97,114,116, 88, 22,100,101,112, 97,114,116,101,100, + 16, 29, 6, 0, 21, 1, 1, 21, 98,101, 97,114, 92, 18,115,111,109,101, 19, + 28, 6, 0, 25, 1, 1, 23,112,108, 97, 99,101,115, 23, 41, 98,101,103, 97, + 116, 20, 27, 6, 0, 27, 1, 1, 23,116,104,121,115,101,108,102, 54, 12, 97, + 98,111,118,101, 20, 26, 6, 0, 29, 1, 1, 21,100,101,112, 97,114,116,101, + 100, 92, 18,115,111,109,101, 15, 25, 6, 0, 21, 1, 1, 19,116,101,108,108, + 19, 43,119, 97,114, 24, 24, 6, 0, 31, 1, 1, 27,115, 97, 99,114,105,102, + 105, 99,101, 92, 13,109,111,114,110,105,110,103, 13, 0, 0, 0, 5, 1,162, + 0, 1,239, 1,221, 1,203, 1,182, 1,162, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 50, 6, 0, 23, 1, 1, + 23,119,114, 97,116,104, 21, 45, 98,101,103, 97,116, 19, 49, 6, 0, 21, 1, + 1, 27,116,114,101,101, 98, 17,109,111,114,110,105,110,103, 16, 48, 6, 0, + 19, 1, 1, 23,115,105,120, 71, 3, 97,110,103,101,108, 16, 47, 6, 0, 21, + 1, 1, 21,100,105,101,100, 7, 19,103,111,110,101, 15, 46, 6, 0, 19, 1, + 1, 21,111,105,108, 81, 25,119, 97,108,107, 10, 0, 0, 0, 40, 0,106, 0, + 1,246, 1,236, 1,226, 1,218, 1,209, 1,199, 1,187, 1,179, 1,169, 1, + 158, 1,145, 1,136, 1,127, 1,117, 1,107, 1, 98, 1, 82, 1, 72, 1, 63, + 1, 51, 1, 42, 1, 30, 1, 20, 1, 12, 1, 3, 0,248, 0,239, 0,225, 0, + 216, 0,207, 0,197, 0,188, 0,180, 0,170, 0,161, 0,152, 0,141, 0,129, + 0,118, 0,106, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,116,114, + 101,101, 49, 11, 3, 27, 1,116,104,121,115,101,108,102, 27, 10, 3, 25, 1, + 116,104,111,117,103,104, 8, 11, 3, 27, 1,116,104,101,114,101,105,110, 15, + 10, 3, 25, 1,116,101,109,112,108,101, 43, 8, 3, 21, 1,116,101,108,108, + 25, 8, 3, 21, 1,115,111,109,101, 11, 9, 3, 23, 1,115,109,111,116,101, + 14, 7, 3, 19, 1,115,105,120, 48, 8, 3, 21, 1,115,104,101,119, 16, 9, + 3, 23, 1,115,101,114,118,101, 37, 8, 3, 21, 1,115,101,110,100, 7, 8, + 3, 21, 1,115, 97,118,101, 9, 13, 3, 31, 1,115, 97, 99,114,105,102,105, + 99,101, 24, 8, 3, 21, 1,112,111,111,114, 40, 10, 3, 25, 1,112,108, 97, + 99,101,115, 28, 8, 3, 21, 1,112, 97,114,116, 30, 7, 3, 19, 1,111,105, + 108, 46, 9, 3, 23, 1,111,102,102,101,114, 3, 11, 3, 27, 1,110,111,116, + 104,105,110,103, 20, 8, 3, 21, 1,110,101, 97,114, 36, 11, 3, 27, 1,109, + 111,114,110,105,110,103, 17, 8, 3, 21, 1,108,111,110,103, 35, 9, 3, 23, + 1,107,110,111,119,110, 23, 15, 3, 35, 1,105,110,104, 97, 98,105,116, 97, + 110,116,115, 45, 8, 3, 21, 1,103,111,110,101, 32, 9, 3, 23, 1,102,114, + 117,105,116, 38, 9, 3, 23, 1,100,119,101,108,116, 18, 8, 3, 21, 1,100, + 111,116,104, 39, 8, 3, 21, 1,100,105,101,100, 47, 12, 3, 29, 1,100,101, + 112, 97,114,116,101,100, 26, 10, 3, 25, 1, 99,117, 98,105,116,115, 33, 9, + 3, 23, 1, 99,104,105,108,100, 42, 7, 3, 19, 1, 99, 97,110, 4, 11, 3, + 27, 1, 98,101,116,119,101,101,110, 10, 9, 3, 23, 1, 98,101,103, 97,116, + 19, 8, 3, 21, 1, 98,101, 97,114, 29, 7, 3, 19, 1, 97,114,107, 13, 9, + 3, 23, 1, 97,110,103,101,114, 5, 9, 3, 23, 1, 97,110,103,101,108, 44, + 9, 3, 23, 1, 97, 98,111,118,101, 22, 10, 0, 0, 0, 9, 1,171, 0, 1, + 247, 1,238, 1,230, 1,221, 1,211, 1,202, 1,191, 1,181, 1,171, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 3, 23, 1,119,114, 97,116,104, 50, 9, 3, 23, 1,119,111,114,107,115, + 2, 10, 3, 25, 1,119,105,115,100,111,109, 6, 8, 3, 21, 1,119,105,110, + 101, 12, 9, 3, 23, 1,119,104,105,108,101, 31, 8, 3, 21, 1,119, 97,121, + 115, 41, 7, 3, 19, 1,119, 97,114, 21, 8, 3, 21, 1,119, 97,108,107, 34, + 8, 3, 23, 9,116,114,117,116,104, 13, 0, 0, 0, 5, 0, 84, 0, 1, 78, + 0,249, 0,177, 1,163, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 19, + 7, 21, 19, 19, 8,129, 33,118,105,101,119,118, 50, 49,118, 50, 49, 67, 82, + 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 50, 49, 40, 97, 44, 98, 44, 99, + 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, + 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 50, 32, 79, 82, 68, 69, + 82, 32, 66, 89, 32, 98, 32, 76, 73, 77, 73, 84, 32, 49, 48, 70, 17, 6, 21, + 19, 19, 8,121,118,105,101,119,118, 53, 48,118, 53, 48, 67, 82, 69, 65, 84, + 69, 32, 86, 73, 69, 87, 32,118, 53, 48, 40, 97, 44, 98, 41, 32, 65, 83, 32, + 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 32, 70, 82, 79, 77, 32,116, 53, 32, + 87, 72, 69, 82, 69, 32, 97, 60, 62, 50, 53, 83, 16, 7, 21, 19, 19, 8,129, + 17,118,105,101,119,118, 52, 48,118, 52, 48, 67, 82, 69, 65, 84, 69, 32, 86, + 73, 69, 87, 32,118, 52, 48, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, + 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, + 32, 70, 82, 79, 77, 32,116, 52, 32, 87, 72, 69, 82, 69, 32, 97, 60, 62, 50, + 53, 83, 15, 7, 21, 19, 19, 8,129, 17,118,105,101,119,118, 51, 48,118, 51, + 48, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 51, 48, 40, 97, 44, + 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, + 97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 51, 32, 87, + 72, 69, 82, 69, 32, 97, 60, 62, 50, 53, 91, 18, 7, 21, 19, 19, 8,129, 33, + 118,105,101,119,118, 49, 49,118, 49, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, + 69, 87, 32,118, 49, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, + 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, 32, + 70, 82, 79, 77, 32,116, 49, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, 98, 32, + 76, 73, 77, 73, 84, 32, 49, 48, 13, 1,163, 0, 4, 0, 40, 0, 1, 70, 0, + 233, 0,152, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,110, 23, 7, 21, 19, 19, 8,129, 71, + 118,105,101,119,118, 49, 50,118, 49, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73, + 69, 87, 32,118, 49, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, + 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,117,109, 40, 97, 41, 44, 32, + 97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,116, 40, 42, 41, 44, 32,109, + 105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, 77, 32,116, 49, 32, 71, 82, + 79, 85, 80, 32, 66, 89, 32, 53, 79, 22, 7, 21, 19, 19, 8,129, 9,118,105, + 101,119,118, 53, 49,118, 53, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, + 32,118, 53, 49, 40, 97, 44, 98, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, + 32, 97, 44, 98, 32, 70, 82, 79, 77, 32,116, 53, 32, 79, 82, 68, 69, 82, 32, + 66, 89, 32, 98, 32, 76, 73, 77, 73, 84, 32, 49, 48, 91, 21, 7, 21, 19, 19, + 8,129, 33,118,105,101,119,118, 52, 49,118, 52, 49, 67, 82, 69, 65, 84, 69, + 32, 86, 73, 69, 87, 32,118, 52, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, + 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, + 44,101, 32, 70, 82, 79, 77, 32,116, 52, 32, 79, 82, 68, 69, 82, 32, 66, 89, + 32, 98, 32, 76, 73, 77, 73, 84, 32, 49, 48, 91, 20, 7, 21, 19, 19, 8,129, + 33,118,105,101,119,118, 51, 49,118, 51, 49, 67, 82, 69, 65, 84, 69, 32, 86, + 73, 69, 87, 32,118, 51, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, + 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, + 32, 70, 82, 79, 77, 32,116, 51, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, 98, + 32, 76, 73, 77, 73, 84, 32, 49, 48, 0, 0, 0, 93, 19, 19, 8,129, 33,118, + 105,101,119,118, 50, 49,118, 50, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, + 87, 32,118, 50, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, + 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, + 82, 79, 77, 32,116, 50, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, 98, 32, 76, + 73, 77, 73, 84, 32, 49, 48, 13, 0, 0, 0, 3, 0, 66, 0, 1,107, 0,214, + 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 17, 26, + 7, 21, 19, 19, 8,130, 13,118,105,101,119,118, 52, 50,118, 52, 50, 67, 82, + 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 52, 50, 40, 97, 44, 98, 44, 99, + 44,100, 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115, + 117,109, 40, 97, 41, 44, 32, 97,118,103, 40, 98, 41, 44, 32, 99,111,117,110, + 116, 40, 42, 41, 44, 32,109,105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, + 77, 32,116, 52, 32, 71, 82, 79, 85, 80, 32, 66, 89, 32, 53, 10, 32, 32, 32, + 32, 72, 65, 86, 73, 78, 71, 32,109,105,110, 40,100, 41, 60, 51, 48, 32, 79, + 82, 68, 69, 82, 32, 66, 89, 32, 51, 44, 32, 49,129, 18, 25, 7, 21, 19, 19, + 8,130, 15,118,105,101,119,118, 51, 50,118, 51, 50, 67, 82, 69, 65, 84, 69, + 32, 86, 73, 69, 87, 32,118, 51, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, + 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,117,109, 40, 97, + 41, 44, 32, 97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,116, 40, 42, 41, + 44, 32,109,105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, 77, 32,116, 51, + 32, 71, 82, 79, 85, 80, 32, 66, 89, 32, 53, 10, 32, 32, 32, 32, 72, 65, 86, + 73, 78, 71, 32, 99,111,117,110,116, 40, 42, 41, 62, 49, 32, 79, 82, 68, 69, + 82, 32, 66, 89, 32, 51, 44, 32, 49,129, 18, 24, 7, 21, 19, 19, 8,130, 15, + 118,105,101,119,118, 50, 50,118, 50, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73, + 69, 87, 32,118, 50, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, + 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,117,109, 40, 97, 41, 44, 32, + 97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,116, 40, 42, 41, 44, 32,109, + 105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, 77, 32,116, 50, 32, 71, 82, + 79, 85, 80, 32, 66, 89, 32, 53, 10, 32, 32, 32, 32, 72, 65, 86, 73, 78, 71, + 32, 99,111,117,110,116, 40, 42, 41, 62, 49, 32, 79, 82, 68, 69, 82, 32, 66, + 89, 32, 51, 44, 32, 49, 13, 1,108, 0, 3, 0, 83, 0, 0,225, 0, 83, 1, + 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 11, 28, 7, 21, 19, + 19, 8,130, 1,118,105,101,119,118, 49, 51,118, 49, 51, 67, 82, 69, 65, 84, + 69, 32, 86, 73, 69, 87, 32,118, 49, 51, 40, 97, 44, 98, 44, 99, 44,100, 44, + 101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, + 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 10, 32, 32, 85, 78, 73, + 79, 78, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, + 32, 70, 82, 79, 77, 32,116, 50, 10, 32, 32, 85, 78, 73, 79, 78, 32, 83, 69, + 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, + 32,116, 51,129, 8, 27, 7, 21, 19, 19, 8,129,123,118,105,101,119,118, 53, + 50,118, 53, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 53, 50, + 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, + 76, 69, 67, 84, 32, 99,111,117,110,116, 40, 42, 41, 44, 32,109,105,110, 40, + 98, 41, 44, 32,115,117, 98,115,116,114, 40, 98, 44, 49, 44, 49, 41, 44, 32, + 109,105,110, 40, 97, 41, 44, 32,109, 97,120, 40, 97, 41, 32, 70, 82, 79, 77, + 32,116, 53, 10, 32, 32, 32, 71, 82, 79, 85, 80, 32, 66, 89, 32, 51, 32, 79, + 82, 68, 69, 82, 32, 66, 89, 32, 49, 0, 0, 0, 28, 21, 19, 19, 8,130, 13, + 118,105,101,119,118, 52, 50,118, 52, 50, 67, 82, 69, 65, 84, 69, 32, 86,118, + 29, 7, 21, 19, 19, 8,129, 87,118,105,101,119,118, 50, 51,118, 50, 51, 67, + 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 50, 51, 40, 97, 44, 98, 44, + 99, 44,100, 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32, + 97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 10, 32, + 32, 69, 88, 67, 69, 80, 84, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, + 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 32, 87, 72, 69, 82, 69, + 32, 98, 60, 50, 53, 13, 0, 0, 0, 3, 0, 40, 0, 1,134, 1, 12, 0, 40, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,129, 97, 32, 7, 21, 19, 19, 8,131, 45,118,105, + 101,119,118, 54, 50,118, 54, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, + 32,118, 54, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 10, + 32, 32, 83, 69, 76, 69, 67, 84, 32,116, 49, 46, 97, 44,116, 50, 46, 98, 44, + 116, 51, 46, 99, 44,116, 52, 46,100, 44,116, 53, 46, 98, 10, 32, 32, 32, 32, + 70, 82, 79, 77, 32,116, 49, 32, 74, 79, 73, 78, 32,116, 50, 32, 79, 78, 32, + 40,116, 49, 46, 97, 61,116, 50, 46, 98, 41, 10, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 74, 79, 73, 78, 32,116, 51, 32, 79, 78, 32, 40,116, 49, + 46, 97, 61,116, 51, 46, 97, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 74, 79, 73, 78, 32,116, 52, 32, 79, 78, 32, 40,116, 52, 46, 98, 61, + 116, 51, 46, 98, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 76, + 69, 70, 84, 32, 74, 79, 73, 78, 32,116, 53, 32, 79, 78, 32, 40,116, 53, 46, + 97, 61,116, 49, 46, 99, 41,120, 31, 7, 21, 19, 19, 8,129, 91,118,105,101, + 119,118, 54, 49,118, 54, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32, + 118, 54, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 10, 32, + 32, 83, 69, 76, 69, 67, 84, 32,116, 50, 46, 97, 44,116, 51, 46, 98, 44,116, + 50, 46, 99, 44,116, 51, 46,100, 44,116, 50, 46,101, 10, 32, 32, 32, 32, 70, + 82, 79, 77, 32,116, 50, 32, 76, 69, 70, 84, 32, 74, 79, 73, 78, 32,116, 51, + 32, 79, 78, 32, 40,116, 50, 46, 97, 61,116, 51, 46, 97, 41,120, 30, 7, 21, + 19, 19, 8,129, 91,118,105,101,119,118, 54, 48,118, 54, 48, 67, 82, 69, 65, + 84, 69, 32, 86, 73, 69, 87, 32,118, 54, 48, 40, 97, 44, 98, 44, 99, 44,100, + 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,116, 49, 46, + 97, 44,116, 50, 46, 98, 44,116, 49, 46, 99, 44,116, 50, 46,100, 44,116, 49, + 46,101, 10, 32, 32, 32, 32, 70, 82, 79, 77, 32,116, 49, 32, 76, 69, 70, 84, + 32, 74, 79, 73, 78, 32,116, 50, 32, 79, 78, 32, 40,116, 49, 46, 97, 61,116, + 50, 46, 98, 41, 13, 0, 0, 0, 1, 1, 73, 0, 1, 73, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 52, 33, 7, 21, 19, 19, 8,130, + 83,118,105,101,119,118, 55, 48,118, 55, 48, 67, 82, 69, 65, 84, 69, 32, 86, + 73, 69, 87, 32,118, 55, 48, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, + 65, 83, 10, 32, 32, 87, 73, 84, 72, 32, 82, 69, 67, 85, 82, 83, 73, 86, 69, + 32, 99, 48, 40,120, 41, 32, 65, 83, 32, 40, 86, 65, 76, 85, 69, 83, 40, 49, + 41, 32, 85, 78, 73, 79, 78, 32, 65, 76, 76, 32, 83, 69, 76, 69, 67, 84, 32, + 120, 43, 49, 32, 70, 82, 79, 77, 32, 99, 48, 32, 87, 72, 69, 82, 69, 32,120, + 60, 57, 41, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,120, 44, 32, 98, 44, 32, + 99, 44, 32,100, 44, 32,101, 32, 70, 82, 79, 77, 32, 99, 48, 32, 74, 79, 73, + 78, 32,116, 49, 32, 79, 78, 32, 40,116, 49, 46, 97, 61, 53, 48, 45, 99, 48, + 46,120, 41, +}; +
diff --git a/third_party/sqlite/src/test/optfuzz-db01.txt b/third_party/sqlite/src/test/optfuzz-db01.txt new file mode 100644 index 0000000..9f01557e --- /dev/null +++ b/third_party/sqlite/src/test/optfuzz-db01.txt
@@ -0,0 +1,142 @@ +-- Run this script through the sqlite3 command-line shell in order to generate +-- a database file containing lots of data for testing purposes. +-- +-- This script assumes that the "bin2c" program is available on ones $PATH. +-- The "bin2c" program reads a binary file and outputs C-code that creates +-- an array of bytes holding the content of that file. +-- +-- This script is designed to create many tables and views all having +-- 5 columns, "a" through "e", and with a variety of integers, short strings, +-- and NULL values. +-- +.open -new testdb01.db +PRAGMA page_size=512; +BEGIN; +CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT, d INT, e INT); +WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<50) +INSERT INTO t1(a,b,c,d,e) SELECT x,abs(random()%51), + abs(random()%100), abs(random()%51), abs(random()%100) FROM c; +CREATE TABLE t2(a INT, b INT, c INT,d INT,e INT,PRIMARY KEY(b,a))WITHOUT ROWID; +INSERT INTO t2 SELECT * FROM t1; +CREATE TABLE t3(a,b,c,d,e); +INSERT INTO t3 SELECT a,b,c,d,e FROM t1 ORDER BY random() LIMIT 5; +INSERT INTO t3 SELECT null,b,c,d,e FROM t1 ORDER BY random() LIMIT 5; +INSERT INTO t3 SELECT a,null,c,d,e FROM t1 ORDER BY random() LIMIT 5; +INSERT INTO t3 SELECT a,b,null,d,e FROM t1 ORDER BY random() LIMIT 5; +INSERT INTO t3 SELECT a,b,c,null,e FROM t1 ORDER BY random() LIMIT 5; +INSERT INTO t3 SELECT a,b,c,d,null FROM t1 ORDER BY random() LIMIT 5; +INSERT INTO t3 SELECT null,null,null,null,null FROM t1 LIMIT 5; +CREATE INDEX t3x1 ON t3(a,b,c,d,e); +CREATE TABLE t4(a INT UNIQUE NOT NULL, b INT UNIQUE NOT NULL,c,d,e); +INSERT OR IGNORE INTO t4 SELECT a,b,c,d,e FROM t3; +CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT UNIQUE,c,d,e); +INSERT INTO t5(b) VALUES + ('truth'), + ('works'), + ('offer'), + ('can'), + ('anger'), + ('wisdom'), + ('send'), + ('though'), + ('save'), + ('between'), + ('some'), + ('wine'), + ('ark'), + ('smote'), + ('therein'), + ('shew'), + ('morning'), + ('dwelt'), + ('begat'), + ('nothing'), + ('war'), + ('above'), + ('known'), + ('sacrifice'), + ('tell'), + ('departed'), + ('thyself'), + ('places'), + ('bear'), + ('part'), + ('while'), + ('gone'), + ('cubits'), + ('walk'), + ('long'), + ('near'), + ('serve'), + ('fruit'), + ('doth'), + ('poor'), + ('ways'), + ('child'), + ('temple'), + ('angel'), + ('inhabitants'), + ('oil'), + ('died'), + ('six'), + ('tree'), + ('wrath'); +UPDATE t1 SET e=(SELECT b FROM t5 WHERE t5.a=(t1.e%51)); +UPDATE t5 SET (c,d,e) = + (SELECT c,d,e FROM t1 WHERE t1.a=abs(t5.a+random()/100)%50+1); +UPDATE t2 SET e=(SELECT b FROM t5 WHERE t5.a=(t2.e%51)); +UPDATE t3 SET e=(SELECT b FROM t5 WHERE t5.a=t3.e); +CREATE INDEX t1e ON t1(e); +CREATE INDEX t2ed ON t2(e,d); +CREATE VIEW v00(a,b,c,d,e) AS SELECT 1,1,1,1,'one'; +CREATE VIEW v10(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t1 WHERE a<>25; +CREATE VIEW v20(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t2 WHERE a<>25; +CREATE VIEW v30(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t3 WHERE a<>25; +CREATE VIEW v40(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t4 WHERE a<>25; +CREATE VIEW v50(a,b) AS SELECT a,b FROM t5 WHERE a<>25; +CREATE VIEW v11(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t1 ORDER BY b LIMIT 10; +CREATE VIEW v21(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t2 ORDER BY b LIMIT 10; +CREATE VIEW v31(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t3 ORDER BY b LIMIT 10; +CREATE VIEW v41(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t4 ORDER BY b LIMIT 10; +CREATE VIEW v51(a,b) AS SELECT a,b FROM t5 ORDER BY b LIMIT 10; +CREATE VIEW v12(a,b,c,d,e) AS + SELECT sum(a), avg(b), count(*), min(d), e FROM t1 GROUP BY 5; +CREATE VIEW v22(a,b,c,d,e) AS + SELECT sum(a), avg(b), count(*), min(d), e FROM t2 GROUP BY 5 + HAVING count(*)>1 ORDER BY 3, 1; +CREATE VIEW v32(a,b,c,d,e) AS + SELECT sum(a), avg(b), count(*), min(d), e FROM t3 GROUP BY 5 + HAVING count(*)>1 ORDER BY 3, 1; +CREATE VIEW v42(a,b,c,d,e) AS + SELECT sum(a), avg(b), count(*), min(d), e FROM t4 GROUP BY 5 + HAVING min(d)<30 ORDER BY 3, 1; +CREATE VIEW v52(a,b,c,d,e) AS + SELECT count(*), min(b), substr(b,1,1), min(a), max(a) FROM t5 + GROUP BY 3 ORDER BY 1; + +CREATE VIEW v13(a,b,c,d,e) AS + SELECT a,b,c,d,e FROM t1 + UNION SELECT a,b,c,d,e FROM t2 + UNION SELECT a,b,c,d,e FROM t3; +CREATE VIEW v23(a,b,c,d,e) AS + SELECT a,b,c,d,e FROM t1 + EXCEPT SELECT a,b,c,d,e FROM t1 WHERE b<25; + +CREATE VIEW v60(a,b,c,d,e) AS + SELECT t1.a,t2.b,t1.c,t2.d,t1.e + FROM t1 LEFT JOIN t2 ON (t1.a=t2.b); +CREATE VIEW v61(a,b,c,d,e) AS + SELECT t2.a,t3.b,t2.c,t3.d,t2.e + FROM t2 LEFT JOIN t3 ON (t2.a=t3.a); +CREATE VIEW v62(a,b,c,d,e) AS + SELECT t1.a,t2.b,t3.c,t4.d,t5.b + FROM t1 JOIN t2 ON (t1.a=t2.b) + JOIN t3 ON (t1.a=t3.a) + JOIN t4 ON (t4.b=t3.b) + LEFT JOIN t5 ON (t5.a=t1.c); +CREATE VIEW v70(a,b,c,d,e) AS + WITH RECURSIVE c0(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c0 WHERE x<9) + SELECT x, b, c, d, e FROM c0 JOIN t1 ON (t1.a=50-c0.x); +COMMIT; +VACUUM; +.shell bin2c testdb01.db
diff --git a/third_party/sqlite/src/test/optfuzz.c b/third_party/sqlite/src/test/optfuzz.c new file mode 100644 index 0000000..0aa30a6 --- /dev/null +++ b/third_party/sqlite/src/test/optfuzz.c
@@ -0,0 +1,309 @@ +/* +** 2018-03-21 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This program attempts to verify the correctness of the SQLite query +** optimizer by fuzzing. +** +** The input is an SQL script, presumably generated by a fuzzer. The +** argument is the name of the input. If no files are named, standard +** input is read. +** +** The SQL script is run twice, once with optimization enabled, and again +** with optimization disabled. If the output is not equivalent, an error +** is printed and the program returns non-zero. +*/ + +/* Include the SQLite amalgamation, after making appropriate #defines. +*/ +#define SQLITE_THREADSAFE 0 +#define SQLITE_OMIT_LOAD_EXTENSION 1 +#define SQLITE_ENABLE_DESERIALIZE 1 +#include "sqlite3.c" + +/* Content of the read-only test database */ +#include "optfuzz-db01.c" + +/* +** Prepare a single SQL statement. Panic if anything goes wrong +*/ +static sqlite3_stmt *prepare_sql(sqlite3 *db, const char *zFormat, ...){ + char *zSql; + int rc; + sqlite3_stmt *pStmt = 0; + va_list ap; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc ){ + printf("Error: %s\nSQL: %s\n", + sqlite3_errmsg(db), zSql); + exit(1); + } + sqlite3_free(zSql); + return pStmt; +} + +/* +** Run SQL. Panic if anything goes wrong +*/ +static void run_sql(sqlite3 *db, const char *zFormat, ...){ + char *zSql; + int rc; + char *zErr = 0; + va_list ap; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + rc = sqlite3_exec(db, zSql, 0, 0, &zErr); + if( rc || zErr ){ + printf("Error: %s\nsqlite3_errmsg: %s\nSQL: %s\n", + zErr, sqlite3_errmsg(db), zSql); + exit(1); + } + sqlite3_free(zSql); +} + +/* +** Run one or more SQL statements contained in zSql against database dbRun. +** Store the input in database dbOut. +*/ +static int optfuzz_exec( + sqlite3 *dbRun, /* The database on which the SQL executes */ + const char *zSql, /* The SQL to be executed */ + sqlite3 *dbOut, /* Store results in this database */ + const char *zOutTab, /* Store results in this table of dbOut */ + int *pnStmt, /* Write the number of statements here */ + int *pnRow, /* Write the number of rows here */ + int bTrace /* Print query results if true */ +){ + int rc = SQLITE_OK; /* Return code */ + const char *zLeftover; /* Tail of unprocessed SQL */ + sqlite3_stmt *pStmt = 0; /* The current SQL statement */ + sqlite3_stmt *pIns = 0; /* Statement to insert into dbOut */ + const char *zCol; /* Single column value */ + int nCol; /* Number of output columns */ + char zLine[4000]; /* Complete row value */ + + run_sql(dbOut, "BEGIN"); + run_sql(dbOut, "CREATE TABLE IF NOT EXISTS staging(x TEXT)"); + run_sql(dbOut, "CREATE TABLE IF NOT EXISTS \"%w\"(x TEXT)", zOutTab); + pIns = prepare_sql(dbOut, "INSERT INTO staging(x) VALUES(?1)"); + *pnRow = *pnStmt = 0; + while( rc==SQLITE_OK && zSql && zSql[0] ){ + zLeftover = 0; + rc = sqlite3_prepare_v2(dbRun, zSql, -1, &pStmt, &zLeftover); + zSql = zLeftover; + assert( rc==SQLITE_OK || pStmt==0 ); + if( rc!=SQLITE_OK ){ + printf("Error with [%s]\n%s\n", zSql, sqlite3_errmsg(dbRun)); + break; + } + if( !pStmt ) continue; + (*pnStmt)++; + nCol = sqlite3_column_count(pStmt); + run_sql(dbOut, "DELETE FROM staging;"); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + int i, j; + for(i=j=0; i<nCol && j<sizeof(zLine)-50; i++){ + int eType = sqlite3_column_type(pStmt, i); + if( eType==SQLITE_NULL ){ + zCol = "NULL"; + }else{ + zCol = (const char*)sqlite3_column_text(pStmt, i); + } + if( i ) zLine[j++] = ','; + if( eType==SQLITE_TEXT ){ + sqlite3_snprintf(sizeof(zLine)-j, zLine+j, "'%q'", zCol); + }else{ + sqlite3_snprintf(sizeof(zLine)-j, zLine+j, "%s", zCol); + } + j += (int)strlen(zLine+j); + } + /* Detect if any row is too large and throw an error, because we will + ** want to go back and look more closely at that case */ + if( j>=sizeof(zLine)-100 ){ + printf("Excessively long output line: %d bytes\n" ,j); + exit(1); + } + if( bTrace ){ + printf("%s\n", zLine); + } + (*pnRow)++; + sqlite3_bind_text(pIns, 1, zLine, j, SQLITE_TRANSIENT); + rc = sqlite3_step(pIns); + assert( rc==SQLITE_DONE ); + rc = sqlite3_reset(pIns); + } + run_sql(dbOut, + "INSERT INTO \"%w\"(x) VALUES('### %q ###')", + zOutTab, sqlite3_sql(pStmt) + ); + run_sql(dbOut, + "INSERT INTO \"%w\"(x) SELECT group_concat(x,char(10))" + " FROM (SELECT x FROM staging ORDER BY x)", + zOutTab + ); + run_sql(dbOut, "COMMIT"); + sqlite3_finalize(pStmt); + pStmt = 0; + } + sqlite3_finalize(pStmt); + sqlite3_finalize(pIns); + return rc; +} + +/* +** Read the content of file zName into memory obtained from sqlite3_malloc64() +** and return a pointer to the buffer. The caller is responsible for freeing +** the memory. +** +** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes +** read. +** +** For convenience, a nul-terminator byte is always appended to the data read +** from the file before the buffer is returned. This byte is not included in +** the final value of (*pnByte), if applicable. +** +** NULL is returned if any error is encountered. The final value of *pnByte +** is undefined in this case. +*/ +static char *readFile(const char *zName, int *pnByte){ + FILE *in = fopen(zName, "rb"); + long nIn; + size_t nRead; + char *pBuf; + if( in==0 ) return 0; + fseek(in, 0, SEEK_END); + nIn = ftell(in); + rewind(in); + pBuf = sqlite3_malloc64( nIn+1 ); + if( pBuf==0 ) return 0; + nRead = fread(pBuf, nIn, 1, in); + fclose(in); + if( nRead!=1 ){ + sqlite3_free(pBuf); + return 0; + } + pBuf[nIn] = 0; + if( pnByte ) *pnByte = nIn; + return pBuf; +} + +int main(int argc, char **argv){ + int nIn = 0; /* Number of input files */ + char **azIn = 0; /* Names of input files */ + sqlite3 *dbOut = 0; /* Database to hold results */ + sqlite3 *dbRun = 0; /* Database used for tests */ + int bTrace = 0; /* Show query results */ + int bShowValid = 0; /* Just list inputs that are valid SQL */ + int nRow, nStmt; /* Number of rows and statements */ + int i, rc; + + for(i=1; i<argc; i++){ + const char *z = argv[i]; + if( z[0]=='-' && z[1]=='-' ) z++; + if( strcmp(z,"-help")==0 ){ + printf("Usage: %s [OPTIONS] FILENAME ...\n", argv[0]); + printf("Options:\n"); + printf(" --help Show his message\n"); + printf(" --output-trace Show each line of SQL output\n"); + printf(" --valid-sql List FILEs that are valid SQL\n"); + return 0; + } + else if( strcmp(z,"-output-trace")==0 ){ + bTrace = 1; + } + else if( strcmp(z,"-valid-sql")==0 ){ + bShowValid = 1; + } + else if( z[0]=='-' ){ + printf("unknown option \"%s\". Use --help for details\n", argv[i]); + return 1; + } + else { + nIn++; + azIn = realloc(azIn, sizeof(azIn[0])*nIn); + if( azIn==0 ){ + printf("out of memory\n"); + exit(1); + } + azIn[nIn-1] = argv[i]; + } + } + + sqlite3_open(":memory:", &dbOut); + sqlite3_open(":memory:", &dbRun); + sqlite3_deserialize(dbRun, "main", data001, sizeof(data001), + sizeof(data001), SQLITE_DESERIALIZE_READONLY); + for(i=0; i<nIn; i++){ + char *zSql = readFile(azIn[i], 0); + sqlite3_stmt *pCk; + sqlite3_exec(dbRun, "ROLLBACK", 0, 0, 0); + if( bShowValid ){ + rc = sqlite3_exec(dbRun, zSql, 0, 0, 0); + if( rc==SQLITE_OK ) printf("%s\n", azIn[i]); + sqlite3_free(zSql); + continue; + } + sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, dbRun, 0); + if( bTrace ) printf("%s: Optimized\n", azIn[i]); + rc = optfuzz_exec(dbRun, zSql, dbOut, "opt", &nStmt, &nRow, bTrace); + if( rc ){ + printf("%s: optimized run failed: %s\n", + azIn[i], sqlite3_errmsg(dbRun)); + }else{ + sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, dbRun, 0xffff); + if( bTrace ) printf("%s: Non-optimized\n", azIn[i]); + rc = optfuzz_exec(dbRun, zSql, dbOut, "noopt", &nStmt, &nRow, bTrace); + if( rc ){ + printf("%s: non-optimized run failed: %s\n", + azIn[i], sqlite3_errmsg(dbRun)); + exit(1); + } + pCk = prepare_sql(dbOut, + "SELECT (SELECT group_concat(x,char(10)) FROM opt)==" + " (SELECT group_concat(x,char(10)) FROM noopt)"); + rc = sqlite3_step(pCk); + if( rc!=SQLITE_ROW ){ + printf("%s: comparison failed\n", sqlite3_errmsg(dbOut)); + exit(1); + } + if( !sqlite3_column_int(pCk, 0) ){ + printf("%s: opt/no-opt outputs differ\n", azIn[i]); + pCk = prepare_sql(dbOut, + "SELECT group_concat(x,char(10)) FROM opt " + "UNION ALL " + "SELECT group_concat(x,char(10)) FROM noopt"); + sqlite3_step(pCk); + printf("opt:\n%s\n", sqlite3_column_text(pCk,0)); + sqlite3_step(pCk); + printf("noopt:\n%s\n", sqlite3_column_text(pCk,0)); + exit(1); + }else{ + printf("%s: %d stmts %d rows ok\n", azIn[i], nStmt, nRow); + } + sqlite3_finalize(pCk); + } + sqlite3_free(zSql); + } + sqlite3_close(dbRun); + sqlite3_close(dbOut); + free(azIn); + if( sqlite3_memory_used() ){ + printf("Memory leak of %lld bytes\n", sqlite3_memory_used()); + exit(1); + } + return 0; +}
diff --git a/third_party/sqlite/src/test/ossfuzz.c b/third_party/sqlite/src/test/ossfuzz.c index 7b28cf6a..a8a637f2a 100644 --- a/third_party/sqlite/src/test/ossfuzz.c +++ b/third_party/sqlite/src/test/ossfuzz.c
@@ -3,11 +3,17 @@ ** (https://github.com/google/oss-fuzz) */ #include <stddef.h> -#include <stdint.h> +#if !defined(_MSC_VER) +# include <stdint.h> +#endif #include <stdio.h> #include <string.h> #include "sqlite3.h" +#if defined(_MSC_VER) +typedef unsigned char uint8_t; +#endif + /* Global debugging settings. OSS-Fuzz will have all debugging turned ** off. But if LLVMFuzzerTestOneInput() is called interactively from ** the ossshell utility program, then these flags might be set. @@ -160,6 +166,9 @@ /* Run the SQL. The sqlite_exec() interface expects a zero-terminated ** string, so make a copy. */ zSql = sqlite3_mprintf("%.*s", (int)size, data); +#ifndef SQLITE_OMIT_COMPLETE + sqlite3_complete(zSql); +#endif sqlite3_exec(cx.db, zSql, exec_handler, (void*)&execCnt, &zErrMsg); /* Show any errors */
diff --git a/third_party/sqlite/src/test/ossshell.c b/third_party/sqlite/src/test/ossshell.c index 00cc339..54849f97 100644 --- a/third_party/sqlite/src/test/ossshell.c +++ b/third_party/sqlite/src/test/ossshell.c
@@ -6,12 +6,18 @@ ** command line and passes them one by one into ossfuzz.c. */ #include <stddef.h> -#include <stdint.h> +#if !defined(_MSC_VER) +# include <stdint.h> +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sqlite3.h" +#if defined(_MSC_VER) +typedef unsigned char uint8_t; +#endif + /* ** The entry point in ossfuzz.c that this routine will be calling */
diff --git a/third_party/sqlite/src/test/pagerfault.test b/third_party/sqlite/src/test/pagerfault.test index fe1e7e2..a97bf437 100644 --- a/third_party/sqlite/src/test/pagerfault.test +++ b/third_party/sqlite/src/test/pagerfault.test
@@ -1203,12 +1203,14 @@ set contents [db eval {SELECT * FROM t1}] if {$contents != "1 2"} { error "Bad database contents ($contents)" } - set sz [file size test.db] - if {$testrc!=0 && $sz!=1024*3 && $sz!=4096*3} { - error "Expected file size to be 3072 or 12288 bytes - actual size $sz bytes" - } - if {$testrc==0 && $sz!=4096*3} { - error "Expected file size to be 12288 bytes - actual size $sz bytes" + if {[atomic_batch_write test.db]==0} { + set sz [file size test.db] + if {$testrc!=0 && $sz!=1024*3 && $sz!=4096*3} { + error "Expected file size 3072 or 12288 bytes - actual size $sz bytes" + } + if {$testrc==0 && $sz!=4096*3} { + error "Expected file size to be 12288 bytes - actual size $sz bytes" + } } }
diff --git a/third_party/sqlite/src/test/printf2.test b/third_party/sqlite/src/test/printf2.test index d30966d1..998038f8 100644 --- a/third_party/sqlite/src/test/printf2.test +++ b/third_party/sqlite/src/test/printf2.test
@@ -148,6 +148,63 @@ SELECT printf('|%,d|%,d|',1234567890,-1234567890); } {|1,234,567,890|-1,234,567,890|} +# 2018-02-19. Unicode characters with %c +do_execsql_test printf2-5.100 { + SELECT printf('(%8c)',char(11106)); +} {{( ⭢)}} +do_execsql_test printf2-5.101 { + SELECT printf('(%-8c)',char(11106)); +} {{(⭢ )}} +do_execsql_test printf2-5.102 { + SELECT printf('(%5.3c)',char(1492)); +} {{( ההה)}} +do_execsql_test printf2-5.103 { + SELECT printf('(%-5.3c)',char(1492)); +} {{(ההה )}} +do_execsql_test printf2-5.104 { + SELECT printf('(%3.3c)',char(1492)); +} {{(ההה)}} +do_execsql_test printf2-5.105 { + SELECT printf('(%-3.3c)',char(1492)); +} {{(ההה)}} +do_execsql_test printf2-5.104 { + SELECT printf('(%2c)',char(1513)); +} {{( ש)}} +do_execsql_test printf2-5.106 { + SELECT printf('(%-2c)',char(1513)); +} {{(ש )}} + +# 2018-02-19. Unicode characters with the "!" flag in %s and friends. +do_execsql_test printf2-6.100 { + SELECT printf('(%!.3s)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {(הנה)} +do_execsql_test printf2-6.101 { + SELECT printf('(%.6s)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {(הנה)} +do_execsql_test printf2-6.102 { + SELECT printf('(%!5.3s)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {{( הנה)}} +do_execsql_test printf2-6.103 { + SELECT printf('(%8.6s)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {{( הנה)}} +do_execsql_test printf2-6.104 { + SELECT printf('(%!-5.3s)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {{(הנה )}} +do_execsql_test printf2-6.105 { + SELECT printf('(%-8.6s)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {{(הנה )}} +do_execsql_test printf2-6.106 { + SELECT printf('(%!.3Q)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {('הנה')} +do_execsql_test printf2-6.107 { + SELECT printf('(%.6Q)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {('הנה')} +do_execsql_test printf2-6.108 { + SELECT printf('(%!7.3Q)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {{( 'הנה')}} +do_execsql_test printf2-6.109 { + SELECT printf('(%10.6Q)','הנה מה־טוב ומה־נעים שבת אחים גם־יחד'); +} {{( 'הנה')}} finish_test
diff --git a/third_party/sqlite/src/test/releasetest.tcl b/third_party/sqlite/src/test/releasetest.tcl index 479f229..abae5dd 100755 --- a/third_party/sqlite/src/test/releasetest.tcl +++ b/third_party/sqlite/src/test/releasetest.tcl
@@ -173,6 +173,7 @@ -DSQLITE_OMIT_TRACE=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_THREADSAFE=2 + -DSQLITE_ENABLE_DESERIALIZE=1 --enable-json1 --enable-fts5 --enable-session } "Locking-Style" {
diff --git a/third_party/sqlite/src/test/rowvalue.test b/third_party/sqlite/src/test/rowvalue.test index 8dba82c..d8675a86 100644 --- a/third_party/sqlite/src/test/rowvalue.test +++ b/third_party/sqlite/src/test/rowvalue.test
@@ -394,4 +394,156 @@ 3 i ii iii iv } +do_execsql_test 17.0 { + CREATE TABLE b1(a, b); + CREATE TABLE b2(x); +} + +do_execsql_test 17.1 { + SELECT * FROM b2 CROSS JOIN b1 + WHERE b2.x=b1.a AND (b1.a, 2) + IN (VALUES(1, 2)); +} {} + +do_execsql_test 18.0 { + CREATE TABLE b3 ( a, b, PRIMARY KEY (a, b) ); + CREATE TABLE b4 ( a ); + CREATE TABLE b5 ( a, b ); + INSERT INTO b3 VALUES (1, 1), (1, 2); + INSERT INTO b4 VALUES (1); + INSERT INTO b5 VALUES (1, 1), (1, 2); +} + +do_execsql_test 18.1 { + SELECT * FROM b3 WHERE (SELECT b3.a, b3.b) IN ( SELECT a, b FROM b5 ) +} {1 1 1 2} +do_execsql_test 18.2 { + SELECT * FROM b3 WHERE (VALUES(b3.a, b3.b)) IN ( SELECT a, b FROM b5 ); +} {1 1 1 2} +do_execsql_test 18.3 { + SELECT * FROM b3 WHERE (b3.a, b3.b) IN ( SELECT a, b FROM b5 ); +} {1 1 1 2} +do_execsql_test 18.4 { + SELECT * FROM b3 JOIN b4 ON b4.a = b3.a + WHERE (SELECT b3.a, b3.b) IN ( SELECT a, b FROM b5 ); +} {1 1 1 1 2 1} +do_execsql_test 18.5 { + SELECT * FROM b3 JOIN b4 ON b4.a = b3.a + WHERE (VALUES(b3.a, b3.b)) IN ( SELECT a, b FROM b5 ); +} {1 1 1 1 2 1} +do_execsql_test 18.6 { + SELECT * FROM b3 JOIN b4 ON b4.a = b3.a + WHERE (b3.a, b3.b) IN ( SELECT a, b FROM b5 ); +} {1 1 1 1 2 1} + + +# 2018-02-13 Ticket https://www.sqlite.org/src/tktview/f484b65f3d6230593c3 +# Incorrect result from a row-value comparison in the WHERE clause. +# +do_execsql_test 19.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY,b); + INSERT INTO t1(a,b) VALUES(1,11),(2,22),(3,33),(4,44); + SELECT * FROM t1 WHERE (a,b)>(0,0) ORDER BY a; +} {1 11 2 22 3 33 4 44} +do_execsql_test 19.2 { + SELECT * FROM t1 WHERE (a,b)>=(0,0) ORDER BY a; +} {1 11 2 22 3 33 4 44} +do_execsql_test 19.3 { + SELECT * FROM t1 WHERE (a,b)<(5,0) ORDER BY a DESC; +} {4 44 3 33 2 22 1 11} +do_execsql_test 19.4 { + SELECT * FROM t1 WHERE (a,b)<=(5,0) ORDER BY a DESC; +} {4 44 3 33 2 22 1 11} +do_execsql_test 19.5 { + SELECT * FROM t1 WHERE (a,b)>(3,0) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.6 { + SELECT * FROM t1 WHERE (a,b)>=(3,0) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.7 { + SELECT * FROM t1 WHERE (a,b)<(3,0) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.8 { + SELECT * FROM t1 WHERE (a,b)<=(3,0) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.9 { + SELECT * FROM t1 WHERE (a,b)>(3,32) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.10 { + SELECT * FROM t1 WHERE (a,b)>(3,33) ORDER BY a; +} {4 44} +do_execsql_test 19.11 { + SELECT * FROM t1 WHERE (a,b)>=(3,33) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.12 { + SELECT * FROM t1 WHERE (a,b)>=(3,34) ORDER BY a; +} {4 44} +do_execsql_test 19.13 { + SELECT * FROM t1 WHERE (a,b)<(3,34) ORDER BY a DESC; +} {3 33 2 22 1 11} +do_execsql_test 19.14 { + SELECT * FROM t1 WHERE (a,b)<(3,33) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.15 { + SELECT * FROM t1 WHERE (a,b)<=(3,33) ORDER BY a DESC; +} {3 33 2 22 1 11} +do_execsql_test 19.16 { + SELECT * FROM t1 WHERE (a,b)<=(3,32) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.21 { + SELECT * FROM t1 WHERE (0,0)<(a,b) ORDER BY a; +} {1 11 2 22 3 33 4 44} +do_execsql_test 19.22 { + SELECT * FROM t1 WHERE (0,0)<=(a,b) ORDER BY a; +} {1 11 2 22 3 33 4 44} +do_execsql_test 19.23 { + SELECT * FROM t1 WHERE (5,0)>(a,b) ORDER BY a DESC; +} {4 44 3 33 2 22 1 11} +do_execsql_test 19.24 { + SELECT * FROM t1 WHERE (5,0)>=(a,b) ORDER BY a DESC; +} {4 44 3 33 2 22 1 11} +do_execsql_test 19.25 { + SELECT * FROM t1 WHERE (3,0)<(a,b) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.26 { + SELECT * FROM t1 WHERE (3,0)<=(a,b) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.27 { + SELECT * FROM t1 WHERE (3,0)>(a,b) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.28 { + SELECT * FROM t1 WHERE (3,0)>=(a,b) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.29 { + SELECT * FROM t1 WHERE (3,32)<(a,b) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.30 { + SELECT * FROM t1 WHERE (3,33)<(a,b) ORDER BY a; +} {4 44} +do_execsql_test 19.31 { + SELECT * FROM t1 WHERE (3,33)<=(a,b) ORDER BY a; +} {3 33 4 44} +do_execsql_test 19.32 { + SELECT * FROM t1 WHERE (3,34)<=(a,b) ORDER BY a; +} {4 44} +do_execsql_test 19.33 { + SELECT * FROM t1 WHERE (3,34)>(a,b) ORDER BY a DESC; +} {3 33 2 22 1 11} +do_execsql_test 19.34 { + SELECT * FROM t1 WHERE (3,33)>(a,b) ORDER BY a DESC; +} {2 22 1 11} +do_execsql_test 19.35 { + SELECT * FROM t1 WHERE (3,33)>=(a,b) ORDER BY a DESC; +} {3 33 2 22 1 11} +do_execsql_test 19.36 { + SELECT * FROM t1 WHERE (3,32)>=(a,b) ORDER BY a DESC; +} {2 22 1 11} + +# 2018-02-18: Memory leak nexted row-value. Detected by OSSFuzz. +# +do_catchsql_test 20.1 { + SELECT 1 WHERE (2,(2,0)) IS (2,(2,0)); +} {0 1} + finish_test
diff --git a/third_party/sqlite/src/test/sessionfuzz-data1.db b/third_party/sqlite/src/test/sessionfuzz-data1.db new file mode 100644 index 0000000..df10e10bc --- /dev/null +++ b/third_party/sqlite/src/test/sessionfuzz-data1.db Binary files differ
diff --git a/third_party/sqlite/src/test/sessionfuzz.c b/third_party/sqlite/src/test/sessionfuzz.c new file mode 100644 index 0000000..372a7c3 --- /dev/null +++ b/third_party/sqlite/src/test/sessionfuzz.c
@@ -0,0 +1,1018 @@ +/* +** 2018-03-01 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements a program used for fuzz-testing the session +** module. +** +** Usage: +** +** sessionfuzz setup -- Generate starter test cases +** sessionfuzz run FILE ... -- Run a test fuzz on FILE +** sesssiofuzz run SQLAR ... -- Run all test cases in the SQL Archive +** +** Compiling: +** +** (1) Have a version of SQLite that supports SQLITE_ENABLE_MEMDB +** in the local directory. +** (2) Run: +** +** gcc -Wall -O3 -o sessionfuzz sessionfuzz.c -lz +** +** Use with AFL (American Fuzzy Lop - http://lcamtuf.coredump.cx/afl/) +** +** (1) ./afl-gcc -O3 -o sessionfuzz sessionfuzz.c -lz +** (2) mkdir session-init session-run session-cases +** (3) cd session-init; ../sessionfuzz setup; cd .. +** (4) ./afl -i session-init -o session-run -- ./sessionfuzz run @@ +** ... let the previous step run for a while. Weeks, maybe. +** (5) ./afl-cmin -i session-run -o session-cases +** +** The afl-cmin command on step (5) writes a minimal set of test cases +** for coverage into the session-cases directory. Gather the cases written +** there into an SQL Archive using a command like this: +** +** sqlite3 session-cases.db -Ac session-cases +** +** Then repeat the test using: +** +** ./sessionfuzz run session-cases.db +*/ + +/* +** We will import the entire SQLite source file to make compiling easier +*/ +#ifdef SQLITE_DEBUG +#undef SQLITE_DEBUG +#endif + +#ifdef SQLITE_THREADSAFE +#undef SQLITE_THREADSAFE +#endif + +#define SQLITE_DEBUG 1 +#define SQLITE_THREADSAFE 0 +#define SQLITE_OMIT_LOAD_EXTENSION 0 +#define SQLITE_ENABLE_SESSION 1 +#define SQLITE_ENABLE_PREUPDATE_HOOK 1 +#define SQLITE_ENABLE_DESERIALIZE 1 +#include "sqlite3.c" + +/* Create a test database. This will be an in-memory database */ +static const char zInitSql[] = + "CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d);\n" + "CREATE TABLE t2(e TEXT PRIMARY KEY NOT NULL,f,g);\n" + "CREATE TABLE t3(w REAL PRIMARY KEY NOT NULL,x,y);\n" + "CREATE TABLE t4(z PRIMARY KEY) WITHOUT ROWID;\n" +; + +/* Code to populate the database */ +static const char zFillSql[] = + "INSERT INTO t1(a,b,c,d) VALUES\n" + " (1,2,3,4),\n" + " (2,3.5,'four',x'556677'),\n" + " (3,null,'xyz',15),\n" + " (4,'bubba',0x80000000,0.0);\n" + "INSERT INTO t1 SELECT a+4,c,d,b FROM t1;\n" + "INSERT INTO t1 SELECT a+8,d,b,c FROM t1;\n" + "INSERT INTO t1 SELECT a+16,d,c,b FROM t1;\n" + "INSERT INTO t1 SELECT a+32,b,d,c FROM t1;\n" + "INSERT INTO t2 SELECT printf('x%dy',a),b,c FROM t1;\n" + "INSERT INTO t3 SELECT a*1.1,b,c FROM t1;\n" + "INSERT INTO t4 SELECT a||','||quote(b) FROM t1;\n" +; + +/* A database file created by running the two scripts above */ +static const unsigned char aDbBytes[] = { + 83, 81, 76,105,116,101, 32,102,111,114,109, 97,116, 32, 51, 0, 2, 0, 1, + 1, 0, 64, 32, 32, 0, 0, 0, 13, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 46, 32,152, 13, 1,186, 0, 6, 0,176, 0, 1,194, 1, 84, 1,150, + 0,238, 1, 48, 0,176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 60, 6, 6, 23, 17, 17, 1,101,116, 97, 98,108,101,116, + 52,116, 52, 7, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 52, + 40,122, 32, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, 89, 41, 32, 87, 73, 84, + 72, 79, 85, 84, 32, 82, 79, 87, 73, 68, 64, 4, 6, 23, 17, 17, 1,109,116, + 97, 98,108,101,116, 51,116, 51, 5, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, + 76, 69, 32,116, 51, 40,119, 32, 82, 69, 65, 76, 32, 80, 82, 73, 77, 65, 82, + 89, 32, 75, 69, 89, 32, 78, 79, 84, 32, 78, 85, 76, 76, 44,120, 44,121, 41, + 34, 5, 5, 23, 55, 17, 1,105,110,100,101,120,115,113,108,105,116,101, 95, + 97,117,116,111,105,110,100,101,120, 95,116, 51, 95, 49,116, 51, 6, 64, 2, + 6, 23, 17, 17, 1,109,116, 97, 98,108,101,116, 50,116, 50, 3, 67, 82, 69, + 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 50, 40,101, 32, 84, 69, 88, 84, + 32, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, 89, 32, 78, 79, 84, 32, 78, 85, + 76, 76, 44,102, 44,103, 41, 34, 3, 5, 23, 55, 17, 1,105,110,100,101,120, + 115,113,108,105,116,101, 95, 97,117,116,111,105,110,100,101,120, 95,116, 50, + 95, 49,116, 50, 4, 0, 0, 0, 8, 0, 0, 0, 0, 60, 1, 6, 23, 17, 17, + 1,101,116, 97, 98,108,101,116, 49,116, 49, 2, 67, 82, 69, 65, 84, 69, 32, + 84, 65, 66, 76, 69, 32,116, 49, 40, 97, 32, 73, 78, 84, 69, 71, 69, 82, 32, + 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, 89, 44, 98, 44, 99, 44,100, 41, 5, + 0, 0, 0, 2, 1,246, 0, 0, 0, 0, 10, 1,251, 1,246, 1,177, 1,155, + 1,145, 1,119, 1,109, 1, 87, 1, 76, 1, 50, 1, 40, 1, 18, 1, 7, 0, + 237, 0,227, 0,205, 0,195, 0,169, 0,159, 0,137, 0,126, 0,100, 0, 90, + 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 20, 26, 5, 0, 21, 7, 18,102,111, + 117,114, 64, 12, 0, 0, 0, 0, 0, 0, 85,102,119, 8, 25, 5, 0, 1, 1, + 1, 3, 2, 4, 24, 24, 5, 0, 23, 7, 5, 98,117, 98, 98, 97, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 9, 23, 5, 0, 0, 1, 19, 15, + 120,121,122, 20, 22, 5, 0, 7, 18, 21, 64, 12, 0, 0, 0, 0, 0, 0, 85, + 102,119,102,111,117,114, 8, 21, 5, 0, 1, 1, 1, 2, 4, 3, 24, 20, 5, + 0, 7, 5, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 98, + 117, 98, 98, 97, 8, 19, 4, 0, 1, 19, 15,120,121,122, 20, 18, 5, 0, 18, + 21, 7, 85,102,119,102,111,117,114, 64, 12, 0, 0, 0, 0, 0, 0, 8, 17, + 5, 0, 1, 1, 1, 4, 3, 2, 24, 16, 5, 0, 23, 5, 7, 98,117, 98, 98, + 97, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 15, 5, 0, + 0, 19, 1,120,121,122, 15, 20, 14, 5, 0, 7, 21, 18, 64, 12, 0, 0, 0, + 0, 0, 0,102,111,117,114, 85,102,119, 8, 13, 5, 0, 1, 1, 1, 2, 3, + 4, 24, 12, 5, 0, 7, 23, 5, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, + 98, 97, 0, 0,128, 0, 0, 0, 9, 11, 5, 0, 1, 0, 19, 15,120,121,122, + 20, 10, 5, 0, 18, 7, 21, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0,102, + 111,117,114, 8, 9, 5, 0, 1, 1, 1, 4, 2, 3, 24, 8, 5, 0, 5, 7, + 23, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, + 97, 8, 7, 4, 0, 19, 1,120,121,122, 15, 20, 6, 5, 0, 21, 18, 7,102, + 111,117,114, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0, 8, 5, 5, 0, 1, + 1, 1, 3, 4, 2, 24, 4, 5, 0, 23, 5, 7, 98,117, 98, 98, 97, 0, 0, + 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 3, 5, 0, 0, 19, 1, + 120,121,122, 15, 20, 2, 5, 0, 7, 21, 18, 64, 12, 0, 0, 0, 0, 0, 0, + 102,111,117,114, 85,102,119, 0, 0, 0, 9, 52, 0, 0, 0, 8, 26, 5, 0, + 0, 0, 2, 1,246, 0, 0, 0, 0, 13, 1,251, 1,246, 1,181, 1,165, 1, + 152, 1,129, 1,118, 1, 97, 1, 87, 1, 64, 1, 52, 1, 30, 1, 17, 0,252, + 0,240, 0,223, 0,209, 0,185, 0,173, 0,152, 0,141, 0,118, 0,106, 0, + 84, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 27, 3, 21, 19,120, + 50, 55,121,120,121,122, 20, 26, 4, 21, 21, 7,120, 50, 54,121,102,111,117, + 114, 64, 12, 0, 0, 0, 0, 0, 0, 10, 25, 4, 21, 1, 1,120, 50, 53,121, + 3, 2, 21, 24, 4, 21, 23, 7,120, 50, 52,121, 98,117, 98, 98, 97, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 23, 4, 21, 0, 1,120, 50, 51,121, 15, 19, 22, + 4, 21, 7, 18,120, 50, 50,121, 64, 12, 0, 0, 0, 0, 0, 0, 85,102,119, + 10, 21, 4, 21, 1, 1,120, 50, 49,121, 2, 4, 22, 20, 4, 21, 7, 5,120, + 50, 48,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 12, 19, + 4, 21, 1, 19,120, 49, 57,121, 15,120,121,122, 15, 18, 4, 21, 18, 21,120, + 49, 56,121, 85,102,119,102,111,117,114, 10, 17, 4, 21, 1, 1,120, 49, 55, + 121, 4, 3, 19, 16, 4, 21, 23, 5,120, 49, 54,121, 98,117, 98, 98, 97, 0, + 0,128, 0, 0, 0, 11, 15, 4, 21, 0, 19,120, 49, 53,121,120,121,122, 20, + 14, 4, 21, 7, 21,120, 49, 52,121, 64, 12, 0, 0, 0, 0, 0, 0,102,111, + 117,114, 10, 13, 4, 21, 1, 1,120, 49, 51,121, 2, 3, 21, 12, 4, 21, 7, + 23,120, 49, 50,121, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 8, + 11, 3, 21, 1,120, 49, 49,121, 15, 19, 10, 4, 21, 18, 7,120, 49, 48,121, + 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0, 9, 9, 4, 19, 1, 1,120, 57, + 121, 4, 2, 21, 8, 4, 19, 5, 7,120, 56,121, 0, 0,128, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 7, 4, 19, 19, 1,120, 55,121,120,121,122, + 15, 14, 6, 4, 19, 21, 18,120, 54,121,102,111,117,114, 85,102,119, 9, 5, + 4, 19, 1, 1,120, 53,121, 3, 4, 18, 4, 4, 19, 23, 5,120, 52,121, 98, + 117, 98, 98, 97, 0, 0,128, 0, 0, 0, 10, 3, 4, 19, 0, 19,120, 51,121, + 120,121,122, 19, 2, 4, 19, 7, 21,120, 50,121, 64, 12, 0, 0, 0, 0, 0, + 0,102,111,117,114, 9, 0, 0, 0, 12, 53, 0, 0, 0, 11, 27, 2, 0, 0, + 0, 1, 1,243, 0, 0, 0, 0, 15, 1,243, 1,220, 1,211, 1,202, 1,193, + 1,184, 1,175, 1,166, 1,159, 1,150, 1,141, 1,132, 1,123, 1,114, 1, + 105, 1, 96, 1, 87, 1, 78, 1, 69, 1, 61, 1, 52, 1, 43, 1, 34, 1, 25, + 1, 16, 1, 7, 0,254, 0,245, 0,236, 0,227, 0,219, 0,210, 0,201, 0, + 192, 0,183, 0,174, 0,165, 0,156, 0,147, 0,138, 0,129, 0,121, 0,112, + 0,103, 0, 0, 0, 8, 3, 21, 1,120, 53, 49,121, 51, 8, 3, 21, 1,120, + 53, 48,121, 50, 7, 3, 19, 1,120, 52,121, 4, 8, 3, 21, 1,120, 52, 57, + 121, 49, 8, 3, 21, 1,120, 52, 56,121, 48, 8, 3, 21, 1,120, 52, 55,121, + 47, 8, 3, 21, 1,120, 52, 54,121, 46, 8, 3, 21, 1,120, 52, 53,121, 45, + 8, 3, 21, 1,120, 52, 52,121, 44, 8, 3, 21, 1,120, 52, 51,121, 43, 8, + 3, 21, 1,120, 52, 50,121, 42, 8, 3, 21, 1,120, 52, 49,121, 41, 8, 3, + 21, 1,120, 52, 48,121, 40, 7, 3, 19, 1,120, 51,121, 3, 8, 3, 21, 1, + 120, 51, 57,121, 39, 8, 3, 21, 1,120, 51, 56,121, 38, 8, 3, 21, 1,120, + 51, 55,121, 37, 8, 3, 21, 1,120, 51, 54,121, 36, 8, 3, 21, 1,120, 51, + 53,121, 35, 8, 3, 21, 1,120, 51, 52,121, 34, 8, 3, 21, 1,120, 51, 51, + 121, 33, 8, 3, 21, 1,120, 51, 50,121, 32, 8, 3, 21, 1,120, 51, 49,121, + 31, 8, 3, 21, 1,120, 51, 48,121, 30, 7, 3, 19, 1,120, 50,121, 2, 8, + 3, 21, 1,120, 50, 57,121, 29, 8, 3, 21, 1,120, 50, 56,121, 28, 8, 3, + 21, 1,120, 50, 55,121, 27, 8, 3, 21, 1,120, 50, 54,121, 26, 8, 3, 21, + 1,120, 50, 53,121, 25, 8, 3, 21, 1,120, 50, 52,121, 24, 8, 3, 21, 1, + 120, 50, 51,121, 23, 8, 3, 21, 1,120, 50, 50,121, 22, 8, 3, 21, 1,120, + 50, 49,121, 21, 8, 3, 21, 1,120, 50, 48,121, 20, 6, 3, 19, 9,120, 49, + 121, 8, 3, 21, 1,120, 49, 57,121, 19, 8, 3, 21, 1,120, 49, 56,121, 18, + 8, 3, 21, 1,120, 49, 55,121, 17, 8, 3, 21, 1,120, 49, 54,121, 16, 8, + 3, 21, 1,120, 49, 53,121, 15, 8, 3, 21, 1,120, 49, 52,121, 14, 8, 3, + 21, 1,120, 49, 51,121, 13, 8, 3, 21, 1,120, 49, 50,121, 12, 8, 3, 21, + 1,120, 0, 0, 0, 14, 8, 3, 21, 1,120, 53, 49,121, 51, 5, 0, 0, 0, + 2, 1,246, 0, 0, 0, 0, 18, 1,251, 1,246, 1,156, 1,135, 1,117, 1, + 89, 1, 73, 1, 55, 1, 41, 1, 14, 0,254, 0,228, 0,211, 0,186, 0,170, + 0,149, 0,131, 0,110, 0, 94, 0, 69, 0, 54, 13, 23, 4, 7, 0, 1, 64, + 57, 76,204,204,204,204,205, 15, 23, 22, 4, 7, 7, 18, 64, 56, 51, 51, 51, + 51, 51, 52, 64, 12, 0, 0, 0, 0, 0, 0, 85,102,119, 14, 21, 4, 7, 1, + 1, 64, 55, 25,153,153,153,153,154, 2, 4, 19, 20, 4, 1, 7, 5, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 16, 19, 4, 7, 1, 19, + 64, 52,230,102,102,102,102,103, 15,120,121,122, 19, 18, 4, 7, 18, 21, 64, + 51,204,204,204,204,204,205, 85,102,119,102,111,117,114, 14, 17, 4, 7, 1, + 1, 64, 50,179, 51, 51, 51, 51, 52, 4, 3, 23, 16, 4, 7, 23, 5, 64, 49, + 153,153,153,153,153,154, 98,117, 98, 98, 97, 0, 0,128, 0, 0, 0, 15, 15, + 4, 7, 0, 19, 64, 48,128, 0, 0, 0, 0, 0,120,121,122, 24, 14, 4, 7, + 7, 21, 64, 46,204,204,204,204,204,206, 64, 12, 0, 0, 0, 0, 0, 0,102, + 111,117,114, 14, 13, 4, 7, 1, 1, 64, 44,153,153,153,153,153,154, 2, 3, + 25, 12, 4, 7, 7, 23, 64, 42,102,102,102,102,102,103, 0, 0, 0, 0, 0, + 0, 0, 0, 98,117, 98, 98, 97, 12, 11, 3, 7, 1, 64, 40, 51, 51, 51, 51, + 51, 52, 15, 16, 10, 4, 1, 18, 7, 11, 85,102,119, 64, 12, 0, 0, 0, 0, + 0, 0, 14, 9, 4, 7, 1, 1, 64, 35,204,204,204,204,204,205, 4, 2, 26, + 8, 4, 7, 5, 7, 64, 33,153,153,153,153,153,154, 0, 0,128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 16, 7, 4, 7, 19, 1, 64, 30,204,204,204, + 204,204,206,120,121,122, 15, 19, 6, 4, 7, 21, 18, 64, 26,102,102,102,102, + 102,103,102,111,117,114, 85,102,119, 14, 5, 4, 7, 1, 1, 64, 22, 0, 0, + 0, 0, 0, 0, 3, 4, 23, 4, 4, 7, 23, 5, 64, 17,153,153,153,153,153, + 154, 98,117, 98, 98, 97, 0, 0,128, 0, 0, 0, 15, 3, 4, 7, 0, 19, 64, + 10,102,102,102,102,102,103,120,121,122, 24, 2, 4, 7, 7, 21, 64, 1,153, + 153,153,153,153,154, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 14, 1, + 4, 7, 1, 1, 0, 0, 0, 17, 45, 0, 0, 0, 16, 23, 2, 0, 0, 0, 1, + 1,239, 0, 0, 0, 0, 20, 1,239, 1,205, 1,192, 1,179, 1,166, 1,153, + 1,140, 1,134, 1,121, 1,108, 1, 95, 1, 82, 1, 69, 1, 56, 1, 43, 1, + 30, 1, 17, 1, 11, 0,254, 0,241, 0,228, 0,215, 0,202, 0,189, 0,176, + 0,163, 0,150, 0,144, 0,131, 0,118, 0,105, 0, 92, 0, 79, 0, 12, 3, + 7, 1, 64, 67, 64, 0, 0, 0, 0, 0, 35, 12, 3, 7, 1, 64, 66,179, 51, + 51, 51, 51, 52, 34, 12, 3, 7, 1, 64, 66, 38,102,102,102,102,103, 33, 12, + 3, 7, 1, 64, 65,153,153,153,153,153,154, 32, 12, 3, 7, 1, 64, 65, 12, + 204,204,204,204,205, 31, 5, 3, 1, 1, 33, 30, 12, 3, 7, 1, 64, 63,230, + 102,102,102,102,103, 29, 12, 3, 7, 1, 64, 62,204,204,204,204,204,206, 28, + 12, 3, 7, 1, 64, 61,179, 51, 51, 51, 51, 52, 27, 12, 3, 7, 1, 64, 60, + 153,153,153,153,153,154, 26, 12, 3, 7, 1, 64, 59,128, 0, 0, 0, 0, 1, + 25, 12, 3, 7, 1, 64, 58,102,102,102,102,102,103, 24, 12, 3, 7, 1, 64, + 57, 76,204,204,204,204,205, 23, 12, 3, 7, 1, 64, 56, 51, 51, 51, 51, 51, + 52, 22, 12, 3, 7, 1, 64, 55, 25,153,153,153,153,154, 21, 5, 3, 1, 1, + 22, 20, 12, 3, 7, 1, 64, 52,230,102,102,102,102,103, 19, 12, 3, 7, 1, + 64, 51,204,204,204,204,204,205, 18, 12, 3, 7, 1, 64, 50,179, 51, 51, 51, + 51, 52, 17, 12, 3, 7, 1, 64, 49,153,153,153,153,153,154, 16, 12, 3, 7, + 1, 64, 48,128, 0, 0, 0, 0, 0, 15, 12, 3, 7, 1, 64, 46,204,204,204, + 204,204,206, 14, 12, 3, 7, 1, 64, 44,153,153,153,153,153,154, 13, 12, 3, + 7, 1, 64, 42,102,102,102,102,102,103, 12, 12, 3, 7, 1, 64, 40, 51, 51, + 51, 51, 51, 52, 11, 5, 3, 1, 1, 11, 10, 12, 3, 7, 1, 64, 35,204,204, + 204,204,204,205, 9, 12, 3, 7, 1, 64, 33,153,153,153,153,153,154, 8, 12, + 3, 7, 1, 64, 30,204,204,204,204,204,206, 7, 12, 3, 7, 1, 64, 26,102, + 102,102,102,102,103, 6, 12, 3, 7, 1, 64, 22, 0, 0, 0, 0, 0, 0, 5, + 12, 3, 7, 1, 64, 17,153,153,153,153,153,154, 4, 12, 3, 7, 1, 64, 10, + 102,102,102,102,102,103, 3, 12, 3, 7, 1, 64, 1,153,153, 0, 0, 0, 19, + 12, 3, 7, 1, 64, 67, 64, 0, 0, 0, 0, 0, 35, 2, 0, 0, 0, 1, 1, + 242, 0, 0, 0, 0, 22, 1,242, 1,218, 1,211, 1,202, 1,192, 1,179, 1, + 172, 1,157, 1,149, 1,141, 1,132, 1,125, 1,116, 1,106, 1, 93, 1, 86, + 1, 74, 1, 63, 1, 47, 1, 40, 1, 31, 1, 16, 1, 8, 0,255, 0,248, 0, + 239, 0,229, 0,216, 0,209, 0,197, 0,186, 0,174, 0,158, 0,151, 0,136, + 0,128, 0,119, 0,112, 0,103, 0, 93, 0, 9, 2, 27, 52, 55, 44, 78, 85, + 76, 76, 8, 2, 25, 52, 54, 44, 51, 46, 53, 6, 2, 21, 52, 53, 44, 50, 8, + 2, 25, 52, 52, 44, 48, 46, 48, 7, 2, 23, 52, 51, 44, 49, 53, 14, 2, 37, + 52, 50, 44, 88, 39, 53, 53, 54, 54, 55, 55, 39, 6, 2, 21, 52, 49, 44, 52, + 15, 2, 39, 52, 48, 44, 50, 49, 52, 55, 52, 56, 51, 54, 52, 56, 11, 2, 31, + 52, 44, 39, 98,117, 98, 98, 97, 39, 10, 2, 29, 51, 57, 44, 39,120,121,122, + 39, 11, 2, 31, 51, 56, 44, 39,102,111,117,114, 39, 6, 2, 21, 51, 55, 44, + 51, 12, 2, 33, 51, 54, 44, 39, 98,117, 98, 98, 97, 39, 9, 2, 27, 51, 53, + 44, 78, 85, 76, 76, 8, 2, 25, 51, 52, 44, 51, 46, 53, 6, 2, 21, 51, 51, + 44, 50, 8, 2, 25, 51, 50, 44, 48, 46, 48, 7, 2, 23, 51, 49, 44, 49, 53, + 14, 2, 37, 51, 48, 44, 88, 39, 53, 53, 54, 54, 55, 55, 39, 8, 2, 25, 51, + 44, 78, 85, 76, 76, 6, 2, 21, 50, 57, 44, 52, 15, 2, 39, 50, 56, 44, 50, + 49, 52, 55, 52, 56, 51, 54, 52, 56, 10, 2, 29, 50, 55, 44, 39,120,121,122, + 39, 11, 2, 31, 50, 54, 44, 39,102,111,117,114, 39, 6, 2, 21, 50, 53, 44, + 51, 12, 2, 33, 50, 52, 44, 39, 98,117, 98, 98, 97, 39, 9, 2, 27, 50, 51, + 44, 78, 85, 76, 76, 8, 2, 25, 50, 50, 44, 51, 46, 53, 6, 2, 21, 50, 49, + 44, 50, 8, 2, 25, 50, 48, 44, 48, 46, 48, 7, 2, 23, 50, 44, 51, 46, 53, + 7, 2, 23, 49, 57, 44, 49, 53, 14, 2, 37, 49, 56, 44, 88, 39, 53, 53, 54, + 54, 55, 55, 39, 6, 2, 21, 49, 55, 44, 52, 12, 2, 33, 49, 54, 44, 39, 98, + 117, 98, 98, 97, 39, 9, 2, 27, 49, 53, 44, 78, 85, 76, 76, 8, 2, 25, 49, + 52, 44, 51, 46, 53, 6, 2, 21, 49, 51, 44, 50, 8, 2, 25, 49, 50, 44, 48, + 46, 48, 7, 2, 23, 49, 49, 44, 49, 53, 14, 2, 37, 49, 48, 44, 88, 0, 0, + 0, 21, 9, 2, 27, 52, 55, 44, 78, 85, 76, 76, 13, 0, 0, 0, 26, 0, 68, + 0, 1,246, 1,224, 1,213, 1,187, 1,177, 1,155, 1,145, 1,119, 1,109, + 1, 87, 1, 76, 1, 50, 1, 40, 1, 18, 1, 7, 0,237, 0,227, 0,205, 0, + 195, 0,169, 0,159, 0,137, 0,126, 0,100, 0, 90, 0, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 26, 5, 0, 21, 7, 18,102,111,117,114, 64, 12, 0, 0, + 0, 0, 0, 0, 85,102,119, 8, 25, 5, 0, 1, 1, 1, 3, 2, 4, 24, 24, + 5, 0, 23, 7, 5, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,128, 0, 0, 0, 9, 23, 5, 0, 0, 1, 19, 15,120,121,122, 20, 22, 5, + 0, 7, 18, 21, 64, 12, 0, 0, 0, 0, 0, 0, 85,102,119,102,111,117,114, + 8, 21, 5, 0, 1, 1, 1, 2, 4, 3, 24, 20, 5, 0, 7, 5, 23, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 98,117, 98, 98, 97, 8, 19, + 4, 0, 1, 19, 15,120,121,122, 20, 18, 5, 0, 18, 21, 7, 85,102,119,102, + 111,117,114, 64, 12, 0, 0, 0, 0, 0, 0, 8, 17, 5, 0, 1, 1, 1, 4, + 3, 2, 24, 16, 5, 0, 23, 5, 7, 98,117, 98, 98, 97, 0, 0,128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 15, 5, 0, 0, 19, 1,120,121,122, + 15, 20, 14, 5, 0, 7, 21, 18, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117, + 114, 85,102,119, 8, 13, 5, 0, 1, 1, 1, 2, 3, 4, 24, 12, 5, 0, 7, + 23, 5, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 0, 0,128, 0, + 0, 0, 9, 11, 5, 0, 1, 0, 19, 15,120,121,122, 20, 10, 5, 0, 18, 7, + 21, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 8, 9, 5, + 0, 1, 1, 1, 4, 2, 3, 24, 8, 5, 0, 5, 7, 23, 0, 0,128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 8, 7, 4, 0, 19, + 1,120,121,122, 15, 20, 6, 5, 0, 21, 18, 7,102,111,117,114, 85,102,119, + 64, 12, 0, 0, 0, 0, 0, 0, 8, 5, 5, 0, 1, 1, 1, 3, 4, 2, 24, + 4, 5, 0, 23, 5, 7, 98,117, 98, 98, 97, 0, 0,128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 3, 5, 0, 0, 19, 1,120,121,122, 15, 20, 2, + 5, 0, 7, 21, 18, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 85,102, + 119, 8, 1, 5, 0, 1, 1, 1, 2, 3, 4, 13, 0, 0, 0, 26, 0, 63, 0, + 1,245, 1,219, 1,209, 1,187, 1,177, 1,151, 1,141, 1,119, 1,108, 1, + 82, 1, 72, 1, 50, 1, 39, 1, 13, 1, 3, 0,237, 0,227, 0,201, 0,191, + 0,169, 0,158, 0,132, 0,122, 0,100, 0, 89, 0, 63, 0, 0, 0, 24, 52, + 5, 0, 7, 23, 5, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 0, + 0,128, 0, 0, 0, 9, 51, 5, 0, 1, 0, 19, 15,120,121,122, 20, 50, 5, + 0, 18, 7, 21, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, + 8, 49, 5, 0, 1, 1, 1, 4, 2, 3, 24, 48, 5, 0, 23, 7, 5, 98,117, + 98, 98, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 9, 47, + 5, 0, 0, 1, 19, 15,120,121,122, 20, 46, 5, 0, 7, 18, 21, 64, 12, 0, + 0, 0, 0, 0, 0, 85,102,119,102,111,117,114, 8, 45, 5, 0, 1, 1, 1, + 2, 4, 3, 24, 44, 5, 0, 7, 5, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,128, 0, 0, 0, 98,117, 98, 98, 97, 8, 43, 4, 0, 1, 19, 15,120,121, + 122, 20, 42, 5, 0, 18, 21, 7, 85,102,119,102,111,117,114, 64, 12, 0, 0, + 0, 0, 0, 0, 8, 41, 5, 0, 1, 1, 1, 4, 3, 2, 24, 40, 5, 0, 5, + 23, 7, 0, 0,128, 0, 0, 0, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 39, 5, 0, 19, 0, 1,120,121,122, 15, 20, 38, 5, 0, 21, 7, + 18,102,111,117,114, 64, 12, 0, 0, 0, 0, 0, 0, 85,102,119, 8, 37, 5, + 0, 1, 1, 1, 3, 2, 4, 24, 36, 5, 0, 23, 7, 5, 98,117, 98, 98, 97, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 9, 35, 5, 0, 0, + 1, 19, 15,120,121,122, 20, 34, 5, 0, 7, 18, 21, 64, 12, 0, 0, 0, 0, + 0, 0, 85,102,119,102,111,117,114, 8, 33, 5, 0, 1, 1, 1, 2, 4, 3, + 24, 32, 5, 0, 7, 5, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, + 0, 0, 98,117, 98, 98, 97, 8, 31, 4, 0, 1, 19, 15,120,121,122, 20, 30, + 5, 0, 18, 21, 7, 85,102,119,102,111,117,114, 64, 12, 0, 0, 0, 0, 0, + 0, 8, 29, 5, 0, 1, 1, 1, 4, 3, 2, 24, 28, 5, 0, 5, 23, 7, 0, + 0,128, 0, 0, 0, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 27, 5, 0, 19, 0, 1,120,121,122, 15, 13, 0, 0, 0, 12, 1, 50, 0, 1, + 246, 1,224, 1,213, 1,187, 1,177, 1,155, 1,145, 1,119, 1,109, 1, 87, + 1, 76, 1, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 64, 5, 0, 7, 23, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 0, 0,128, 0, 0, 0, + 9, 63, 5, 0, 1, 0, 19, 15,120,121,122, 20, 62, 5, 0, 18, 7, 21, 85, + 102,119, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 8, 61, 5, 0, 1, + 1, 1, 4, 2, 3, 24, 60, 5, 0, 5, 7, 23, 0, 0,128, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 8, 59, 4, 0, 19, 1,120, + 121,122, 15, 20, 58, 5, 0, 21, 18, 7,102,111,117,114, 85,102,119, 64, 12, + 0, 0, 0, 0, 0, 0, 8, 57, 5, 0, 1, 1, 1, 3, 4, 2, 24, 56, 5, + 0, 23, 5, 7, 98,117, 98, 98, 97, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 9, 55, 5, 0, 0, 19, 1,120,121,122, 15, 20, 54, 5, 0, + 7, 21, 18, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 85,102,119, 8, + 53, 5, 0, 1, 1, 1, 2, 3, 4, 13, 0, 0, 0, 27, 0, 72, 0, 1,245, + 1,224, 1,212, 1,192, 1,181, 1,165, 1,152, 1,129, 1,118, 1, 97, 1, + 87, 1, 64, 1, 52, 1, 30, 1, 17, 0,252, 0,240, 0,223, 0,209, 0,185, + 0,173, 0,152, 0,141, 0,118, 0,106, 0, 84, 0, 72, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10, 27, 3, 21, 19,120, 50, 55,121,120,121,122, 20, 26, + 4, 21, 21, 7,120, 50, 54,121,102,111,117,114, 64, 12, 0, 0, 0, 0, 0, + 0, 10, 25, 4, 21, 1, 1,120, 50, 53,121, 3, 2, 21, 24, 4, 21, 23, 7, + 120, 50, 52,121, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, 0, 0, 9, 23, + 4, 21, 0, 1,120, 50, 51,121, 15, 19, 22, 4, 21, 7, 18,120, 50, 50,121, + 64, 12, 0, 0, 0, 0, 0, 0, 85,102,119, 10, 21, 4, 21, 1, 1,120, 50, + 49,121, 2, 4, 22, 20, 4, 21, 7, 5,120, 50, 48,121, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,128, 0, 0, 0, 12, 19, 4, 21, 1, 19,120, 49, 57,121, + 15,120,121,122, 15, 18, 4, 21, 18, 21,120, 49, 56,121, 85,102,119,102,111, + 117,114, 10, 17, 4, 21, 1, 1,120, 49, 55,121, 4, 3, 19, 16, 4, 21, 23, + 5,120, 49, 54,121, 98,117, 98, 98, 97, 0, 0,128, 0, 0, 0, 11, 15, 4, + 21, 0, 19,120, 49, 53,121,120,121,122, 20, 14, 4, 21, 7, 21,120, 49, 52, + 121, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 10, 13, 4, 21, 1, 1, + 120, 49, 51,121, 2, 3, 21, 12, 4, 21, 7, 23,120, 49, 50,121, 0, 0, 0, + 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 8, 11, 3, 21, 1,120, 49, 49,121, + 15, 19, 10, 4, 21, 18, 7,120, 49, 48,121, 85,102,119, 64, 12, 0, 0, 0, + 0, 0, 0, 9, 9, 4, 19, 1, 1,120, 57,121, 4, 2, 21, 8, 4, 19, 5, + 7,120, 56,121, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 7, 4, 19, 19, 1,120, 55,121,120,121,122, 15, 14, 6, 4, 19, 21, 18,120, + 54,121,102,111,117,114, 85,102,119, 9, 5, 4, 19, 1, 1,120, 53,121, 3, + 4, 18, 4, 4, 19, 23, 5,120, 52,121, 98,117, 98, 98, 97, 0, 0,128, 0, + 0, 0, 10, 3, 4, 19, 0, 19,120, 51,121,120,121,122, 19, 2, 4, 19, 7, + 21,120, 50,121, 64, 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 9, 1, 4, + 19, 1, 1,120, 49,121, 2, 3, 13, 0, 0, 0, 26, 0, 78, 0, 1,235, 1, + 223, 1,206, 1,192, 1,168, 1,156, 1,135, 1,124, 1,101, 1, 89, 1, 67, + 1, 55, 1, 34, 1, 22, 1, 5, 0,247, 0,223, 0,211, 0,190, 0,179, 0, + 156, 0,144, 0,123, 0,113, 0, 90, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 53, 4, 21, 1, 1,120, 53, 51, + 121, 2, 3, 21, 52, 4, 21, 7, 23,120, 53, 50,121, 0, 0, 0, 0, 0, 0, + 0, 0, 98,117, 98, 98, 97, 8, 51, 3, 21, 1,120, 53, 49,121, 15, 19, 50, + 4, 21, 18, 7,120, 53, 48,121, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0, + 10, 49, 4, 21, 1, 1,120, 52, 57,121, 4, 2, 21, 48, 4, 21, 23, 7,120, + 52, 56,121, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, 0, 0, 9, 47, 4, + 21, 0, 1,120, 52, 55,121, 15, 19, 46, 4, 21, 7, 18,120, 52, 54,121, 64, + 12, 0, 0, 0, 0, 0, 0, 85,102,119, 10, 45, 4, 21, 1, 1,120, 52, 53, + 121, 2, 4, 22, 44, 4, 21, 7, 5,120, 52, 52,121, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,128, 0, 0, 0, 12, 43, 4, 21, 1, 19,120, 52, 51,121, 15, + 120,121,122, 15, 42, 4, 21, 18, 21,120, 52, 50,121, 85,102,119,102,111,117, + 114, 10, 41, 4, 21, 1, 1,120, 52, 49,121, 4, 3, 19, 40, 4, 21, 5, 23, + 120, 52, 48,121, 0, 0,128, 0, 0, 0, 98,117, 98, 98, 97, 10, 39, 3, 21, + 19,120, 51, 57,121,120,121,122, 20, 38, 4, 21, 21, 7,120, 51, 56,121,102, + 111,117,114, 64, 12, 0, 0, 0, 0, 0, 0, 10, 37, 4, 21, 1, 1,120, 51, + 55,121, 3, 2, 21, 36, 4, 21, 23, 7,120, 51, 54,121, 98,117, 98, 98, 97, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 35, 4, 21, 0, 1,120, 51, 53,121, 15, + 19, 34, 4, 21, 7, 18,120, 51, 52,121, 64, 12, 0, 0, 0, 0, 0, 0, 85, + 102,119, 10, 33, 4, 21, 1, 1,120, 51, 51,121, 2, 4, 22, 32, 4, 21, 7, + 5,120, 51, 50,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, + 12, 31, 4, 21, 1, 19,120, 51, 49,121, 15,120,121,122, 15, 30, 4, 21, 18, + 21,120, 51, 48,121, 85,102,119,102,111,117,114, 10, 29, 4, 21, 1, 1,120, + 50, 57,121, 4, 3, 19, 28, 4, 21, 5, 23,120, 50, 56,121, 0, 0,128, 0, + 0, 0, 98,117, 98, 98, 97, 13, 0, 0, 0, 11, 1, 67, 0, 1,234, 1,221, + 1,200, 1,188, 1,171, 1,157, 1,133, 1,121, 1,100, 1, 90, 1, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 64, 4, 21, 7, 23,120, 54, 52,121, 0, 0, + 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 8, 63, 3, 21, 1,120, 54, 51, + 121, 15, 19, 62, 4, 21, 18, 7,120, 54, 50,121, 85,102,119, 64, 12, 0, 0, + 0, 0, 0, 0, 10, 61, 4, 21, 1, 1,120, 54, 49,121, 4, 2, 22, 60, 4, + 21, 5, 7,120, 54, 48,121, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 59, 4, 21, 19, 1,120, 53, 57,121,120,121,122, 15, 15, 58, 4, + 21, 21, 18,120, 53, 56,121,102,111,117,114, 85,102,119, 10, 57, 4, 21, 1, + 1,120, 53, 55,121, 3, 4, 19, 56, 4, 21, 23, 5,120, 53, 54,121, 98,117, + 98, 98, 97, 0, 0,128, 0, 0, 0, 11, 55, 4, 21, 0, 19,120, 53, 53,121, + 120,121,122, 20, 54, 4, 21, 7, 21,120, 53, 52,121, 64, 12, 0, 0, 0, 0, + 0, 0,102,111,117,114, 10, 0, 0, 0, 45, 0,112, 0, 1,247, 1,238, 1, + 229, 1,220, 1,211, 1,202, 1,193, 1,184, 1,175, 1,166, 1,159, 1,150, + 1,141, 1,132, 1,123, 1,114, 1,105, 1, 96, 1, 87, 1, 78, 1, 69, 1, + 61, 1, 52, 1, 43, 1, 34, 1, 25, 1, 16, 1, 7, 0,254, 0,245, 0,236, + 0,227, 0,219, 0,210, 0,201, 0,192, 0,183, 0,174, 0,165, 0,156, 0, + 147, 0,138, 0,129, 0,121, 0,112, 0,103, 0, 0, 0, 0, 0, 0, 9,120, + 53, 49,121, 51, 8, 3, 21, 1,120, 53, 48,121, 50, 7, 3, 19, 1,120, 52, + 121, 4, 8, 3, 21, 1,120, 52, 57,121, 49, 8, 3, 21, 1,120, 52, 56,121, + 48, 8, 3, 21, 1,120, 52, 55,121, 47, 8, 3, 21, 1,120, 52, 54,121, 46, + 8, 3, 21, 1,120, 52, 53,121, 45, 8, 3, 21, 1,120, 52, 52,121, 44, 8, + 3, 21, 1,120, 52, 51,121, 43, 8, 3, 21, 1,120, 52, 50,121, 42, 8, 3, + 21, 1,120, 52, 49,121, 41, 8, 3, 21, 1,120, 52, 48,121, 40, 7, 3, 19, + 1,120, 51,121, 3, 8, 3, 21, 1,120, 51, 57,121, 39, 8, 3, 21, 1,120, + 51, 56,121, 38, 8, 3, 21, 1,120, 51, 55,121, 37, 8, 3, 21, 1,120, 51, + 54,121, 36, 8, 3, 21, 1,120, 51, 53,121, 35, 8, 3, 21, 1,120, 51, 52, + 121, 34, 8, 3, 21, 1,120, 51, 51,121, 33, 8, 3, 21, 1,120, 51, 50,121, + 32, 8, 3, 21, 1,120, 51, 49,121, 31, 8, 3, 21, 1,120, 51, 48,121, 30, + 7, 3, 19, 1,120, 50,121, 2, 8, 3, 21, 1,120, 50, 57,121, 29, 8, 3, + 21, 1,120, 50, 56,121, 28, 8, 3, 21, 1,120, 50, 55,121, 27, 8, 3, 21, + 1,120, 50, 54,121, 26, 8, 3, 21, 1,120, 50, 53,121, 25, 8, 3, 21, 1, + 120, 50, 52,121, 24, 8, 3, 21, 1,120, 50, 51,121, 23, 8, 3, 21, 1,120, + 50, 50,121, 22, 8, 3, 21, 1,120, 50, 49,121, 21, 8, 3, 21, 1,120, 50, + 48,121, 20, 6, 3, 19, 9,120, 49,121, 8, 3, 21, 1,120, 49, 57,121, 19, + 8, 3, 21, 1,120, 49, 56,121, 18, 8, 3, 21, 1,120, 49, 55,121, 17, 8, + 3, 21, 1,120, 49, 54,121, 16, 8, 3, 21, 1,120, 49, 53,121, 15, 8, 3, + 21, 1,120, 49, 52,121, 14, 8, 3, 21, 1,120, 49, 51,121, 13, 8, 3, 21, + 1,120, 49, 50,121, 12, 8, 3, 21, 1,120, 49, 49,121, 11, 8, 3, 21, 1, + 120, 49, 48,121, 10, 10, 0, 0, 0, 18, 1, 99, 0, 1,247, 1,238, 1,229, + 1,220, 1,211, 1,202, 1,193, 1,184, 1,176, 1,167, 1,158, 1,149, 1, + 140, 1,131, 1,123, 1,115, 1,107, 1, 99, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 3, 19, 1,120, 57,121, 9, 7, 3, 19, 1,120, 56,121, 8, 7, 3, 19, 1, + 120, 55,121, 7, 7, 3, 19, 1,120, 54,121, 6, 8, 3, 21, 1,120, 54, 52, + 121, 64, 8, 3, 21, 1,120, 54, 51,121, 63, 8, 3, 21, 1,120, 54, 50,121, + 62, 8, 3, 21, 1,120, 54, 49,121, 61, 8, 3, 21, 1,120, 54, 48,121, 60, + 7, 3, 19, 1,120, 53,121, 5, 8, 3, 21, 1,120, 53, 57,121, 59, 8, 3, + 21, 1,120, 53, 56,121, 58, 8, 3, 21, 1,120, 53, 55,121, 57, 8, 3, 21, + 1,120, 53, 54,121, 56, 8, 3, 21, 1,120, 53, 53,121, 55, 8, 3, 21, 1, + 120, 53, 52,121, 54, 8, 3, 21, 1,120, 53, 51,121, 53, 8, 3, 21, 1,120, + 53, 50,121, 52, 13, 0, 0, 0, 23, 0, 54, 0, 1,240, 1,214, 1,197, 1, + 172, 1,156, 1,135, 1,117, 1, 89, 1, 73, 1, 55, 1, 41, 1, 14, 0,254, + 0,228, 0,211, 0,186, 0,170, 0,149, 0,131, 0,110, 0, 94, 0, 69, 0, + 54, 13, 23, 4, 7, 0, 1, 64, 57, 76,204,204,204,204,205, 15, 23, 22, 4, + 7, 7, 18, 64, 56, 51, 51, 51, 51, 51, 52, 64, 12, 0, 0, 0, 0, 0, 0, + 85,102,119, 14, 21, 4, 7, 1, 1, 64, 55, 25,153,153,153,153,154, 2, 4, + 19, 20, 4, 1, 7, 5, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, + 0, 0, 16, 19, 4, 7, 1, 19, 64, 52,230,102,102,102,102,103, 15,120,121, + 122, 19, 18, 4, 7, 18, 21, 64, 51,204,204,204,204,204,205, 85,102,119,102, + 111,117,114, 14, 17, 4, 7, 1, 1, 64, 50,179, 51, 51, 51, 51, 52, 4, 3, + 23, 16, 4, 7, 23, 5, 64, 49,153,153,153,153,153,154, 98,117, 98, 98, 97, + 0, 0,128, 0, 0, 0, 15, 15, 4, 7, 0, 19, 64, 48,128, 0, 0, 0, 0, + 0,120,121,122, 24, 14, 4, 7, 7, 21, 64, 46,204,204,204,204,204,206, 64, + 12, 0, 0, 0, 0, 0, 0,102,111,117,114, 14, 13, 4, 7, 1, 1, 64, 44, + 153,153,153,153,153,154, 2, 3, 25, 12, 4, 7, 7, 23, 64, 42,102,102,102, + 102,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 12, 11, 3, + 7, 1, 64, 40, 51, 51, 51, 51, 51, 52, 15, 16, 10, 4, 1, 18, 7, 11, 85, + 102,119, 64, 12, 0, 0, 0, 0, 0, 0, 14, 9, 4, 7, 1, 1, 64, 35,204, + 204,204,204,204,205, 4, 2, 26, 8, 4, 7, 5, 7, 64, 33,153,153,153,153, + 153,154, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 7, 4, + 7, 19, 1, 64, 30,204,204,204,204,204,206,120,121,122, 15, 19, 6, 4, 7, + 21, 18, 64, 26,102,102,102,102,102,103,102,111,117,114, 85,102,119, 14, 5, + 4, 7, 1, 1, 64, 22, 0, 0, 0, 0, 0, 0, 3, 4, 23, 4, 4, 7, 23, + 5, 64, 17,153,153,153,153,153,154, 98,117, 98, 98, 97, 0, 0,128, 0, 0, + 0, 15, 3, 4, 7, 0, 19, 64, 10,102,102,102,102,102,103,120,121,122, 24, + 2, 4, 7, 7, 21, 64, 1,153,153,153,153,153,154, 64, 12, 0, 0, 0, 0, + 0, 0,102,111,117,114, 14, 1, 4, 7, 1, 1, 63,241,153,153,153,153,153, + 154, 2, 3, 13, 0, 0, 0, 22, 0, 68, 0, 1,229, 1,213, 1,187, 1,171, + 1,146, 1,130, 1,116, 1, 98, 1, 70, 1, 54, 1, 29, 1, 14, 0,243, 0, + 227, 0,201, 0,185, 0,167, 0,151, 0,130, 0,112, 0, 84, 0, 68, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 45, 4, 7, 1, + 1, 64, 72,192, 0, 0, 0, 0, 1, 2, 4, 26, 44, 4, 7, 7, 5, 64, 72, + 51, 51, 51, 51, 51, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, + 0, 16, 43, 4, 7, 1, 19, 64, 71,166,102,102,102,102,103, 15,120,121,122, + 19, 42, 4, 7, 18, 21, 64, 71, 25,153,153,153,153,154, 85,102,119,102,111, + 117,114, 14, 41, 4, 7, 1, 1, 64, 70,140,204,204,204,204,205, 4, 3, 16, + 40, 4, 1, 5, 23, 44, 0, 0,128, 0, 0, 0, 98,117, 98, 98, 97, 14, 39, + 3, 7, 19, 64, 69,115, 51, 51, 51, 51, 52,120,121,122, 24, 38, 4, 7, 21, + 7, 64, 68,230,102,102,102,102,103,102,111,117,114, 64, 12, 0, 0, 0, 0, + 0, 0, 14, 37, 4, 7, 1, 1, 64, 68, 89,153,153,153,153,154, 3, 2, 25, + 36, 4, 7, 23, 7, 64, 67,204,204,204,204,204,205, 98,117, 98, 98, 97, 0, + 0, 0, 0, 0, 0, 0, 0, 13, 35, 4, 7, 0, 1, 64, 67, 64, 0, 0, 0, + 0, 0, 15, 23, 34, 4, 7, 7, 18, 64, 66,179, 51, 51, 51, 51, 52, 64, 12, + 0, 0, 0, 0, 0, 0, 85,102,119, 14, 33, 4, 7, 1, 1, 64, 66, 38,102, + 102,102,102,103, 2, 4, 26, 32, 4, 7, 7, 5, 64, 65,153,153,153,153,153, + 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 16, 31, 4, 7, + 1, 19, 64, 65, 12,204,204,204,204,205, 15,120,121,122, 12, 30, 4, 1, 18, + 21, 33, 85,102,119,102,111,117,114, 14, 29, 4, 7, 1, 1, 64, 63,230,102, + 102,102,102,103, 4, 3, 23, 28, 4, 7, 5, 23, 64, 62,204,204,204,204,204, + 206, 0, 0,128, 0, 0, 0, 98,117, 98, 98, 97, 14, 27, 3, 7, 19, 64, 61, + 179, 51, 51, 51, 51, 52,120,121,122, 24, 26, 4, 7, 21, 7, 64, 60,153,153, + 153,153,153,154,102,111,117,114, 64, 12, 0, 0, 0, 0, 0, 0, 14, 25, 4, + 7, 1, 1, 64, 59,128, 0, 0, 0, 0, 1, 3, 2, 25, 24, 4, 7, 23, 7, + 64, 58,102,102,102,102,102,103, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, + 0, 0, 13, 0, 0, 0, 19, 0,121, 0, 1,231, 1,216, 1,189, 1,173, 1, + 148, 1,134, 1,107, 1, 91, 1, 65, 1, 48, 1, 23, 1, 7, 0,242, 0,224, + 0,203, 0,187, 0,162, 0,148, 0,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 64, 4, 7, 7, 23, 64, 81,153,153, + 153,153,153,154, 0, 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 12, 63, + 3, 7, 1, 64, 81, 83, 51, 51, 51, 51, 52, 15, 23, 62, 4, 7, 18, 7, 64, + 81, 12,204,204,204,204,205, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0, 14, + 61, 4, 7, 1, 1, 64, 80,198,102,102,102,102,103, 4, 2, 19, 60, 4, 1, + 5, 7, 66, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 59, + 4, 7, 19, 1, 64, 80, 57,153,153,153,153,154,120,121,122, 15, 19, 58, 4, + 7, 21, 18, 64, 79,230,102,102,102,102,103,102,111,117,114, 85,102,119, 14, + 57, 4, 7, 1, 1, 64, 79, 89,153,153,153,153,154, 3, 4, 23, 56, 4, 7, + 23, 5, 64, 78,204,204,204,204,204,206, 98,117, 98, 98, 97, 0, 0,128, 0, + 0, 0, 15, 55, 4, 7, 0, 19, 64, 78, 64, 0, 0, 0, 0, 1,120,121,122, + 24, 54, 4, 7, 7, 21, 64, 77,179, 51, 51, 51, 51, 52, 64, 12, 0, 0, 0, + 0, 0, 0,102,111,117,114, 14, 53, 4, 7, 1, 1, 64, 77, 38,102,102,102, + 102,103, 2, 3, 25, 52, 4, 7, 7, 23, 64, 76,153,153,153,153,153,154, 0, + 0, 0, 0, 0, 0, 0, 0, 98,117, 98, 98, 97, 12, 51, 3, 7, 1, 64, 76, + 12,204,204,204,204,205, 15, 23, 50, 4, 7, 18, 7, 64, 75,128, 0, 0, 0, + 0, 1, 85,102,119, 64, 12, 0, 0, 0, 0, 0, 0, 14, 49, 4, 7, 1, 1, + 64, 74,243, 51, 51, 51, 51, 52, 4, 2, 25, 48, 4, 7, 23, 7, 64, 74,102, + 102,102,102,102,103, 98,117, 98, 98, 97, 0, 0, 0, 0, 0, 0, 0, 0, 13, + 47, 4, 7, 0, 1, 64, 73,217,153,153,153,153,154, 15, 23, 46, 4, 7, 7, + 18, 64, 73, 76,204,204,204,204,205, 64, 12, 0, 0, 0, 0, 0, 0, 85,102, + 119, 10, 0, 0, 0, 34, 0, 92, 0, 1,244, 1,231, 1,218, 1,205, 1,192, + 1,179, 1,166, 1,153, 1,140, 1,134, 1,121, 1,108, 1, 95, 1, 82, 1, + 69, 1, 56, 1, 43, 1, 30, 1, 17, 1, 11, 0,254, 0,241, 0,228, 0,215, + 0,202, 0,189, 0,176, 0,163, 0,150, 0,144, 0,131, 0,118, 0,105, 0, + 92, 0, 79, 0, 0, 0, 0, 13, 64, 67, 64, 0, 0, 0, 0, 0, 35, 12, 3, + 7, 1, 64, 66,179, 51, 51, 51, 51, 52, 34, 12, 3, 7, 1, 64, 66, 38,102, + 102,102,102,103, 33, 12, 3, 7, 1, 64, 65,153,153,153,153,153,154, 32, 12, + 3, 7, 1, 64, 65, 12,204,204,204,204,205, 31, 5, 3, 1, 1, 33, 30, 12, + 3, 7, 1, 64, 63,230,102,102,102,102,103, 29, 12, 3, 7, 1, 64, 62,204, + 204,204,204,204,206, 28, 12, 3, 7, 1, 64, 61,179, 51, 51, 51, 51, 52, 27, + 12, 3, 7, 1, 64, 60,153,153,153,153,153,154, 26, 12, 3, 7, 1, 64, 59, + 128, 0, 0, 0, 0, 1, 25, 12, 3, 7, 1, 64, 58,102,102,102,102,102,103, + 24, 12, 3, 7, 1, 64, 57, 76,204,204,204,204,205, 23, 12, 3, 7, 1, 64, + 56, 51, 51, 51, 51, 51, 52, 22, 12, 3, 7, 1, 64, 55, 25,153,153,153,153, + 154, 21, 5, 3, 1, 1, 22, 20, 12, 3, 7, 1, 64, 52,230,102,102,102,102, + 103, 19, 12, 3, 7, 1, 64, 51,204,204,204,204,204,205, 18, 12, 3, 7, 1, + 64, 50,179, 51, 51, 51, 51, 52, 17, 12, 3, 7, 1, 64, 49,153,153,153,153, + 153,154, 16, 12, 3, 7, 1, 64, 48,128, 0, 0, 0, 0, 0, 15, 12, 3, 7, + 1, 64, 46,204,204,204,204,204,206, 14, 12, 3, 7, 1, 64, 44,153,153,153, + 153,153,154, 13, 12, 3, 7, 1, 64, 42,102,102,102,102,102,103, 12, 12, 3, + 7, 1, 64, 40, 51, 51, 51, 51, 51, 52, 11, 5, 3, 1, 1, 11, 10, 12, 3, + 7, 1, 64, 35,204,204,204,204,204,205, 9, 12, 3, 7, 1, 64, 33,153,153, + 153,153,153,154, 8, 12, 3, 7, 1, 64, 30,204,204,204,204,204,206, 7, 12, + 3, 7, 1, 64, 26,102,102,102,102,102,103, 6, 12, 3, 7, 1, 64, 22, 0, + 0, 0, 0, 0, 0, 5, 12, 3, 7, 1, 64, 17,153,153,153,153,153,154, 4, + 12, 3, 7, 1, 64, 10,102,102,102,102,102,103, 3, 12, 3, 7, 1, 64, 1, + 153,153,153,153,153,154, 2, 11, 3, 7, 9, 63,241,153,153,153,153,153,154, + 10, 0, 0, 0, 29, 0,149, 0, 1,243, 1,230, 1,217, 1,204, 1,198, 1, + 185, 1,172, 1,159, 1,146, 1,133, 1,120, 1,107, 1, 94, 1, 81, 1, 68, + 1, 55, 1, 42, 1, 29, 1, 16, 1, 3, 0,246, 0,233, 0,220, 0,207, 0, + 201, 0,188, 0,175, 0,162, 0,149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 3, 7, + 1, 64, 81,153,153,153,153,153,154, 64, 12, 3, 7, 1, 64, 81, 83, 51, 51, + 51, 51, 52, 63, 12, 3, 7, 1, 64, 81, 12,204,204,204,204,205, 62, 12, 3, + 7, 1, 64, 80,198,102,102,102,102,103, 61, 5, 3, 1, 1, 66, 60, 12, 3, + 7, 1, 64, 80, 57,153,153,153,153,154, 59, 12, 3, 7, 1, 64, 79,230,102, + 102,102,102,103, 58, 12, 3, 7, 1, 64, 79, 89,153,153,153,153,154, 57, 12, + 3, 7, 1, 64, 78,204,204,204,204,204,206, 56, 12, 3, 7, 1, 64, 78, 64, + 0, 0, 0, 0, 1, 55, 12, 3, 7, 1, 64, 77,179, 51, 51, 51, 51, 52, 54, + 12, 3, 7, 1, 64, 77, 38,102,102,102,102,103, 53, 12, 3, 7, 1, 64, 76, + 153,153,153,153,153,154, 52, 12, 3, 7, 1, 64, 76, 12,204,204,204,204,205, + 51, 12, 3, 7, 1, 64, 75,128, 0, 0, 0, 0, 1, 50, 12, 3, 7, 1, 64, + 74,243, 51, 51, 51, 51, 52, 49, 12, 3, 7, 1, 64, 74,102,102,102,102,102, + 103, 48, 12, 3, 7, 1, 64, 73,217,153,153,153,153,154, 47, 12, 3, 7, 1, + 64, 73, 76,204,204,204,204,205, 46, 12, 3, 7, 1, 64, 72,192, 0, 0, 0, + 0, 1, 45, 12, 3, 7, 1, 64, 72, 51, 51, 51, 51, 51, 52, 44, 12, 3, 7, + 1, 64, 71,166,102,102,102,102,103, 43, 12, 3, 7, 1, 64, 71, 25,153,153, + 153,153,154, 42, 12, 3, 7, 1, 64, 70,140,204,204,204,204,205, 41, 5, 3, + 1, 1, 44, 40, 12, 3, 7, 1, 64, 69,115, 51, 51, 51, 51, 52, 39, 12, 3, + 7, 1, 64, 68,230,102,102,102,102,103, 38, 12, 3, 7, 1, 64, 68, 89,153, + 153,153,153,154, 37, 12, 3, 7, 1, 64, 67,204,204,204,204,204,205, 36, 10, + 0, 0, 0, 41, 0,103, 0, 1,250, 1,235, 1,227, 1,218, 1,211, 1,202, + 1,192, 1,179, 1,172, 1,157, 1,149, 1,141, 1,132, 1,125, 1,116, 1, + 106, 1, 93, 1, 86, 1, 74, 1, 63, 1, 47, 1, 40, 1, 31, 1, 16, 1, 8, + 0,255, 0,248, 0,239, 0,229, 0,216, 0,209, 0,197, 0,186, 0,174, 0, + 158, 0,151, 0,136, 0,128, 0,119, 0,112, 0,103, 0, 93, 0, 0, 0, 0, + 10, 55, 44, 78, 85, 76, 76, 8, 2, 25, 52, 54, 44, 51, 46, 53, 6, 2, 21, + 52, 53, 44, 50, 8, 2, 25, 52, 52, 44, 48, 46, 48, 7, 2, 23, 52, 51, 44, + 49, 53, 14, 2, 37, 52, 50, 44, 88, 39, 53, 53, 54, 54, 55, 55, 39, 6, 2, + 21, 52, 49, 44, 52, 15, 2, 39, 52, 48, 44, 50, 49, 52, 55, 52, 56, 51, 54, + 52, 56, 11, 2, 31, 52, 44, 39, 98,117, 98, 98, 97, 39, 10, 2, 29, 51, 57, + 44, 39,120,121,122, 39, 11, 2, 31, 51, 56, 44, 39,102,111,117,114, 39, 6, + 2, 21, 51, 55, 44, 51, 12, 2, 33, 51, 54, 44, 39, 98,117, 98, 98, 97, 39, + 9, 2, 27, 51, 53, 44, 78, 85, 76, 76, 8, 2, 25, 51, 52, 44, 51, 46, 53, + 6, 2, 21, 51, 51, 44, 50, 8, 2, 25, 51, 50, 44, 48, 46, 48, 7, 2, 23, + 51, 49, 44, 49, 53, 14, 2, 37, 51, 48, 44, 88, 39, 53, 53, 54, 54, 55, 55, + 39, 8, 2, 25, 51, 44, 78, 85, 76, 76, 6, 2, 21, 50, 57, 44, 52, 15, 2, + 39, 50, 56, 44, 50, 49, 52, 55, 52, 56, 51, 54, 52, 56, 10, 2, 29, 50, 55, + 44, 39,120,121,122, 39, 11, 2, 31, 50, 54, 44, 39,102,111,117,114, 39, 6, + 2, 21, 50, 53, 44, 51, 12, 2, 33, 50, 52, 44, 39, 98,117, 98, 98, 97, 39, + 9, 2, 27, 50, 51, 44, 78, 85, 76, 76, 8, 2, 25, 50, 50, 44, 51, 46, 53, + 6, 2, 21, 50, 49, 44, 50, 8, 2, 25, 50, 48, 44, 48, 46, 48, 7, 2, 23, + 50, 44, 51, 46, 53, 7, 2, 23, 49, 57, 44, 49, 53, 14, 2, 37, 49, 56, 44, + 88, 39, 53, 53, 54, 54, 55, 55, 39, 6, 2, 21, 49, 55, 44, 52, 12, 2, 33, + 49, 54, 44, 39, 98,117, 98, 98, 97, 39, 9, 2, 27, 49, 53, 44, 78, 85, 76, + 76, 8, 2, 25, 49, 52, 44, 51, 46, 53, 6, 2, 21, 49, 51, 44, 50, 8, 2, + 25, 49, 50, 44, 48, 46, 48, 7, 2, 23, 49, 49, 44, 49, 53, 14, 2, 37, 49, + 48, 44, 88, 39, 53, 53, 54, 54, 55, 55, 39, 5, 2, 19, 49, 44, 50, 10, 0, + 0, 0, 22, 1, 32, 0, 1,243, 1,236, 1,230, 1,215, 1,207, 1,198, 1, + 191, 1,182, 1,172, 1,159, 1,152, 1,140, 1,129, 1,118, 1,102, 1, 95, + 1, 80, 1, 72, 1, 63, 1, 53, 1, 38, 1, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 2, 19, 57, 44, 52, 14, 2, 37, 56, 44, 50, 49, 52, 55, 52, 56, 51, + 54, 52, 56, 9, 2, 27, 55, 44, 39,120,121,122, 39, 8, 2, 25, 54, 52, 44, + 48, 46, 48, 7, 2, 23, 54, 51, 44, 49, 53, 14, 2, 37, 54, 50, 44, 88, 39, + 53, 53, 54, 54, 55, 55, 39, 6, 2, 21, 54, 49, 44, 52, 15, 2, 39, 54, 48, + 44, 50, 49, 52, 55, 52, 56, 51, 54, 52, 56, 10, 2, 29, 54, 44, 39,102,111, + 117,114, 39, 10, 2, 29, 53, 57, 44, 39,120,121,122, 39, 11, 2, 31, 53, 56, + 44, 39,102,111,117,114, 39, 6, 2, 21, 53, 55, 44, 51, 12, 2, 33, 53, 54, + 44, 39, 98,117, 98, 98, 97, 39, 9, 2, 27, 53, 53, 44, 78, 85, 76, 76, 8, + 2, 25, 53, 52, 44, 51, 46, 53, 6, 2, 21, 53, 51, 44, 50, 8, 2, 25, 53, + 50, 44, 48, 46, 48, 7, 2, 23, 53, 49, 44, 49, 53, 14, 2, 37, 53, 48, 44, + 88, 39, 53, 53, 54, 54, 55, 55, 39, 5, 2, 19, 53, 44, 51, 6, 2, 21, 52, + 57, 44, 52, 12, 2, 33, 52, 56, 44, 39, 98,117, 98, 98, 97, 39, +}; + +/* Help message */ +static const char zHelp[] = + "Usage:\n" + " sessionfuzz setup -- Generate seed files c1.txt, c2.txt, etc.\n" + " sessionfuzz run FILE ... -- Run against fuzzed changeset FILE\n" + " sessionfuzz run SQLAR ... -- Run against all files in the SQL Archive\n" +; + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include "zlib.h" + +/* +** Implementation of the "sqlar_uncompress(X,SZ)" SQL function +** +** Parameter SZ is interpreted as an integer. If it is less than or +** equal to zero, then this function returns a copy of X. Or, if +** SZ is equal to the size of X when interpreted as a blob, also +** return a copy of X. Otherwise, decompress blob X using zlib +** utility function uncompress() and return the results (another +** blob). +*/ +static void sqlarUncompressFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + uLong nData; + uLongf sz; + + assert( argc==2 ); + sz = sqlite3_value_int(argv[1]); + + if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){ + sqlite3_result_value(context, argv[0]); + }else{ + const Bytef *pData= sqlite3_value_blob(argv[0]); + Bytef *pOut = sqlite3_malloc(sz); + if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){ + sqlite3_result_error(context, "error in uncompress()", -1); + }else{ + sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT); + } + sqlite3_free(pOut); + } +} + + +/* Run a chunk of SQL. If any errors happen, print an error message +** and exit. +*/ +static void runSql(sqlite3 *db, const char *zSql){ + int rc; + char *zErr = 0; + rc = sqlite3_exec(db, zSql, 0, 0, &zErr); + if( rc || zErr ){ + fprintf(stderr, "SQL failed: rc=%d zErr=[%s]\n", rc, zErr); + fprintf(stderr, "SQL: [%s]\n", zSql); + exit(1); + } +} + +/* +** Write buffer to disk +*/ +static void writeFile(const char *zFilename, const void *pData, int nData){ + FILE *out; + int n; + out = fopen(zFilename, "wb"); + if( out==0 ){ + fprintf(stderr, "cannot open \"%s\" for writing\n", zFilename); + exit(1); + } + n = (int)fwrite(pData, 1, nData, out); + fclose(out); + if( n!=nData ){ + fprintf(stderr, "only wrote %d of %d bytes to \"%s\"\n",n,nData,zFilename); + exit(1); + } +} + +/* +** Generate a changeset from session pSess and write it to zFile +*/ +static void makeChangeset(const char *zFile, sqlite3_session *pSess){ + void *pChg; + int nChg; + int rc; + rc = sqlite3session_changeset(pSess, &nChg, &pChg); + if( rc ){ + fprintf(stderr, "sqlite3session_changeset() returned %d\n", rc); + exit(1); + } + writeFile(zFile, pChg, nChg); + sqlite3_free(pChg); +} + +/* +** Read a file from disk. Space to hold the answer is obtained from +** sqlite3_malloc64(). +*/ +static void readFile(const char *zName, void **ppData, int *pnData){ + FILE *in = fopen(zName, "rb"); + long nIn; + size_t nRead; + char *pBuf; + *ppData = 0; + *pnData = 0; + if( in==0 ){ + fprintf(stderr, "Cannot open \"%s\" for reading\n", zName); + exit(1); + } + fseek(in, 0, SEEK_END); + nIn = ftell(in); + rewind(in); + pBuf = sqlite3_malloc64( nIn+1 ); + if( pBuf==0 ){ + fprintf(stderr, "Failed to malloc %lld bytes\n", (sqlite3_int64)(nIn+1)); + exit(1); + } + nRead = fread(pBuf, 1, nIn, in); + fclose(in); + if( nRead!=(size_t)nIn ){ + fprintf(stderr, "Read only %d of %d bytes from %s\n", (int)nRead, (int)nIn, + zName); + exit(1); + } + pBuf[nIn] = 0; + *pnData = nIn; + *ppData = pBuf; +} + +/* +** The conflict callback +*/ +static int conflictCall( + void *NotUsed, + int eConflict, + sqlite3_changeset_iter *p +){ + (void)NotUsed; + (void)p; + printf("Conflict %d\n", eConflict); + return SQLITE_CHANGESET_OMIT; +} + +/* +** Reset the database file +*/ +static void db_reset(sqlite3 *db){ + unsigned char *pData; + int nData; + int rc; + + nData = sizeof(aDbBytes); + pData = sqlite3_malloc64( nData ); + if( pData==0 ){ + fprintf(stderr, "could not allocate %d bytes\n", nData); + exit(1); + } + memcpy(pData, aDbBytes, nData); + rc = sqlite3_deserialize(db, 0, pData, nData, nData, + SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE); + if( rc ){ + fprintf(stderr, "sqlite3_deserialize() failed with %d: %s\n", + rc, sqlite3_errmsg(db)); + exit(1); + } +} + +/* +** Given a full file pathname, return a pointer to the tail. +** Example: +** +** input: /home/drh/sqlite/abc.db +** output: abc.db +*/ +static const char *fileTail(const char *z){ + const char *zOut = z; + while( z[0] ){ + if( z[0]=='/' && z[1]!=0 ) zOut = &z[1]; + z++; + } + return zOut; +} + +int main(int argc, char **argv){ + const char *zCmd; + sqlite3 *db; + int rc; + sqlite3_session *pSess; + sqlite3_stmt *pStmt; + void *pChgset; + int nChgset; + int bVerbose = 0; + + if( argc<2 ){ + fprintf(stderr, "%s", zHelp); + exit(1); + } + rc = sqlite3_open_v2(":memory:",&db, + SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, "memdb"); + if( rc ){ + fprintf(stderr, "Failed to open :memory: database: %s\n", + sqlite3_errmsg(db)); + exit(1); + } + db_reset(db); + zCmd = argv[1]; + if( strcmp(zCmd, "setup")==0 ){ + if( argc!=2 ){ + fprintf(stdout, "Wrong number of arguments.\n%s", zHelp); + exit(1); + } + runSql(db, zFillSql); + rc = sqlite3session_create(db, "main", &pSess); + if( rc ){ + fprintf(stderr, "sqlite3session_create() returns %d\n", rc); + exit(1); + } + rc = sqlite3session_attach(pSess, 0); + if( rc ){ + fprintf(stderr, "sqlite3session_attach(db,0) returns %d\n", rc); + exit(1); + } + runSql(db, "INSERT INTO t4(z) VALUES('');"); + makeChangeset("c1.txt", pSess); + runSql(db, + "UPDATE t1 SET b=c, c=b WHERE a IN (5,7);\n" + "DELETE FROM t2 WHERE rowid IN (8,2);\n" + "INSERT OR IGNORE INTO t4 SELECT b FROM t1 WHERE b IS TRUE LIMIT 2;"); + makeChangeset("c2.txt", pSess); + runSql(db, "UPDATE t3 SET x=y, y=NULL WHERE rowid IN (1,3);"); + makeChangeset("c3.txt", pSess); + sqlite3session_delete(pSess); + }else + if( strcmp(zCmd, "run")==0 ){ + int i; + if( argc<3 ){ + fprintf(stdout, "Wrong number of arguments.\n%s", zHelp); + exit(1); + } + for(i=2; i<argc; i++){ + if( strcmp(argv[i],"-v")==0 ){ + bVerbose = 1; + continue; + } + readFile(argv[i], &pChgset, &nChgset); + if( nChgset >= 512 + && memcmp(pChgset, "SQLite format 3", 16)==0 + ){ + sqlite3 *db2; + sqlite3_stmt *pStmt2; + int nCase = 0; + /* This file is an SQL Archive containing many changesets */ + if( !bVerbose ){ printf("%s: ", fileTail(argv[i])); fflush(stdout); } + sqlite3_open_v2(":memory:", &db2, + SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, "memdb"); + sqlite3_deserialize(db2, 0, pChgset, nChgset, nChgset, + SQLITE_DESERIALIZE_READONLY | SQLITE_DESERIALIZE_FREEONCLOSE); + sqlite3_create_function(db2, "sqlar_uncompress", 2, SQLITE_UTF8, 0, + sqlarUncompressFunc, 0, 0); + rc = sqlite3_prepare_v2(db2, "SELECT name, sqlar_uncompress(data,sz)" + " FROM sqlar", -1, &pStmt2, 0); + if( rc ){ + fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db2)); + exit(1); + } + while( SQLITE_ROW==sqlite3_step(pStmt2) ){ + if( bVerbose ){ + printf("%s/%s:", fileTail(argv[i]), sqlite3_column_text(pStmt2,0)); + fflush(stdout); + } + runSql(db, "BEGIN"); + pChgset = (unsigned char*)sqlite3_column_blob(pStmt2, 1); + nChgset = sqlite3_column_bytes(pStmt2, 1); + rc = sqlite3changeset_apply(db, nChgset, pChgset, 0, conflictCall, 0); + if( bVerbose ){ + printf(" Ok. rc=%d\n", rc); + fflush(stdout); + } + runSql(db, "ROLLBACK"); + nCase++; + } + sqlite3_finalize(pStmt2); + sqlite3_close(db2); + if( bVerbose ) printf("%s: ", fileTail(argv[i])); + printf(" %d cases, 0 crashes\n", nCase); + fflush(stdout); + }else{ + /* The named file is just an ordinary changeset */ + printf("%s:", fileTail(argv[i])); + fflush(stdout); + runSql(db, "BEGIN"); + rc = sqlite3changeset_apply(db, nChgset, pChgset, 0, conflictCall, 0); + printf(" %d\n", rc); + fflush(stdout); + runSql(db, "ROLLBACK"); + sqlite3_free(pChgset); + } + } + }else + { + fprintf(stderr, "%s", zHelp); + exit(1); + } + rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check;", -1, &pStmt, 0); + if( rc ){ + fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + exit(1); + } + if( sqlite3_step(pStmt)!=SQLITE_ROW + || strcmp((const char*)sqlite3_column_text(pStmt,0),"ok")!=0 + ){ + fprintf(stderr, "Integrity check failed!\n"); + do{ + fprintf(stderr, "%s\n", sqlite3_column_text(pStmt,0)); + }while( sqlite3_step(pStmt)==SQLITE_ROW ); + } + sqlite3_finalize(pStmt); + sqlite3_close(db); + if( sqlite3_memory_used()>0 ){ + fprintf(stderr, "memory leak of %lld bytes\n", + sqlite3_memory_used()); + exit(1); + } + return 0; +}
diff --git a/third_party/sqlite/src/test/shell1.test b/third_party/sqlite/src/test/shell1.test index 81883a8d..9b5fdd4 100644 --- a/third_party/sqlite/src/test/shell1.test +++ b/third_party/sqlite/src/test/shell1.test
@@ -1097,4 +1097,46 @@ } {} } +db close +forcedelete test.db test.db-journal test.db-wal +sqlite3 db test.db + +# The shell tool ".schema" command uses virtual table "pragma_database_list" +# +ifcapable vtab { + +do_test shell1-7.1.1 { + db eval { + CREATE TABLE Z (x TEXT PRIMARY KEY); + CREATE TABLE _ (x TEXT PRIMARY KEY); + CREATE TABLE YY (x TEXT PRIMARY KEY); + CREATE TABLE __ (x TEXT PRIMARY KEY); + CREATE TABLE WWW (x TEXT PRIMARY KEY); + CREATE TABLE ___ (x TEXT PRIMARY KEY); + } +} {} +do_test shell1-7.1.2 { + catchcmd "test.db" ".schema _" +} {0 {CREATE TABLE Z (x TEXT PRIMARY KEY); +CREATE TABLE _ (x TEXT PRIMARY KEY);}} +do_test shell1-7.1.3 { + catchcmd "test.db" ".schema \\\\_" +} {0 {CREATE TABLE _ (x TEXT PRIMARY KEY);}} +do_test shell1-7.1.4 { + catchcmd "test.db" ".schema __" +} {0 {CREATE TABLE YY (x TEXT PRIMARY KEY); +CREATE TABLE __ (x TEXT PRIMARY KEY);}} +do_test shell1-7.1.5 { + catchcmd "test.db" ".schema \\\\_\\\\_" +} {0 {CREATE TABLE __ (x TEXT PRIMARY KEY);}} +do_test shell1-7.1.6 { + catchcmd "test.db" ".schema ___" +} {0 {CREATE TABLE WWW (x TEXT PRIMARY KEY); +CREATE TABLE ___ (x TEXT PRIMARY KEY);}} +do_test shell1-7.1.7 { + catchcmd "test.db" ".schema \\\\_\\\\_\\\\_" +} {0 {CREATE TABLE ___ (x TEXT PRIMARY KEY);}} + +} + finish_test
diff --git a/third_party/sqlite/src/test/sort5.test b/third_party/sqlite/src/test/sort5.test index baac655..2021583 100644 --- a/third_party/sqlite/src/test/sort5.test +++ b/third_party/sqlite/src/test/sort5.test
@@ -73,6 +73,7 @@ forcedelete test.db sqlite3 db test.db -vfs tvfs execsql { CREATE TABLE t1(x) } +execsql { PRAGMA temp_store = 1 } # Each iteration of the following loop attempts to sort 10001 records # each a bit over 100 bytes in size. In total a little more than 1MiB @@ -88,6 +89,9 @@ 5 4096 -9000 0 6 1024 -9000 0 } { + if {$::TEMP_STORE>2} { + set bTemp 0 + } do_execsql_test 2.$tn.0 " PRAGMA page_size = $pgsz; VACUUM;
diff --git a/third_party/sqlite/src/test/speed4p.test b/third_party/sqlite/src/test/speed4p.test index 45788c6..ba7ed8f 100644 --- a/third_party/sqlite/src/test/speed4p.test +++ b/third_party/sqlite/src/test/speed4p.test
@@ -168,7 +168,6 @@ set script { db eval BEGIN for {set ii 1} {$ii < 10000} {incr ii} { - set v [expr {$ii*3}] db eval {UPDATE t1 SET i=i+1 WHERE rowid=$ii} } db eval COMMIT
diff --git a/third_party/sqlite/src/test/speedtest1.c b/third_party/sqlite/src/test/speedtest1.c index a80bd5f..a830560d 100644 --- a/third_party/sqlite/src/test/speedtest1.c +++ b/third_party/sqlite/src/test/speedtest1.c
@@ -1646,6 +1646,207 @@ } /* +*/ +void testset_trigger(void){ + int jj, ii; + char zNum[2000]; /* A number name */ + + const int NROW = 500*g.szTest; + const int NROW2 = 100*g.szTest; + + speedtest1_exec( + "BEGIN;" + "CREATE TABLE t1(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);" + "CREATE TABLE t2(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);" + "CREATE TABLE t3(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);" + "CREATE VIEW v1 AS SELECT rowid, i, t FROM t1;" + "CREATE VIEW v2 AS SELECT rowid, i, t FROM t2;" + "CREATE VIEW v3 AS SELECT rowid, i, t FROM t3;" + ); + for(jj=1; jj<=3; jj++){ + speedtest1_prepare("INSERT INTO t%d VALUES(NULL,?1,?2)", jj); + for(ii=0; ii<NROW; ii++){ + int x1 = speedtest1_random() % NROW; + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + } + speedtest1_exec( + "CREATE INDEX i1 ON t1(t);" + "CREATE INDEX i2 ON t2(t);" + "CREATE INDEX i3 ON t3(t);" + "COMMIT;" + ); + + speedtest1_begin_test(100, "speed4p-join1"); + speedtest1_prepare( + "SELECT * FROM t1, t2, t3 WHERE t1.oid = t2.oid AND t2.oid = t3.oid" + ); + speedtest1_run(); + speedtest1_end_test(); + + speedtest1_begin_test(110, "speed4p-join2"); + speedtest1_prepare( + "SELECT * FROM t1, t2, t3 WHERE t1.t = t2.t AND t2.t = t3.t" + ); + speedtest1_run(); + speedtest1_end_test(); + + speedtest1_begin_test(120, "speed4p-view1"); + for(jj=1; jj<=3; jj++){ + speedtest1_prepare("SELECT * FROM v%d WHERE rowid = ?", jj); + for(ii=0; ii<NROW2; ii+=3){ + sqlite3_bind_int(g.pStmt, 1, ii*3); + speedtest1_run(); + } + } + speedtest1_end_test(); + + speedtest1_begin_test(130, "speed4p-table1"); + for(jj=1; jj<=3; jj++){ + speedtest1_prepare("SELECT * FROM t%d WHERE rowid = ?", jj); + for(ii=0; ii<NROW2; ii+=3){ + sqlite3_bind_int(g.pStmt, 1, ii*3); + speedtest1_run(); + } + } + speedtest1_end_test(); + + speedtest1_begin_test(140, "speed4p-table1"); + for(jj=1; jj<=3; jj++){ + speedtest1_prepare("SELECT * FROM t%d WHERE rowid = ?", jj); + for(ii=0; ii<NROW2; ii+=3){ + sqlite3_bind_int(g.pStmt, 1, ii*3); + speedtest1_run(); + } + } + speedtest1_end_test(); + + speedtest1_begin_test(150, "speed4p-subselect1"); + speedtest1_prepare("SELECT " + "(SELECT t FROM t1 WHERE rowid = ?1)," + "(SELECT t FROM t2 WHERE rowid = ?1)," + "(SELECT t FROM t3 WHERE rowid = ?1)" + ); + for(jj=0; jj<NROW2; jj++){ + sqlite3_bind_int(g.pStmt, 1, jj*3); + speedtest1_run(); + } + speedtest1_end_test(); + + speedtest1_begin_test(160, "speed4p-rowid-update"); + speedtest1_exec("BEGIN"); + speedtest1_prepare("UPDATE t1 SET i=i+1 WHERE rowid=?1"); + for(jj=0; jj<NROW2; jj++){ + sqlite3_bind_int(g.pStmt, 1, jj); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + speedtest1_exec("CREATE TABLE t5(t TEXT PRIMARY KEY, i INTEGER);"); + speedtest1_begin_test(170, "speed4p-insert-ignore"); + speedtest1_exec("INSERT OR IGNORE INTO t5 SELECT t, i FROM t1"); + speedtest1_end_test(); + + speedtest1_exec( + "CREATE TABLE log(op TEXT, r INTEGER, i INTEGER, t TEXT);" + "CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);" + "CREATE TRIGGER t4_trigger1 AFTER INSERT ON t4 BEGIN" + " INSERT INTO log VALUES('INSERT INTO t4', new.rowid, new.i, new.t);" + "END;" + "CREATE TRIGGER t4_trigger2 AFTER UPDATE ON t4 BEGIN" + " INSERT INTO log VALUES('UPDATE OF t4', new.rowid, new.i, new.t);" + "END;" + "CREATE TRIGGER t4_trigger3 AFTER DELETE ON t4 BEGIN" + " INSERT INTO log VALUES('DELETE OF t4', old.rowid, old.i, old.t);" + "END;" + "BEGIN;" + ); + + speedtest1_begin_test(180, "speed4p-trigger1"); + speedtest1_prepare("INSERT INTO t4 VALUES(NULL, ?1, ?2)"); + for(jj=0; jj<NROW2; jj++){ + speedtest1_numbername(jj, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, jj); + sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_end_test(); + + /* + ** Note: Of the queries, only half actually update a row. This property + ** was copied over from speed4p.test, where it was probably introduced + ** inadvertantly. + */ + speedtest1_begin_test(190, "speed4p-trigger2"); + speedtest1_prepare("UPDATE t4 SET i = ?1, t = ?2 WHERE rowid = ?3"); + for(jj=1; jj<=NROW2*2; jj+=2){ + speedtest1_numbername(jj*2, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, jj*2); + sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC); + sqlite3_bind_int(g.pStmt, 3, jj); + speedtest1_run(); + } + speedtest1_end_test(); + + /* + ** Note: Same again. + */ + speedtest1_begin_test(200, "speed4p-trigger3"); + speedtest1_prepare("DELETE FROM t4 WHERE rowid = ?1"); + for(jj=1; jj<=NROW2*2; jj+=2){ + sqlite3_bind_int(g.pStmt, 1, jj*2); + speedtest1_run(); + } + speedtest1_end_test(); + speedtest1_exec("COMMIT"); + + /* + ** The following block contains the same tests as the above block that + ** tests triggers, with one crucial difference: no triggers are defined. + ** So the difference in speed between these tests and the preceding ones + ** is the amount of time taken to compile and execute the trigger programs. + */ + speedtest1_exec( + "DROP TABLE t4;" + "DROP TABLE log;" + "VACUUM;" + "CREATE TABLE t4(rowid INTEGER PRIMARY KEY, i INTEGER, t TEXT);" + "BEGIN;" + ); + speedtest1_begin_test(210, "speed4p-notrigger1"); + speedtest1_prepare("INSERT INTO t4 VALUES(NULL, ?1, ?2)"); + for(jj=0; jj<NROW2; jj++){ + speedtest1_numbername(jj, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, jj); + sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_end_test(); + speedtest1_begin_test(210, "speed4p-notrigger2"); + speedtest1_prepare("UPDATE t4 SET i = ?1, t = ?2 WHERE rowid = ?3"); + for(jj=1; jj<=NROW2*2; jj+=2){ + speedtest1_numbername(jj*2, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, jj*2); + sqlite3_bind_text(g.pStmt, 2, zNum, -1, SQLITE_STATIC); + sqlite3_bind_int(g.pStmt, 3, jj); + speedtest1_run(); + } + speedtest1_end_test(); + speedtest1_begin_test(220, "speed4p-notrigger3"); + speedtest1_prepare("DELETE FROM t4 WHERE rowid = ?1"); + for(jj=1; jj<=NROW2*2; jj+=2){ + sqlite3_bind_int(g.pStmt, 1, jj*2); + speedtest1_run(); + } + speedtest1_end_test(); + speedtest1_exec("COMMIT"); +} + +/* ** A testset used for debugging speedtest1 itself. */ void testset_debug1(void){ @@ -1945,6 +2146,8 @@ testset_cte(); }else if( strcmp(zTSet,"fp")==0 ){ testset_fp(); + }else if( strcmp(zTSet,"trigger")==0 ){ + testset_trigger(); }else if( strcmp(zTSet,"rtree")==0 ){ #ifdef SQLITE_ENABLE_RTREE testset_rtree(6, 147); @@ -1953,7 +2156,8 @@ "the R-Tree tests\n"); #endif }else{ - fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree fp\n", + fatal_error("unknown testset: \"%s\"\n" + "Choices: cte debug1 fp main orm rtree trigger\n", zTSet); } speedtest1_final();
diff --git a/third_party/sqlite/src/test/spellfix.test b/third_party/sqlite/src/test/spellfix.test index f9cb797..d198b9b 100644 --- a/third_party/sqlite/src/test/spellfix.test +++ b/third_party/sqlite/src/test/spellfix.test
@@ -279,7 +279,7 @@ do_tracesql_test 6.2.3 { SELECT word, distance FROM t3 WHERE rowid = 10 AND word MATCH 'kiiner'; } {keener 300 - {SELECT id, word, rank, k1 FROM "main"."t3_vocab" WHERE langid=0 AND k2>=?1 AND k2<?2} + {SELECT id, word, rank, coalesce(k1,word) FROM "main"."t3_vocab" WHERE langid=0 AND k2>=?1 AND k2<?2} } }
diff --git a/third_party/sqlite/src/test/spellfix4.test b/third_party/sqlite/src/test/spellfix4.test new file mode 100644 index 0000000..caf6d51 --- /dev/null +++ b/third_party/sqlite/src/test/spellfix4.test
@@ -0,0 +1,353 @@ +# 2018-02-14 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Test cases for the editdist3() function in the spellfix extension. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix spellfix4 + +ifcapable !vtab { finish_test ; return } + +load_static_extension db spellfix + +do_execsql_test 100 { + CREATE TABLE cost1(iLang, cFrom, cTo, iCost); + INSERT INTO cost1 VALUES + (0, '', '?', 97), + (0, '?', '', 98), + (0, '?', '?', 99), + (0, 'm', 'n', 50), + (0, 'n', 'm', 50) + ; + SELECT editdist3('cost1'); + SELECT editdist3('anchor','amchor'); +} {{} 50} +do_execsql_test 110 { + SELECT editdist3('anchor','anchoxr'); +} {97} +do_execsql_test 111 { + SELECT editdist3('anchor','xanchor'); +} {97} +do_execsql_test 112 { + SELECT editdist3('anchor','anchorx'); +} {97} +do_execsql_test 120 { + SELECT editdist3('anchor','anchr'); +} {98} +do_execsql_test 121 { + SELECT editdist3('anchor','ancho'); +} {98} +do_execsql_test 122 { + SELECT editdist3('anchor','nchor'); +} {98} +do_execsql_test 130 { + SELECT editdist3('anchor','anchur'); +} {99} +do_execsql_test 131 { + SELECT editdist3('anchor','onchor'); +} {99} +do_execsql_test 132 { + SELECT editdist3('anchor','anchot'); +} {99} +do_execsql_test 140 { + SELECT editdist3('anchor','omchor'); +} {149} + +do_execsql_test 200 { + INSERT INTO cost1 VALUES + (0, 'a', 'ä', 5), + (0, 'ss', 'ß', 8) + ; + SELECT editdist3('cost1'); + SELECT editdist3('strasse','straße'); + SELECT editdist3('straße','strasse'); +} {{} 8 196} +do_execsql_test 210 { + SELECT editdist3('baume','bäume'); +} {5} +do_execsql_test 220 { + SELECT editdist3('baum','bäume'); +} {102} +do_execsql_test 230 { + INSERT INTO cost1 VALUES + (0, 'ä', 'a', 5), + (0, 'ß', 'ss', 8) + ; + SELECT editdist3('cost1'); + SELECT editdist3('strasse','straße'); + SELECT editdist3('straße','strasse'); +} {{} 8 8} + +do_execsql_test 300 { + DELETE FROM cost1; + INSERT INTO cost1 VALUES + (0, '', '?', 97), + (0, '?', '', 98), + (0, '?', '?', 99), + (0, 'a', 'e', 50), + (0, 'a', 'i', 70), + (0, 'a', 'o', 75), + (0, 'a', 'u', 81), + (0, 'e', 'a', 50), + (0, 'e', 'i', 52), + (0, 'e', 'o', 72), + (0, 'e', 'u', 82), + (0, 'i', 'a', 70), + (0, 'i', 'e', 52), + (0, 'i', 'o', 75), + (0, 'i', 'u', 83), + (0, 'o', 'a', 75), + (0, 'o', 'e', 72), + (0, 'o', 'i', 75), + (0, 'o', 'u', 40), + (0, 'u', 'a', 81), + (0, 'u', 'e', 82), + (0, 'u', 'i', 83), + (0, 'u', 'o', 40), + (0, 'm', 'n', 45), + (0, 'n', 'm', 45) + ; + CREATE TABLE words(x TEXT); + INSERT INTO words VALUES + ('abraham'), + ('action'), + ('africa'), + ('aladdin'), + ('alert'), + ('alien'), + ('amazon'), + ('analog'), + ('animal'), + ('apollo'), + ('archive'), + ('arnold'), + ('aspirin'), + ('august'), + ('average'), + ('bahama'), + ('bambino'), + ('barcode'), + ('bazooka'), + ('belgium'), + ('between'), + ('biology'), + ('blonde'), + ('border'), + ('brave'), + ('british'), + ('bucket'), + ('button'), + ('caesar'), + ('camilla'), + ('cannon'), + ('caramel'), + ('carpet'), + ('catalog'), + ('century'), + ('chaos'), + ('chef'), + ('china'), + ('circus'), + ('classic'), + ('clinic'), + ('coconut'), + ('combine'), + ('complex'), + ('congo'), + ('convert'), + ('cosmos'), + ('crack'), + ('crown'), + ('cyclone'), + ('deal'), + ('delete'), + ('denver'), + ('detail'), + ('diana'), + ('direct'), + ('dolby'), + ('double'), + ('dublin'), + ('echo'), + ('edition'), + ('electra'), + ('emotion'), + ('enjoy'), + ('escape'), + ('everest'), + ('exile'), + ('express'), + ('family'), + ('ferrari'), + ('filter'), + ('fish'), + ('florida'), + ('ford'), + ('forum'), + ('frank'), + ('frozen'), + ('gallery'), + ('garlic'), + ('geneva'), + ('gibson'), + ('gloria'), + ('gordon'), + ('gravity'), + ('ground'), + ('habitat'), + ('harlem'), + ('hazard'), + ('herbert'), + ('hobby'), + ('house'), + ('icon'), + ('immune'), + ('india'), + ('inside'), + ('isotope'), + ('jamaica'), + ('jazz'), + ('joker'), + ('juliet'), + ('jupiter'), + ('kevin'), + ('korea'), + ('latin'), + ('legal'), + ('lexicon'), + ('limbo'), + ('lithium'), + ('logo'), + ('lucas'), + ('madrid'), + ('major'), + ('manual'), + ('mars'), + ('maximum'), + ('medical'), + ('mental'), + ('meter'), + ('miguel'), + ('mimosa'), + ('miranda'), + ('modern'), + ('money'), + ('morgan'), + ('motor'), + ('mystic'), + ('nebula'), + ('network'), + ('nice'), + ('nitro'), + ('norway'), + ('nurse'), + ('octavia'), + ('olympic'), + ('opus'), + ('orient'), + ('othello'), + ('pacific'), + ('panama'), + ('paper'), + ('parking'), + ('pasta'), + ('paul'), + ('people'), + ('permit'), + ('phrase'), + ('pilgrim'), + ('planet'), + ('pocket'), + ('police'), + ('popular'), + ('prefer'), + ('presto'), + ('private'), + ('project'), + ('proxy'), + ('python'), + ('quota'), + ('rainbow'), + ('raymond'), + ('region'), + ('report'), + ('reward'), + ('risk'), + ('robot'), + ('rose'), + ('russian'), + ('sailor'), + ('salt'), + ('saturn'), + ('scorpio'), + ('second'), + ('seminar'), + ('shadow'), + ('shave'), + ('shock'), + ('silence'), + ('sinatra'), + ('sleep'), + ('social'), + ('sonata'), + ('spain'), + ('sphere'), + ('spray'), + ('state'), + ('stone'), + ('strong'), + ('sugar'), + ('supreme'), + ('swing'), + ('talent'), + ('telecom'), + ('thermos'), + ('tina'), + ('tommy'), + ('torso'), + ('trade'), + ('trick'), + ('tropic'), + ('turtle'), + ('uniform'), + ('user'), + ('vega'), + ('vertigo'), + ('village'), + ('visible'), + ('vocal'), + ('voyage'), + ('weekend'), + ('winter'), + ('year'), + ('zipper') + ; + SELECT editdist3('cost1'); +} {{}} +do_execsql_test 310 { + SELECT editdist3(a.x,b.x), a.x, b.x + FROM words a, words b + WHERE a.x<b.x + ORDER BY 1, 2 + LIMIT 20 +} {139 bucket pocket 144 meter motor 149 manual mental 169 crack trick 173 sinatra sonata 174 edition emotion 174 major motor 174 risk rose 174 state stone 194 deal detail 196 alert talent 196 analog catalog 196 deal legal 196 ford forum 196 risk trick 196 stone strong 197 china tina 197 congo logo 197 diana tina 197 florida gloria} +do_execsql_test 320 { + SELECT md5sum(ed||'/'||sx||'/'||sy||',') FROM ( + SELECT editdist3(a.x,b.x) AS ed, a.x AS sx, b.x AS sy + FROM words a, words b + WHERE a.x<b.x + ORDER BY 1, 2 + ) +} {69d0a31872203a775e19325ea98cd053} + +finish_test
diff --git a/third_party/sqlite/src/test/subquery2.test b/third_party/sqlite/src/test/subquery2.test index eb5a039..8d45cbb4 100644 --- a/third_party/sqlite/src/test/subquery2.test +++ b/third_party/sqlite/src/test/subquery2.test
@@ -148,5 +148,54 @@ ); } {a 4 b 3 c 2 d 1} +#------------------------------------------------------------------------- + +do_execsql_test 4.0 { + CREATE TABLE t6(x); +} + +foreach {tn sql} { + 1 { + SELECT 'abc' FROM ( + SELECT x FROM t6 ORDER BY 1 + UNION ALL + SELECT x FROM t6 + ) + } + 2 { + SELECT 'abc' FROM ( + SELECT x FROM t6 + UNION ALL + SELECT x FROM t6 ORDER BY 1 + UNION ALL + SELECT x FROM t6 + ) + } + 3 { + SELECT 'abc' FROM ( + SELECT x FROM t6 ORDER BY 1 + UNION ALL + SELECT x FROM t6 ORDER BY 1 + UNION ALL + SELECT x FROM t6 + ) + } + 4 { + SELECT 'abc' FROM ( + SELECT x FROM t6 + UNION ALL + SELECT x FROM t6 ORDER BY 1 + UNION ALL + SELECT x FROM t6 ORDER BY 1 + UNION ALL + SELECT x FROM t6 + ) + } +} { + do_catchsql_test 4.$tn $sql [list {*}{ + 1 {ORDER BY clause should come after UNION ALL not before} + }] +} + finish_test
diff --git a/third_party/sqlite/src/test/tclsqlite.test b/third_party/sqlite/src/test/tclsqlite.test index 26936fa..dc4aff8 100644 --- a/third_party/sqlite/src/test/tclsqlite.test +++ b/third_party/sqlite/src/test/tclsqlite.test
@@ -22,19 +22,19 @@ # Check the error messages generated by tclsqlite # -set r "sqlite_orig HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" +set r "sqlite_orig HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" if {[sqlite3 -has-codec]} { append r " ?-key CODECKEY?" } do_test tcl-1.1 { - set v [catch {sqlite3 bogus} msg] + set v [catch {sqlite3 -bogus} msg] regsub {really_sqlite3} $msg {sqlite3} msg lappend v $msg } [list 1 "wrong # args: should be \"$r\""] do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg -} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}} +} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, deserialize, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, serialize, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}} do_test tcl-1.2.1 { set v [catch {db cache bogus} msg] lappend v $msg
diff --git a/third_party/sqlite/src/test/tempdb2.test b/third_party/sqlite/src/test/tempdb2.test index 6cc169f..4efe52e 100644 --- a/third_party/sqlite/src/test/tempdb2.test +++ b/third_party/sqlite/src/test/tempdb2.test
@@ -16,6 +16,9 @@ db close sqlite3 db "" +set unlocked unlocked +if {$::TEMP_STORE>=2} { set unlocked unknown } + proc int2str {i} { string range [string repeat "$i." 450] 0 899 } db func int2str int2str @@ -55,7 +58,7 @@ COMMIT; PRAGMA lock_status; -} {main unlocked temp closed} +} [list main $unlocked temp closed] do_execsql_test 1.2 { UPDATE t1 SET b=int2str(2);
diff --git a/third_party/sqlite/src/test/temptable2.test b/third_party/sqlite/src/test/temptable2.test index 397f7837..2017c9d 100644 --- a/third_party/sqlite/src/test/temptable2.test +++ b/third_party/sqlite/src/test/temptable2.test
@@ -344,7 +344,7 @@ } ifcapable mmap { - if {[permutation]!="journaltest"} { + if {[permutation]!="journaltest" && $::TEMP_STORE<2} { # The journaltest permutation does not support mmap, so this part of # the test is omitted. do_execsql_test 10.2 { PRAGMA mmap_size = 512000 } 512000
diff --git a/third_party/sqlite/src/test/tester.tcl b/third_party/sqlite/src/test/tester.tcl index 420d493..7ab369b 100644 --- a/third_party/sqlite/src/test/tester.tcl +++ b/third_party/sqlite/src/test/tester.tcl
@@ -2309,6 +2309,16 @@ return $prog } +# Call sqlite3_expanded_sql() on all statements associated with database +# connection $db. This sometimes finds use-after-free bugs if run with +# valgrind or address-sanitizer. +proc expand_all_sql {db} { + set stmt "" + while {[set stmt [sqlite3_next_stmt $db $stmt]]!=""} { + sqlite3_expanded_sql $stmt + } +} + # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1.
diff --git a/third_party/sqlite/src/test/thread001.test b/third_party/sqlite/src/test/thread001.test index 038420d7..8d1b06a 100644 --- a/third_party/sqlite/src/test/thread001.test +++ b/third_party/sqlite/src/test/thread001.test
@@ -141,5 +141,6 @@ } sqlite3_enable_shared_cache $::enable_shared_cache +catch { db close } set sqlite_open_file_count 0 finish_test
diff --git a/third_party/sqlite/src/test/trace3.test b/third_party/sqlite/src/test/trace3.test index 271009a3..538dcd0 100644 --- a/third_party/sqlite/src/test/trace3.test +++ b/third_party/sqlite/src/test/trace3.test
@@ -121,6 +121,27 @@ set ::stmtlist(record) } {/^\{-?\d+ -?\d+\}$/} +do_test trace3-4.3 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record profile + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set stmt [lindex [lindex $::stmtlist(record) 0] 0] + set ns [lindex [lindex $::stmtlist(record) 0] 1] + list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds +} {/^-?\d+ 1$/} +do_test trace3-4.4 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record 2 + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set stmt [lindex [lindex $::stmtlist(record) 0] 0] + set ns [lindex [lindex $::stmtlist(record) 0] 1] + list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds +} {/^-?\d+ 1$/} + do_test trace3-5.1 { set ::stmtlist(record) {} db trace_v2 trace_v2_record row
diff --git a/third_party/sqlite/src/test/walro2.test b/third_party/sqlite/src/test/walro2.test index 0c19d632..de83dd9c 100644 --- a/third_party/sqlite/src/test/walro2.test +++ b/third_party/sqlite/src/test/walro2.test
@@ -39,6 +39,18 @@ } } +# Most systems allocate the *-shm file in 32KB trunks. But on UNIX systems +# for which the getpagesize() call returns greater than 32K, the *-shm +# file is allocated in page-sized units (since you cannot mmap part of +# a page). The following code sets variable $MINSHMSZ to the smallest +# possible *-shm file (i.e. the greater of 32KB and the system page-size). +# +do_execsql_test 0.0 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); +} {wal} +set MINSHMSZ [file size test.db-shm] + foreach bZeroShm {0 1} { set TN [expr $bZeroShm+1] do_multiclient_test tn { @@ -169,7 +181,7 @@ } {a b c d e f g h 1 2} do_test $TN.3.2.2 { list [file size test.db-wal] [file size test.db-shm] - } {0 32768} + } [list 0 $MINSHMSZ] do_test $TN.3.3.0 { code2 { sqlite3 db2 test.db } @@ -182,7 +194,7 @@ code2 { db2 close } code1 { db close } list [file size test.db-wal] [file size test.db-shm] - } [list [wal_file_size 4 1024] 32768] + } [list [wal_file_size 4 1024] $MINSHMSZ] do_test $TN.3.3.1 { code1 { sqlite3 db file:test.db?readonly_shm=1 } sql1 { SELECT * FROM t1 } @@ -196,7 +208,7 @@ } code2 { db2 close } list [file size test.db-wal] [file size test.db-shm] - } [list [wal_file_size 4 1024] 32768] + } [list [wal_file_size 4 1024] $MINSHMSZ] do_test $TN.3.3.3 { sql1 { SELECT * FROM t1 } } {i ii}
diff --git a/third_party/sqlite/src/test/walthread.test b/third_party/sqlite/src/test/walthread.test index a881d57..644014e 100644 --- a/third_party/sqlite/src/test/walthread.test +++ b/third_party/sqlite/src/test/walthread.test
@@ -327,59 +327,61 @@ # the number of write-transactions performed using a rollback journal. # For example, "192 w, 185 r". # -do_thread_test2 walthread-2 -seconds $seconds(walthread-2) -init { - execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE) } -} -thread RB 2 { +if {[atomic_batch_write test.db]==0} { + do_thread_test2 walthread-2 -seconds $seconds(walthread-2) -init { + execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE) } + } -thread RB 2 { - db close - set nRun 0 - set nDel 0 - while {[tt_continue]} { - sqlite3 db test.db - db busy busyhandler - db eval { SELECT * FROM sqlite_master } - catch { db eval { PRAGMA journal_mode = DELETE } } - db eval { - BEGIN; - INSERT INTO t1 VALUES(NULL, randomblob(100+$E(pid))); - } - incr nRun 1 - incr nDel [file exists test.db-journal] - if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { - error "File-system looks bad..." - } - db eval COMMIT - - integrity_check db close - } - list $nRun $nDel - set {} "[expr $nRun-$nDel] w, $nDel r" + set nRun 0 + set nDel 0 + while {[tt_continue]} { + sqlite3 db test.db + db busy busyhandler + db eval { SELECT * FROM sqlite_master } + catch { db eval { PRAGMA journal_mode = DELETE } } + db eval { + BEGIN; + INSERT INTO t1 VALUES(NULL, randomblob(100+$E(pid))); + } + incr nRun 1 + incr nDel [file exists test.db-journal] + if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { + error "File-system looks bad..." + } + db eval COMMIT -} -thread WAL 2 { - db close - set nRun 0 - set nDel 0 - while {[tt_continue]} { - sqlite3 db test.db - db busy busyhandler - db eval { SELECT * FROM sqlite_master } - catch { db eval { PRAGMA journal_mode = WAL } } - db eval { - BEGIN; - INSERT INTO t1 VALUES(NULL, randomblob(110+$E(pid))); + integrity_check + db close } - incr nRun 1 - incr nDel [file exists test.db-journal] - if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { - error "File-system looks bad..." - } - db eval COMMIT + list $nRun $nDel + set {} "[expr $nRun-$nDel] w, $nDel r" - integrity_check + } -thread WAL 2 { db close + set nRun 0 + set nDel 0 + while {[tt_continue]} { + sqlite3 db test.db + db busy busyhandler + db eval { SELECT * FROM sqlite_master } + catch { db eval { PRAGMA journal_mode = WAL } } + db eval { + BEGIN; + INSERT INTO t1 VALUES(NULL, randomblob(110+$E(pid))); + } + incr nRun 1 + incr nDel [file exists test.db-journal] + if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { + error "File-system looks bad..." + } + db eval COMMIT + + integrity_check + db close + } + set {} "[expr $nRun-$nDel] w, $nDel r" } - set {} "[expr $nRun-$nDel] w, $nDel r" } do_thread_test walthread-3 -seconds $seconds(walthread-3) -init {
diff --git a/third_party/sqlite/src/test/whereF.test b/third_party/sqlite/src/test/whereF.test index b0a49f0..63a25db 100644 --- a/third_party/sqlite/src/test/whereF.test +++ b/third_party/sqlite/src/test/whereF.test
@@ -215,4 +215,98 @@ } {{{"foo":"meep","other":12345}}} } +# 2018-01-27 +# Ticket https://sqlite.org/src/tktview/ec32177c99ccac2b180fd3ea2083 +# Incorrect result when using the new OR clause factoring optimization +# +# This is the original test case as reported on the sqlite-users mailing +# list +# +do_execsql_test 7.1 { + DROP TABLE IF EXISTS cd; + CREATE TABLE cd ( cdid INTEGER PRIMARY KEY NOT NULL, genreid integer ); + CREATE INDEX cd_idx_genreid ON cd (genreid); + INSERT INTO cd ( cdid, genreid ) VALUES + ( 1, 1 ), + ( 2, NULL ), + ( 3, NULL ), + ( 4, NULL ), + ( 5, NULL ); + + SELECT cdid + FROM cd me + WHERE 2 > ( + SELECT COUNT( * ) + FROM cd rownum__emulation + WHERE + ( + me.genreid IS NOT NULL + AND + rownum__emulation.genreid IS NULL + ) + OR + ( + me.genreid IS NOT NULL + AND + rownum__emulation.genreid IS NOT NULL + AND + rownum__emulation.genreid < me.genreid + ) + OR + ( + ( me.genreid = rownum__emulation.genreid OR ( me.genreid IS NULL + AND rownum__emulation.genreid IS NULL ) ) + AND + rownum__emulation.cdid > me.cdid + ) + ); +} {4 5} + +# Simplified test cases from the ticket +# +do_execsql_test 7.2 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1(a,b) VALUES(1,1); + CREATE TABLE t2(aa INTEGER PRIMARY KEY, bb); + INSERT INTO t2(aa,bb) VALUES(1,1),(2,NULL),(3,NULL); + SELECT ( + SELECT COUNT(*) FROM t2 + WHERE ( t1.b IS NOT NULL AND t2.bb IS NULL ) + OR ( t2.bb < t1.b ) + OR ( t1.b IS t2.bb AND t2.aa > t1.a ) + ) + FROM t1; +} {2} + +# The fix for ticket ec32177c99ccac2b180fd3ea2083 only makes a difference +# in the output when there is a TERM_VNULL entry in the WhereClause array. +# And TERM_VNULL entries are only generated when compiling with +# SQLITE_ENABLE_STAT4. Nevertheless, it is correct that TERM_VIRTUAL terms +# should not participate in the factoring optimization. In all cases other +# than TERM_VNULL, participation is harmless, but it does consume a few +# extra CPU cycles. +# +# The following test verifies that the TERM_VIRTUAL terms resulting from +# a GLOB operator do not appear anywhere in the generated code. This +# confirms that the problem is fixed, even on builds that omit STAT4. +# +do_execsql_test 7.3 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); + INSERT INTO t1(a,b) VALUES(1,'abcxyz'); + CREATE TABLE t2(aa INTEGER PRIMARY KEY, bb TEXT); + INSERT INTO t2(aa,bb) VALUES(1,'abc'),(2,'wxyz'),(3,'xyz'); + CREATE INDEX t2bb ON t2(bb); + EXPLAIN SELECT ( + SELECT COUNT(*) FROM t2 + WHERE ( t1.b GLOB 'a*z' AND t2.bb='xyz' ) + OR ( t2.bb = t1.b ) + OR ( t2.aa = t1.a ) + ) + FROM t1; +} {~/ (Lt|Ge) /} + finish_test
diff --git a/third_party/sqlite/src/test/with4.test b/third_party/sqlite/src/test/with4.test new file mode 100644 index 0000000..b0eeba6 --- /dev/null +++ b/third_party/sqlite/src/test/with4.test
@@ -0,0 +1,52 @@ +# 2018-02-15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the WITH clause in TRIGGERs and VIEWs. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix with4 + +ifcapable {!cte} { + finish_test + return +} + +do_execsql_test 100 { + ATTACH ':memory:' AS aux; + CREATE TABLE main.t1(a,b); + CREATE TABLE aux.t2(x,y); + INSERT INTO t1 VALUES(1,2); + INSERT INTO t2 VALUES(3,4); +} {} +do_catchsql_test 110 { + CREATE VIEW v1 AS SELECT * FROM t1, aux.t2; +} {1 {view v1 cannot reference objects in database aux}} +do_catchsql_test 120 { + CREATE VIEW v2 AS WITH v(m,n) AS (SELECT x,y FROM aux.t2) SELECT * FROM t1, v; +} {1 {view v2 cannot reference objects in database aux}} +do_catchsql_test 130 { + CREATE VIEW v2 AS WITH v(m,n) AS (SELECT 5,?2) SELECT * FROM t1, v; +} {1 {parameters are not allowed in views}} + +do_catchsql_test 200 { + CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN + WITH v(m,n) AS (SELECT x,y FROM aux.t2) SELECT * FROM t1, v; + END; +} {1 {trigger r1 cannot reference objects in database aux}} +do_catchsql_test 210 { + CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN + WITH v(m,n) AS (SELECT 5,?2) SELECT * FROM t1, v; + END; +} {1 {trigger cannot use variables}} + +finish_test
diff --git a/third_party/sqlite/src/test/without_rowid1.test b/third_party/sqlite/src/test/without_rowid1.test index fa644fc..fafcbe2a 100644 --- a/third_party/sqlite/src/test/without_rowid1.test +++ b/third_party/sqlite/src/test/without_rowid1.test
@@ -341,6 +341,19 @@ SELECT type, name, '|' FROM sqlite_master; } {table t1 | index t1x |} +# 2018-04-05: OSSFuzz found that the following was accessing an +# unintialized memory cell. Which was not actually causing a +# malfunction, but does cause an assert() to fail. +# +do_execsql_test 9.0 { + CREATE TABLE t2(b, c, PRIMARY KEY(b,c)) WITHOUT ROWID; + CREATE UNIQUE INDEX t2b ON t2(b); + UPDATE t2 SET b=1 WHERE b=''; +} + +do_execsql_test 10.1 { + DELETE FROM t2 WHERE b=1 +} finish_test
diff --git a/third_party/sqlite/src/test/zipfile.test b/third_party/sqlite/src/test/zipfile.test index edf3fe6..bef7f31af 100644 --- a/third_party/sqlite/src/test/zipfile.test +++ b/third_party/sqlite/src/test/zipfile.test
@@ -10,6 +10,8 @@ #*********************************************************************** # +package require Tcl 8.6 + set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix zipfile @@ -21,6 +23,145 @@ puts "Skipping zipfile tests, hit load error: $error" finish_test; return } +if {[catch {load_static_extension db fileio} error]} { + puts "Skipping zipfile tests, hit load error: $error" + finish_test; return +} + +proc readfile {f} { + set fd [open $f] + fconfigure $fd -translation binary -encoding binary + set data [read $fd] + close $fd + set data +} + +unset -nocomplain ::UNZIP + +if {[catch {exec unzip} msg]==0 && \ + [regexp -line {^UnZip \d+\.\d+ .*? Info-ZIP\.} $msg]} { + set ::UNZIP unzip + proc fix_stat_mode {name mode} { + if {$::tcl_platform(platform)=="windows"} { + # + # NOTE: Set or unset the write bits of the file permissions + # based on the read-only attribute because the Win32 + # version of UnZip does this. + # + set writebits 0x12; # 0o22 + set result $mode + if {[file attributes $name -readonly]} { + set result [expr {$result | $writebits}] + } else { + set result [expr {$result & ~$writebits}] + } + return $result + } else { + return $mode + } + } + proc do_unzip {file} { + forcedelete test_unzip + file mkdir test_unzip + exec $::UNZIP -d test_unzip $file + + db func modefix fix_stat_mode + + set res [db eval { + SELECT replace(name,'test_unzip/',''),modefix(name,mode),mtime,data + FROM fsdir('test_unzip') + WHERE name!='test_unzip' + ORDER BY name + }] + set res + } +} + + +# The argument is a blob (not a hex string) containing a zip archive. +# This proc removes the extended timestamp fields from the archive +# and returns the result. +# +proc remove_timestamps {blob} { + set hex [binary encode hex $blob] + set hex [string map {55540500 00000500} $hex] + binary decode hex $hex +} + + +# Argument $file is the name of a zip archive on disk. This function +# executes test cases to check that the results of each of the following +# are the same: +# +# SELECT * FROM zipfile($file) +# SELECT * FROM zipfile( readfile($file) ) +# SELECT * FROM zipfile( +# (SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file)) +# ) +# +proc do_zipfile_blob_test {tn file} { + + db func r readfile + set q1 {SELECT name,mode,mtime,method,quote(data) FROM zipfile($file)} + set q2 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( r($file) )} + set q3 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( + ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) + )} + + + set r1 [db eval $q1] + set r2 [db eval $q2] + set r3 [db eval $q3] + #puts $r1 + #puts $r2 + #puts $r3 + + uplevel [list do_test $tn.1 [list set {} $r2] $r1] + uplevel [list do_test $tn.2 [list set {} $r3] $r1] +} + +# Argument $file is a zip file on disk. This command runs tests to: +# +# 1. Unpack the archive with unix command [unzip] and compare the +# results to reading the same archive using the zipfile() table +# valued function. +# +# 2. Creates a new archive with the same contents using the zipfile() +# aggregate function as follows: +# +# SELECT writefile('test_unzip.zip', +# ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) +# ); +# +# Then tests that unpacking the new archive using [unzip] produces +# the same results as in (1). +# +proc do_unzip_test {tn file} { + db func sss strip_slash + + db eval { + SELECT writefile('test_unzip.zip', + ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) + ); + } + + set r1 [db eval { + SELECT sss(name),mode,mtime,data FROM zipfile($file) ORDER BY name + }] + set r2 [do_unzip $file] + set r3 [do_unzip test_unzip.zip] + + uplevel [list do_test $tn.1 [list set {} $r2] $r1] + uplevel [list do_test $tn.2 [list set {} $r3] $r1] +} +proc strip_slash {in} { regsub {/$} $in {} } + +proc do_zip_tests {tn file} { + uplevel do_zipfile_blob_test $tn.1 $file + if {[info exists ::UNZIP]} { + uplevel do_unzip_test $tn.2 $file + } +} forcedelete test.zip do_execsql_test 1.0 { @@ -39,11 +180,19 @@ do_catchsql_test 1.1.0.1 { INSERT INTO zz(name, mode, mtime, sz, rawdata, method) VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0); -} {1 {constraint failed}} -do_catchsql_test 1.1.0.1 { - INSERT INTO zz(name, mtime, sz, rawdata, method) +} {1 {rawdata must be NULL}} +do_catchsql_test 1.1.0.2 { + INSERT INTO zz(name, mtime, sz, data, method) VALUES('g.txt', 1000000002, 5, '12345', 0); -} {1 {constraint failed}} +} {1 {sz must be NULL}} +do_catchsql_test 1.1.0.3 { + INSERT INTO zz(name, mtime, rawdata, method) + VALUES('g.txt', 1000000002, '12345', 0); +} {1 {rawdata must be NULL}} +do_catchsql_test 1.1.0.4 { + INSERT INTO zz(name, data, method) + VALUES('g.txt', '12345', 7); +} {1 {unknown compression method: 7}} do_execsql_test 1.1.1 { INSERT INTO zz(name, mode, mtime, data, method) @@ -60,12 +209,14 @@ f.txt 1000000000 abcde g.txt 1000000002 12345 } +do_zip_tests 1.2a test.zip do_execsql_test 1.3 { INSERT INTO zz(name, mode, mtime, data) VALUES('h.txt', '-rw-r--r--', 1000000004, 'aaaaaaaaaabbbbbbbbbb' ); } +do_zip_tests 1.3a test.zip do_execsql_test 1.4 { SELECT name, mtime, data, method FROM zipfile('test.zip'); @@ -85,6 +236,9 @@ h.txt 1 } } +do_catchsql_test 1.4.2 { + SELECT zipfile_cds(mode) FROM zipfile('test.zip'); +} {0 {{} {} {}}} do_execsql_test 1.5.1 { BEGIN; @@ -112,6 +266,7 @@ h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8 i.txt 33188 1000000006 zxcvb 0 } +do_zip_tests 1.6.1a test.zip do_execsql_test 1.6.2 { UPDATE zz SET mtime=4 WHERE name='i.txt'; @@ -122,57 +277,99 @@ i.txt 33188 4 zxcvb 0 } -do_execsql_test 1.6.3 { - UPDATE zz SET mode='-rw-r--r-x' WHERE name='h.txt'; - SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); -} { - f.txt 33188 1000000000 abcde 0 - h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 - i.txt 33188 4 zxcvb 0 +if {$::tcl_platform(platform)=="unix"} { + set modes -rw-r--r-x + set perms 33189 +} else { + set modes -rw-r--r--; # no execute bits on Win32 + set perms 33188 } +do_execsql_test 1.6.3 { + UPDATE zz SET mode=$modes WHERE name='h.txt'; + SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); +} [string map [list %perms% $perms] { + f.txt 33188 1000000000 abcde 0 + h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8 + i.txt 33188 4 zxcvb 0 +}] +do_zip_tests 1.6.3a test.zip + do_execsql_test 1.6.4 { UPDATE zz SET name = 'blue.txt' WHERE name='f.txt'; SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); -} { +} [string map [list %perms% $perms] { blue.txt 33188 1000000000 abcde 0 - h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 + h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8 i.txt 33188 4 zxcvb 0 -} +}] +do_zip_tests 1.6.4a test.zip do_execsql_test 1.6.5 { UPDATE zz SET data = 'edcba' WHERE name='blue.txt'; SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); -} { +} [string map [list %perms% $perms] { blue.txt 33188 1000000000 edcba 0 - h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 + h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8 i.txt 33188 4 zxcvb 0 -} +}] do_execsql_test 1.6.6 { UPDATE zz SET mode=NULL, data = NULL WHERE name='blue.txt'; SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); -} { +} [string map [list %perms% $perms] { blue.txt/ 16877 1000000000 {} 0 - h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 + h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8 i.txt 33188 4 zxcvb 0 -} +}] do_catchsql_test 1.6.7 { UPDATE zz SET data=NULL WHERE name='i.txt' -} {1 {constraint failed}} +} {1 {zipfile: mode does not match data}} do_execsql_test 1.6.8 { SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); -} { +} [string map [list %perms% $perms] { blue.txt/ 16877 1000000000 {} 0 - h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 + h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8 i.txt 33188 4 zxcvb 0 +}] + +do_execsql_test 1.6.9 { + UPDATE zz SET data = '' WHERE name='i.txt'; + SELECT name,mode,mtime,data,method from zipfile('test.zip'); +} [string map [list %perms% $perms] { + blue.txt/ 16877 1000000000 {} 0 + h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8 + i.txt 33188 4 {} 0 +}] + +do_execsql_test 1.6.10 { + SELECT a.name, a.data + FROM zz AS a, zz AS b + WHERE a.name=+b.name AND +a.mode=b.mode +} { + blue.txt/ {} + h.txt aaaaaaaaaabbbbbbbbbb + i.txt {} } +do_execsql_test 1.6.11 { + SELECT name, data FROM zz WHERE name LIKE '%txt' +} { + h.txt aaaaaaaaaabbbbbbbbbb + i.txt {} +} + +do_execsql_test 1.7 { + DELETE FROM zz; + SELECT * FROM zz; +} {} + #------------------------------------------------------------------------- db close forcedelete test.zip reset_db +load_static_extension db fileio load_static_extension db zipfile do_execsql_test 2.1 { CREATE VIRTUAL TABLE zzz USING zipfile('test.zip'); @@ -199,14 +396,20 @@ dirname2/ 16877 {} dirname2/file1.txt 33188 abcdefghijklmnop } +do_zip_tests 2.4a test.zip -# If on unix, check that the [unzip] utility can unpack our archive. +# Check that the [unzip] utility can unpack our archive. # -if {$::tcl_platform(platform)=="unix"} { +if {[info exists ::UNZIP]} { do_test 2.5.1 { forcedelete dirname forcedelete dirname2 - set rc [catch { exec unzip test.zip > /dev/null } msg] + if {$::tcl_platform(platform)=="unix"} { + set null /dev/null + } else { + set null NUL + } + set rc [catch { exec $::UNZIP test.zip > $null } msg] list $rc $msg } {0 {}} do_test 2.5.2 { file isdir dirname3 } 1 @@ -224,6 +427,7 @@ reset_db forcedelete test.zip load_static_extension db zipfile +load_static_extension db fileio do_execsql_test 3.0 { CREATE VIRTUAL TABLE temp.x1 USING zipfile('test.zip'); @@ -238,19 +442,323 @@ } { do_catchsql_test 3.1.$tn.0 { INSERT INTO x1(name, data) VALUES($fname, NULL); - } {1 {constraint failed}} + } [list 1 "duplicate name: \"$fname/\""] do_catchsql_test 3.1.$tn.1 { INSERT INTO x1(name, data) VALUES($fname || '/', NULL); - } {1 {constraint failed}} + } [list 1 "duplicate name: \"$fname/\""] do_catchsql_test 3.1.$tn.2 { INSERT INTO x1(name, data) VALUES($fname, 'abcd'); - } {1 {constraint failed}} + } [list 1 "duplicate name: \"$fname\""] } do_catchsql_test 3.2 { SELECT rowid FROM x1 } {1 {no such column: rowid}} +#------------------------------------------------------------------------- +# Test some error conditions. +# +do_catchsql_test 4.1 { + CREATE VIRTUAL TABLE yyy USING zipfile(); +} {1 {zipfile constructor requires one argument}} +do_catchsql_test 4.2 { + CREATE VIRTUAL TABLE yyy USING zipfile('test.zip', 'test.zip'); +} {1 {zipfile constructor requires one argument}} + +do_catchsql_test 4.3 { + SELECT * FROM zipfile() +} {1 {zipfile() function requires an argument}} + +do_catchsql_test 4.4 { + SELECT * FROM zipfile('/path/that/does/not/exist') +} {1 {cannot open file: /path/that/does/not/exist}} + +foreach {tn mode} { + 1 abcd + 2 brwxrwxrwx + 3 lrwxrrxrwx +} { + do_catchsql_test 4.5.$tn { + WITH m(m) AS ( SELECT $mode) + SELECT zipfile('a.txt', m, 1000, 'xyz') FROM m + } [list 1 "zipfile: parse error in mode: $mode"] +} + +do_catchsql_test 4.6 { + WITH c(name,data) AS ( SELECT 'a.txt', 'abc') + SELECT zipfile(name) FROM c +} {1 {wrong number of arguments to function zipfile()}} + +do_catchsql_test 4.7 { + WITH c(name,data) AS ( + SELECT 'a.txt', 'abc' UNION ALL + SELECT NULL, 'def' + ) + SELECT zipfile(name,data) FROM c +} {1 {first argument to zipfile() must be non-NULL}} + +do_catchsql_test 4.8 { + WITH c(name,data,method) AS ( + SELECT 'a.txt', 'abc', 0 + UNION SELECT 'b.txt', 'def', 8 + UNION SELECT 'c.txt', 'ghi', 16 + ) + SELECT zipfile(name,NULL,NULL,data,method) FROM c +} {1 {illegal method value: 16}} + +do_catchsql_test 4.9 { + WITH c(name,data) AS ( + SELECT 'a.txt', 'abc' + UNION SELECT 'b.txt', 'def' + UNION SELECT 'c.txt/', 'ghi' + ) + SELECT zipfile(name,NULL,NULL,data) FROM c +} {1 {non-directory name must not end with /}} + +#-------------------------------------------------------------------------- + +db func rt remove_timestamps +do_execsql_test 5.0 { + WITH c(name,mtime,data) AS ( + SELECT 'a.txt', 946684800, 'abc' + ) + SELECT name,mtime,data FROM zipfile( + ( SELECT rt( zipfile(name,NULL,mtime,data,NULL) ) FROM c ) + ) +} { + a.txt 946684800 abc +} + +if {[info exists ::UNZIP]} { +ifcapable datetime { + forcedelete test1.zip test2.zip + do_test 6.0 { + execsql { + WITH c(name,mtime,data) AS ( + SELECT 'a.txt', 946684800, 'abc' UNION ALL + SELECT 'b.txt', 1000000000, 'abc' UNION ALL + SELECT 'c.txt', 1111111000, 'abc' + ) + SELECT writefile('test1.zip', rt( zipfile(name, NULL, mtime, data) ) ), + writefile('test2.zip', ( zipfile(name, NULL, mtime, data) ) ) + FROM c; + } + forcedelete test_unzip + file mkdir test_unzip + exec $::UNZIP -d test_unzip test1.zip + + db eval { + SELECT name, strftime('%s', mtime, 'unixepoch', 'localtime') + FROM fsdir('test_unzip') WHERE name!='test_unzip' + ORDER BY name + } + } [list {*}{ + test_unzip/a.txt 946684800 + test_unzip/b.txt 1000000000 + test_unzip/c.txt 1111111000 + }] + + # fsdir() issue reported on the mailing list on 2018-03-14 by Jack Thaw. + do_test 6.0b { + db eval { + SELECT sum(name LIKE '%/a.txt') + FROM (VALUES(1),(2),(3)) CROSS JOIN fsdir('test_unzip') + } + } {3} + + do_execsql_test 6.1 { + SELECT name, mtime, data FROM zipfile('test1.zip') + } { + a.txt 946684800 abc + b.txt 1000000000 abc + c.txt 1111111000 abc + } + + do_test 6.2 { + forcedelete test_unzip + file mkdir test_unzip + exec $::UNZIP -d test_unzip test2.zip + + db eval { + SELECT name, mtime + FROM fsdir('test_unzip') WHERE name!='test_unzip' + ORDER BY name + } + } [list {*}{ + test_unzip/a.txt 946684800 + test_unzip/b.txt 1000000000 + test_unzip/c.txt 1111111000 + }] + + do_execsql_test 6.3 { + SELECT name, mtime, sz, rawdata, data FROM zipfile('test2.zip') + } { + a.txt 946684800 3 abc abc + b.txt 1000000000 3 abc abc + c.txt 1111111000 3 abc abc + } +} +} + +#------------------------------------------------------------------------- +# Force an IO error by truncating the zip archive to zero bytes in size +# while it is being read. +forcedelete test.zip +do_test 7.0 { + execsql { + WITH c(name,data) AS ( + SELECT '1', randomblob(1000000) UNION ALL + SELECT '2', randomblob(1000000) UNION ALL + SELECT '3', randomblob(1000000) + ) + SELECT writefile('test.zip', zipfile(name, data) ) FROM c; + } + + list [catch { + db eval { SELECT name, data FROM zipfile('test.zip') } { + if {$name==2} { close [open test.zip w+] } + } + } msg] $msg +} {1 {error in fread()}} + +forcedelete test.zip +do_execsql_test 8.0.1 { + CREATE VIRTUAL TABLE zz USING zipfile('test.zip'); + BEGIN; + INSERT INTO zz(name, data) VALUES('a.txt', '1'); + INSERT INTO zz(name, data) VALUES('b.txt', '2'); + INSERT INTO zz(name, data) VALUES('c.txt', '1'); + INSERT INTO zz(name, data) VALUES('d.txt', '2'); + SELECT name, data FROM zz; +} { + a.txt 1 b.txt 2 c.txt 1 d.txt 2 +} +do_test 8.0.2 { + db eval { SELECT name, data FROM zz } { + if { $data=="2" } { db eval { DELETE FROM zz WHERE name=$name } } + } + execsql { SELECT name, data FROM zz } +} {a.txt 1 c.txt 1} +do_test 8.0.3 { + db eval { SELECT name, data FROM zz } { + db eval { DELETE FROM zz WHERE name=$name } + } + execsql { SELECT name, data FROM zz } +} {} +execsql COMMIT + +catch { forcedelete test_unzip } +catch { file mkdir test_unzip } +do_execsql_test 8.1.1 { + CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip'); +} +do_catchsql_test 8.1.2 { + INSERT INTO nogood(name, data) VALUES('abc', 'def'); +} {1 {zipfile: failed to open file test_unzip for writing}} + +do_execsql_test 8.2.1 { + DROP TABLE nogood; + BEGIN; + CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip'); +} +do_catchsql_test 8.2.2 { + INSERT INTO nogood(name, data) VALUES('abc', 'def'); +} {1 {zipfile: failed to open file test_unzip for writing}} +do_execsql_test 8.2.3 { + COMMIT; +} + +forcedelete test.zip +do_execsql_test 8.3.1 { + BEGIN; + CREATE VIRTUAL TABLE ok USING zipfile('test.zip'); + INSERT INTO ok(name, data) VALUES ('sqlite3', 'elf'); + COMMIT; +} + +#------------------------------------------------------------------------- +# Test that the zipfile aggregate correctly adds and removes "/" from +# the ends of directory file names. +do_execsql_test 9.0 { + WITH src(nm) AS ( + VALUES('dir1') UNION ALL + VALUES('dir2/') UNION ALL + VALUES('dir3//') UNION ALL + VALUES('dir4///') UNION ALL + VALUES('/') + ) + SELECT name FROM zipfile((SELECT zipfile(nm, NULL) FROM src)) +} {dir1/ dir2/ dir3/ dir4/ /} + +#------------------------------------------------------------------------- +# INSERT OR REPLACE and INSERT OR IGNORE +# +catch {db close} +forcedelete test.zip test.db +sqlite3 db :memory: +load_static_extension db zipfile +load_static_extension db fileio + +do_execsql_test 10.0 { + CREATE VIRTUAL TABLE z USING zipfile('test.zip'); +} {} +do_catchsql_test 10.1 { + INSERT INTO z(name,data) VALUES('a0','one'),('a0','two'); +} {1 {duplicate name: "a0"}} +do_execsql_test 10.2 { + SELECT name, data FROM z; +} {a0 one} +do_execsql_test 10.3 { + REPLACE INTO z(name,data) VALUES('a0','three'),('a0','four'); +} {} +do_execsql_test 10.4 { + SELECT name, data FROM z; +} {a0 four} +do_execsql_test 10.5 { + INSERT OR IGNORE INTO z(name,data) VALUES('a0','five'),('a0','six'); +} {} +do_execsql_test 10.6 { + SELECT name, data FROM z; +} {a0 four} + +do_execsql_test 11.1 { + DELETE FROM z; +} {} +do_execsql_test 11.2 { + SELECT name, data FROM z; +} {} +do_execsql_test 11.3 { + INSERT INTO z (name,data) VALUES ('b0','one'); + SELECT name, data FROM z; +} {b0 one} +do_execsql_test 11.4 { + UPDATE z SET name = 'b1' WHERE name = 'b0'; + SELECT name, data FROM z; +} {b1 one} +do_execsql_test 11.5 { + INSERT INTO z (name,data) VALUES ('b0','one'); + SELECT name, data FROM z ORDER BY name; +} {b0 one b1 one} +do_catchsql_test 11.6 { + UPDATE z SET name = 'b1' WHERE name = 'b0'; +} {1 {duplicate name: "b1"}} +do_execsql_test 11.7 { + UPDATE z SET data = 'two' WHERE name = 'b0'; + SELECT name, data FROM z ORDER BY name; +} {b0 two b1 one} +do_catchsql_test 11.8 { + UPDATE z SET name = 'b1'; +} {1 {duplicate name: "b1"}} +do_catchsql_test 11.9 { + UPDATE z SET name = 'b2'; +} {1 {duplicate name: "b2"}} +do_execsql_test 11.10 { + UPDATE z SET name = name; + SELECT name, data FROM z ORDER BY name; +} {b0 two b2 one} +do_execsql_test 11.11 { + UPDATE z SET name = name || 'suffix'; + SELECT name, data FROM z ORDER BY name; +} {b0suffix two b2suffix one} finish_test -
diff --git a/third_party/sqlite/src/test/zipfile2.test b/third_party/sqlite/src/test/zipfile2.test new file mode 100644 index 0000000..25588669 --- /dev/null +++ b/third_party/sqlite/src/test/zipfile2.test
@@ -0,0 +1,246 @@ +# 2018 January 30 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +package require Tcl 8.6 + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix zipfile2 + +ifcapable !vtab { + finish_test; return +} +if {[catch {load_static_extension db zipfile} error]} { + puts "Skipping zipfile2 tests, hit load error: $error" + finish_test; return +} + +proc blobliteral {str} { + set concat [string map {" " "" "\n" ""} $str] + return "X'$concat'" +} + +proc blob {str} { + binary decode hex $str +} + +proc findall {needle haystack} { + set L [list] + set start 0 + while { [set idx [string first $needle $haystack $start]]>=0 } { + lappend L $idx + set start [expr $idx+1] + } + set L +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE aaa USING zipfile('testzip'); + CREATE VIRTUAL TABLE bbb USING zipfile("testzip"); + CREATE VIRTUAL TABLE ccc USING zipfile(`testzip`); + CREATE VIRTUAL TABLE ddd USING zipfile([testzip]); + CREATE VIRTUAL TABLE eee USING zipfile(testzip); + CREATE VIRTUAL TABLE fff USING zipfile('test''zip'); +} + +if {$::tcl_platform(platform)=="windows"} { + set res {1 {cannot open file: testdir}} +} else { + set res {1 {error in fread()}} +} +do_test 2.0 { + forcedelete testdir + file mkdir testdir + execsql { CREATE VIRTUAL TABLE hhh USING zipfile('testdir') } + catchsql { SELECT * FROM hhh } +} $res + + +set archive { + 504B0304140000080000D4A52BEC09F3B6E0110000001100000005000900612E + 747874555405000140420F00636F6E74656E7473206F6620612E747874504B03 + 04140000080000D4A52BECD98916A7110000001100000005000900622E747874 + 555405000140420F00636F6E74656E7473206F6620622E747874504B01021E03 + 140000080000D4A52BEC09F3B6E0110000001100000005000900000000000000 + 0000A48100000000612E747874555405000140420F00504B01021E0314000008 + 0000D4A52BECD98916A71100000011000000050009000000000000000000A481 + 3D000000622E747874555405000140420F00504B050600000000020002007800 + 00007A0000000000 +} + +if 0 { + # This test is broken - the archive generated is slightly different + # depending on the zlib version used. + do_execsql_test 3.1 { + WITH contents(name,mtime,data) AS ( + VALUES('a.txt', 1000000, 'contents of a.txt') UNION ALL + VALUES('b.txt', 1000000, 'contents of b.txt') + ) SELECT quote( zipfile(name,NULL,mtime,data) ) FROM contents; + } [blobliteral $archive] +} + + +set blob [blob $archive] +do_execsql_test 3.2 { + SELECT name,mtime,data FROM zipfile($blob) +} { + a.txt 1000000 {contents of a.txt} + b.txt 1000000 {contents of b.txt} +} + +# Corrupt each of the 0x50 0x4B (ascii "PK") headers in the file +# Test that in each case this causes an error. +# +set L [findall 504B $archive] +for {set i 0} {$i < [llength $L]} {incr i} { + set idx [lindex $L $i] + set a [string replace $archive $idx [expr $idx+3] 0000] + set blob [blob $a] + do_catchsql_test 3.3.$i { + SELECT name,mtime,data FROM zipfile($blob) + } {/1 .*/} +} + +# Change the "extra info id" for all extended-timestamp fields. +set L [findall 5554 $archive] +for {set i 0} {$i < [llength $L]} {incr i} { + set idx [lindex $L $i] + set a [string replace $archive $idx [expr $idx+3] 1234] + set blob [blob $a] + do_execsql_test 3.4.$i { + SELECT name,data FROM zipfile($blob) + } { + a.txt {contents of a.txt} + b.txt {contents of b.txt} + } +} + +for {set i 0} {$i < [llength $L]} {incr i} { + set idx [lindex $L $i] + set a [string replace $archive [expr $idx+8] [expr $idx+9] 00] + set blob [blob $a] + do_execsql_test 3.5.$i { + SELECT name,data FROM zipfile($blob) + } { + a.txt {contents of a.txt} + b.txt {contents of b.txt} + } +} + +# set blob [db one { +# WITH contents(name,mtime,data) AS ( +# VALUES('a.txt', 1000000, 'aaaaaaaaaaaaaaaaaaaaaaa') +# ) SELECT quote( zipfile(name,NULL,mtime,data) ) FROM contents; +# }] +# set blob [string range $blob 2 end] +# set blob [string range $blob 0 end-1] +# while {[string length $blob]>0} { +# puts [string range $blob 0 63] +# set blob [string range $blob 64 end] +# } +# exit + +set archive2 { + 504B0304140000080800D4A52BEC08F54C6E050000001700000005000900612E + 747874555405000140420F004B4CC40A00504B01021E03140000080800D4A52B + EC08F54C6E0500000017000000050009000000000000000000A4810000000061 + 2E747874555405000140420F00504B050600000000010001003C000000310000 + 000000 +} +set blob [blob $archive2] +do_execsql_test 4.0 { + SELECT name,mtime,data,method FROM zipfile($blob) +} { + a.txt 1000000 aaaaaaaaaaaaaaaaaaaaaaa 8 +} + +set L [findall 17000000 $archive2] +set a $archive2 +foreach i $L { set a [string replace $a $i [expr $i+7] 16000000] } +set blob [blob $a] +do_catchsql_test 4.1 { + SELECT name,mtime,data,method FROM zipfile($blob) +} {1 {inflate() failed (0)}} + +# Check the response to an unknown compression method (set data to NULL). +set blob [blob [string map {0800 0900} $archive2]] +do_execsql_test 4.2 { + SELECT name,mtime,data IS NULL,method FROM zipfile($blob) +} {a.txt 1000000 1 9} + +# Corrupt the EOCDS signature bytes in various ways. +foreach {tn sub} { + 1 {504B0500} + 2 {504B0006} + 3 {50000506} + 4 {004B0506} +} { + set blob [blob [string map [list 504B0506 $sub] $archive2]] + do_catchsql_test 4.3.$tn { + SELECT * FROM zipfile($blob) + } {1 {cannot find end of central directory record}} +} + +#------------------------------------------------------------------------- +# Test that a zero-length file with a '/' at the end is treated as +# a directory (data IS NULL). Even if the mode doesn't indicate +# that it is a directory. + +do_test 5.0 { + set blob [db one { + WITH c(n, d) AS ( + SELECT 'notadir', '' + ) + SELECT zipfile(n, d) FROM c + }] + + set hex [binary encode hex $blob] + set hex [string map {6e6f7461646972 6e6f746164692f} $hex] + set blob2 [binary decode hex $hex] + + execsql { SELECT name, data IS NULL FROM zipfile($blob2) } +} {notadi/ 1} + +#------------------------------------------------------------------------- +# Test that duplicate entries may not be created using UPDATE +# statements. +# +forcedelete test.zip +do_execsql_test 6.0 { + CREATE VIRTUAL TABLE temp.zip USING zipfile('test.zip'); + INSERT INTO temp.zip (name,data) VALUES ('test1','test'); + INSERT INTO temp.zip (name,data) VALUES ('test2','test'); +} +do_catchsql_test 6.1 { + UPDATE temp.zip SET name='test1' WHERE name='test2' +} {1 {duplicate name: "test1"}} + +forcedelete test.zip +do_catchsql_test 6.2 { + DROP TABLE zip; + CREATE VIRTUAL TABLE temp.zip USING zipfile('test.zip'); + INSERT INTO temp.zip (name,data) VALUES ('test','test'); + UPDATE temp.zip set name=name||'new' where name='test'; + INSERT INTO temp.zip (name,data) VALUES ('test','test'); + UPDATE temp.zip set name=name||'new' where name='test'; +} {1 {duplicate name: "testnew"}} + +forcedelete test.zip +do_execsql_test 6.3 { + INSERT INTO temp.zip (name,data) VALUES ('test1','test'); + INSERT INTO temp.zip (name,data) VALUES ('test2','test'); + UPDATE OR REPLACE zip SET name='test2' WHERE name='test1'; + SELECT name FROM zip; +} {test2} + +finish_test +
diff --git a/third_party/sqlite/src/test/zipfilefault.test b/third_party/sqlite/src/test/zipfilefault.test new file mode 100644 index 0000000..61e1fc9 --- /dev/null +++ b/third_party/sqlite/src/test/zipfilefault.test
@@ -0,0 +1,166 @@ +# 2018 January 30 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set testprefix zipfilefault + +ifcapable !vtab { + finish_test; return +} +if {[catch {load_static_extension db zipfile} error]} { + puts "Skipping zipfile2 tests, hit load error: $error" + finish_test; return +} + +faultsim_save_and_close +do_faultsim_test 1 -prep { + faultsim_restore_and_reopen + load_static_extension db zipfile + execsql { DROP TABLE IF EXISTS aaa } +} -body { + execsql { CREATE VIRTUAL TABLE aaa USING zipfile('test.zip') } +} -test { + faultsim_test_result {0 {}} +} + +forcedelete test.zip +sqlite3 db test.db +load_static_extension db zipfile +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE setup USING zipfile('test.zip'); + INSERT INTO setup(name, data) VALUES('a.txt', '1234567890'); +} + +do_faultsim_test 2.1 -faults oom* -body { + execsql { SELECT name,data FROM zipfile('test.zip') } +} -test { + faultsim_test_result {0 {a.txt 1234567890}} +} +ifcapable json1 { + do_faultsim_test 2.2 -faults oom* -body { + execsql { + SELECT json_extract( zipfile_cds(z), '$.version-made-by' ) + FROM zipfile('test.zip') + } + } -test { + faultsim_test_result {0 798} + } +} + +forcedelete test.zip +reset_db +load_static_extension db zipfile +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE setup USING zipfile('test.zip'); + INSERT INTO setup(name, data) VALUES('a.txt', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa'); +} + +do_faultsim_test 3 -faults oom* -body { + execsql { SELECT name,data FROM zipfile('test.zip') } +} -test { + faultsim_test_result {0 {a.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaa}} +} + +do_faultsim_test 4 -faults oom* -body { + execsql { + WITH c(n, d) AS ( + SELECT 1, 'aaaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbb' + ) + SELECT name, data FROM zipfile( + (SELECT zipfile(n, d) FROM c) + ); + } +} -test { + faultsim_test_result {0 {1 aaaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbb}} +} + +reset_db +sqlite3_db_config_lookaside db 0 0 0 +load_static_extension db zipfile + +do_execsql_test 5.0 { + CREATE VIRTUAL TABLE setup USING zipfile('test.zip') +} + +do_faultsim_test 5.1 -faults oom* -prep { + forcedelete test.zip +} -body { + execsql { + INSERT INTO setup(name, data) + VALUES('a.txt', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa'); + } +} -test { + faultsim_test_result {0 {}} +} + +do_faultsim_test 5.2 -faults oom* -prep { + forcedelete test.zip +} -body { + execsql { + INSERT INTO setup(name, data) VALUES('dir', NULL) + } +} -test { + faultsim_test_result {0 {}} +} + +do_faultsim_test 5.3 -faults oom* -prep { + forcedelete test.zip + execsql { + DROP TABLE IF EXISTS setup; + BEGIN; + CREATE VIRTUAL TABLE setup USING zipfile('test.zip') + } +} -body { + execsql { + INSERT INTO setup(name, data) VALUES('dir', NULL) + } +} -test { + catchsql { COMMIT } + faultsim_test_result {0 {}} +} + +do_faultsim_test 6.1 -faults oom* -body { + execsql { + WITH c(n, d) AS ( + VALUES('a.txt', '1234567890') UNION ALL + VALUES('dir', NULL) + ) + SELECT zipfile(n, d) IS NULL FROM c; + } +} -test { + faultsim_test_result {0 0} +} + +set big [string repeat 0123456789 1000] +do_faultsim_test 6.2 -faults oom* -body { + execsql { + WITH c(n, d) AS ( + VALUES('a.txt', $big) + ) + SELECT zipfile(n, NULL, NULL, d, 0) IS NULL FROM c; + } +} -test { + faultsim_test_result {0 0} +} + +do_faultsim_test 7.0 -faults oom* -prep { + catch { db close } + sqlite3 db "" +} -body { + load_static_extension db zipfile +} -test { +} + + +finish_test
diff --git a/third_party/sqlite/src/tool/addopcodes.tcl b/third_party/sqlite/src/tool/addopcodes.tcl index 80ba5c8..1af98eaa 100644 --- a/third_party/sqlite/src/tool/addopcodes.tcl +++ b/third_party/sqlite/src/tool/addopcodes.tcl
@@ -22,6 +22,7 @@ # ILLEGAL *must* be the last two token codes and they must be in that order. # set extras { + TRUEFALSE ISNOT FUNCTION COLUMN @@ -29,6 +30,7 @@ AGG_COLUMN UMINUS UPLUS + TRUTH REGISTER VECTOR SELECT_COLUMN
diff --git a/third_party/sqlite/src/tool/lemon.c b/third_party/sqlite/src/tool/lemon.c index 96bbed7..f06a14f 100644 --- a/third_party/sqlite/src/tool/lemon.c +++ b/third_party/sqlite/src/tool/lemon.c
@@ -3254,6 +3254,7 @@ struct state *stp; struct config *cfp; struct action *ap; + struct rule *rp; FILE *fp; fp = file_open(lemp,".out","wb"); @@ -3306,8 +3307,21 @@ } } } + if( sp->prec>=0 ) fprintf(fp," (precedence=%d)", sp->prec); fprintf(fp, "\n"); } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Rules:\n"); + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(fp, "%4d: ", rp->iRule); + rule_print(fp, rp); + fprintf(fp,"."); + if( rp->precsym ){ + fprintf(fp," [%s precedence=%d]", + rp->precsym->name, rp->precsym->prec); + } + fprintf(fp,"\n"); + } fclose(fp); return; }
diff --git a/third_party/sqlite/src/tool/lempar.c b/third_party/sqlite/src/tool/lempar.c index dda2ac1..b511dfe 100644 --- a/third_party/sqlite/src/tool/lempar.c +++ b/third_party/sqlite/src/tool/lempar.c
@@ -511,7 +511,8 @@ #endif do{ i = yy_shift_ofst[stateno]; - assert( i>=0 && i+YYNTOKEN<=sizeof(yy_lookahead)/sizeof(yy_lookahead[0]) ); + assert( i>=0 ); + assert( i+YYNTOKEN<=(int)sizeof(yy_lookahead)/sizeof(yy_lookahead[0]) ); assert( iLookAhead!=YYNOCODE ); assert( iLookAhead < YYNTOKEN ); i += iLookAhead;
diff --git a/third_party/sqlite/src/tool/mkopts.tcl b/third_party/sqlite/src/tool/mkopts.tcl index e3ddcb9..88f645bb 100644 --- a/third_party/sqlite/src/tool/mkopts.tcl +++ b/third_party/sqlite/src/tool/mkopts.tcl
@@ -11,7 +11,7 @@ if {$line==""} continue regsub -all "\[ \t\n,\]+" [string trim $line] { } line foreach token [split $line { }] { - if {![regexp {(([a-zA-Z]+)_)?([_a-zA-Z]+)} $token all px p2 name]} continue + if {![regexp {(([a-zA-Z]+)_)?([_a-zA-Z0-9]+)} $token all px p2 name]} continue lappend namelist [string tolower $name] if {$px!=""} {set prefix $p2} } @@ -23,7 +23,7 @@ global col if {$col==0} {puts -nonewline " "} if {$col<2} { - puts -nonewline [format " %-21s" $x] + puts -nonewline [format " %-25s" $x] incr col } else { puts $x
diff --git a/third_party/sqlite/src/tool/mksqlite3c.tcl b/third_party/sqlite/src/tool/mksqlite3c.tcl index 8ea3e81c..f2d93aa 100644 --- a/third_party/sqlite/src/tool/mksqlite3c.tcl +++ b/third_party/sqlite/src/tool/mksqlite3c.tcl
@@ -320,6 +320,7 @@ os_unix.c os_win.c + memdb.c bitvec.c pcache.c
diff --git a/third_party/sqlite/src/tool/mksqlite3h.tcl b/third_party/sqlite/src/tool/mksqlite3h.tcl index 5b4c48b..216bd4e 100644 --- a/third_party/sqlite/src/tool/mksqlite3h.tcl +++ b/third_party/sqlite/src/tool/mksqlite3h.tcl
@@ -72,6 +72,9 @@ set declpattern4 \ {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3changegroup_[_a-zA-Z0-9]+)(\(.*)$} +set declpattern5 \ + {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3rebaser_[_a-zA-Z0-9]+)(\(.*)$} + # Force the output to use unix line endings, even on Windows. fconfigure stdout -translation lf @@ -121,7 +124,8 @@ if {[regexp $declpattern1 $line all rettype funcname rest] || \ [regexp $declpattern2 $line all rettype funcname rest] || \ [regexp $declpattern3 $line all rettype funcname rest] || \ - [regexp $declpattern4 $line all rettype funcname rest]} { + [regexp $declpattern4 $line all rettype funcname rest] || \ + [regexp $declpattern5 $line all rettype funcname rest]} { set line SQLITE_API append line " " [string trim $rettype] if {[string index $rettype end] ne "*"} {
diff --git a/third_party/sqlite/src/tool/speed-check.sh b/third_party/sqlite/src/tool/speed-check.sh index 1f5b41db..c54d10b 100644 --- a/third_party/sqlite/src/tool/speed-check.sh +++ b/third_party/sqlite/src/tool/speed-check.sh
@@ -39,6 +39,8 @@ BASELINE="trunk" doExplain=0 doCachegrind=1 +doVdbeProfile=0 +doWal=1 while test "$1" != ""; do case $1 in --reprepare) @@ -62,8 +64,11 @@ --temp) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --temp 6" ;; + --legacy) + doWal=0 + ;; --wal) - SPEEDTEST_OPTS="$SPEEDTEST_OPTS --journal wal" + doWal=1 ;; --size) shift; SIZE=$1 @@ -78,6 +83,7 @@ rm -f vdbe_profile.out CC_OPTS="$CC_OPTS -DVDBE_PROFILE" doCachegrind=0 + doVdbeProfile=1 ;; --lean) CC_OPTS="$CC_OPTS $LEAN_OPTS" @@ -132,11 +138,17 @@ esac shift done +if test $doWal -eq 1; then + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --journal wal" +fi SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE" echo "NAME = $NAME" | tee summary-$NAME.txt echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o +if test $doVdbeProfile -eq 1; then + rm -f vdbe_profile.out +fi $CC -g -Os -Wall -I. $CC_OPTS -c sqlite3.c size sqlite3.o | tee -a summary-$NAME.txt if test $doExplain -eq 1; then @@ -163,6 +175,10 @@ if test $doExplain -eq 1; then ./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt fi -if test "$NAME" != "$BASELINE"; then +if test $doVdbeProfile -eq 1; then + tclsh ../sqlite/tool/vdbe_profile.tcl >vdbeprofile-$NAME.txt + open vdbeprofile-$NAME.txt +fi +if test "$NAME" != "$BASELINE" -a $doVdbeProfile -ne 1; then fossil test-diff --tk -c 20 cout-$BASELINE.txt cout-$NAME.txt fi
diff --git a/tools/clang/plugins/CMakeLists.txt b/tools/clang/plugins/CMakeLists.txt index 6be8b2eb..bd76ad84 100644 --- a/tools/clang/plugins/CMakeLists.txt +++ b/tools/clang/plugins/CMakeLists.txt
@@ -2,7 +2,9 @@ ChromeClassTester.cpp FindBadConstructsAction.cpp FindBadConstructsConsumer.cpp - CheckIPCVisitor.cpp) + CheckIPCVisitor.cpp + Util.cpp +) if(WIN32) # Clang doesn't support loadable modules on Windows. Unfortunately, building @@ -23,7 +25,7 @@ cr_add_test(plugins_test python tests/test.py ${CMAKE_BINARY_DIR}/bin/clang - ) + ) else() add_llvm_loadable_module(libFindBadConstructs ${plugin_sources}) add_dependencies(libFindBadConstructs clang) @@ -34,5 +36,5 @@ python tests/test.py ${CMAKE_BINARY_DIR}/bin/clang $<TARGET_FILE:libFindBadConstructs> - ) + ) endif()
diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp index 10575353..ecc3e82 100644 --- a/tools/clang/plugins/ChromeClassTester.cpp +++ b/tools/clang/plugins/ChromeClassTester.cpp
@@ -9,6 +9,7 @@ #include <algorithm> +#include "Util.h" #include "clang/AST/AST.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -63,8 +64,8 @@ // We ignore all classes that end with "Matcher" because they're probably // GMock artifacts. - if (ends_with(base_name, "Matcher")) - return; + if (!options_.check_gmock_objects && ends_with(base_name, "Matcher")) + return; CheckChromeClass(location_type, location, record); } @@ -103,12 +104,9 @@ if (filename.find("/gen/") != std::string::npos) return LocationType::kThirdParty; - // TODO(dcheng, tkent): The WebKit directory is being renamed to Blink. Clean - // this up once the rename is done. - if (filename.find("/third_party/WebKit/") != std::string::npos || - (filename.find("/third_party/blink/") != std::string::npos && - // Browser-side code should always use the full range of checks. - filename.find("/third_party/blink/browser/") == std::string::npos)) { + if (filename.find("/third_party/blink/") != std::string::npos && + // Browser-side code should always use the full range of checks. + filename.find("/third_party/blink/browser/") == std::string::npos) { return LocationType::kBlink; } @@ -125,10 +123,6 @@ return LocationType::kChrome; } -std::string ChromeClassTester::GetNamespace(const Decl* record) { - return GetNamespaceImpl(record->getDeclContext(), std::string()); -} - bool ChromeClassTester::HasIgnoredBases(const CXXRecordDecl* record) { for (const auto& base : record->bases()) { CXXRecordDecl* base_record = base.getType()->getAsCXXRecordDecl(); @@ -212,29 +206,6 @@ ignored_base_classes_.emplace("IPC::NoParams"); } -std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context, - const std::string& candidate) { - switch (context->getDeclKind()) { - case Decl::TranslationUnit: { - return candidate; - } - case Decl::Namespace: { - const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context); - std::string name_str; - llvm::raw_string_ostream OS(name_str); - if (decl->isAnonymousNamespace()) - OS << "<anonymous namespace>"; - else - OS << *decl; - return GetNamespaceImpl(context->getParent(), - OS.str()); - } - default: { - return GetNamespaceImpl(context->getParent(), candidate); - } - } -} - bool ChromeClassTester::IsIgnoredType(const std::string& base_name) { return ignored_record_names_.find(base_name) != ignored_record_names_.end(); }
diff --git a/tools/clang/plugins/ChromeClassTester.h b/tools/clang/plugins/ChromeClassTester.h index 63710fd..33234e1 100644 --- a/tools/clang/plugins/ChromeClassTester.h +++ b/tools/clang/plugins/ChromeClassTester.h
@@ -45,11 +45,6 @@ }; LocationType ClassifyLocation(clang::SourceLocation loc); - // Utility method for subclasses to determine the namespace of the - // specified record, if any. Unnamed namespaces will be identified as - // "<anonymous namespace>". - std::string GetNamespace(const clang::Decl* record); - // Utility method to check whether the given record has any of the ignored // base classes. bool HasIgnoredBases(const clang::CXXRecordDecl* record); @@ -72,8 +67,6 @@ // Utility methods used for filtering out non-chrome classes (and ones we // deliberately ignore) in HandleTagDeclDefinition(). - std::string GetNamespaceImpl(const clang::DeclContext* context, - const std::string& candidate); bool IsIgnoredType(const std::string& base_name); // Attempts to determine the filename for the given SourceLocation.
diff --git a/tools/clang/plugins/FindBadConstructsAction.cpp b/tools/clang/plugins/FindBadConstructsAction.cpp index 23066a0b..916f0de 100644 --- a/tools/clang/plugins/FindBadConstructsAction.cpp +++ b/tools/clang/plugins/FindBadConstructsAction.cpp
@@ -48,16 +48,13 @@ // TODO(rsleevi): Remove this once http://crbug.com/123295 is fixed. options_.check_base_classes = true; } else if (args[i] == "enforce-in-thirdparty-webkit") { - options_.enforce_in_thirdparty_webkit = true; + // TODO(dcheng): Remove completely. } else if (args[i] == "check-enum-max-value") { - // TODO(tsepez): Enable this by default once http://crbug.com/356815 - // and http://crbug.com/356816 are fixed. - options_.check_enum_max_value = true; - } else if (args[i] == "no-realpath") { - // Ignored. - // TODO(mostynb@vewd.com): remove this completely. + // TODO(dcheng): Remove completely. } else if (args[i] == "check-ipc") { options_.check_ipc = true; + } else if (args[i] == "check-gmock-objects") { + options_.check_gmock_objects = true; } else { parsed = false; llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n";
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp index aaf6baa..5d430c38 100644 --- a/tools/clang/plugins/FindBadConstructsConsumer.cpp +++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp
@@ -4,8 +4,9 @@ #include "FindBadConstructsConsumer.h" -#include "clang/Frontend/CompilerInstance.h" +#include "Util.h" #include "clang/AST/Attr.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Lexer.h" #include "clang/Sema/Sema.h" #include "llvm/Support/raw_ostream.h" @@ -27,10 +28,47 @@ return type; } +bool InTestingNamespace(const Decl* record) { + return GetNamespace(record).find("testing") != std::string::npos; +} + bool IsGtestTestFixture(const CXXRecordDecl* decl) { return decl->getQualifiedNameAsString() == "testing::Test"; } +bool IsMethodInTestingNamespace(const CXXMethodDecl* method) { + for (auto* overridden : method->overridden_methods()) { + if (IsMethodInTestingNamespace(overridden) || + // Provide an exception for ::testing::Test. gtest itself uses some + // magic to try to make sure SetUp()/TearDown() aren't capitalized + // incorrectly, but having the plugin enforce override is also nice. + (InTestingNamespace(overridden) && + !IsGtestTestFixture(overridden->getParent()))) { + return true; + } + } + + return false; +} + +bool IsGmockObject(const CXXRecordDecl* decl) { + // If |record| has member variables whose types are in the "testing" namespace + // (which is how gmock works behind the scenes), there's a really high chance + // that |record| is a gmock object. + for (auto* field : decl->fields()) { + CXXRecordDecl* record_type = field->getTypeSourceInfo() + ->getTypeLoc() + .getTypePtr() + ->getAsCXXRecordDecl(); + if (record_type) { + if (InTestingNamespace(record_type)) { + return true; + } + } + } + return false; +} + bool IsPodOrTemplateType(const CXXRecordDecl& record) { return record.isPOD() || record.getDescribedClassTemplate() || @@ -255,9 +293,6 @@ } void FindBadConstructsConsumer::CheckEnumMaxValue(EnumDecl* decl) { - if (!options_.check_enum_max_value) - return; - if (!decl->isScoped()) return; @@ -437,29 +472,6 @@ } } -bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) { - return GetNamespace(record).find("testing") != std::string::npos; -} - -bool FindBadConstructsConsumer::IsMethodInTestingNamespace( - const CXXMethodDecl* method) { - for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); - i != method->end_overridden_methods(); - ++i) { - const CXXMethodDecl* overridden = *i; - if (IsMethodInTestingNamespace(overridden) || - // Provide an exception for ::testing::Test. gtest itself uses some - // magic to try to make sure SetUp()/TearDown() aren't capitalized - // incorrectly, but having the plugin enforce override is also nice. - (InTestingNamespace(overridden) && - !IsGtestTestFixture(overridden->getParent()))) { - return true; - } - } - - return false; -} - SuppressibleDiagnosticBuilder FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored( SourceLocation loc, @@ -468,16 +480,13 @@ ClassifyLocation(instance().getSourceManager().getSpellingLoc(loc)); bool ignored = type == LocationType::kThirdParty; if (type == LocationType::kBlink) { - if (!options_.enforce_in_thirdparty_webkit) { - ignored = true; - } else if (diagnostic_id == diag_no_explicit_ctor_ || - diagnostic_id == diag_no_explicit_copy_ctor_ || - diagnostic_id == diag_inline_complex_ctor_ || - diagnostic_id == diag_no_explicit_dtor_ || - diagnostic_id == diag_inline_complex_dtor_ || - diagnostic_id == - diag_refcounted_with_protected_non_virtual_dtor_ || - diagnostic_id == diag_virtual_with_inline_body_) { + if (diagnostic_id == diag_no_explicit_ctor_ || + diagnostic_id == diag_no_explicit_copy_ctor_ || + diagnostic_id == diag_inline_complex_ctor_ || + diagnostic_id == diag_no_explicit_dtor_ || + diagnostic_id == diag_inline_complex_dtor_ || + diagnostic_id == diag_refcounted_with_protected_non_virtual_dtor_ || + diagnostic_id == diag_virtual_with_inline_body_) { // Certain checks are ignored in Blink for historical reasons. // TODO(dcheng): Make this list smaller. ignored = true; @@ -493,22 +502,10 @@ SourceLocation record_location, CXXRecordDecl* record, bool warn_on_inline_bodies) { - // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a - // trick to get around that. If a class has member variables whose types are - // in the "testing" namespace (which is how gmock works behind the scenes), - // there's a really high chance we won't care about these errors - for (CXXRecordDecl::field_iterator it = record->field_begin(); - it != record->field_end(); - ++it) { - CXXRecordDecl* record_type = it->getTypeSourceInfo() - ->getTypeLoc() - .getTypePtr() - ->getAsCXXRecordDecl(); - if (record_type) { - if (InTestingNamespace(record_type)) { - return; - } - } + if (IsGmockObject(record)) { + if (!options_.check_gmock_objects) + return; + warn_on_inline_bodies = false; } for (CXXRecordDecl::method_iterator it = record->method_begin(); @@ -771,7 +768,6 @@ bool FindBadConstructsConsumer::IsRefCounted( const CXXBaseSpecifier* base, CXXBasePath& path) { - FindBadConstructsConsumer* self = this; const TemplateSpecializationType* base_type = dyn_cast<TemplateSpecializationType>( UnwrapType(base->getType().getTypePtr())); @@ -789,7 +785,7 @@ // Check for both base::RefCounted and base::RefCountedThreadSafe. if (base_name.compare(0, 10, "RefCounted") == 0 && - self->GetNamespace(decl) == "base") { + GetNamespace(decl) == "base") { return true; } }
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.h b/tools/clang/plugins/FindBadConstructsConsumer.h index 9dbf19e..601d611d 100644 --- a/tools/clang/plugins/FindBadConstructsConsumer.h +++ b/tools/clang/plugins/FindBadConstructsConsumer.h
@@ -68,9 +68,6 @@ void CheckCtorDtorWeight(clang::SourceLocation record_location, clang::CXXRecordDecl* record); - bool InTestingNamespace(const clang::Decl* record); - bool IsMethodInTestingNamespace(const clang::CXXMethodDecl* method); - // Returns a diagnostic builder that only emits the diagnostic if the spelling // location (the actual characters that make up the token) is not in an // ignored file. This is useful for situations where the token might originate
diff --git a/tools/clang/plugins/Options.h b/tools/clang/plugins/Options.h index 44ae6ad..82e9dec 100644 --- a/tools/clang/plugins/Options.h +++ b/tools/clang/plugins/Options.h
@@ -9,9 +9,8 @@ struct Options { bool check_base_classes = false; - bool enforce_in_thirdparty_webkit = false; // Use in Blink code itself - bool check_enum_max_value = false; bool check_ipc = false; + bool check_gmock_objects = false; }; } // namespace chrome_checker
diff --git a/tools/clang/plugins/Util.cpp b/tools/clang/plugins/Util.cpp new file mode 100644 index 0000000..d4c3ac8 --- /dev/null +++ b/tools/clang/plugins/Util.cpp
@@ -0,0 +1,37 @@ +// Copyright 2018 The Chromium 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 "Util.h" + +#include "clang/AST/Decl.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +std::string GetNamespaceImpl(const clang::DeclContext* context, + const std::string& candidate) { + switch (context->getDeclKind()) { + case clang::Decl::TranslationUnit: { + return candidate; + } + case clang::Decl::Namespace: { + const auto* decl = llvm::dyn_cast<clang::NamespaceDecl>(context); + std::string name_str; + llvm::raw_string_ostream OS(name_str); + if (decl->isAnonymousNamespace()) + OS << "<anonymous namespace>"; + else + OS << *decl; + return GetNamespaceImpl(context->getParent(), OS.str()); + } + default: { return GetNamespaceImpl(context->getParent(), candidate); } + } +} + +} // namespace + +std::string GetNamespace(const clang::Decl* record) { + return GetNamespaceImpl(record->getDeclContext(), std::string()); +}
diff --git a/tools/clang/plugins/Util.h b/tools/clang/plugins/Util.h new file mode 100644 index 0000000..f9860b6 --- /dev/null +++ b/tools/clang/plugins/Util.h
@@ -0,0 +1,17 @@ +// Copyright 2018 The Chromium 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 TOOLS_CLANG_PLUGINS_UTIL_H_ +#define TOOLS_CLANG_PLUGINS_UTIL_H_ + +#include <string> + +#include "clang/AST/DeclBase.h" + +// Utility method for subclasses to determine the namespace of the +// specified record, if any. Unnamed namespaces will be identified as +// "<anonymous namespace>". +std::string GetNamespace(const clang::Decl* record); + +#endif // TOOLS_CLANG_PLUGINS_UTIL_H_
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index b85833b..cffb861b 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -114,10 +114,10 @@ 'CrWinAsan(dll)': 'asan_clang_shared_v8_heap_x86_full_symbols_release', 'CrWinAsanCov': 'asan_clang_edge_fuzzer_static_v8_heap_x86_full_symbols_release', - 'CrWinClangLLD': 'clang_tot_official_static_use_lld_x86', - 'CrWinClangLLD64': 'clang_tot_shared_release_use_lld_dcheck', - 'CrWinClngLLD64dbg': 'clang_tot_full_symbols_shared_debug_use_lld', - 'CrWinClngLLDdbg': 'clang_tot_full_symbols_shared_debug_use_lld_x86', + 'CrWinClangLLD': 'clang_tot_official_static_no_lld_x86', + 'CrWinClangLLD64': 'clang_tot_shared_release_no_lld_dcheck', + 'CrWinClngLLD64dbg': 'clang_tot_full_symbols_shared_debug_no_lld', + 'CrWinClngLLDdbg': 'clang_tot_full_symbols_shared_debug_no_lld_x86', 'linux-win_cross-rel': 'clang_tot_win_release_cross', 'ToTAndroid': 'android_clang_tot_release_minimal_symbols', 'ToTAndroid64': 'android_clang_tot_release_arm64', @@ -383,7 +383,7 @@ 'chromium.memory': { 'Android CFI': 'android_cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on_goma', 'Linux ASan LSan Builder': 'asan_lsan_release_trybot', - 'Linux CFI': 'cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on_goma', + 'Linux CFI': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_goma', 'Linux Chromium OS ASan LSan Builder': 'asan_lsan_chromeos_release_trybot', 'Linux ChromiumOS MSan Builder': 'chromeos_msan_release_bot', 'Linux MSan Builder': 'msan_release_bot', @@ -616,7 +616,7 @@ 'linux_chromium_archive_rel_ng': 'release_bot', 'linux_chromium_asan_rel_ng': 'asan_lsan_release_trybot', 'linux_chromium_browser_side_navigation_rel': 'release_trybot', - 'linux_chromium_cfi_rel_ng': 'cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on_goma', + 'linux_chromium_cfi_rel_ng': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_goma', 'linux_chromium_chromeos_asan_rel_ng': 'asan_lsan_chromeos_release_trybot', 'linux_chromium_chromeos_msan_rel_ng': 'chromeos_msan_release_bot', 'linux_chromium_clobber_deterministic': 'release_trybot', @@ -1058,8 +1058,8 @@ 'cfi_full', 'cfi_icall', 'cfi_diag', 'cfi_recover', 'thin_lto', 'release', 'static', ], - 'cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on_goma': [ - 'cfi_full', 'cfi_diag', 'thin_lto', 'release', 'static', 'dcheck_always_on', 'goma', + 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_goma': [ + 'cfi_full', 'cfi_icall', 'cfi_diag', 'thin_lto', 'release', 'static', 'dcheck_always_on', 'goma', ], 'cfi_full_diag_icall_release_static_dcheck_always_on': [ @@ -1124,24 +1124,24 @@ 'clang_tot', 'shared', 'debug', ], - 'clang_tot_full_symbols_shared_debug_use_lld': [ - 'clang_tot', 'full_symbols', 'shared', 'debug', 'use_lld', + 'clang_tot_full_symbols_shared_debug_no_lld': [ + 'clang_tot', 'full_symbols', 'shared', 'debug', 'no_lld', ], - 'clang_tot_full_symbols_shared_debug_use_lld_x86': [ - 'clang_tot', 'full_symbols', 'shared', 'debug', 'use_lld', 'x86', + 'clang_tot_full_symbols_shared_debug_no_lld_x86': [ + 'clang_tot', 'full_symbols', 'shared', 'debug', 'no_lld', 'x86', ], 'clang_tot_shared_debug_x86': [ 'clang_tot', 'shared', 'debug', 'x86', ], - 'clang_tot_shared_release_use_lld_dcheck': [ - 'clang_tot', 'minimal_symbols', 'shared', 'release', 'use_lld', 'dcheck_always_on', + 'clang_tot_shared_release_no_lld_dcheck': [ + 'clang_tot', 'minimal_symbols', 'shared', 'release', 'no_lld', 'dcheck_always_on', ], - 'clang_tot_official_static_use_lld_x86': [ - 'clang_tot', 'minimal_symbols', 'official', 'static', 'release', 'use_lld', 'x86', + 'clang_tot_official_static_no_lld_x86': [ + 'clang_tot', 'minimal_symbols', 'official', 'static', 'release', 'no_lld', 'x86', ], 'clang_tot_minimal_symbols_shared_release': [ @@ -2085,6 +2085,10 @@ 'gn_args': 'use_lld=true', }, + 'no_lld': { + 'gn_args': 'use_lld=false', + }, + 'use_vaapi': { 'gn_args': 'use_vaapi=true', },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 3399ea8..a3cb4fe 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -1560,6 +1560,15 @@ <int value="5" label="INSUFFICIENT_RESOURCES"/> </enum> +<enum name="AshNightLightDisplayCrtcCtmSupportType"> + <summary> + Defines the possible displays' color transform matrix support types + </summary> + <int value="0" label="No display support"/> + <int value="1" label="Mixed display support"/> + <int value="2" label="All displays support"/> +</enum> + <enum name="AshNightLightScheduleType"> <summary>Defines the possible Night Light schedule types</summary> <int value="0" label="None"/> @@ -39614,6 +39623,7 @@ <int value="25" label="REMOTE_FILE"/> <int value="26" label="SAMPLED_UNSUPPORTED_FILE"/> <int value="27" label="VERDICT_UNKNOWN"/> + <int value="28" label="DOWNLOAD_DESTROYED"/> </enum> <enum name="SBClientDownloadCheckResult"> @@ -45991,6 +46001,9 @@ <int value="4" label="Seed Corrupt Base64 Data"/> <int value="5" label="Seed Corrupt Protobuf"/> <int value="6" label="Seed Corrupt Gzip Data"/> + <int value="7" label="Seed Load Timed Out"/> + <int value="8" label="Seed Load Interrupted"/> + <int value="9" label="Seed Load Other Failure"/> </enum> <enum name="VariationsSeedStoreResult">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 0fe593ce..c782f20b 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -3691,6 +3691,17 @@ </summary> </histogram> +<histogram name="Ash.NightLight.DisplayCrtcCtmSupport" + enum="AshNightLightDisplayCrtcCtmSupportType"> + <owner>afakhry@chromium.org</owner> + <summary> + The type of CRTC color transorm matrix support for the currently connected + displays. Emitted when cursor compositing status is refreshed, but only when + Night Light is on. The cursor compositing status is refreshed whenever there + is a change in Night Light status or color temperature. + </summary> +</histogram> + <histogram name="Ash.NightLight.ScheduleType" enum="AshNightLightScheduleType"> <owner>afakhry@chromium.org</owner> <summary> @@ -9339,8 +9350,9 @@ <histogram name="BrowserWindow.Resize.Duration" units="ms"> <owner>sadrul@chromium.org</owner> + <owner>mustash-team@google.com</owner> <summary> - Duration of an interactive resize from start to end. Measure only on + Duration of an interactive resize from start to end. Measured only on Windows. </summary> </histogram> @@ -9349,7 +9361,7 @@ <owner>sadrul@chromium.org</owner> <summary> Size changed between two consecutive steps during browser-window resize. - Measure only on Windows. + Measured only on Windows. </summary> </histogram> @@ -9357,7 +9369,7 @@ <owner>sadrul@chromium.org</owner> <summary> Number of intermediate resize-steps taken to complete the resize from start - to end. Measure only on Windows. + to end. Measured only on Windows. </summary> </histogram> @@ -100537,6 +100549,17 @@ </summary> </histogram> +<histogram name="Variations.SeedLoadBlockingTime" units="ms"> + <owner>paulmiller@chromium.org</owner> + <summary> + Records the time spent blocking WebView startup to wait for the variatinos + seed. This is less than the entire time needed to load the seed, since + startup is only blocked if loading is not complete by the time the seed is + needed. This is logged once per WebView startup, whether or not the load was + successful. + </summary> +</histogram> + <histogram name="Variations.SeedLoadResult" enum="VariationsSeedLoadResult"> <owner>asvitkine@chromium.org</owner> <summary> @@ -106687,6 +106710,15 @@ </summary> </histogram> +<histogram name="Windows.NativeWindowVisibility" units="windows"> + <owner>fdoray@chromium.org</owner> + <summary> + The number of Chrome browser windows in a given visibility state. This is + computed using ComputeNativeWindowOcclusionStatus() and is recorded every 10 + minutes. + </summary> +</histogram> + <histogram name="Windows.ParentProcessNameHash" enum="ProcessNameHash"> <owner>wfh@chromium.org</owner> <summary> @@ -111721,6 +111753,17 @@ name="Net.ResourceDispatcherHost.PeakOutstandingRequests"/> </histogram_suffixes> +<histogram_suffixes name="NativeWindowVisibility" separator="."> + <suffix name="Hidden" + label="The native window is not visible because it is in a minimized + window"/> + <suffix name="Occluded" + label="The native window is fully covered by other windows"/> + <suffix name="Visible" + label="The native window is visible or partially visible."/> + <affected-histogram name="Windows.NativeWindowVisibility"/> +</histogram_suffixes> + <histogram_suffixes name="NatType" separator="."> <suffix name="NoNAT"/> <suffix name="NonSymNAT"/>
diff --git a/tools/metrics/histograms/pretty_print.py b/tools/metrics/histograms/pretty_print.py index 05d68a3..3cbae2dd 100755 --- a/tools/metrics/histograms/pretty_print.py +++ b/tools/metrics/histograms/pretty_print.py
@@ -85,6 +85,19 @@ The pretty-printed version. """ tree = xml.dom.minidom.parseString(raw_xml) + return PrettyPrintHistogramsTree(tree) + + +def PrettyPrintHistogramsTree(tree): + """Pretty-print the given xml.dom.minidom.Document object. + + Args: + tree: The xml.dom.minidom.Document object. + + Returns: + The pretty-printed version as an XML string. + """ + assert isinstance(tree, xml.dom.minidom.Document) # Prevent accidentally adding enums to histograms.xml DropNodesByTagName(tree, 'enums') canonicalizeUnits(tree)
diff --git a/tools/metrics/md2xml.py b/tools/metrics/md2xml.py new file mode 100755 index 0000000..78fd4b7 --- /dev/null +++ b/tools/metrics/md2xml.py
@@ -0,0 +1,190 @@ +#!/usr/bin/env python +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Parses a markdown file, extracts documentation for UMA metrics from the doc, +and writes that into histograms.xml file. + +The syntax for the markdown this script processes is as follows: + . The first line for each UMA metric should be: '## [metric name]'. + . The following lines should include the additional information about the + metric, in a markdown list, in '[name]: [value]' format. For example: + + * units: pixels + * owners: first@chromium.org, second@example.com + + . The description, and explanation, of the metric should be after an empty + line after the list of attributes. + . Each UMA metric section should end with a line '---'. If there are non-UMA + sections at the beginning of the doc, then the first UMA section should be + preceeded by a '---' line. + +A complete example: + +=== sample.md +# A sample markdown document. +This is a sample markdown. It has some documentation for UMA metrics too. + +# Motivation +The purpose of this sample doc is to be a guide for writing such docs. + +--- +## ExampleMetric.First +* units: smiles +* owners: firstowner@chromium.org, second@example.org +* os: windows, mac +* added: 2018-03-01 +* expires: 2023-01-01 + +ExampleMetric.First measures the first example. +--- +## ExampleMetric.Second +* units: happiness + +This measures the second example. + +""" + +import datetime +import os +import re +import sys +import time +import xml.dom.minidom + +sys.path.append(os.path.join(os.path.dirname(__file__), 'common')) +import path_util + +sys.path.append(os.path.join(os.path.dirname(__file__), 'histograms')) +import pretty_print + + +SupportedTags = [ + "added", + "expires", + "enum", + "os", + "owners", + "tags", + "units", +] + +def IsTagKnown(tag): + return tag in SupportedTags + + +def IsTagValid(tag, value): + assert IsTagKnown(tag) + if tag == 'added' or tag == 'expires': + if re.match('^M[0-9]{2,3}$', value): + return True + date = re.match('^([0-9]{4})-([0-9]{2})-([0-9]{2})$', value) + return date and datetime.date(int(date.group(1)), int(date.group(2)), + int(date.group(3))) + return True + + +class Trace: + def __init__(self, msg): + self.msg_ = msg + self.start_ = None + + def __enter__(self): + self.start_ = time.time() + sys.stdout.write('%s ...' % (self.msg_)) + sys.stdout.flush() + + def __exit__(self, exc_type, exc_val, exc_tb): + sys.stdout.write(' Done (%.3f sec)\n' % (time.time() - self.start_)) + + +def GetMetricsFromMdFile(mdfile): + """Returns an array of metrics parsed from the markdown file. See the top of + the file for documentation on the format of the markdown file. + """ + with open(mdfile) as f: + raw_md = f.read() + metrics = [] + sections = re.split('\n---+\n', raw_md) + tag_pattern = re.compile('^\* ([^:]*): (.*)$') + for section in sections: + if len(section.strip()) == 0: break + lines = section.strip().split('\n') + # The first line should have the header, containing the name of the metric. + header_match = re.match('^##+ ', lines[0]) + if not header_match: continue + metric = {} + metric['name'] = lines[0][len(header_match.group(0)):] + for i in range(1, len(lines)): + if len(lines[i]) == 0: + i += 1 + break + match = tag_pattern.match(lines[i]) + assert match + assert IsTagKnown(match.group(1)), 'Unknown tag: "%s".' % (match.group(1)) + assert IsTagValid(match.group(1), match.group(2)), 'Invalid value "%s" ' \ + 'for tag "%s".' % (match.group(2), match.group(1)) + metric[match.group(1)] = match.group(2) + assert i < len(lines), 'No summary found for "%s"' % metric['name'] + metric['summary'] = '\n'.join(lines[i:]) + assert 'owners' in metric, 'Must have owners for "%s"' % metric['name'] + assert 'enum' in metric or 'units' in metric, 'Metric "%s" must have ' \ + 'a unit listed in "enum" or "units".' % metric['name'] + metrics.append(metric) + return metrics + + +def CreateNode(tree, tag, text): + node = tree.createElement(tag) + node.appendChild(tree.createTextNode(text)) + return node + + +def main(): + if len(sys.argv) != 2: + sys.stderr.write('Usage: %s <path-to-md-file>\n' % (sys.argv[0])) + sys.exit(1) + + with Trace('Reading histograms.xml') as t: + xml_path = path_util.GetHistogramsFile() + with open(xml_path, 'rb') as f: + raw_xml = f.read() + + with Trace('Parsing xml') as t: + tree = xml.dom.minidom.parseString(raw_xml) + histograms = tree.getElementsByTagName('histograms') + if histograms.length != 1: + sys.stderr.write('histograms.xml should have exactly one "histograms" ' + 'section.\n'); + sys.exit(1) + histograms = histograms[0] + + with Trace('Parsing md file %s' % (sys.argv[1])) as t: + metrics = GetMetricsFromMdFile(sys.argv[1]) + + with Trace('Adding parsed metrics') as t: + for metric in metrics: + node = tree.createElement('histogram') + node.setAttribute('name', metric['name']) + if 'units' in metric: + node.setAttribute('units', metric['units']) + elif 'enum' in metric: + node.setAttribute('enum', metric['enum']) + owners = metric['owners'].split(',') + for owner in owners: + node.appendChild(CreateNode(tree, 'owner', owner)) + node.appendChild(CreateNode(tree, 'summary', metric['summary'])) + # TODO(sad): This always appends the metric to the list. This should + # also update if there is an already existing metric, instead of adding a + # new one. + histograms.appendChild(node) + + with Trace('Pretty printing into histograms.xml') as t: + new_xml = pretty_print.PrettyPrintHistogramsTree(tree) + with open(xml_path, 'wb') as f: + f.write(new_xml) + + +if __name__ == '__main__': + main()
diff --git a/tools/origin_trials/OWNERS b/tools/origin_trials/OWNERS index 0d67a41..73686a7 100644 --- a/tools/origin_trials/OWNERS +++ b/tools/origin_trials/OWNERS
@@ -1 +1 @@ -file://content/common/origin_trials/OWNERS +file://third_party/blink/common/origin_trials/OWNERS
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.h b/ui/accelerated_widget_mac/accelerated_widget_mac.h index 0a20628d..aea9a404 100644 --- a/ui/accelerated_widget_mac/accelerated_widget_mac.h +++ b/ui/accelerated_widget_mac/accelerated_widget_mac.h
@@ -32,13 +32,6 @@ // NSView. virtual NSView* AcceleratedWidgetGetNSView() const = 0; - // Retrieve the vsync parameters for the monitor on which the NSView currently - // is being displayed. - // TODO(ccameron): This is not the appropriate place for this function. A - // helper library to query monitor vsync parameters should be added. - virtual void AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const = 0; - // Called on swap completion. This is used to update background colors and to // suppressing drawing of blank windows until content is available. virtual void AcceleratedWidgetSwapCompleted() = 0; @@ -87,8 +80,6 @@ // gfx::CALayerFrameSink implementation: void SetSuspended(bool suspended) override; void UpdateCALayerTree(const gfx::CALayerParams& ca_layer_params) override; - void GetVSyncParameters(base::TimeTicks* timebase, - base::TimeDelta* interval) const override; // The AcceleratedWidgetMacNSView that is using this as its internals. AcceleratedWidgetMacNSView* view_ = nullptr;
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.mm b/ui/accelerated_widget_mac/accelerated_widget_mac.mm index 97e7b50..f178b4d3 100644 --- a/ui/accelerated_widget_mac/accelerated_widget_mac.mm +++ b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
@@ -108,16 +108,6 @@ return last_swap_size_dip_ == dip_size; } -void AcceleratedWidgetMac::GetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const { - if (view_) { - view_->AcceleratedWidgetGetVSyncParameters(timebase, interval); - } else { - *timebase = base::TimeTicks(); - *interval = base::TimeDelta(); - } -} - // static AcceleratedWidgetMac* AcceleratedWidgetMac::Get(gfx::AcceleratedWidget widget) { WidgetToHelperMap::const_iterator found =
diff --git a/ui/accelerated_widget_mac/ca_layer_frame_sink.h b/ui/accelerated_widget_mac/ca_layer_frame_sink.h index ed5e09e..8cab65b5 100644 --- a/ui/accelerated_widget_mac/ca_layer_frame_sink.h +++ b/ui/accelerated_widget_mac/ca_layer_frame_sink.h
@@ -24,11 +24,6 @@ // |ca_layer_params|. virtual void UpdateCALayerTree(const gfx::CALayerParams& ca_layer_params) = 0; - // Retrieve the vsync parameters for the monitor on which the embedder is - // currently displayed. - virtual void GetVSyncParameters(base::TimeTicks* timebase, - base::TimeDelta* interval) const = 0; - // If suspended, the embedder will ignore updates until un-supended. virtual void SetSuspended(bool suspended) = 0; };
diff --git a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java index c647328..68caaab 100644 --- a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java +++ b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
@@ -82,6 +82,7 @@ mAnchoredPopupWindow.setPreferredHorizontalOrientation( AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER); mAnchoredPopupWindow.setUpdateOrientationOnChange(true); + mAnchoredPopupWindow.setOutsideTouchable(true); } /**
diff --git a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java index 792e6b12..d4f22454 100644 --- a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java +++ b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
@@ -248,6 +248,19 @@ } /** + * If set to true, popup will be notified when an outside touch happens. + * It is not the equivalent of closing the popup on all touch events. The user can + * still interact with the popup by sending inside touch events. + * If set to false, the popup won't be notified about the outside touch event. + * + * @param touchable Whether or not to notify the popup when an outside touch + * happens. The default is {@code false}. + */ + public void setOutsideTouchable(boolean touchable) { + mPopupWindow.setOutsideTouchable(touchable); + } + + /** * Sets the preferred vertical orientation of the popup with respect to the anchor Rect such as * above or below the anchor. This should be called before the popup is shown. * @param orientation The vertical orientation preferred.
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn index d53dc711..db86169 100644 --- a/ui/app_list/BUILD.gn +++ b/ui/app_list/BUILD.gn
@@ -48,8 +48,6 @@ "views/apps_grid_view.cc", "views/apps_grid_view.h", "views/apps_grid_view_folder_delegate.h", - "views/assistant_bubble_view.cc", - "views/assistant_bubble_view.h", "views/assistant_container_view.cc", "views/assistant_container_view.h", "views/contents_view.cc",
diff --git a/ui/app_list/views/assistant_container_view.cc b/ui/app_list/views/assistant_container_view.cc index c5d19fd0..3b11ce5 100644 --- a/ui/app_list/views/assistant_container_view.cc +++ b/ui/app_list/views/assistant_container_view.cc
@@ -6,23 +6,12 @@ #include "ui/app_list/app_list_constants.h" #include "ui/app_list/views/app_list_view.h" -#include "ui/app_list/views/assistant_bubble_view.h" #include "ui/app_list/views/contents_view.h" namespace app_list { -namespace { - -constexpr int kPaddingDip = 16; - -} // namespace - AssistantContainerView::AssistantContainerView(ContentsView* contents_view) - : contents_view_(contents_view), - assistant_bubble_view_(new AssistantBubbleView( - contents_view->app_list_view()->assistant_controller())) { - AddChildView(assistant_bubble_view_); -} + : contents_view_(contents_view) {} gfx::Size AssistantContainerView::CalculatePreferredSize() const { if (!GetWidget()) { @@ -32,20 +21,6 @@ contents_view_->GetDisplayHeight()); } -void AssistantContainerView::ChildPreferredSizeChanged(views::View* child) { - // TODO(dmblack): Animate layout changes. - Layout(); - SchedulePaint(); -} - -void AssistantContainerView::Layout() { - // Pin bubble view to bottom lefthand corner. - gfx::Size size = assistant_bubble_view_->GetPreferredSize(); - assistant_bubble_view_->SetBounds(kPaddingDip, - height() - size.height() - kPaddingDip, - size.width(), size.height()); -} - bool AssistantContainerView::ShouldShowSearchBox() const { return false; }
diff --git a/ui/app_list/views/assistant_container_view.h b/ui/app_list/views/assistant_container_view.h index dc2818c..cfcc455 100644 --- a/ui/app_list/views/assistant_container_view.h +++ b/ui/app_list/views/assistant_container_view.h
@@ -10,7 +10,6 @@ namespace app_list { -class AssistantBubbleView; class ContentsView; class APP_LIST_EXPORT AssistantContainerView : public HorizontalPage { @@ -20,15 +19,12 @@ // Overridden from views::View. gfx::Size CalculatePreferredSize() const override; - void ChildPreferredSizeChanged(views::View* child) override; - void Layout() override; // Overridden from HorizontalPage. bool ShouldShowSearchBox() const override; private: ContentsView* const contents_view_; // Not owned. - AssistantBubbleView* assistant_bubble_view_; // Owned by view hierarchy. DISALLOW_COPY_AND_ASSIGN(AssistantContainerView); };
diff --git a/ui/app_list/views/suggestion_chip_view.h b/ui/app_list/views/suggestion_chip_view.h index 872c653..758f3fd 100644 --- a/ui/app_list/views/suggestion_chip_view.h +++ b/ui/app_list/views/suggestion_chip_view.h
@@ -6,6 +6,7 @@ #define UI_APP_LIST_VIEWS_SUGGESTION_CHIP_VIEW_H_ #include "base/macros.h" +#include "ui/app_list/app_list_export.h" #include "ui/views/view.h" namespace views { @@ -17,7 +18,7 @@ class SuggestionChipView; // Listener which receives notification of suggestion chip events. -class SuggestionChipListener { +class APP_LIST_EXPORT SuggestionChipListener { public: // Invoked when the specified |sender| is pressed. virtual void OnSuggestionChipPressed(SuggestionChipView* sender) = 0; @@ -27,7 +28,7 @@ }; // View representing a suggestion chip. -class SuggestionChipView : public views::View { +class APP_LIST_EXPORT SuggestionChipView : public views::View { public: SuggestionChipView(const base::string16& text, SuggestionChipListener* listener = nullptr);
diff --git a/ui/aura/mus/DEPS b/ui/aura/mus/DEPS index 8e72cf0..a79ec49 100644 --- a/ui/aura/mus/DEPS +++ b/ui/aura/mus/DEPS
@@ -1,6 +1,5 @@ include_rules = [ "+cc/base/switches.h", - "+cc/ipc/compositor_frame_sink.mojom.h", "+cc/trees/layer_tree_frame_sink_client.h", "+cc/trees/layer_tree_frame_sink.h", "+cc/scheduler/begin_frame_source.h",
diff --git a/ui/compositor/test/test_compositor_host_mac.mm b/ui/compositor/test/test_compositor_host_mac.mm index 6dc33799..11447a4 100644 --- a/ui/compositor/test/test_compositor_host_mac.mm +++ b/ui/compositor/test/test_compositor_host_mac.mm
@@ -84,9 +84,6 @@ // AcceleratedWidgetMacNSView NSView* AcceleratedWidgetGetNSView() const override { return view_; } - void AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, - base::TimeDelta* interval) const override {} void AcceleratedWidgetSwapCompleted() override {} private:
diff --git a/ui/latency/latency_tracker.cc b/ui/latency/latency_tracker.cc index 85e2592..ae305f7 100644 --- a/ui/latency/latency_tracker.cc +++ b/ui/latency/latency_tracker.cc
@@ -54,15 +54,12 @@ } // namespace -LatencyTracker::LatencyTracker(bool metric_sampling, - ukm::SourceId ukm_source_id) - : metric_sampling_(metric_sampling), ukm_source_id_(ukm_source_id) { -} +LatencyTracker::LatencyTracker() = default; -void LatencyTracker::OnEventStart(LatencyInfo* latency) { - static uint64_t global_trace_id = 0; - latency->set_trace_id(++global_trace_id); - latency->set_ukm_source_id(ukm_source_id_); +void LatencyTracker::OnGpuSwapBuffersCompleted( + const std::vector<ui::LatencyInfo>& latency_info) { + for (const auto& latency : latency_info) + OnGpuSwapBuffersCompleted(latency); } void LatencyTracker::OnGpuSwapBuffersCompleted(const LatencyInfo& latency) { @@ -104,6 +101,10 @@ } } +void LatencyTracker::DisableMetricSamplingForTesting() { + metric_sampling_ = false; +} + void LatencyTracker::ReportUkmScrollLatency( const InputMetricEvent& metric_event, const LatencyInfo::LatencyComponent& start_component,
diff --git a/ui/latency/latency_tracker.h b/ui/latency/latency_tracker.h index 24397fc..4c62633 100644 --- a/ui/latency/latency_tracker.h +++ b/ui/latency/latency_tracker.h
@@ -6,7 +6,6 @@ #define UI_LATENCY_LATENCY_TRACKER_H_ #include "base/macros.h" -#include "services/metrics/public/cpp/ukm_source_id.h" #include "ui/latency/latency_info.h" namespace ui { @@ -15,19 +14,17 @@ // components logged by content::RenderWidgetHostLatencyTracker. class LatencyTracker { public: - explicit LatencyTracker(bool metric_sampling, - ukm::SourceId ukm_source_id = ukm::kInvalidSourceId); + LatencyTracker(); ~LatencyTracker() = default; - void OnEventStart(LatencyInfo* latency); - // Terminates latency tracking for events that triggered rendering, also // performing relevant UMA latency reporting. // Called when GPU buffers swap completes. + void OnGpuSwapBuffersCompleted(const std::vector<LatencyInfo>& latency_info); void OnGpuSwapBuffersCompleted(const LatencyInfo& latency); - protected: - ukm::SourceId ukm_source_id() const { return ukm_source_id_; } + // Disables sampling of high volume metrics in unit tests. + void DisableMetricSamplingForTesting(); private: enum class InputMetricEvent { @@ -71,7 +68,8 @@ // Whether the sampling is needed for high volume metrics. This will be off // when we are in unit tests. This is a temporary field so we can come up with // a more permanent solution for crbug.com/739169. - bool metric_sampling_; + bool metric_sampling_ = true; + // The i'th member of this array stores the sampling rate for the i'th // input metric event type. Initializing SamplingScheme with number X means // that from every X events one will be reported. Note that the first event @@ -83,7 +81,6 @@ SamplingScheme(5), // SCROLL_BEGIN_WHEEL SamplingScheme(2), // SCROLL_UPDATE_WHEEL }; - const ukm::SourceId ukm_source_id_; DISALLOW_COPY_AND_ASSIGN(LatencyTracker); };
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js index 888275f..051216c 100644 --- a/ui/login/display_manager.js +++ b/ui/login/display_manager.js
@@ -199,7 +199,7 @@ * @const */ var DEMO_MODE_SETUP_AVAILABLE_SCREEN_GROUP = [ - SCREEN_GAIA_SIGNIN, + SCREEN_OOBE_NETWORK, ]; /**
diff --git a/ui/ozone/demo/gl_renderer.h b/ui/ozone/demo/gl_renderer.h index 6935f91..34bb9d0 100644 --- a/ui/ozone/demo/gl_renderer.h +++ b/ui/ozone/demo/gl_renderer.h
@@ -32,16 +32,14 @@ // Renderer: bool Initialize() override; - protected: - virtual void RenderFrame(); - virtual void PostRenderFrameTask(gfx::SwapResult result); + private: + void RenderFrame(); + void PostRenderFrameTask(gfx::SwapResult result); + void OnPresentation(const gfx::PresentationFeedback& feedback); scoped_refptr<gl::GLSurface> surface_; scoped_refptr<gl::GLContext> context_; - private: - void OnPresentation(const gfx::PresentationFeedback& feedback); - base::WeakPtrFactory<GlRenderer> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(GlRenderer);
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc index eee7388..f668669 100644 --- a/ui/ozone/demo/surfaceless_gl_renderer.cc +++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -18,6 +18,7 @@ #include "ui/gl/gl_image.h" #include "ui/gl/gl_image_native_pixmap.h" #include "ui/gl/gl_surface.h" +#include "ui/gl/init/gl_factory.h" #include "ui/ozone/public/overlay_candidates_ozone.h" #include "ui/ozone/public/overlay_manager_ozone.h" #include "ui/ozone/public/ozone_platform.h" @@ -116,10 +117,11 @@ gfx::AcceleratedWidget widget, const scoped_refptr<gl::GLSurface>& surface, const gfx::Size& size) - : GlRenderer(widget, surface, size), + : RendererBase(widget, size), overlay_checker_(ui::OzonePlatform::GetInstance() ->GetOverlayManager() ->CreateOverlayCandidates(widget)), + surface_(surface), weak_ptr_factory_(this) {} SurfacelessGlRenderer::~SurfacelessGlRenderer() { @@ -129,8 +131,19 @@ } bool SurfacelessGlRenderer::Initialize() { - if (!GlRenderer::Initialize()) + context_ = gl::init::CreateGLContext(nullptr, surface_.get(), + gl::GLContextAttribs()); + if (!context_.get()) { + LOG(ERROR) << "Failed to create GL context"; return false; + } + + surface_->Resize(size_, 1.f, gl::GLSurface::ColorSpace::UNSPECIFIED, true); + + if (!context_->MakeCurrent(surface_.get())) { + LOG(ERROR) << "Failed to make GL context current"; + return false; + } base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch("partial-primary-plane")) @@ -157,6 +170,9 @@ } disable_primary_plane_ = command_line->HasSwitch("disable-primary-plane"); + + // Schedule the initial render. + PostRenderFrameTask(gfx::SwapResult::SWAP_ACK); return true; } @@ -234,7 +250,9 @@ } FALLTHROUGH; // We want to render a new frame anyways. case gfx::SwapResult::SWAP_ACK: - GlRenderer::PostRenderFrameTask(result); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&SurfacelessGlRenderer::RenderFrame, + weak_ptr_factory_.GetWeakPtr())); break; case gfx::SwapResult::SWAP_FAILED: LOG(FATAL) << "Failed to swap buffers"; @@ -242,4 +260,10 @@ } } +void SurfacelessGlRenderer::OnPresentation( + const gfx::PresentationFeedback& feedback) { + DCHECK(surface_->SupportsPresentationCallback()); + LOG_IF(ERROR, feedback.timestamp.is_null()) << "Last frame is discarded!"; +} + } // namespace ui
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.h b/ui/ozone/demo/surfaceless_gl_renderer.h index bc346e74..d5f0c60 100644 --- a/ui/ozone/demo/surfaceless_gl_renderer.h +++ b/ui/ozone/demo/surfaceless_gl_renderer.h
@@ -18,7 +18,7 @@ namespace ui { class OverlayCandidatesOzone; -class SurfacelessGlRenderer : public GlRenderer { +class SurfacelessGlRenderer : public RendererBase { public: SurfacelessGlRenderer(gfx::AcceleratedWidget widget, const scoped_refptr<gl::GLSurface>& surface, @@ -29,9 +29,9 @@ bool Initialize() override; private: - // GlRenderer: - void RenderFrame() override; - void PostRenderFrameTask(gfx::SwapResult result) override; + void RenderFrame(); + void PostRenderFrameTask(gfx::SwapResult result); + void OnPresentation(const gfx::PresentationFeedback& feedback); class BufferWrapper { public: @@ -64,6 +64,9 @@ int back_buffer_ = 0; + scoped_refptr<gl::GLSurface> surface_; + scoped_refptr<gl::GLContext> context_; + base::WeakPtrFactory<SurfacelessGlRenderer> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(SurfacelessGlRenderer);
diff --git a/ui/views/cocoa/bridged_native_widget.h b/ui/views/cocoa/bridged_native_widget.h index 9af5c2db..83d31a7 100644 --- a/ui/views/cocoa/bridged_native_widget.h +++ b/ui/views/cocoa/bridged_native_widget.h
@@ -271,8 +271,6 @@ // Overridden from ui::AcceleratedWidgetMac: NSView* AcceleratedWidgetGetNSView() const override; - void AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const override; void AcceleratedWidgetSwapCompleted() override; // Overridden from BridgedNativeWidgetOwner:
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm index 195a7ce8..6406ac55 100644 --- a/ui/views/cocoa/bridged_native_widget.mm +++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -1036,13 +1036,6 @@ return compositor_superview_; } -void BridgedNativeWidget::AcceleratedWidgetGetVSyncParameters( - base::TimeTicks* timebase, base::TimeDelta* interval) const { - // TODO(tapted): Add vsync support. - *timebase = base::TimeTicks(); - *interval = base::TimeDelta(); -} - void BridgedNativeWidget::AcceleratedWidgetSwapCompleted() { // Ignore frames arriving "late" for an old size. A frame at the new size // should arrive soon.
diff --git a/ui/web_dialogs/test/test_web_contents_handler.cc b/ui/web_dialogs/test/test_web_contents_handler.cc index 7f8e5e5..62c1ed3 100644 --- a/ui/web_dialogs/test/test_web_contents_handler.cc +++ b/ui/web_dialogs/test/test_web_contents_handler.cc
@@ -4,6 +4,8 @@ #include "ui/web_dialogs/test/test_web_contents_handler.h" +#include "content/public/browser/web_contents.h" + namespace ui { namespace test { @@ -20,13 +22,13 @@ return NULL; } -void TestWebContentsHandler::AddNewContents(content::BrowserContext* context, - content::WebContents* source, - content::WebContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_rect, - bool user_gesture) { -} +void TestWebContentsHandler::AddNewContents( + content::BrowserContext* context, + content::WebContents* source, + std::unique_ptr<content::WebContents> new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) {} } // namespace test } // namespace ui
diff --git a/ui/web_dialogs/test/test_web_contents_handler.h b/ui/web_dialogs/test/test_web_contents_handler.h index 190c040..8f199627 100644 --- a/ui/web_dialogs/test/test_web_contents_handler.h +++ b/ui/web_dialogs/test/test_web_contents_handler.h
@@ -26,7 +26,7 @@ const content::OpenURLParams& params) override; void AddNewContents(content::BrowserContext* context, content::WebContents* source, - content::WebContents* new_contents, + std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture) override;
diff --git a/ui/web_dialogs/web_dialog_web_contents_delegate.cc b/ui/web_dialogs/web_dialog_web_contents_delegate.cc index 758974b6..08bf98a 100644 --- a/ui/web_dialogs/web_dialog_web_contents_delegate.cc +++ b/ui/web_dialogs/web_dialog_web_contents_delegate.cc
@@ -45,7 +45,10 @@ WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) { - handler_->AddNewContents(browser_context_, source, new_contents, disposition, + // TODO(erikchen): Refactor AddNewContents to take strong ownership semantics. + // https://crbug.com/832879. + handler_->AddNewContents(browser_context_, source, + base::WrapUnique(new_contents), disposition, initial_rect, user_gesture); }
diff --git a/ui/web_dialogs/web_dialog_web_contents_delegate.h b/ui/web_dialogs/web_dialog_web_contents_delegate.h index c9e04b5..a660e33f 100644 --- a/ui/web_dialogs/web_dialog_web_contents_delegate.h +++ b/ui/web_dialogs/web_dialog_web_contents_delegate.h
@@ -32,12 +32,13 @@ content::BrowserContext* context, content::WebContents* source, const content::OpenURLParams& params) = 0; - virtual void AddNewContents(content::BrowserContext* context, - content::WebContents* source, - content::WebContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_rect, - bool user_gesture) = 0; + virtual void AddNewContents( + content::BrowserContext* context, + content::WebContents* source, + std::unique_ptr<content::WebContents> new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) = 0; }; // context and handler must be non-NULL.
diff --git a/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn b/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn index f484f74..fb433a4d 100644 --- a/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn +++ b/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn
@@ -15,5 +15,4 @@ "//third_party/polymer/v1_0/components-chromium/iron-behaviors:iron-button-state-extracted", "//third_party/polymer/v1_0/components-chromium/paper-behaviors:paper-ripple-behavior-extracted", ] - externs_list = [ "$externs_path/pending.js" ] }
diff --git a/ui/webui/resources/cr_elements/cr_toggle/compiled_resources2.gyp b/ui/webui/resources/cr_elements/cr_toggle/compiled_resources2.gyp index 57b0399..04592082 100644 --- a/ui/webui/resources/cr_elements/cr_toggle/compiled_resources2.gyp +++ b/ui/webui/resources/cr_elements/cr_toggle/compiled_resources2.gyp
@@ -8,7 +8,6 @@ 'dependencies': [ '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-behaviors/compiled_resources2.gyp:iron-button-state-extracted', '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-behaviors/compiled_resources2.gyp:paper-ripple-behavior-extracted', - '<(EXTERNS_GYP):pending', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], },
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html index 4df0b9a..1f586eb 100644 --- a/ui/webui/resources/cr_elements/paper_button_style_css.html +++ b/ui/webui/resources/cr_elements/paper_button_style_css.html
@@ -28,7 +28,6 @@ border-radius: 4px; color: var(--text-or-bg-color); flex-shrink: 0; - font-size: 12px; font-weight: 500; height: 36px; margin: 0;
diff --git a/webrunner/common/webrunner_content_client.cc b/webrunner/common/webrunner_content_client.cc index 4c0bed4b..4f27078 100644 --- a/webrunner/common/webrunner_content_client.cc +++ b/webrunner/common/webrunner_content_client.cc
@@ -42,7 +42,7 @@ resource_id); } -content::OriginTrialPolicy* WebRunnerContentClient::GetOriginTrialPolicy() { +blink::OriginTrialPolicy* WebRunnerContentClient::GetOriginTrialPolicy() { NOTIMPLEMENTED(); return nullptr; }
diff --git a/webrunner/common/webrunner_content_client.h b/webrunner/common/webrunner_content_client.h index d44e9365..9f6ee97 100644 --- a/webrunner/common/webrunner_content_client.h +++ b/webrunner/common/webrunner_content_client.h
@@ -23,7 +23,7 @@ ui::ScaleFactor scale_factor) const override; base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; gfx::Image& GetNativeImageNamed(int resource_id) const override; - content::OriginTrialPolicy* GetOriginTrialPolicy() override; + blink::OriginTrialPolicy* GetOriginTrialPolicy() override; private: DISALLOW_COPY_AND_ASSIGN(WebRunnerContentClient);